@@ -76,6 +76,6 @@ // `insta env` — show or switch the deployment environment (prod | staging). | ||
| // files were written for the previous environment and are keyed by a different server name, so | ||
| // they keep talking to it until setup is re-run. (The installer path is fine — install.sh runs | ||
| // `env use` before `setup agent`.) | ||
| info(' re-point this machine\'s agents at it with: insta setup agent'); | ||
| // they keep talking to it until setup is re-run. --env is REQUIRED in the hint: since 0.0.38 a | ||
| // bare `setup agent` forces prod, which would silently undo the switch the user just made. | ||
| info(` re-point this machine's agents at it with: insta setup agent --env ${target}`); | ||
| } | ||
| //# sourceMappingURL=env.js.map |
@@ -40,3 +40,6 @@ // `insta feedback` — report an InstaCloud-side hurdle to the InstaCloud team. | ||
| const FEEDBACK_INGEST_TOKEN = process.env.INSTA_FEEDBACK_TOKEN || 'insta-feedback-public-v1'; | ||
| const FEEDBACK_TIMEOUT_MS = 10_000; | ||
| // 15s gives the backend's scale-to-zero cold start room to answer (the ingest service waits out | ||
| // the DB wake and persists, so a report can land after the old 10s deadline gave up on it). | ||
| // An expired deadline is reported as UNCONFIRMED, not failed — the report may well be stored. | ||
| const FEEDBACK_TIMEOUT_MS = 15_000; | ||
| const MAX_FILE_BYTES = 256 * 1024; | ||
@@ -182,4 +185,6 @@ function resolveCliVersion() { | ||
| catch (e) { | ||
| const timedOut = e instanceof Error && e.name === 'TimeoutError'; | ||
| return { status: 'error', error: timedOut ? `timed out after ${FEEDBACK_TIMEOUT_MS / 1000}s` : `network error: ${e instanceof Error ? e.message : String(e)}` }; | ||
| if (e instanceof Error && e.name === 'TimeoutError') { | ||
| return { status: 'unconfirmed', error: `no response after ${FEEDBACK_TIMEOUT_MS / 1000}s — the report may have been recorded anyway` }; | ||
| } | ||
| return { status: 'error', error: `network error: ${e instanceof Error ? e.message : String(e)}` }; | ||
| } | ||
@@ -216,2 +221,10 @@ let body = {}; | ||
| const result = await submit(payload, deps.fetchImpl ?? fetch); | ||
| if (result.status === 'unconfirmed') { | ||
| // NOT a failure claim: the request was still in flight at the deadline and the server | ||
| // finishes what it started, so saying "not submitted" here would be a false negative. | ||
| if (opts.json) | ||
| return printJson({ status: 'unconfirmed', error: result.error }); | ||
| process.stderr.write(`warning: feedback receipt unconfirmed (${result.error}) — continue with your task, do not retry\n`); | ||
| return; | ||
| } | ||
| if (result.status === 'error') { | ||
@@ -218,0 +231,0 @@ // Deliberate exit 0: an agent CANNOT fix a down/rate-limited backend, and feedback must never |
@@ -13,5 +13,6 @@ // `insta setup agent` — make this machine's coding agents InstaCloud-native in one step | ||
| import { ApiClient } from '../api.js'; | ||
| import { resolveEnv } from '../config.js'; | ||
| import { DEFAULT_ENV, ENVS, mcpServerName } from '../env.js'; | ||
| import { readPersistedGlobal, resolveEnv } from '../config.js'; | ||
| import { DEFAULT_ENV, ENVS, ENV_NAMES, envForApiUrl, envFromEnvVar, isEnvName, mcpServerName } from '../env.js'; | ||
| import { info } from '../util.js'; | ||
| import { envUse } from './env.js'; | ||
| import { installAgentConfigs } from './mcp.js'; | ||
@@ -320,6 +321,45 @@ import { detectChannel } from './upgrade.js'; | ||
| } | ||
| export async function setupAgent(opts, run = defaultRunner, mint, installConfigs = installAgentConfigs, ensure = (r) => ensureCliInstalled(r)) { | ||
| /** The environment `setup agent` should target, and whether the machine must be switched to it | ||
| * first. Pure — decides only; the caller performs the switch. | ||
| * | ||
| * The contract (CLI ≥ 0.0.38): the public one-liner `npx -y insta setup agent` means PRODUCTION, | ||
| * full stop — a leftover `insta env use staging` from last month must not silently give a new | ||
| * onboarding run staging skills. Staging is an explicit ask: `--env staging` (or $INSTA_ENV). | ||
| * Two deliberate exceptions leave the machine alone: | ||
| * - an explicit $INSTA_API_URL (insta-oss, a preview) with no --env — a hand-written URL is the | ||
| * most specific instruction there is; | ||
| * - a persisted CUSTOM apiUrl with no --env — same reasoning, chosen via login --api-url. */ | ||
| export function planSetupEnv(flagEnv, persistedApiUrl, apiUrlOverride = process.env.INSTA_API_URL, envVar = envFromEnvVar()) { | ||
| if (flagEnv !== undefined) { | ||
| const want = flagEnv.trim().toLowerCase(); | ||
| if (!isEnvName(want)) | ||
| throw new Error(`unknown --env "${flagEnv}" — expected one of: ${ENV_NAMES.join(', ')}`); | ||
| // A contradicting ambient override must ERROR, not lose quietly: everything after this plan | ||
| // resolves through resolveEnv(), where $INSTA_API_URL (and $INSTA_ENV) outrank the persisted | ||
| // config — proceeding would persist one environment and install another's skills/MCP. | ||
| if (apiUrlOverride) | ||
| throw new Error(`--env ${want} conflicts with $INSTA_API_URL=${apiUrlOverride} — unset one`); | ||
| if (envVar && envVar !== want) | ||
| throw new Error(`--env ${want} conflicts with $INSTA_ENV=${envVar} — unset one`); | ||
| return { target: want, switch: envForApiUrl(persistedApiUrl) !== want }; | ||
| } | ||
| if (apiUrlOverride) | ||
| return { target: null, switch: false }; | ||
| const persisted = envForApiUrl(persistedApiUrl); | ||
| if (persisted === null) | ||
| return { target: null, switch: false }; // custom host, chosen deliberately | ||
| const target = envVar ?? DEFAULT_ENV; | ||
| return { target, switch: persisted !== target }; | ||
| } | ||
| export async function setupAgent(opts, run = defaultRunner, mint, installConfigs = installAgentConfigs, ensure = (r) => ensureCliInstalled(r), readStored = readPersistedGlobal, switchEnv = (n) => envUse(n)) { | ||
| if (!opts.yes && !process.stdout.isTTY) { | ||
| info('non-interactive shell — assuming -y'); | ||
| } | ||
| // Pin the environment BEFORE anything is installed (see planSetupEnv). A required switch goes | ||
| // through `env use` — the one path that persists the choice and drops the now-foreign session — | ||
| // and announces itself, so the machine can never end up with its CLI on one deployment and its | ||
| // skills/MCP on another. | ||
| const plan = planSetupEnv(opts.env, (await readStored()).apiUrl); | ||
| if (plan.switch && plan.target) | ||
| await switchEnv(plan.target); | ||
| // BEFORE the skill install: the skill tells agents to run `insta …`, so a durable CLI must | ||
@@ -356,3 +396,12 @@ // exist by the time it lands. | ||
| info(`✓ MCP — also configured for ${others.join(', ')} (restart those tools to pick it up)`); | ||
| // The checkmarks end the install, but the user's next move shouldn't be guesswork. Local state | ||
| // only (no network): a config with a session means logged in — good enough for a hint. | ||
| // Deliberately prod-hosted prompt.md even on a staging setup: it is generic onboarding (the | ||
| // login/create commands in it are env-aware at runtime), and staging serves no equivalent. | ||
| const stored = await readStored(); | ||
| const loggedIn = !!(stored.accessToken || stored.user); | ||
| info(loggedIn | ||
| ? 'next: `insta project create <name>` in your app repo — or tell your agent: "Fetch https://instacloud.com/prompt.md and follow it"' | ||
| : 'next: `insta login --oauth github` (headless: `insta login --device`), then `insta project create <name>` — or tell your agent: "Fetch https://instacloud.com/prompt.md and follow it"'); | ||
| } | ||
| //# sourceMappingURL=setup.js.map |
+2
-1
@@ -85,4 +85,5 @@ #!/usr/bin/env node | ||
| const setupCmd = program.command('setup').description('Set up this machine for InstaCloud agent workflows'); | ||
| setupCmd.command('agent').description('Install the insta CLI (if missing), the insta skill for all coding agents, and the MCP server') | ||
| setupCmd.command('agent').description('Install the insta CLI (if missing), the insta skill for all coding agents, and the MCP server — targets production; pass --env staging for the staging deployment') | ||
| .option('-y, --yes', 'non-interactive') | ||
| .option('--env <prod|staging>', 'deployment to set this machine up for (default: prod — switches and persists, like `insta env use`)') | ||
| .option('--mcp-token', 'register the MCP server with a minted insta_ API token instead of OAuth (headless machines / CI)') | ||
@@ -89,0 +90,0 @@ .action(guard((o) => setup.setupAgent(o))); |
+1
-1
| { | ||
| "name": "insta", | ||
| "version": "0.0.37", | ||
| "version": "0.0.38", | ||
| "type": "module", | ||
@@ -5,0 +5,0 @@ "description": "InstaCloud CLI — a thin client of the platform control-plane API.", |
+11
-3
@@ -32,4 +32,4 @@ # insta-cli | ||
| qualifies; if the global install can't write, setup continues and prints the exact | ||
| version-pinned `npm install -g` fallback to run yourself). E2e-validated on macOS/Linux; the | ||
| Windows spawn paths are unit-tested: | ||
| version-pinned `npm install -g` fallback to run yourself). E2e-validated on macOS, Linux, | ||
| and Windows (PowerShell + cmd): | ||
@@ -40,2 +40,10 @@ ```bash | ||
| This command means **production** (CLI ≥ 0.0.38): if the machine was previously switched to | ||
| staging it switches back — announced, session dropped, like `insta env use prod`. Staging is | ||
| its own explicit command, which also persists the choice: | ||
| ```bash | ||
| npx -y insta setup agent --env staging | ||
| ``` | ||
| On macOS/Linux without Node, the native-binary installer puts the `insta` CLI on PATH (the | ||
@@ -184,3 +192,3 @@ skill + MCP steps it then runs still need Node — the skills tool runs via npx). Never run it | ||
| | `insta env` | `show` · `use <prod\|staging>` | | ||
| | `insta setup` | `agent` — install the CLI (if missing), the skill, and MCP for every coding agent | | ||
| | `insta setup` | `agent` — install the CLI (if missing), the skill, and MCP for every coding agent; targets prod, `--env staging` for staging | | ||
| | `insta mcp` | `install` — register the remote MCP server only | | ||
@@ -187,0 +195,0 @@ | `insta org` | `list` · `create` (one free org per user) | |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
298088
1.73%5163
1.24%243
3.4%60
1.69%