setup-agent 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # setup-agent
2
2
 
3
- One checkout, shared live between the browser that opened it and the coding agent installing its products. The state is a Statewire host (`setup-agent/host`) served from a Cloudflare Durable Object (`apps/checkout-worker`); the CLI (`npx setup-agent <url>`) is a thin Statewire client the agent drives from its shell.
3
+ One setup, shared live between the browser that opened it and the coding agent installing its products. The state is a Statewire host (`setup-agent/host`) served from a Cloudflare Durable Object (`apps/checkout-worker`); the CLI (`npx setup-agent <url>`) is a thin Statewire client the agent drives from its shell.
4
4
 
5
5
  ```sh
6
- npx setup-agent <url> # instructions plus the current checkout
6
+ npx setup-agent <url> # instructions plus the current setup
7
7
  npx setup-agent <url> stream # one JSON array of events per line; run it under a Monitor
8
8
  npx setup-agent <url> wait start # wait for the user to click Begin plan in their browser
9
9
  npx setup-agent <url> log "Sure, I will keep the existing chat route" # a reply, or something no question or step conveys
@@ -21,9 +21,9 @@ npx setup-agent <url> done --summary "installed the thread and a chat route" --p
21
21
 
22
22
  ## Phases
23
23
 
24
- A checkout opens `waiting` with its products and nothing else: no steps, no questions. After connecting, the agent tells the user to return to their browser and click **Begin plan**. It keeps the stream running and blocks on `wait start`. The browser sends `checkout/begin-plan`, moving the checkout to `planning` and emitting `planning.started` on the stream. Questions and plan submissions are refused while waiting. When a product needs another one the project lacks, the agent proposes it with `ask "<why>" --product <slug>`; the browser, which owns the catalog, accepts with `checkout/add-product` (the product and its guide join `products`, the input answers `added`) or dismisses the input. In `planning`, the agent investigates the project, asks what the code does not decide, and proposes a markdown plan with `plan` carrying its findings. `log` is for replying to the user and for what no question or step conveys. The user approves the plan or asks for changes with feedback; every revision is kept. Approval moves the checkout to `installing`, where the agent declares its own steps with `add-step` and works through them with `step`. `add-step` and `step` are refused before approval. `done` marks the remaining steps done and proposes closing, with an optional `--summary` and an optional `--preview <url>` naming a dev server the agent left running; a preview must be an http(s) URL on localhost, and the browser offers to open it. `done` does not close. The browser shows the proposal with a close action that sends `checkout/finish`, and the stream prints `finished`. Until then the agent stays idle on the stream, because the user may send a follow-up message; after handling one it runs `done` again to renew the proposal.
24
+ A setup opens `waiting` with its products and nothing else: no steps, no questions. After connecting, the agent tells the user to return to their browser and click **Begin plan**. It keeps the stream running and blocks on `wait start`. The browser sends `checkout/begin-plan`, moving the setup to `planning` and emitting `planning.started` on the stream. Questions and plan submissions are refused while waiting. When a product needs another one the project lacks, the agent proposes it with `ask "<why>" --product <slug>`; the browser, which owns the catalog, accepts with `checkout/add-product` (the product and its guide join `products`, the input answers `added`) or dismisses the input. In `planning`, the agent investigates the project, asks what the code does not decide, and proposes a markdown plan with `plan` carrying its findings. `log` is for replying to the user and for what no question or step conveys. The user approves the plan or asks for changes with feedback; every revision is kept. Approval moves the setup to `installing`, where the agent declares its own steps with `add-step` and works through them with `step`. `add-step` and `step` are refused before approval. `done` marks the remaining steps done and proposes closing, with an optional `--summary` and an optional `--preview <url>` naming a dev server the agent left running; a preview must be an http(s) URL on localhost, and the browser offers to open it. `done` does not close. The browser shows the proposal with a close action that sends `checkout/finish`, and the stream prints `finished`. Until then the agent stays idle on the stream, because the user may send a follow-up message; after handling one it runs `done` again to renew the proposal.
25
25
 
26
- The URL is the browser's link to its agent, not one checkout. A finished or cancelled checkout stays on the link, and the browser's next setup replaces it with a new `checkout/create` carrying a new `id`: the plans, steps, inputs and log start over, the agent's presence carries across when it is still connected, and the stream prints `checkout.created` with the new products. The agent keeps the stream running after `finished` or `cancelled` and blocks on `wait start`, which returns once the next checkout begins planning; a checkout already closed when the wait begins is skipped rather than reported.
26
+ The URL is the browser's link to its agent, not one setup. A finished or cancelled setup stays on the link, and the browser's next setup replaces it with a new `checkout/create` carrying a new `id`: the plans, steps, inputs and log start over, the agent's presence carries across when it is still connected, and the stream prints `setup.created` with the new products. The agent keeps the stream running after `finished` or `cancelled` and blocks on `wait start`, which returns once the next setup begins planning; a setup already closed when the wait begins is skipped rather than reported.
27
27
 
28
28
  ## Inputs
29
29
 
package/dist/cli.d.ts CHANGED
@@ -2,6 +2,8 @@
2
2
  import { Checkout } from "./protocol.js";
3
3
  import { CheckoutClient } from "./client.js";
4
4
  //#region src/cli.d.ts
5
+ declare const HELP: string;
6
+ declare const INSTRUCTIONS: (url: string) => string;
5
7
  type Parsed = {
6
8
  positional: string[];
7
9
  flags: Map<string, string | true>;
@@ -47,11 +49,11 @@ type StreamEvent = {
47
49
  event: "cancelled";
48
50
  next: string;
49
51
  } | {
50
- event: "checkout.created";
52
+ event: "setup.created";
51
53
  products: string[];
52
54
  };
53
55
  declare const diffEvents: (before: Checkout.State, after: Checkout.State) => StreamEvent[];
54
- /** Resolves once a checkout is planning or installing, or once the one being waited on is cancelled; a checkout already closed when the wait begins is over, so the wait is for the next one. */
56
+ /** Resolves once a setup is planning or installing, or once the one being waited on is cancelled; a setup already closed when the wait begins is over, so the wait is for the next one. */
55
57
  declare const waitForStart: (client: Pick<CheckoutClient, "state" | "subscribe">) => Promise<Checkout.Status>;
56
58
  declare const askSeed: (rest: readonly string[], flags: Parsed["flags"]) => Checkout.InputSeed & {
57
59
  stepId?: string;
@@ -60,5 +62,5 @@ declare const askSeed: (rest: readonly string[], flags: Parsed["flags"]) => Chec
60
62
  declare const upsertEnvLine: (content: string, key: string, value: string) => string;
61
63
  declare const main: (argv: readonly string[]) => Promise<undefined>;
62
64
  //#endregion
63
- export { askSeed, detectAgentKind, diffEvents, isDirectInvocation, main, upsertEnvLine, waitForStart };
65
+ export { HELP, INSTRUCTIONS, askSeed, detectAgentKind, diffEvents, isDirectInvocation, main, upsertEnvLine, waitForStart };
64
66
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","names":[],"sources":["../src/cli.ts"],"mappings":";;;KAsGK;EAAW;EAAsB,OAAO;;;cA8ChC,kBAAmB,MAAK,OAAO;;cA2C/B,qBACX,MAAK,OAAO,YACZ;EAAS;GACT;EAAU;;KA8FP;EACC;;EACA;EAA0B;EAAmB;;EAE7C;EACA;EACA;;EAEA;EACA;;EAEA;;EAEA;EAA0B;EAAiB;;EAC3C;EAAwB;;EACxB;EAAiC;EAAkB;;EACnD;EAAmB;;EACnB;EAAoB;;EACpB;EAA2B;;cAKpB,aACX,QAAQ,SAAS,OACjB,OAAO,SAAS,UACf;;cAkHU,eACX,QAAQ,KAAK,2CAAsC,QAAA,SAAA;cAyExC,UACX,yBACA,OAAO,oBACN,SAAS;EAAc;;;cAoFb,gBAAiB,iBAAiB,aAAa;cA2B/C,OAAc,4BAAuB"}
1
+ {"version":3,"file":"cli.d.ts","names":[],"sources":["../src/cli.ts"],"mappings":";;;cAsBa;cAmCA,eACX;KA4CG;EAAW;EAAsB,OAAO;;;cA8ChC,kBAAmB,MAAK,OAAO;;cA2C/B,qBACX,MAAK,OAAO,YACZ;EAAS;GACT;EAAU;;KA8FP;EACC;;EACA;EAA0B;EAAmB;;EAE7C;EACA;EACA;;EAEA;EACA;;EAEA;;EAEA;EAA0B;EAAiB;;EAC3C;EAAwB;;EACxB;EAAiC;EAAkB;;EACnD;EAAmB;;EACnB;EAAoB;;EACpB;EAAwB;;cAKjB,aACX,QAAQ,SAAS,OACjB,OAAO,SAAS,UACf;;cAkHU,eACX,QAAQ,KAAK,2CAAsC,QAAA,SAAA;cAyExC,UACX,yBACA,OAAO,oBACN,SAAS;EAAc;;;cAoFb,gBAAiB,iBAAiB,aAAa;cA2B/C,OAAc,4BAAuB"}
package/dist/cli.js CHANGED
@@ -9,8 +9,8 @@ import { resolve } from "node:path";
9
9
  const HELP = `setup-agent <url> [command]
10
10
 
11
11
  Commands (the url always comes first):
12
- status Print the checkout as JSON.
13
- stream Follow the checkout: one JSON array of events per line.
12
+ status Print the setup as JSON.
13
+ stream Follow the setup: one JSON array of events per line.
14
14
  log <text> Send a message to the user: a reply, something no question or step conveys, or one wrap-up when finished.
15
15
  ack <message-id> Acknowledge a user message.
16
16
  ask <prompt> [--placeholder <text>] [--optional] [--step <step-id>] [--wait]
@@ -25,8 +25,8 @@ Commands (the url always comes first):
25
25
  --single is for options that exclude each other (one framework, one model, yes or no); the answer is then one id.
26
26
  --icons is required and names an icon for every option, from: ${OPTION_ICONS.join(", ")}.
27
27
  ask <prompt> --product <slug> [--wait]
28
- Propose adding a product to this checkout, such as one another product needs first. Answers "added"; the product and its guide then appear in "status".
29
- wait start Block until the user clicks Begin plan in their browser; after a finished or cancelled checkout, until the next one begins.
28
+ Propose adding a product to this setup, such as one another product needs first. Answers "added"; the product and its guide then appear in "status".
29
+ wait start Block until the user clicks Begin plan in their browser; after a finished or cancelled setup, until the next one begins.
30
30
  wait <input-id> Block until the user answers or dismisses an input. A multi-select answer prints as an array.
31
31
  plan (<markdown> | --file <path>) [--wait]
32
32
  Propose the plan for the user to approve. --wait blocks until they decide.
@@ -40,17 +40,17 @@ Commands (the url always comes first):
40
40
  done [--summary <text>] [--preview <url>]
41
41
  Mark every remaining step done and propose closing; the user closes it.
42
42
  `;
43
- const INSTRUCTIONS = (url) => `You are the coding agent for a checkout the user opened in their browser. The browser shows this checkout live; every command below updates it. The user watches the browser, not this terminal: once setup begins, send questions through "ask" so the user can answer in their browser.
43
+ const INSTRUCTIONS = (url) => `You are the coding agent for a setup the user opened in their browser. The browser shows this setup live; every command below updates it. The user watches the browser, not this terminal: once setup begins, send questions through "ask" so the user can answer in their browser.
44
44
 
45
45
  First, start a persistent Monitor on
46
46
  npx setup-agent ${url} stream
47
- and keep it running: the browser shows you as connected only while it runs, and it is how answers reach you. The URL is the user's link to you from their browser, not one checkout: it outlives this setup, and their next setup arrives on the same stream. Each line is a JSON array of events: "planning.started", "input.answered" ({inputId, answer, note?}; answer is an array for a choice question not asked with --single), "input.dismissed", "plan.approved", "plan.changes-requested" ({feedback}), "message.created" ({messageId, text}), "finished", "cancelled", "checkout.created" ({products}).
47
+ and keep it running: the browser shows you as connected only while it runs, and it is how answers reach you. The URL is the user's link to you from their browser, not one setup: it outlives this one, and their next setup arrives on the same stream. Each line is a JSON array of events: "planning.started", "input.answered" ({inputId, answer, note?}; answer is an array for a choice question not asked with --single), "input.dismissed", "plan.approved", "plan.changes-requested" ({feedback}), "message.created" ({messageId, text}), "finished", "cancelled", "setup.created" ({products}).
48
48
 
49
49
  The user can message you at any time, including while a question is waiting. Read "message.created" events, acknowledge each with "npx setup-agent ${url} ack <message-id>", and reply using "log" with what you will do. Pending messages are replayed when the stream starts and listed in "status". A message is not an answer or plan approval; keep those decisions explicit.
50
50
 
51
51
  "log" is a message to the user, and the user reads every one: send it only to reply to their message, to tell them something no question or step conveys (a blocker, a deviation from the plan, a decision you made for them), or once as a wrap-up when the install is finished. Findings go in the plan, progress in step notes, questions in "ask"; do not narrate.
52
52
 
53
- Before planning, check the current checkout status. If it is "waiting", tell the user in your own conversation: "I'm connected. Go back to your browser and click Begin plan. You can watch my progress, answer questions, and steer me there." Then run:
53
+ Before planning, check the current setup status. If it is "waiting", tell the user in your own conversation: "I'm connected. Go back to your browser and click Begin plan. You can watch my progress, answer questions, and steer me there." Then run:
54
54
  npx setup-agent ${url} wait start
55
55
  Keep the stream running while you wait. Do not inspect the project, fetch guides, ask setup questions, or propose a plan until this command returns {"ok":true}. Only the user in the browser should start planning; never invoke the browser's start command yourself. "planning.started" also arrives on the stream. If the status is already "planning" or "installing", resume that phase without asking the user to begin again. If it is "done" or "cancelled", that setup is over: tell the user you are connected and waiting for their next setup, and run "wait start", which returns once the next one begins.
56
56
 
@@ -58,16 +58,16 @@ The remaining phases are plan, then install.
58
58
 
59
59
  1. Plan. Investigate only; change nothing yet. If the current directory is not an app to install into (a monorepo root, an empty folder, a library), ask where to install first:
60
60
  npx setup-agent ${url} ask "Which project should I install into?" --placeholder "An absolute path, or a path for a new Next.js app" --wait
61
- Read the project: its app framework and package manager, the agent framework and model provider it already uses, where components live, any existing chat route. Fetch each product's "guide" URL from the checkout below; it is the install reference. Ask for what the project does not decide, using the standard questions so the browser can render them well:
61
+ Read the project: its app framework and package manager, the agent framework and model provider it already uses, where components live, any existing chat route. Fetch each product's "guide" URL from the setup below; it is the install reference. Ask for what the project does not decide, using the standard questions so the browser can render them well:
62
62
  npx setup-agent ${url} ask --preset framework
63
63
  npx setup-agent ${url} ask --preset llm-provider
64
- If a product's guide needs another product the project does not have yet (most need assistant-ui installed and rendering a thread), do not stop and do not install it on the side. Propose adding it to this checkout:
64
+ If a product's guide needs another product the project does not have yet (most need assistant-ui installed and rendering a thread), do not stop and do not install it on the side. Propose adding it to this setup:
65
65
  npx setup-agent ${url} ask "assistant-ui is not installed in this project yet. Add it to this setup?" --product assistant-ui --wait
66
66
  On "added", run "status", fetch the new product's guide, and install it first in your plan. If the user dismisses it, follow what the guide says about the missing product.
67
67
  Skip a preset the code already answers (a chat route on the AI SDK, a provider key in .env.local) and log why. Ask everything at once, keep investigating, and block with "wait <input-id>" only when nothing else can proceed. A framework answer is "<framework>:<language>", for example "ai-sdk:typescript". A model answer is JSON {"provider","model","reasoningEffort"?}. An answer may carry a "note" from the user; follow it. A choice answer that matches none of the options is the user's own text.
68
68
  Then write the plan as markdown and submit it:
69
69
  npx setup-agent ${url} plan --file <path> --wait
70
- Use these headings, each with bullets: "## What I found" (facts as \`- **Label:** value\`: app framework, package manager, agent framework, model provider and model, where components live), "## What I will install" (packages and files, one per bullet), "## Steps" (a numbered list, one line each), and "## Open questions" only if any remain. Keep it under 30 lines; no preamble. "plan --wait" prints {"decision":"approve"} or {"decision":"revise","feedback":"..."}. On revise, address the feedback (ask again if you need to) and submit a new plan; repeat until approved. Do not change the project before approval: the checkout refuses steps until then.
70
+ Use these headings, each with bullets: "## What I found" (facts as \`- **Label:** value\`: app framework, package manager, agent framework, model provider and model, where components live), "## What I will install" (packages and files, one per bullet), "## Steps" (a numbered list, one line each), and "## Open questions" only if any remain. Keep it under 30 lines; no preamble. "plan --wait" prints {"decision":"approve"} or {"decision":"revise","feedback":"..."}. On revise, address the feedback (ask again if you need to) and submit a new plan; repeat until approved. Do not change the project before approval: the setup refuses steps until then.
71
71
 
72
72
  2. Install. Declare the steps from your plan up front, in order:
73
73
  npx setup-agent ${url} add-step "<title>" --detail "<one line>"
@@ -78,9 +78,9 @@ which writes ENV_VAR=<key> into that file. Use the answer's model in the chat ro
78
78
 
79
79
  When every product is installed and verified, run
80
80
  npx setup-agent ${url} done --summary "<one or two lines on what you installed and how to try it>" --preview http://localhost:<port>
81
- Before that, start the project's dev server in the background when the project has one, check that the page you changed loads, and pass the address as --preview (the page itself when it is not the root); leave the server running so the user can try the result. Omit --preview when there is nothing to open. This proposes closing the checkout; only the user closes it. Do not stop the stream or exit: stay idle on it, because the user may follow up with a "message.created" event. Handle it, then run "done" again. The stream prints "finished" once the user closes the checkout, and "cancelled" if they end it early. Keep the stream running after either: the user's next setup starts on this same URL and arrives as "checkout.created" with its products. Run
81
+ Before that, start the project's dev server in the background when the project has one, check that the page you changed loads, and pass the address as --preview (the page itself when it is not the root); leave the server running so the user can try the result. Omit --preview when there is nothing to open. This proposes closing the setup; only the user closes it. Do not stop the stream or exit: stay idle on it, because the user may follow up with a "message.created" event. Handle it, then run "done" again. The stream prints "finished" once the user closes the setup, and "cancelled" if they end it early. Keep the stream running after either: the user's next setup starts on this same URL and arrives as "setup.created" with its products. Run
82
82
  npx setup-agent ${url} wait start
83
- which returns once that setup begins, then run "status" for its products, guides and instructions and go through plan and install again. A "checkout.created" event while you are still working means the user replaced this setup: drop what you were doing and start over the same way.
83
+ which returns once that setup begins, then run "status" for its products, guides and instructions and go through plan and install again. A "setup.created" event while you are still working means the user replaced this setup: drop what you were doing and start over the same way.
84
84
  `;
85
85
  const parseArgs = (argv) => {
86
86
  const positional = [];
@@ -193,7 +193,7 @@ const launchAgent = async (url) => {
193
193
  });
194
194
  process.exit(code);
195
195
  };
196
- const requireState = (client) => client.state ?? fail("the checkout has no state");
196
+ const requireState = (client) => client.state ?? fail("the setup has no state");
197
197
  const summary = (state) => {
198
198
  const plan = currentPlan(state);
199
199
  return {
@@ -266,7 +266,7 @@ const diffEvents = (before, after) => {
266
266
  next: STAY_CONNECTED
267
267
  });
268
268
  if (after.id !== null && after.id !== before.id) events.push({
269
- event: "checkout.created",
269
+ event: "setup.created",
270
270
  products: after.products.map((product) => product.slug)
271
271
  });
272
272
  return events;
@@ -313,7 +313,7 @@ const stream = async (client) => {
313
313
  process.on("SIGTERM", stop);
314
314
  await new Promise(() => {});
315
315
  };
316
- /** Resolves once a checkout is planning or installing, or once the one being waited on is cancelled; a checkout already closed when the wait begins is over, so the wait is for the next one. */
316
+ /** Resolves once a setup is planning or installing, or once the one being waited on is cancelled; a setup already closed when the wait begins is over, so the wait is for the next one. */
317
317
  const waitForStart = (client) => new Promise((resolve) => {
318
318
  let previous;
319
319
  const check = () => {
@@ -483,7 +483,7 @@ const main = async (argv) => {
483
483
  const kind = detectAgentKind();
484
484
  await commands["agent/intro"]({ ...kind !== void 0 && { kind } }).catch(() => {});
485
485
  process.stdout.write(INSTRUCTIONS(url));
486
- process.stdout.write("\nCurrent checkout:\n");
486
+ process.stdout.write("\nCurrent setup:\n");
487
487
  print(summary(requireState(client)));
488
488
  break;
489
489
  }
@@ -618,7 +618,7 @@ const main = async (argv) => {
618
618
  print({
619
619
  ok: true,
620
620
  status: "proposed",
621
- next: "Keep the stream running and stay idle. The user closes the checkout or sends a follow-up message."
621
+ next: "Keep the stream running and stay idle. The user closes the setup or sends a follow-up message."
622
622
  });
623
623
  break;
624
624
  }
@@ -631,6 +631,6 @@ const main = async (argv) => {
631
631
  }
632
632
  };
633
633
  //#endregion
634
- export { askSeed, detectAgentKind, diffEvents, isDirectInvocation, main, upsertEnvLine, waitForStart };
634
+ export { HELP, INSTRUCTIONS, askSeed, detectAgentKind, diffEvents, isDirectInvocation, main, upsertEnvLine, waitForStart };
635
635
 
636
636
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":["connectCheckout","CheckoutClient","INPUT_PRESETS","isPresetId","presetInput","spawn","readFile","writeFile","createInterface","resolve","AGENT_HEARTBEAT_MS","OPTION_ICONS","classifyChoiceAnswer","currentPlan","isAgentPresent","isClosed","isOptionIcon","parseModelAnswer","parseMultipleAnswer","stepProgress","Checkout","HELP","join","INSTRUCTIONS","url","Parsed","positional","flags","Map","parseArgs","argv","i","length","arg","startsWith","push","name","slice","next","undefined","set","flagText","value","get","fail","message","process","stderr","write","exit","print","stdout","JSON","stringify","AGENT_ENV","kind","variable","detectAgentKind","env","NodeJS","ProcessEnv","find","Launcher","command","args","prompt","install","LAUNCHERS","isDirectInvocation","stdin","isTTY","chooseLauncher","rl","input","output","forEach","launcher","index","answer","question","catch","trim","byNumber","Number","byName","toLowerCase","close","launchAgent","child","stdio","code","Promise","on","error","ErrnoException","status","requireState","client","State","state","summary","plan","agentConnected","products","instructions","revision","feedback","progress","steps","inputs","log","pendingMessages","filter","entry","role","acknowledgedAt","STEP_STATUSES","StepStatus","StreamEvent","event","messageId","text","inputId","note","custom","STAY_CONNECTED","diffEvents","before","after","events","some","previous","id","candidate","answerFields","plans","map","product","slug","stream","pending","timer","ReturnType","setTimeout","flush","subscribe","commands","cwd","heartbeat","setInterval","stop","clearInterval","finally","dispose","waitForStart","Pick","Status","check","started","ended","unsubscribe","waitForInput","Input","waitForPlan","Plan","decided","ok","decision","splitList","split","Boolean","askSeed","rest","InputSeed","stepId","optional","has","fallback","preset","Object","keys","only","found","description","seed","default","Error","String","choices","icons","icon","includes","missing","options","label","multiple","placeholder","upsertEnvLine","content","key","line","lines","replace","findIndex","RegExp","test","const","answered","main","first","usage","title","detail","result","active","file","markdown","then","target","envKey","response","fetch","encodeURIComponent","current","preview"],"sources":["../src/cli.ts"],"sourcesContent":["/// <reference types=\"node\" />\nimport { connectCheckout } from \"./client\";\nimport type { CheckoutClient } from \"./client\";\nimport { INPUT_PRESETS, isPresetId, presetInput } from \"./presets\";\nimport { spawn } from \"node:child_process\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { createInterface } from \"node:readline/promises\";\nimport { resolve } from \"node:path\";\nimport {\n AGENT_HEARTBEAT_MS,\n OPTION_ICONS,\n classifyChoiceAnswer,\n currentPlan,\n isAgentPresent,\n isClosed,\n isOptionIcon,\n parseModelAnswer,\n parseMultipleAnswer,\n stepProgress,\n} from \"./protocol\";\nimport type { Checkout } from \"./protocol\";\n\nconst HELP = `setup-agent <url> [command]\n\nCommands (the url always comes first):\n status Print the checkout as JSON.\n stream Follow the checkout: one JSON array of events per line.\n log <text> Send a message to the user: a reply, something no question or step conveys, or one wrap-up when finished.\n ack <message-id> Acknowledge a user message.\n ask <prompt> [--placeholder <text>] [--optional] [--step <step-id>] [--wait]\n Ask the user for a line of text.\n ask [prompt] --preset <framework|llm-provider|project> [--only <id,id>] [--choices <id=description,...>] [--default <id>] [--optional] [--step <step-id>] [--wait]\n Ask a standard question. --only restricts the options (one id locks it in); --default preselects one.\n \"framework\" answers \"<framework>:<language>\"; \"llm-provider\" answers JSON with provider, model and reasoningEffort; the key itself arrives through \"env\".\n \"project\" lists the React projects you found as --choices \"<path>=<what it is>,...\" ahead of \"New project\"; it answers a path, or \"new:<next|vite|react-router|tanstack-start|expo>\".\n ask <prompt> --choices <id,id,...> --icons <id=icon,...> [--single] [--default <id>] [--optional] [--step <step-id>] [--wait]\n Ask the user to pick from your own options; they may also type their own answer.\n The user checks any number of options and the answer is a JSON array of option ids (their own text is one entry).\n --single is for options that exclude each other (one framework, one model, yes or no); the answer is then one id.\n --icons is required and names an icon for every option, from: ${OPTION_ICONS.join(\", \")}.\n ask <prompt> --product <slug> [--wait]\n Propose adding a product to this checkout, such as one another product needs first. Answers \"added\"; the product and its guide then appear in \"status\".\n wait start Block until the user clicks Begin plan in their browser; after a finished or cancelled checkout, until the next one begins.\n wait <input-id> Block until the user answers or dismisses an input. A multi-select answer prints as an array.\n plan (<markdown> | --file <path>) [--wait]\n Propose the plan for the user to approve. --wait blocks until they decide.\n wait plan Block until the user approves the plan or asks for changes.\n env <input-id> <ENV_VAR> [--file <path>]\n Write the API key behind an answered llm-provider input to an env file (default .env.local) as ENV_VAR=<key>, without printing it.\n add-step <title> [--detail <text>] [--product <slug>] [--active]\n Declare a step of the install (after the plan is approved). Prints its id.\n step <step-id> <status> [--note <text>]\n Set a step to pending|active|done|skipped|blocked.\n done [--summary <text>] [--preview <url>]\n Mark every remaining step done and propose closing; the user closes it.\n`;\n\nconst INSTRUCTIONS = (\n url: string,\n) => `You are the coding agent for a checkout the user opened in their browser. The browser shows this checkout live; every command below updates it. The user watches the browser, not this terminal: once setup begins, send questions through \"ask\" so the user can answer in their browser.\n\nFirst, start a persistent Monitor on\n npx setup-agent ${url} stream\nand keep it running: the browser shows you as connected only while it runs, and it is how answers reach you. The URL is the user's link to you from their browser, not one checkout: it outlives this setup, and their next setup arrives on the same stream. Each line is a JSON array of events: \"planning.started\", \"input.answered\" ({inputId, answer, note?}; answer is an array for a choice question not asked with --single), \"input.dismissed\", \"plan.approved\", \"plan.changes-requested\" ({feedback}), \"message.created\" ({messageId, text}), \"finished\", \"cancelled\", \"checkout.created\" ({products}).\n\nThe user can message you at any time, including while a question is waiting. Read \"message.created\" events, acknowledge each with \"npx setup-agent ${url} ack <message-id>\", and reply using \"log\" with what you will do. Pending messages are replayed when the stream starts and listed in \"status\". A message is not an answer or plan approval; keep those decisions explicit.\n\n\"log\" is a message to the user, and the user reads every one: send it only to reply to their message, to tell them something no question or step conveys (a blocker, a deviation from the plan, a decision you made for them), or once as a wrap-up when the install is finished. Findings go in the plan, progress in step notes, questions in \"ask\"; do not narrate.\n\nBefore planning, check the current checkout status. If it is \"waiting\", tell the user in your own conversation: \"I'm connected. Go back to your browser and click Begin plan. You can watch my progress, answer questions, and steer me there.\" Then run:\n npx setup-agent ${url} wait start\nKeep the stream running while you wait. Do not inspect the project, fetch guides, ask setup questions, or propose a plan until this command returns {\"ok\":true}. Only the user in the browser should start planning; never invoke the browser's start command yourself. \"planning.started\" also arrives on the stream. If the status is already \"planning\" or \"installing\", resume that phase without asking the user to begin again. If it is \"done\" or \"cancelled\", that setup is over: tell the user you are connected and waiting for their next setup, and run \"wait start\", which returns once the next one begins.\n\nThe remaining phases are plan, then install.\n\n1. Plan. Investigate only; change nothing yet. If the current directory is not an app to install into (a monorepo root, an empty folder, a library), ask where to install first:\n npx setup-agent ${url} ask \"Which project should I install into?\" --placeholder \"An absolute path, or a path for a new Next.js app\" --wait\nRead the project: its app framework and package manager, the agent framework and model provider it already uses, where components live, any existing chat route. Fetch each product's \"guide\" URL from the checkout below; it is the install reference. Ask for what the project does not decide, using the standard questions so the browser can render them well:\n npx setup-agent ${url} ask --preset framework\n npx setup-agent ${url} ask --preset llm-provider\nIf a product's guide needs another product the project does not have yet (most need assistant-ui installed and rendering a thread), do not stop and do not install it on the side. Propose adding it to this checkout:\n npx setup-agent ${url} ask \"assistant-ui is not installed in this project yet. Add it to this setup?\" --product assistant-ui --wait\nOn \"added\", run \"status\", fetch the new product's guide, and install it first in your plan. If the user dismisses it, follow what the guide says about the missing product.\nSkip a preset the code already answers (a chat route on the AI SDK, a provider key in .env.local) and log why. Ask everything at once, keep investigating, and block with \"wait <input-id>\" only when nothing else can proceed. A framework answer is \"<framework>:<language>\", for example \"ai-sdk:typescript\". A model answer is JSON {\"provider\",\"model\",\"reasoningEffort\"?}. An answer may carry a \"note\" from the user; follow it. A choice answer that matches none of the options is the user's own text.\nThen write the plan as markdown and submit it:\n npx setup-agent ${url} plan --file <path> --wait\nUse these headings, each with bullets: \"## What I found\" (facts as \\`- **Label:** value\\`: app framework, package manager, agent framework, model provider and model, where components live), \"## What I will install\" (packages and files, one per bullet), \"## Steps\" (a numbered list, one line each), and \"## Open questions\" only if any remain. Keep it under 30 lines; no preamble. \"plan --wait\" prints {\"decision\":\"approve\"} or {\"decision\":\"revise\",\"feedback\":\"...\"}. On revise, address the feedback (ask again if you need to) and submit a new plan; repeat until approved. Do not change the project before approval: the checkout refuses steps until then.\n\n2. Install. Declare the steps from your plan up front, in order:\n npx setup-agent ${url} add-step \"<title>\" --detail \"<one line>\"\nwhich prints the step id. Then work through them: \"step <id> active\" before, \"step <id> done --note '<one line on what you did>'\" after; \"skipped\" when the step is already satisfied, \"blocked\" when you cannot continue, saying why in --note. Add steps as the work reveals them. Ask when you need another decision or value (--choices for a pick, plain for text; --optional when you can proceed without an answer; --wait only when the answer is the very next thing you need). A choice question is multi-select: the user checks any number of your options, and the answer is an array of the ids they checked, plus their own text as an entry when they typed one. Pass --single only when the options exclude each other (one framework, one model, yes or no); the answer is then one id. Every option must carry an icon, given as --icons \"<id>=<icon>,...\" with the closest name from this pack: ${OPTION_ICONS.join(\", \")}. A question without an icon for each option is refused. Open inputs the user has not answered are listed under \"inputs\" in \"status\"; check them before asking again.\nThe API key behind a model answer is not in the answer and you never see it: pick the variable name the provider's SDK reads (OPENAI_API_KEY, ANTHROPIC_API_KEY, ...) and run\n npx setup-agent ${url} env <input-id> <ENV_VAR> --file .env.local\nwhich writes ENV_VAR=<key> into that file. Use the answer's model in the chat route and map reasoningEffort onto the provider's setting when it is set. Never invent a key or a model name.\n\nWhen every product is installed and verified, run\n npx setup-agent ${url} done --summary \"<one or two lines on what you installed and how to try it>\" --preview http://localhost:<port>\nBefore that, start the project's dev server in the background when the project has one, check that the page you changed loads, and pass the address as --preview (the page itself when it is not the root); leave the server running so the user can try the result. Omit --preview when there is nothing to open. This proposes closing the checkout; only the user closes it. Do not stop the stream or exit: stay idle on it, because the user may follow up with a \"message.created\" event. Handle it, then run \"done\" again. The stream prints \"finished\" once the user closes the checkout, and \"cancelled\" if they end it early. Keep the stream running after either: the user's next setup starts on this same URL and arrives as \"checkout.created\" with its products. Run\n npx setup-agent ${url} wait start\nwhich returns once that setup begins, then run \"status\" for its products, guides and instructions and go through plan and install again. A \"checkout.created\" event while you are still working means the user replaced this setup: drop what you were doing and start over the same way.\n`;\n\ntype Parsed = { positional: string[]; flags: Map<string, string | true> };\n\nconst parseArgs = (argv: readonly string[]): Parsed => {\n const positional: string[] = [];\n const flags = new Map<string, string | true>();\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (!arg.startsWith(\"--\")) {\n positional.push(arg);\n continue;\n }\n const name = arg.slice(2);\n const next = argv[i + 1];\n if (next !== undefined && !next.startsWith(\"--\")) {\n flags.set(name, next);\n i++;\n } else {\n flags.set(name, true);\n }\n }\n return { positional, flags };\n};\n\nconst flagText = (flags: Parsed[\"flags\"], name: string) => {\n const value = flags.get(name);\n return typeof value === \"string\" ? value : undefined;\n};\n\nconst fail = (message: string): never => {\n process.stderr.write(`setup-agent: ${message}\\n`);\n return process.exit(1);\n};\n\nconst print = (value: unknown) => {\n process.stdout.write(`${JSON.stringify(value)}\\n`);\n};\n\nconst AGENT_ENV: readonly [kind: string, variable: string][] = [\n [\"claude\", \"CLAUDECODE\"],\n [\"codex\", \"CODEX_SANDBOX\"],\n [\"codex\", \"CODEX_SANDBOX_NETWORK_DISABLED\"],\n [\"cursor\", \"CURSOR_AGENT\"],\n [\"gemini\", \"GEMINI_CLI\"],\n];\n\n/** The coding agent this process runs under, from the variables each one exports to its shell. */\nexport const detectAgentKind = (env: NodeJS.ProcessEnv = process.env) =>\n AGENT_ENV.find(([, variable]) => env[variable])?.[0];\n\ntype Launcher = {\n kind: string;\n name: string;\n command: string;\n args: (prompt: string) => string[];\n install: string;\n};\n\nconst LAUNCHERS: Launcher[] = [\n {\n kind: \"claude\",\n name: \"Claude Code\",\n command: \"claude\",\n args: (prompt) => [prompt],\n install: \"npm install -g @anthropic-ai/claude-code\",\n },\n {\n kind: \"codex\",\n name: \"Codex\",\n command: \"codex\",\n args: (prompt) => [prompt],\n install: \"npm install -g @openai/codex\",\n },\n {\n kind: \"cursor\",\n name: \"Cursor\",\n command: \"agent\",\n args: (prompt) => [prompt],\n install: \"curl https://cursor.com/install -fsS | bash\",\n },\n {\n kind: \"gemini\",\n name: \"Gemini CLI\",\n command: \"gemini\",\n args: (prompt) => [\"-i\", prompt],\n install: \"npm install -g @google/gemini-cli\",\n },\n];\n\n/** True when a person typed the command into a terminal rather than an agent running it from its shell. */\nexport const isDirectInvocation = (\n env: NodeJS.ProcessEnv = process.env,\n stdin: { isTTY?: boolean | undefined } = process.stdin,\n stdout: { isTTY?: boolean | undefined } = process.stdout,\n) =>\n detectAgentKind(env) === undefined &&\n stdin.isTTY === true &&\n stdout.isTTY === true;\n\nconst chooseLauncher = async () => {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n process.stdout.write(\n \"This command is meant for a coding agent, and none is running here.\\nStart one to do the install:\\n\",\n );\n LAUNCHERS.forEach((launcher, index) => {\n process.stdout.write(` ${index + 1}. ${launcher.name}\\n`);\n });\n while (true) {\n const answer = (\n await rl.question(\"Choose [1]: \").catch(() => process.exit(1))\n ).trim();\n if (answer === \"\") return LAUNCHERS[0]!;\n const byNumber = LAUNCHERS[Number(answer) - 1];\n if (byNumber) return byNumber;\n const byName = LAUNCHERS.find(\n (launcher) =>\n launcher.kind === answer.toLowerCase() ||\n launcher.name.toLowerCase() === answer.toLowerCase(),\n );\n if (byName) return byName;\n process.stdout.write(\"Enter a number from the list.\\n\");\n }\n } finally {\n rl.close();\n }\n};\n\nconst launchAgent = async (url: string) => {\n const launcher = await chooseLauncher();\n const prompt = `Run \\`npx setup-agent ${url}\\` and follow the instructions it prints.`;\n process.stdout.write(`\\nStarting ${launcher.name}...\\n\\n`);\n const child = spawn(launcher.command, launcher.args(prompt), {\n stdio: \"inherit\",\n });\n const code = await new Promise<number>((resolve) => {\n child.on(\"error\", (error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") {\n process.stderr.write(\n `${launcher.name} is not installed. Install it with:\\n ${launcher.install}\\nthen run this command again.\\n`,\n );\n } else {\n process.stderr.write(`${error.message}\\n`);\n }\n resolve(1);\n });\n child.on(\"exit\", (status) => resolve(status ?? 1));\n });\n process.exit(code);\n};\n\nconst requireState = (client: CheckoutClient): Checkout.State =>\n client.state ?? fail(\"the checkout has no state\");\n\nconst summary = (state: Checkout.State) => {\n const plan = currentPlan(state);\n return {\n status: state.status,\n agentConnected: isAgentPresent(state),\n products: state.products,\n instructions: state.instructions,\n plan:\n plan === undefined\n ? null\n : {\n revision: plan.revision,\n status: plan.status,\n ...(plan.feedback !== undefined && { feedback: plan.feedback }),\n },\n progress: stepProgress(state),\n steps: state.steps,\n inputs: state.inputs,\n log: state.log.slice(-20),\n pendingMessages: state.log.filter(\n (entry) => entry.role === \"user\" && entry.acknowledgedAt === undefined,\n ),\n };\n};\n\nconst STEP_STATUSES: readonly Checkout.StepStatus[] = [\n \"pending\",\n \"active\",\n \"done\",\n \"skipped\",\n \"blocked\",\n];\n\ntype StreamEvent =\n | { event: \"planning.started\" }\n | { event: \"message.created\"; messageId: string; text: string }\n | {\n event: \"input.answered\";\n inputId: string;\n prompt: string;\n /** The entries of a multiple choice answer, else the answer text. */\n answer: string | string[];\n note?: string;\n /** True when a choice answer is, or a multiple one contains, the user's own text rather than an option. */\n custom?: true;\n }\n | { event: \"input.dismissed\"; inputId: string; prompt: string }\n | { event: \"plan.approved\"; revision: number }\n | { event: \"plan.changes-requested\"; revision: number; feedback: string }\n | { event: \"finished\"; next: string }\n | { event: \"cancelled\"; next: string }\n | { event: \"checkout.created\"; products: string[] };\n\nconst STAY_CONNECTED =\n \"This setup is over, not your connection. Keep the stream running, tell the user you are connected and waiting for their next setup, and run wait start.\";\n\nexport const diffEvents = (\n before: Checkout.State,\n after: Checkout.State,\n): StreamEvent[] => {\n const events: StreamEvent[] = [];\n if (before.status === \"waiting\" && after.status === \"planning\") {\n events.push({ event: \"planning.started\" });\n }\n for (const entry of after.log) {\n if (\n entry.role === \"user\" &&\n entry.acknowledgedAt === undefined &&\n !before.log.some((previous) => previous.id === entry.id)\n ) {\n events.push({\n event: \"message.created\",\n messageId: entry.id,\n text: entry.text,\n });\n }\n }\n for (const input of after.inputs) {\n const previous = before.inputs.find(\n (candidate) => candidate.id === input.id,\n );\n if (previous?.status === input.status) continue;\n if (input.status === \"answered\") {\n const answer = input.answer ?? \"\";\n events.push({\n event: \"input.answered\",\n inputId: input.id,\n prompt: input.prompt,\n ...answerFields(input, answer),\n });\n } else if (input.status === \"dismissed\" && after.status !== \"cancelled\") {\n events.push({\n event: \"input.dismissed\",\n inputId: input.id,\n prompt: input.prompt,\n });\n }\n }\n for (const plan of after.plans) {\n const previous = before.plans.find(\n (candidate) => candidate.revision === plan.revision,\n );\n if (previous?.status === plan.status || plan.status === \"proposed\") {\n continue;\n }\n if (plan.status === \"approved\") {\n events.push({ event: \"plan.approved\", revision: plan.revision });\n } else {\n events.push({\n event: \"plan.changes-requested\",\n revision: plan.revision,\n feedback: plan.feedback ?? \"\",\n });\n }\n }\n if (before.status !== \"done\" && after.status === \"done\") {\n events.push({ event: \"finished\", next: STAY_CONNECTED });\n }\n if (before.status !== \"cancelled\" && after.status === \"cancelled\") {\n events.push({ event: \"cancelled\", next: STAY_CONNECTED });\n }\n if (after.id !== null && after.id !== before.id) {\n events.push({\n event: \"checkout.created\",\n products: after.products.map((product) => product.slug),\n });\n }\n return events;\n};\n\nconst stream = async (client: CheckoutClient) => {\n let previous = requireState(client);\n let pending: StreamEvent[] = diffEvents({ ...previous, log: [] }, previous);\n let timer: ReturnType<typeof setTimeout> | null = null;\n const flush = () => {\n timer = null;\n if (pending.length === 0) return;\n print(pending);\n pending = [];\n };\n client.subscribe(() => {\n const next = client.state;\n if (next === undefined) return;\n pending.push(...diffEvents(previous, next));\n previous = next;\n if (pending.length > 0 && timer === null) timer = setTimeout(flush, 200);\n });\n flush();\n const kind = detectAgentKind();\n await client.commands[\"agent/hello\"]({\n cwd: process.cwd(),\n ...(kind !== undefined && { kind }),\n });\n const heartbeat = setInterval(() => {\n void client.commands[\"agent/heartbeat\"]().catch(() => {});\n }, AGENT_HEARTBEAT_MS);\n const stop = () => {\n clearInterval(heartbeat);\n flush();\n void client.commands[\"agent/bye\"]()\n .catch(() => {})\n .finally(() => {\n client.dispose();\n process.exit(0);\n });\n };\n process.on(\"SIGHUP\", stop);\n process.on(\"SIGINT\", stop);\n process.on(\"SIGTERM\", stop);\n await new Promise(() => {});\n};\n\n/** Resolves once a checkout is planning or installing, or once the one being waited on is cancelled; a checkout already closed when the wait begins is over, so the wait is for the next one. */\nexport const waitForStart = (\n client: Pick<CheckoutClient, \"state\" | \"subscribe\">,\n) =>\n new Promise<Checkout.Status>((resolve) => {\n let previous: Checkout.Status | undefined;\n const check = () => {\n const state = client.state;\n if (state === undefined) return;\n const started =\n state.status === \"planning\" || state.status === \"installing\";\n const ended = isClosed(state) && previous === \"waiting\";\n previous = state.status;\n if (!started && !ended) return;\n unsubscribe();\n resolve(state.status);\n };\n const unsubscribe = client.subscribe(check);\n check();\n });\n\nconst waitForInput = (client: CheckoutClient, inputId: string) =>\n new Promise<Checkout.Input>((resolve) => {\n const check = () => {\n const state = client.state;\n const input = state?.inputs.find((candidate) => candidate.id === inputId);\n if (!input) return;\n if (input.status !== \"open\" || state?.status === \"cancelled\") {\n unsubscribe();\n resolve(input);\n }\n };\n const unsubscribe = client.subscribe(check);\n check();\n });\n\nconst waitForPlan = (client: CheckoutClient, revision: number) =>\n new Promise<Checkout.Plan | undefined>((resolve) => {\n const check = () => {\n const state = client.state;\n const plan = state?.plans.find(\n (candidate) => candidate.revision === revision,\n );\n if (\n plan === undefined ||\n plan.status !== \"proposed\" ||\n state?.status === \"cancelled\"\n ) {\n unsubscribe();\n resolve(plan);\n }\n };\n const unsubscribe = client.subscribe(check);\n check();\n });\n\nconst decided = (plan: Checkout.Plan | undefined, state: Checkout.State) => {\n if (state.status === \"cancelled\") return { ok: false, status: \"cancelled\" };\n if (plan === undefined) return fail(\"the plan disappeared\");\n return plan.status === \"approved\"\n ? { ok: true, decision: \"approve\", revision: plan.revision }\n : {\n ok: true,\n decision: \"revise\",\n revision: plan.revision,\n feedback: plan.feedback ?? \"\",\n };\n};\n\nconst splitList = (value: string | undefined) =>\n value\n ?.split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean);\n\nexport const askSeed = (\n rest: readonly string[],\n flags: Parsed[\"flags\"],\n): Checkout.InputSeed & { stepId?: string } => {\n const stepId = flagText(flags, \"step\");\n const optional = flags.has(\"optional\");\n const fallback = flagText(flags, \"default\");\n const preset = flagText(flags, \"preset\");\n if (preset !== undefined) {\n if (!isPresetId(preset)) {\n return fail(\n `unknown preset \"${preset}\"; use ${Object.keys(INPUT_PRESETS).join(\" or \")}`,\n );\n }\n const only = splitList(flagText(flags, \"only\"));\n const found = splitList(flagText(flags, \"choices\"))?.map((entry) => {\n const [id = \"\", ...description] = entry.split(\"=\");\n return description.length === 0\n ? { id }\n : { id, description: description.join(\"=\") };\n });\n try {\n const seed = presetInput(preset, {\n ...(only !== undefined && { only }),\n ...(found !== undefined && { found }),\n ...(fallback !== undefined && { default: fallback }),\n optional,\n ...(stepId !== undefined && { stepId }),\n });\n return rest[0] === undefined ? seed : { ...seed, prompt: rest[0] };\n } catch (error) {\n return fail(error instanceof Error ? error.message : String(error));\n }\n }\n const prompt = rest[0] ?? fail(\"usage: ask <prompt>\");\n const product = flagText(flags, \"product\");\n if (product !== undefined) {\n return {\n kind: \"product\",\n prompt,\n product,\n optional,\n ...(stepId !== undefined && { stepId }),\n };\n }\n const choices = splitList(flagText(flags, \"choices\"));\n if (choices !== undefined) {\n if (flags.has(\"single\") && flags.has(\"multiple\"))\n return fail(\"--single and --multiple exclude each other\");\n const icons = new Map<string, string>();\n for (const entry of splitList(flagText(flags, \"icons\")) ?? []) {\n const [id = \"\", icon = \"\"] = entry.split(\"=\", 2);\n if (!choices.includes(id))\n return fail(`no choice \"${id}\" to put an icon on`);\n if (!isOptionIcon(icon)) {\n return fail(\n `unknown icon \"${icon}\"; pick one of ${OPTION_ICONS.join(\", \")}`,\n );\n }\n icons.set(id, icon);\n }\n const missing = choices.filter((id) => !icons.has(id));\n if (missing.length > 0) {\n return fail(\n `every option needs an icon; ${missing.map((id) => `\"${id}\"`).join(\", \")} ${missing.length === 1 ? \"has\" : \"have\"} none. Pass --icons \"${choices.map((id) => `${id}=<icon>`).join(\",\")}\" with icons from: ${OPTION_ICONS.join(\", \")}`,\n );\n }\n return {\n kind: \"choice\",\n prompt,\n options: choices.map((id) => ({ id, label: id, icon: icons.get(id)! })),\n ...(!flags.has(\"single\") && { multiple: true }),\n ...(fallback !== undefined && { default: fallback }),\n optional,\n ...(stepId !== undefined && { stepId }),\n };\n }\n const placeholder = flagText(flags, \"placeholder\");\n return {\n prompt,\n ...(placeholder !== undefined && { placeholder }),\n optional,\n ...(stepId !== undefined && { stepId }),\n };\n};\n\n/** Sets `key=value` in dotenv text, replacing an existing line for the key. */\nexport const upsertEnvLine = (content: string, key: string, value: string) => {\n const line = `${key}=${value}`;\n const lines = content === \"\" ? [] : content.replace(/\\n$/, \"\").split(\"\\n\");\n const index = lines.findIndex((entry) =>\n new RegExp(`^(export\\\\s+)?${key}\\\\s*=`).test(entry),\n );\n if (index === -1) lines.push(line);\n else lines[index] = line;\n return `${lines.join(\"\\n\")}\\n`;\n};\n\nconst answerFields = (input: Checkout.Input, answer: string) => ({\n answer: (input.multiple && parseMultipleAnswer(answer)) || answer,\n ...(input.note !== undefined && { note: input.note }),\n ...(input.kind === \"choice\" &&\n classifyChoiceAnswer(input, answer) === \"custom\" && {\n custom: true as const,\n }),\n});\n\nconst answered = (input: Checkout.Input) => ({\n ok: true,\n inputId: input.id,\n status: input.status,\n ...(input.answer !== undefined && answerFields(input, input.answer)),\n});\n\nexport const main = async (argv: readonly string[]) => {\n const { positional, flags } = parseArgs(argv);\n const [first, command = \"intro\", ...rest] = positional;\n if (flags.has(\"help\") || flags.has(\"h\") || first === undefined) {\n process.stdout.write(HELP);\n return process.exit(first === undefined ? 1 : 0);\n }\n const url = first;\n if (command === \"intro\" && isDirectInvocation()) return launchAgent(url);\n\n const client = await connectCheckout(url).catch((error: unknown) =>\n fail(error instanceof Error ? error.message : String(error)),\n );\n const commands = client.commands;\n\n try {\n switch (command) {\n case \"intro\": {\n const kind = detectAgentKind();\n await commands[\"agent/intro\"]({\n ...(kind !== undefined && { kind }),\n }).catch(() => {});\n process.stdout.write(INSTRUCTIONS(url));\n process.stdout.write(\"\\nCurrent checkout:\\n\");\n print(summary(requireState(client)));\n break;\n }\n case \"status\": {\n print(summary(requireState(client)));\n break;\n }\n case \"stream\": {\n await stream(client);\n break;\n }\n case \"step\": {\n const usage =\n \"usage: step <step-id> <pending|active|done|skipped|blocked>\";\n const stepId = rest[0] ?? fail(usage);\n const status = rest[1];\n if (!STEP_STATUSES.includes(status as Checkout.StepStatus)) fail(usage);\n const note = flagText(flags, \"note\");\n await commands[\"agent/step\"]({\n stepId,\n status: status as Checkout.StepStatus,\n ...(note !== undefined && { note }),\n });\n print({ ok: true, stepId, status });\n break;\n }\n case \"add-step\": {\n const title = rest[0] ?? fail(\"usage: add-step <title>\");\n const detail = flagText(flags, \"detail\");\n const product = flagText(flags, \"product\");\n const result = await commands[\"agent/add-step\"]({\n title,\n ...(detail !== undefined && { detail }),\n ...(product !== undefined && { product }),\n ...(flags.has(\"active\") && { active: true }),\n });\n print({ ok: true, ...result });\n break;\n }\n case \"plan\": {\n const file = flagText(flags, \"file\");\n const markdown =\n file !== undefined\n ? await readFile(resolve(file), \"utf8\").catch(() =>\n fail(`could not read ${file}`),\n )\n : (rest[0] ?? fail(\"usage: plan <markdown> | plan --file <path>\"));\n const { revision } = await commands[\"agent/plan\"]({ markdown });\n if (!flags.has(\"wait\")) {\n print({ ok: true, revision });\n break;\n }\n const plan = await waitForPlan(client, revision);\n print(decided(plan, requireState(client)));\n break;\n }\n case \"ask\": {\n const { inputId } = await commands[\"agent/ask\"](askSeed(rest, flags));\n if (!flags.has(\"wait\")) {\n print({ ok: true, inputId });\n break;\n }\n print(await waitForInput(client, inputId).then(answered));\n break;\n }\n case \"wait\": {\n const target =\n rest[0] ?? fail(\"usage: wait start | wait <input-id> | wait plan\");\n const state = requireState(client);\n if (target === \"start\") {\n const status = await waitForStart(client);\n print({\n ok: status === \"planning\" || status === \"installing\",\n status,\n });\n break;\n }\n if (target === \"plan\") {\n const plan = currentPlan(state) ?? fail(\"no plan has been proposed\");\n print(\n decided(\n await waitForPlan(client, plan.revision),\n requireState(client),\n ),\n );\n break;\n }\n if (!state.inputs.some((input) => input.id === target)) {\n fail(`no input \"${target}\"`);\n }\n print(await waitForInput(client, target).then(answered));\n break;\n }\n case \"env\": {\n const usage = \"usage: env <input-id> <ENV_VAR> [--file <path>]\";\n const inputId = rest[0] ?? fail(usage);\n const envKey = rest[1] ?? fail(usage);\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(envKey)) {\n fail(`\"${envKey}\" is not an environment variable name`);\n }\n const input =\n requireState(client).inputs.find(\n (candidate) => candidate.id === inputId,\n ) ?? fail(`no input \"${inputId}\"`);\n if (input.status !== \"answered\") {\n fail(`input \"${inputId}\" is ${input.status}`);\n }\n if (parseModelAnswer(input.answer ?? \"\") === undefined) {\n fail(`input \"${inputId}\" is not an llm-provider answer`);\n }\n const file = resolve(flagText(flags, \"file\") ?? \".env.local\");\n const response = await fetch(\n `${url.replace(/\\/$/, \"\")}/secret/${encodeURIComponent(inputId)}`,\n );\n if (response.status === 404) {\n fail(`the key for \"${inputId}\" was already taken or never deposited`);\n }\n if (!response.ok) fail(`could not fetch the key: ${response.status}`);\n const value = await response.text();\n const current = await readFile(file, \"utf8\").catch(() => \"\");\n await writeFile(file, upsertEnvLine(current, envKey, value));\n print({ ok: true, envKey, file });\n break;\n }\n case \"ack\": {\n const messageId = rest[0] ?? fail(\"usage: ack <message-id>\");\n await commands[\"agent/ack\"]({ messageId });\n print({ ok: true });\n break;\n }\n case \"log\": {\n const text = rest[0] ?? fail(\"usage: log <text>\");\n await commands[\"agent/log\"]({ text });\n print({ ok: true });\n break;\n }\n case \"done\": {\n const summary = flagText(flags, \"summary\");\n const preview = flagText(flags, \"preview\");\n await commands[\"agent/done\"]({\n ...(summary !== undefined && { summary }),\n ...(preview !== undefined && { preview }),\n });\n print({\n ok: true,\n status: \"proposed\",\n next: \"Keep the stream running and stay idle. The user closes the checkout or sends a follow-up message.\",\n });\n break;\n }\n default:\n fail(`unknown command \"${command}\"\\n\\n${HELP}`);\n }\n } catch (error) {\n fail(error instanceof Error ? error.message : String(error));\n } finally {\n client.dispose();\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","names":["connectCheckout","CheckoutClient","INPUT_PRESETS","isPresetId","presetInput","spawn","readFile","writeFile","createInterface","resolve","AGENT_HEARTBEAT_MS","OPTION_ICONS","classifyChoiceAnswer","currentPlan","isAgentPresent","isClosed","isOptionIcon","parseModelAnswer","parseMultipleAnswer","stepProgress","Checkout","HELP","join","INSTRUCTIONS","url","Parsed","positional","flags","Map","parseArgs","argv","i","length","arg","startsWith","push","name","slice","next","undefined","set","flagText","value","get","fail","message","process","stderr","write","exit","print","stdout","JSON","stringify","AGENT_ENV","kind","variable","detectAgentKind","env","NodeJS","ProcessEnv","find","Launcher","command","args","prompt","install","LAUNCHERS","isDirectInvocation","stdin","isTTY","chooseLauncher","rl","input","output","forEach","launcher","index","answer","question","catch","trim","byNumber","Number","byName","toLowerCase","close","launchAgent","child","stdio","code","Promise","on","error","ErrnoException","status","requireState","client","State","state","summary","plan","agentConnected","products","instructions","revision","feedback","progress","steps","inputs","log","pendingMessages","filter","entry","role","acknowledgedAt","STEP_STATUSES","StepStatus","StreamEvent","event","messageId","text","inputId","note","custom","STAY_CONNECTED","diffEvents","before","after","events","some","previous","id","candidate","answerFields","plans","map","product","slug","stream","pending","timer","ReturnType","setTimeout","flush","subscribe","commands","cwd","heartbeat","setInterval","stop","clearInterval","finally","dispose","waitForStart","Pick","Status","check","started","ended","unsubscribe","waitForInput","Input","waitForPlan","Plan","decided","ok","decision","splitList","split","Boolean","askSeed","rest","InputSeed","stepId","optional","has","fallback","preset","Object","keys","only","found","description","seed","default","Error","String","choices","icons","icon","includes","missing","options","label","multiple","placeholder","upsertEnvLine","content","key","line","lines","replace","findIndex","RegExp","test","const","answered","main","first","usage","title","detail","result","active","file","markdown","then","target","envKey","response","fetch","encodeURIComponent","current","preview"],"sources":["../src/cli.ts"],"sourcesContent":["/// <reference types=\"node\" />\nimport { connectCheckout } from \"./client\";\nimport type { CheckoutClient } from \"./client\";\nimport { INPUT_PRESETS, isPresetId, presetInput } from \"./presets\";\nimport { spawn } from \"node:child_process\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { createInterface } from \"node:readline/promises\";\nimport { resolve } from \"node:path\";\nimport {\n AGENT_HEARTBEAT_MS,\n OPTION_ICONS,\n classifyChoiceAnswer,\n currentPlan,\n isAgentPresent,\n isClosed,\n isOptionIcon,\n parseModelAnswer,\n parseMultipleAnswer,\n stepProgress,\n} from \"./protocol\";\nimport type { Checkout } from \"./protocol\";\n\nexport const HELP = `setup-agent <url> [command]\n\nCommands (the url always comes first):\n status Print the setup as JSON.\n stream Follow the setup: one JSON array of events per line.\n log <text> Send a message to the user: a reply, something no question or step conveys, or one wrap-up when finished.\n ack <message-id> Acknowledge a user message.\n ask <prompt> [--placeholder <text>] [--optional] [--step <step-id>] [--wait]\n Ask the user for a line of text.\n ask [prompt] --preset <framework|llm-provider|project> [--only <id,id>] [--choices <id=description,...>] [--default <id>] [--optional] [--step <step-id>] [--wait]\n Ask a standard question. --only restricts the options (one id locks it in); --default preselects one.\n \"framework\" answers \"<framework>:<language>\"; \"llm-provider\" answers JSON with provider, model and reasoningEffort; the key itself arrives through \"env\".\n \"project\" lists the React projects you found as --choices \"<path>=<what it is>,...\" ahead of \"New project\"; it answers a path, or \"new:<next|vite|react-router|tanstack-start|expo>\".\n ask <prompt> --choices <id,id,...> --icons <id=icon,...> [--single] [--default <id>] [--optional] [--step <step-id>] [--wait]\n Ask the user to pick from your own options; they may also type their own answer.\n The user checks any number of options and the answer is a JSON array of option ids (their own text is one entry).\n --single is for options that exclude each other (one framework, one model, yes or no); the answer is then one id.\n --icons is required and names an icon for every option, from: ${OPTION_ICONS.join(\", \")}.\n ask <prompt> --product <slug> [--wait]\n Propose adding a product to this setup, such as one another product needs first. Answers \"added\"; the product and its guide then appear in \"status\".\n wait start Block until the user clicks Begin plan in their browser; after a finished or cancelled setup, until the next one begins.\n wait <input-id> Block until the user answers or dismisses an input. A multi-select answer prints as an array.\n plan (<markdown> | --file <path>) [--wait]\n Propose the plan for the user to approve. --wait blocks until they decide.\n wait plan Block until the user approves the plan or asks for changes.\n env <input-id> <ENV_VAR> [--file <path>]\n Write the API key behind an answered llm-provider input to an env file (default .env.local) as ENV_VAR=<key>, without printing it.\n add-step <title> [--detail <text>] [--product <slug>] [--active]\n Declare a step of the install (after the plan is approved). Prints its id.\n step <step-id> <status> [--note <text>]\n Set a step to pending|active|done|skipped|blocked.\n done [--summary <text>] [--preview <url>]\n Mark every remaining step done and propose closing; the user closes it.\n`;\n\nexport const INSTRUCTIONS = (\n url: string,\n) => `You are the coding agent for a setup the user opened in their browser. The browser shows this setup live; every command below updates it. The user watches the browser, not this terminal: once setup begins, send questions through \"ask\" so the user can answer in their browser.\n\nFirst, start a persistent Monitor on\n npx setup-agent ${url} stream\nand keep it running: the browser shows you as connected only while it runs, and it is how answers reach you. The URL is the user's link to you from their browser, not one setup: it outlives this one, and their next setup arrives on the same stream. Each line is a JSON array of events: \"planning.started\", \"input.answered\" ({inputId, answer, note?}; answer is an array for a choice question not asked with --single), \"input.dismissed\", \"plan.approved\", \"plan.changes-requested\" ({feedback}), \"message.created\" ({messageId, text}), \"finished\", \"cancelled\", \"setup.created\" ({products}).\n\nThe user can message you at any time, including while a question is waiting. Read \"message.created\" events, acknowledge each with \"npx setup-agent ${url} ack <message-id>\", and reply using \"log\" with what you will do. Pending messages are replayed when the stream starts and listed in \"status\". A message is not an answer or plan approval; keep those decisions explicit.\n\n\"log\" is a message to the user, and the user reads every one: send it only to reply to their message, to tell them something no question or step conveys (a blocker, a deviation from the plan, a decision you made for them), or once as a wrap-up when the install is finished. Findings go in the plan, progress in step notes, questions in \"ask\"; do not narrate.\n\nBefore planning, check the current setup status. If it is \"waiting\", tell the user in your own conversation: \"I'm connected. Go back to your browser and click Begin plan. You can watch my progress, answer questions, and steer me there.\" Then run:\n npx setup-agent ${url} wait start\nKeep the stream running while you wait. Do not inspect the project, fetch guides, ask setup questions, or propose a plan until this command returns {\"ok\":true}. Only the user in the browser should start planning; never invoke the browser's start command yourself. \"planning.started\" also arrives on the stream. If the status is already \"planning\" or \"installing\", resume that phase without asking the user to begin again. If it is \"done\" or \"cancelled\", that setup is over: tell the user you are connected and waiting for their next setup, and run \"wait start\", which returns once the next one begins.\n\nThe remaining phases are plan, then install.\n\n1. Plan. Investigate only; change nothing yet. If the current directory is not an app to install into (a monorepo root, an empty folder, a library), ask where to install first:\n npx setup-agent ${url} ask \"Which project should I install into?\" --placeholder \"An absolute path, or a path for a new Next.js app\" --wait\nRead the project: its app framework and package manager, the agent framework and model provider it already uses, where components live, any existing chat route. Fetch each product's \"guide\" URL from the setup below; it is the install reference. Ask for what the project does not decide, using the standard questions so the browser can render them well:\n npx setup-agent ${url} ask --preset framework\n npx setup-agent ${url} ask --preset llm-provider\nIf a product's guide needs another product the project does not have yet (most need assistant-ui installed and rendering a thread), do not stop and do not install it on the side. Propose adding it to this setup:\n npx setup-agent ${url} ask \"assistant-ui is not installed in this project yet. Add it to this setup?\" --product assistant-ui --wait\nOn \"added\", run \"status\", fetch the new product's guide, and install it first in your plan. If the user dismisses it, follow what the guide says about the missing product.\nSkip a preset the code already answers (a chat route on the AI SDK, a provider key in .env.local) and log why. Ask everything at once, keep investigating, and block with \"wait <input-id>\" only when nothing else can proceed. A framework answer is \"<framework>:<language>\", for example \"ai-sdk:typescript\". A model answer is JSON {\"provider\",\"model\",\"reasoningEffort\"?}. An answer may carry a \"note\" from the user; follow it. A choice answer that matches none of the options is the user's own text.\nThen write the plan as markdown and submit it:\n npx setup-agent ${url} plan --file <path> --wait\nUse these headings, each with bullets: \"## What I found\" (facts as \\`- **Label:** value\\`: app framework, package manager, agent framework, model provider and model, where components live), \"## What I will install\" (packages and files, one per bullet), \"## Steps\" (a numbered list, one line each), and \"## Open questions\" only if any remain. Keep it under 30 lines; no preamble. \"plan --wait\" prints {\"decision\":\"approve\"} or {\"decision\":\"revise\",\"feedback\":\"...\"}. On revise, address the feedback (ask again if you need to) and submit a new plan; repeat until approved. Do not change the project before approval: the setup refuses steps until then.\n\n2. Install. Declare the steps from your plan up front, in order:\n npx setup-agent ${url} add-step \"<title>\" --detail \"<one line>\"\nwhich prints the step id. Then work through them: \"step <id> active\" before, \"step <id> done --note '<one line on what you did>'\" after; \"skipped\" when the step is already satisfied, \"blocked\" when you cannot continue, saying why in --note. Add steps as the work reveals them. Ask when you need another decision or value (--choices for a pick, plain for text; --optional when you can proceed without an answer; --wait only when the answer is the very next thing you need). A choice question is multi-select: the user checks any number of your options, and the answer is an array of the ids they checked, plus their own text as an entry when they typed one. Pass --single only when the options exclude each other (one framework, one model, yes or no); the answer is then one id. Every option must carry an icon, given as --icons \"<id>=<icon>,...\" with the closest name from this pack: ${OPTION_ICONS.join(\", \")}. A question without an icon for each option is refused. Open inputs the user has not answered are listed under \"inputs\" in \"status\"; check them before asking again.\nThe API key behind a model answer is not in the answer and you never see it: pick the variable name the provider's SDK reads (OPENAI_API_KEY, ANTHROPIC_API_KEY, ...) and run\n npx setup-agent ${url} env <input-id> <ENV_VAR> --file .env.local\nwhich writes ENV_VAR=<key> into that file. Use the answer's model in the chat route and map reasoningEffort onto the provider's setting when it is set. Never invent a key or a model name.\n\nWhen every product is installed and verified, run\n npx setup-agent ${url} done --summary \"<one or two lines on what you installed and how to try it>\" --preview http://localhost:<port>\nBefore that, start the project's dev server in the background when the project has one, check that the page you changed loads, and pass the address as --preview (the page itself when it is not the root); leave the server running so the user can try the result. Omit --preview when there is nothing to open. This proposes closing the setup; only the user closes it. Do not stop the stream or exit: stay idle on it, because the user may follow up with a \"message.created\" event. Handle it, then run \"done\" again. The stream prints \"finished\" once the user closes the setup, and \"cancelled\" if they end it early. Keep the stream running after either: the user's next setup starts on this same URL and arrives as \"setup.created\" with its products. Run\n npx setup-agent ${url} wait start\nwhich returns once that setup begins, then run \"status\" for its products, guides and instructions and go through plan and install again. A \"setup.created\" event while you are still working means the user replaced this setup: drop what you were doing and start over the same way.\n`;\n\ntype Parsed = { positional: string[]; flags: Map<string, string | true> };\n\nconst parseArgs = (argv: readonly string[]): Parsed => {\n const positional: string[] = [];\n const flags = new Map<string, string | true>();\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (!arg.startsWith(\"--\")) {\n positional.push(arg);\n continue;\n }\n const name = arg.slice(2);\n const next = argv[i + 1];\n if (next !== undefined && !next.startsWith(\"--\")) {\n flags.set(name, next);\n i++;\n } else {\n flags.set(name, true);\n }\n }\n return { positional, flags };\n};\n\nconst flagText = (flags: Parsed[\"flags\"], name: string) => {\n const value = flags.get(name);\n return typeof value === \"string\" ? value : undefined;\n};\n\nconst fail = (message: string): never => {\n process.stderr.write(`setup-agent: ${message}\\n`);\n return process.exit(1);\n};\n\nconst print = (value: unknown) => {\n process.stdout.write(`${JSON.stringify(value)}\\n`);\n};\n\nconst AGENT_ENV: readonly [kind: string, variable: string][] = [\n [\"claude\", \"CLAUDECODE\"],\n [\"codex\", \"CODEX_SANDBOX\"],\n [\"codex\", \"CODEX_SANDBOX_NETWORK_DISABLED\"],\n [\"cursor\", \"CURSOR_AGENT\"],\n [\"gemini\", \"GEMINI_CLI\"],\n];\n\n/** The coding agent this process runs under, from the variables each one exports to its shell. */\nexport const detectAgentKind = (env: NodeJS.ProcessEnv = process.env) =>\n AGENT_ENV.find(([, variable]) => env[variable])?.[0];\n\ntype Launcher = {\n kind: string;\n name: string;\n command: string;\n args: (prompt: string) => string[];\n install: string;\n};\n\nconst LAUNCHERS: Launcher[] = [\n {\n kind: \"claude\",\n name: \"Claude Code\",\n command: \"claude\",\n args: (prompt) => [prompt],\n install: \"npm install -g @anthropic-ai/claude-code\",\n },\n {\n kind: \"codex\",\n name: \"Codex\",\n command: \"codex\",\n args: (prompt) => [prompt],\n install: \"npm install -g @openai/codex\",\n },\n {\n kind: \"cursor\",\n name: \"Cursor\",\n command: \"agent\",\n args: (prompt) => [prompt],\n install: \"curl https://cursor.com/install -fsS | bash\",\n },\n {\n kind: \"gemini\",\n name: \"Gemini CLI\",\n command: \"gemini\",\n args: (prompt) => [\"-i\", prompt],\n install: \"npm install -g @google/gemini-cli\",\n },\n];\n\n/** True when a person typed the command into a terminal rather than an agent running it from its shell. */\nexport const isDirectInvocation = (\n env: NodeJS.ProcessEnv = process.env,\n stdin: { isTTY?: boolean | undefined } = process.stdin,\n stdout: { isTTY?: boolean | undefined } = process.stdout,\n) =>\n detectAgentKind(env) === undefined &&\n stdin.isTTY === true &&\n stdout.isTTY === true;\n\nconst chooseLauncher = async () => {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n process.stdout.write(\n \"This command is meant for a coding agent, and none is running here.\\nStart one to do the install:\\n\",\n );\n LAUNCHERS.forEach((launcher, index) => {\n process.stdout.write(` ${index + 1}. ${launcher.name}\\n`);\n });\n while (true) {\n const answer = (\n await rl.question(\"Choose [1]: \").catch(() => process.exit(1))\n ).trim();\n if (answer === \"\") return LAUNCHERS[0]!;\n const byNumber = LAUNCHERS[Number(answer) - 1];\n if (byNumber) return byNumber;\n const byName = LAUNCHERS.find(\n (launcher) =>\n launcher.kind === answer.toLowerCase() ||\n launcher.name.toLowerCase() === answer.toLowerCase(),\n );\n if (byName) return byName;\n process.stdout.write(\"Enter a number from the list.\\n\");\n }\n } finally {\n rl.close();\n }\n};\n\nconst launchAgent = async (url: string) => {\n const launcher = await chooseLauncher();\n const prompt = `Run \\`npx setup-agent ${url}\\` and follow the instructions it prints.`;\n process.stdout.write(`\\nStarting ${launcher.name}...\\n\\n`);\n const child = spawn(launcher.command, launcher.args(prompt), {\n stdio: \"inherit\",\n });\n const code = await new Promise<number>((resolve) => {\n child.on(\"error\", (error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") {\n process.stderr.write(\n `${launcher.name} is not installed. Install it with:\\n ${launcher.install}\\nthen run this command again.\\n`,\n );\n } else {\n process.stderr.write(`${error.message}\\n`);\n }\n resolve(1);\n });\n child.on(\"exit\", (status) => resolve(status ?? 1));\n });\n process.exit(code);\n};\n\nconst requireState = (client: CheckoutClient): Checkout.State =>\n client.state ?? fail(\"the setup has no state\");\n\nconst summary = (state: Checkout.State) => {\n const plan = currentPlan(state);\n return {\n status: state.status,\n agentConnected: isAgentPresent(state),\n products: state.products,\n instructions: state.instructions,\n plan:\n plan === undefined\n ? null\n : {\n revision: plan.revision,\n status: plan.status,\n ...(plan.feedback !== undefined && { feedback: plan.feedback }),\n },\n progress: stepProgress(state),\n steps: state.steps,\n inputs: state.inputs,\n log: state.log.slice(-20),\n pendingMessages: state.log.filter(\n (entry) => entry.role === \"user\" && entry.acknowledgedAt === undefined,\n ),\n };\n};\n\nconst STEP_STATUSES: readonly Checkout.StepStatus[] = [\n \"pending\",\n \"active\",\n \"done\",\n \"skipped\",\n \"blocked\",\n];\n\ntype StreamEvent =\n | { event: \"planning.started\" }\n | { event: \"message.created\"; messageId: string; text: string }\n | {\n event: \"input.answered\";\n inputId: string;\n prompt: string;\n /** The entries of a multiple choice answer, else the answer text. */\n answer: string | string[];\n note?: string;\n /** True when a choice answer is, or a multiple one contains, the user's own text rather than an option. */\n custom?: true;\n }\n | { event: \"input.dismissed\"; inputId: string; prompt: string }\n | { event: \"plan.approved\"; revision: number }\n | { event: \"plan.changes-requested\"; revision: number; feedback: string }\n | { event: \"finished\"; next: string }\n | { event: \"cancelled\"; next: string }\n | { event: \"setup.created\"; products: string[] };\n\nconst STAY_CONNECTED =\n \"This setup is over, not your connection. Keep the stream running, tell the user you are connected and waiting for their next setup, and run wait start.\";\n\nexport const diffEvents = (\n before: Checkout.State,\n after: Checkout.State,\n): StreamEvent[] => {\n const events: StreamEvent[] = [];\n if (before.status === \"waiting\" && after.status === \"planning\") {\n events.push({ event: \"planning.started\" });\n }\n for (const entry of after.log) {\n if (\n entry.role === \"user\" &&\n entry.acknowledgedAt === undefined &&\n !before.log.some((previous) => previous.id === entry.id)\n ) {\n events.push({\n event: \"message.created\",\n messageId: entry.id,\n text: entry.text,\n });\n }\n }\n for (const input of after.inputs) {\n const previous = before.inputs.find(\n (candidate) => candidate.id === input.id,\n );\n if (previous?.status === input.status) continue;\n if (input.status === \"answered\") {\n const answer = input.answer ?? \"\";\n events.push({\n event: \"input.answered\",\n inputId: input.id,\n prompt: input.prompt,\n ...answerFields(input, answer),\n });\n } else if (input.status === \"dismissed\" && after.status !== \"cancelled\") {\n events.push({\n event: \"input.dismissed\",\n inputId: input.id,\n prompt: input.prompt,\n });\n }\n }\n for (const plan of after.plans) {\n const previous = before.plans.find(\n (candidate) => candidate.revision === plan.revision,\n );\n if (previous?.status === plan.status || plan.status === \"proposed\") {\n continue;\n }\n if (plan.status === \"approved\") {\n events.push({ event: \"plan.approved\", revision: plan.revision });\n } else {\n events.push({\n event: \"plan.changes-requested\",\n revision: plan.revision,\n feedback: plan.feedback ?? \"\",\n });\n }\n }\n if (before.status !== \"done\" && after.status === \"done\") {\n events.push({ event: \"finished\", next: STAY_CONNECTED });\n }\n if (before.status !== \"cancelled\" && after.status === \"cancelled\") {\n events.push({ event: \"cancelled\", next: STAY_CONNECTED });\n }\n if (after.id !== null && after.id !== before.id) {\n events.push({\n event: \"setup.created\",\n products: after.products.map((product) => product.slug),\n });\n }\n return events;\n};\n\nconst stream = async (client: CheckoutClient) => {\n let previous = requireState(client);\n let pending: StreamEvent[] = diffEvents({ ...previous, log: [] }, previous);\n let timer: ReturnType<typeof setTimeout> | null = null;\n const flush = () => {\n timer = null;\n if (pending.length === 0) return;\n print(pending);\n pending = [];\n };\n client.subscribe(() => {\n const next = client.state;\n if (next === undefined) return;\n pending.push(...diffEvents(previous, next));\n previous = next;\n if (pending.length > 0 && timer === null) timer = setTimeout(flush, 200);\n });\n flush();\n const kind = detectAgentKind();\n await client.commands[\"agent/hello\"]({\n cwd: process.cwd(),\n ...(kind !== undefined && { kind }),\n });\n const heartbeat = setInterval(() => {\n void client.commands[\"agent/heartbeat\"]().catch(() => {});\n }, AGENT_HEARTBEAT_MS);\n const stop = () => {\n clearInterval(heartbeat);\n flush();\n void client.commands[\"agent/bye\"]()\n .catch(() => {})\n .finally(() => {\n client.dispose();\n process.exit(0);\n });\n };\n process.on(\"SIGHUP\", stop);\n process.on(\"SIGINT\", stop);\n process.on(\"SIGTERM\", stop);\n await new Promise(() => {});\n};\n\n/** Resolves once a setup is planning or installing, or once the one being waited on is cancelled; a setup already closed when the wait begins is over, so the wait is for the next one. */\nexport const waitForStart = (\n client: Pick<CheckoutClient, \"state\" | \"subscribe\">,\n) =>\n new Promise<Checkout.Status>((resolve) => {\n let previous: Checkout.Status | undefined;\n const check = () => {\n const state = client.state;\n if (state === undefined) return;\n const started =\n state.status === \"planning\" || state.status === \"installing\";\n const ended = isClosed(state) && previous === \"waiting\";\n previous = state.status;\n if (!started && !ended) return;\n unsubscribe();\n resolve(state.status);\n };\n const unsubscribe = client.subscribe(check);\n check();\n });\n\nconst waitForInput = (client: CheckoutClient, inputId: string) =>\n new Promise<Checkout.Input>((resolve) => {\n const check = () => {\n const state = client.state;\n const input = state?.inputs.find((candidate) => candidate.id === inputId);\n if (!input) return;\n if (input.status !== \"open\" || state?.status === \"cancelled\") {\n unsubscribe();\n resolve(input);\n }\n };\n const unsubscribe = client.subscribe(check);\n check();\n });\n\nconst waitForPlan = (client: CheckoutClient, revision: number) =>\n new Promise<Checkout.Plan | undefined>((resolve) => {\n const check = () => {\n const state = client.state;\n const plan = state?.plans.find(\n (candidate) => candidate.revision === revision,\n );\n if (\n plan === undefined ||\n plan.status !== \"proposed\" ||\n state?.status === \"cancelled\"\n ) {\n unsubscribe();\n resolve(plan);\n }\n };\n const unsubscribe = client.subscribe(check);\n check();\n });\n\nconst decided = (plan: Checkout.Plan | undefined, state: Checkout.State) => {\n if (state.status === \"cancelled\") return { ok: false, status: \"cancelled\" };\n if (plan === undefined) return fail(\"the plan disappeared\");\n return plan.status === \"approved\"\n ? { ok: true, decision: \"approve\", revision: plan.revision }\n : {\n ok: true,\n decision: \"revise\",\n revision: plan.revision,\n feedback: plan.feedback ?? \"\",\n };\n};\n\nconst splitList = (value: string | undefined) =>\n value\n ?.split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean);\n\nexport const askSeed = (\n rest: readonly string[],\n flags: Parsed[\"flags\"],\n): Checkout.InputSeed & { stepId?: string } => {\n const stepId = flagText(flags, \"step\");\n const optional = flags.has(\"optional\");\n const fallback = flagText(flags, \"default\");\n const preset = flagText(flags, \"preset\");\n if (preset !== undefined) {\n if (!isPresetId(preset)) {\n return fail(\n `unknown preset \"${preset}\"; use ${Object.keys(INPUT_PRESETS).join(\" or \")}`,\n );\n }\n const only = splitList(flagText(flags, \"only\"));\n const found = splitList(flagText(flags, \"choices\"))?.map((entry) => {\n const [id = \"\", ...description] = entry.split(\"=\");\n return description.length === 0\n ? { id }\n : { id, description: description.join(\"=\") };\n });\n try {\n const seed = presetInput(preset, {\n ...(only !== undefined && { only }),\n ...(found !== undefined && { found }),\n ...(fallback !== undefined && { default: fallback }),\n optional,\n ...(stepId !== undefined && { stepId }),\n });\n return rest[0] === undefined ? seed : { ...seed, prompt: rest[0] };\n } catch (error) {\n return fail(error instanceof Error ? error.message : String(error));\n }\n }\n const prompt = rest[0] ?? fail(\"usage: ask <prompt>\");\n const product = flagText(flags, \"product\");\n if (product !== undefined) {\n return {\n kind: \"product\",\n prompt,\n product,\n optional,\n ...(stepId !== undefined && { stepId }),\n };\n }\n const choices = splitList(flagText(flags, \"choices\"));\n if (choices !== undefined) {\n if (flags.has(\"single\") && flags.has(\"multiple\"))\n return fail(\"--single and --multiple exclude each other\");\n const icons = new Map<string, string>();\n for (const entry of splitList(flagText(flags, \"icons\")) ?? []) {\n const [id = \"\", icon = \"\"] = entry.split(\"=\", 2);\n if (!choices.includes(id))\n return fail(`no choice \"${id}\" to put an icon on`);\n if (!isOptionIcon(icon)) {\n return fail(\n `unknown icon \"${icon}\"; pick one of ${OPTION_ICONS.join(\", \")}`,\n );\n }\n icons.set(id, icon);\n }\n const missing = choices.filter((id) => !icons.has(id));\n if (missing.length > 0) {\n return fail(\n `every option needs an icon; ${missing.map((id) => `\"${id}\"`).join(\", \")} ${missing.length === 1 ? \"has\" : \"have\"} none. Pass --icons \"${choices.map((id) => `${id}=<icon>`).join(\",\")}\" with icons from: ${OPTION_ICONS.join(\", \")}`,\n );\n }\n return {\n kind: \"choice\",\n prompt,\n options: choices.map((id) => ({ id, label: id, icon: icons.get(id)! })),\n ...(!flags.has(\"single\") && { multiple: true }),\n ...(fallback !== undefined && { default: fallback }),\n optional,\n ...(stepId !== undefined && { stepId }),\n };\n }\n const placeholder = flagText(flags, \"placeholder\");\n return {\n prompt,\n ...(placeholder !== undefined && { placeholder }),\n optional,\n ...(stepId !== undefined && { stepId }),\n };\n};\n\n/** Sets `key=value` in dotenv text, replacing an existing line for the key. */\nexport const upsertEnvLine = (content: string, key: string, value: string) => {\n const line = `${key}=${value}`;\n const lines = content === \"\" ? [] : content.replace(/\\n$/, \"\").split(\"\\n\");\n const index = lines.findIndex((entry) =>\n new RegExp(`^(export\\\\s+)?${key}\\\\s*=`).test(entry),\n );\n if (index === -1) lines.push(line);\n else lines[index] = line;\n return `${lines.join(\"\\n\")}\\n`;\n};\n\nconst answerFields = (input: Checkout.Input, answer: string) => ({\n answer: (input.multiple && parseMultipleAnswer(answer)) || answer,\n ...(input.note !== undefined && { note: input.note }),\n ...(input.kind === \"choice\" &&\n classifyChoiceAnswer(input, answer) === \"custom\" && {\n custom: true as const,\n }),\n});\n\nconst answered = (input: Checkout.Input) => ({\n ok: true,\n inputId: input.id,\n status: input.status,\n ...(input.answer !== undefined && answerFields(input, input.answer)),\n});\n\nexport const main = async (argv: readonly string[]) => {\n const { positional, flags } = parseArgs(argv);\n const [first, command = \"intro\", ...rest] = positional;\n if (flags.has(\"help\") || flags.has(\"h\") || first === undefined) {\n process.stdout.write(HELP);\n return process.exit(first === undefined ? 1 : 0);\n }\n const url = first;\n if (command === \"intro\" && isDirectInvocation()) return launchAgent(url);\n\n const client = await connectCheckout(url).catch((error: unknown) =>\n fail(error instanceof Error ? error.message : String(error)),\n );\n const commands = client.commands;\n\n try {\n switch (command) {\n case \"intro\": {\n const kind = detectAgentKind();\n await commands[\"agent/intro\"]({\n ...(kind !== undefined && { kind }),\n }).catch(() => {});\n process.stdout.write(INSTRUCTIONS(url));\n process.stdout.write(\"\\nCurrent setup:\\n\");\n print(summary(requireState(client)));\n break;\n }\n case \"status\": {\n print(summary(requireState(client)));\n break;\n }\n case \"stream\": {\n await stream(client);\n break;\n }\n case \"step\": {\n const usage =\n \"usage: step <step-id> <pending|active|done|skipped|blocked>\";\n const stepId = rest[0] ?? fail(usage);\n const status = rest[1];\n if (!STEP_STATUSES.includes(status as Checkout.StepStatus)) fail(usage);\n const note = flagText(flags, \"note\");\n await commands[\"agent/step\"]({\n stepId,\n status: status as Checkout.StepStatus,\n ...(note !== undefined && { note }),\n });\n print({ ok: true, stepId, status });\n break;\n }\n case \"add-step\": {\n const title = rest[0] ?? fail(\"usage: add-step <title>\");\n const detail = flagText(flags, \"detail\");\n const product = flagText(flags, \"product\");\n const result = await commands[\"agent/add-step\"]({\n title,\n ...(detail !== undefined && { detail }),\n ...(product !== undefined && { product }),\n ...(flags.has(\"active\") && { active: true }),\n });\n print({ ok: true, ...result });\n break;\n }\n case \"plan\": {\n const file = flagText(flags, \"file\");\n const markdown =\n file !== undefined\n ? await readFile(resolve(file), \"utf8\").catch(() =>\n fail(`could not read ${file}`),\n )\n : (rest[0] ?? fail(\"usage: plan <markdown> | plan --file <path>\"));\n const { revision } = await commands[\"agent/plan\"]({ markdown });\n if (!flags.has(\"wait\")) {\n print({ ok: true, revision });\n break;\n }\n const plan = await waitForPlan(client, revision);\n print(decided(plan, requireState(client)));\n break;\n }\n case \"ask\": {\n const { inputId } = await commands[\"agent/ask\"](askSeed(rest, flags));\n if (!flags.has(\"wait\")) {\n print({ ok: true, inputId });\n break;\n }\n print(await waitForInput(client, inputId).then(answered));\n break;\n }\n case \"wait\": {\n const target =\n rest[0] ?? fail(\"usage: wait start | wait <input-id> | wait plan\");\n const state = requireState(client);\n if (target === \"start\") {\n const status = await waitForStart(client);\n print({\n ok: status === \"planning\" || status === \"installing\",\n status,\n });\n break;\n }\n if (target === \"plan\") {\n const plan = currentPlan(state) ?? fail(\"no plan has been proposed\");\n print(\n decided(\n await waitForPlan(client, plan.revision),\n requireState(client),\n ),\n );\n break;\n }\n if (!state.inputs.some((input) => input.id === target)) {\n fail(`no input \"${target}\"`);\n }\n print(await waitForInput(client, target).then(answered));\n break;\n }\n case \"env\": {\n const usage = \"usage: env <input-id> <ENV_VAR> [--file <path>]\";\n const inputId = rest[0] ?? fail(usage);\n const envKey = rest[1] ?? fail(usage);\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(envKey)) {\n fail(`\"${envKey}\" is not an environment variable name`);\n }\n const input =\n requireState(client).inputs.find(\n (candidate) => candidate.id === inputId,\n ) ?? fail(`no input \"${inputId}\"`);\n if (input.status !== \"answered\") {\n fail(`input \"${inputId}\" is ${input.status}`);\n }\n if (parseModelAnswer(input.answer ?? \"\") === undefined) {\n fail(`input \"${inputId}\" is not an llm-provider answer`);\n }\n const file = resolve(flagText(flags, \"file\") ?? \".env.local\");\n const response = await fetch(\n `${url.replace(/\\/$/, \"\")}/secret/${encodeURIComponent(inputId)}`,\n );\n if (response.status === 404) {\n fail(`the key for \"${inputId}\" was already taken or never deposited`);\n }\n if (!response.ok) fail(`could not fetch the key: ${response.status}`);\n const value = await response.text();\n const current = await readFile(file, \"utf8\").catch(() => \"\");\n await writeFile(file, upsertEnvLine(current, envKey, value));\n print({ ok: true, envKey, file });\n break;\n }\n case \"ack\": {\n const messageId = rest[0] ?? fail(\"usage: ack <message-id>\");\n await commands[\"agent/ack\"]({ messageId });\n print({ ok: true });\n break;\n }\n case \"log\": {\n const text = rest[0] ?? fail(\"usage: log <text>\");\n await commands[\"agent/log\"]({ text });\n print({ ok: true });\n break;\n }\n case \"done\": {\n const summary = flagText(flags, \"summary\");\n const preview = flagText(flags, \"preview\");\n await commands[\"agent/done\"]({\n ...(summary !== undefined && { summary }),\n ...(preview !== undefined && { preview }),\n });\n print({\n ok: true,\n status: \"proposed\",\n next: \"Keep the stream running and stay idle. The user closes the setup or sends a follow-up message.\",\n });\n break;\n }\n default:\n fail(`unknown command \"${command}\"\\n\\n${HELP}`);\n }\n } catch (error) {\n fail(error instanceof Error ? error.message : String(error));\n } finally {\n client.dispose();\n }\n};\n"],"mappings":"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"}
package/dist/client.d.ts CHANGED
@@ -2,7 +2,7 @@ import { Checkout } from "./protocol.js";
2
2
  import { StatewireClient } from "statewire";
3
3
  //#region src/client.d.ts
4
4
  type CheckoutClient = StatewireClient<Checkout.State | undefined, Checkout.Commands>;
5
- /** Attach to a checkout by its base URL and resolve once its state has arrived. */
5
+ /** Attach to a setup by its base URL and resolve once its state has arrived. */
6
6
  declare const connectCheckout: (url: string) => Promise<CheckoutClient>;
7
7
  //#endregion
8
8
  export { CheckoutClient, connectCheckout };
package/dist/client.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { StatewireClient, StatewireHttp } from "statewire";
2
2
  //#region src/client.ts
3
- /** Attach to a checkout by its base URL and resolve once its state has arrived. */
3
+ /** Attach to a setup by its base URL and resolve once its state has arrived. */
4
4
  const connectCheckout = (url) => new Promise((resolve, reject) => {
5
5
  const client = new StatewireClient({
6
6
  transport: StatewireHttp({ url }),
@@ -11,7 +11,7 @@ const connectCheckout = (url) => new Promise((resolve, reject) => {
11
11
  if (client.connection.status !== "stopped") return;
12
12
  unsubscribe();
13
13
  client.dispose();
14
- reject(/* @__PURE__ */ new Error(`checkout unreachable: ${client.connection.reason}${client.connection.message ? ` (${client.connection.message})` : ""}`));
14
+ reject(/* @__PURE__ */ new Error(`setup unreachable: ${client.connection.reason}${client.connection.message ? ` (${client.connection.message})` : ""}`));
15
15
  return;
16
16
  }
17
17
  unsubscribe();
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["StatewireClient","StatewireHttp","Checkout","CheckoutClient","State","Commands","connectCheckout","url","Promise","resolve","reject","client","transport","onError","error","settle","state","undefined","connection","status","unsubscribe","dispose","Error","reason","message","subscribe"],"sources":["../src/client.ts"],"sourcesContent":["import { StatewireClient, StatewireHttp } from \"statewire\";\nimport type { Checkout } from \"./protocol\";\n\nexport type CheckoutClient = StatewireClient<\n Checkout.State | undefined,\n Checkout.Commands\n>;\n\n/** Attach to a checkout by its base URL and resolve once its state has arrived. */\nexport const connectCheckout = (url: string): Promise<CheckoutClient> =>\n new Promise((resolve, reject) => {\n const client: CheckoutClient = new StatewireClient({\n transport: StatewireHttp({ url }),\n onError: (error) => reject(error),\n });\n const settle = () => {\n if (client.state === undefined) {\n if (client.connection.status !== \"stopped\") return;\n unsubscribe();\n client.dispose();\n reject(\n new Error(\n `checkout unreachable: ${client.connection.reason}${\n client.connection.message ? ` (${client.connection.message})` : \"\"\n }`,\n ),\n );\n return;\n }\n unsubscribe();\n resolve(client);\n };\n const unsubscribe = client.subscribe(settle);\n settle();\n });\n"],"mappings":";;;AASA,MAAaM,mBAAmBC,QAC9B,IAAIC,SAASC,SAASC,WAAW;CAC/B,MAAMC,SAAyB,IAAIX,gBAAgB;EACjDY,WAAWX,cAAc,EAAEM,IAAI,CAAC;EAChCM,UAAUC,UAAUJ,OAAOI,KAAK;CAClC,CAAC;CACD,MAAMC,eAAe;EACnB,IAAIJ,OAAOK,UAAUC,KAAAA,GAAW;GAC9B,IAAIN,OAAOO,WAAWC,WAAW,WAAW;GAC5CC,YAAY;GACZT,OAAOU,QAAQ;GACfX,uBACE,IAAIY,MACF,yBAAyBX,OAAOO,WAAWK,SACzCZ,OAAOO,WAAWM,UAAU,KAAKb,OAAOO,WAAWM,QAAO,KAAM,IAEpE,CACF;GACA;EACF;EACAJ,YAAY;EACZX,QAAQE,MAAM;CAChB;CACA,MAAMS,cAAcT,OAAOc,UAAUV,MAAM;CAC3CA,OAAO;AACT,CAAC"}
1
+ {"version":3,"file":"client.js","names":["StatewireClient","StatewireHttp","Checkout","CheckoutClient","State","Commands","connectCheckout","url","Promise","resolve","reject","client","transport","onError","error","settle","state","undefined","connection","status","unsubscribe","dispose","Error","reason","message","subscribe"],"sources":["../src/client.ts"],"sourcesContent":["import { StatewireClient, StatewireHttp } from \"statewire\";\nimport type { Checkout } from \"./protocol\";\n\nexport type CheckoutClient = StatewireClient<\n Checkout.State | undefined,\n Checkout.Commands\n>;\n\n/** Attach to a setup by its base URL and resolve once its state has arrived. */\nexport const connectCheckout = (url: string): Promise<CheckoutClient> =>\n new Promise((resolve, reject) => {\n const client: CheckoutClient = new StatewireClient({\n transport: StatewireHttp({ url }),\n onError: (error) => reject(error),\n });\n const settle = () => {\n if (client.state === undefined) {\n if (client.connection.status !== \"stopped\") return;\n unsubscribe();\n client.dispose();\n reject(\n new Error(\n `setup unreachable: ${client.connection.reason}${\n client.connection.message ? ` (${client.connection.message})` : \"\"\n }`,\n ),\n );\n return;\n }\n unsubscribe();\n resolve(client);\n };\n const unsubscribe = client.subscribe(settle);\n settle();\n });\n"],"mappings":";;;AASA,MAAaM,mBAAmBC,QAC9B,IAAIC,SAASC,SAASC,WAAW;CAC/B,MAAMC,SAAyB,IAAIX,gBAAgB;EACjDY,WAAWX,cAAc,EAAEM,IAAI,CAAC;EAChCM,UAAUC,UAAUJ,OAAOI,KAAK;CAClC,CAAC;CACD,MAAMC,eAAe;EACnB,IAAIJ,OAAOK,UAAUC,KAAAA,GAAW;GAC9B,IAAIN,OAAOO,WAAWC,WAAW,WAAW;GAC5CC,YAAY;GACZT,OAAOU,QAAQ;GACfX,uBACE,IAAIY,MACF,sBAAsBX,OAAOO,WAAWK,SACtCZ,OAAOO,WAAWM,UAAU,KAAKb,OAAOO,WAAWM,QAAO,KAAM,IAEpE,CACF;GACA;EACF;EACAJ,YAAY;EACZX,QAAQE,MAAM;CAChB;CACA,MAAMS,cAAcT,OAAOc,UAAUV,MAAM;CAC3CA,OAAO;AACT,CAAC"}
package/dist/host.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { Checkout } from "./protocol.js";
2
2
  //#region src/host.d.ts
3
- /** The checkout statewire host; pass the durable object's restored snapshot. */
3
+ /** The setup statewire host; pass the durable object's restored snapshot. */
4
4
  declare const CheckoutHost: (restored?: unknown) => import("@assistant-ui/tap").ResourceElement<{
5
- state: Checkout.State;
6
- commands: import("statewire").Statewire.CommandsProxy<import("statewire/host").StatewireHost.HandledCommands<Checkout.Commands>>;
7
- snapshot: () => unknown;
8
- subscribe: (listener: () => void) => () => void;
5
+ state: any;
6
+ commands: any;
7
+ snapshot: any;
8
+ subscribe: (listener: () => void) => () => any;
9
9
  }>;
10
10
  //#endregion
11
11
  export { type Checkout, CheckoutHost };
@@ -1 +1 @@
1
- {"version":3,"file":"host.d.ts","names":[],"sources":["../src/host.ts"],"mappings":";;;cA+aa,eAAgB,mDAAkB;SAAlC,SAAA;+GAAA,SAAA;;EARa,YAAA"}
1
+ {"version":3,"file":"host.d.ts","names":[],"sources":["../src/host.ts"],"mappings":";;;cA+aa,eAAgB,mDAAkB;;;;EARrB,YAAA"}
package/dist/host.js CHANGED
@@ -19,8 +19,8 @@ const useCheckoutHost = (restored) => {
19
19
  };
20
20
  });
21
21
  const requireOpen = () => {
22
- if (state.createdAt === null) throw reject("not-created", "the checkout has not been created yet");
23
- if (isClosed(state)) throw reject("closed", `the checkout is ${state.status}`);
22
+ if (state.createdAt === null) throw reject("not-created", "the setup has not been created yet");
23
+ if (isClosed(state)) throw reject("closed", `the setup is ${state.status}`);
24
24
  };
25
25
  const requireStarted = () => {
26
26
  requireOpen();
@@ -49,7 +49,7 @@ const useCheckoutHost = (restored) => {
49
49
  const kind = seed.kind ?? "text";
50
50
  if (kind === "product") {
51
51
  if (!seed.product) throw reject("invalid-input", "a product input names a product");
52
- if (state.products.some((product) => product.slug === seed.product)) throw reject("invalid-input", `"${seed.product}" is already part of this checkout`);
52
+ if (state.products.some((product) => product.slug === seed.product)) throw reject("invalid-input", `"${seed.product}" is already part of this setup`);
53
53
  } else if (kind !== "text" && !seed.options?.length) throw reject("invalid-input", `a ${kind} input needs options`);
54
54
  if (seed.multiple && (kind !== "choice" || seed.options?.some((option) => option.variants))) throw reject("invalid-input", "only a choice whose options have no variants accepts several answers");
55
55
  if (seed.default !== void 0 && !seed.options?.some((option_0) => option_0.id === seed.default)) throw reject("invalid-input", `no option "${seed.default}" to default to`);
@@ -99,7 +99,7 @@ const useCheckoutHost = (restored) => {
99
99
  };
100
100
  const commands = useStatewireCommands({
101
101
  "checkout/create": ({ id: id_0, products, instructions }) => {
102
- if (state.id === id_0) throw reject("already-created", "the checkout already exists");
102
+ if (state.id === id_0) throw reject("already-created", "the setup already exists");
103
103
  const fresh = initialCheckoutState();
104
104
  if (!isAgentPresent(state)) state.agent = fresh.agent;
105
105
  state.id = id_0;
@@ -283,7 +283,7 @@ const useCheckoutHost = (restored) => {
283
283
  }
284
284
  };
285
285
  };
286
- /** The checkout statewire host; pass the durable object's restored snapshot. */
286
+ /** The setup statewire host; pass the durable object's restored snapshot. */
287
287
  const CheckoutHost = (restored) => resource(useCheckoutHost)(restored);
288
288
  //#endregion
289
289
  export { CheckoutHost };
package/dist/host.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"host.js","names":["resource","StatewireReject","getStateHost","useStatewireCommands","useStatewireState","classifyChoiceAnswer","currentPlan","initialCheckoutState","isAgentPresent","isClosed","isValidModelAnswer","parsePreviewUrl","Checkout","reject","reason","RejectionReason","message","payload","useCheckoutHost","restored","state","State","initial","undefined","snapshot","Partial","version","agent","requireOpen","createdAt","status","requireStarted","requireInstalling","findStep","stepId","step","steps","find","candidate","id","findInput","inputId","input","inputs","touchAgent","lastSeenAt","Date","now","connected","ask","seed","InputSeed","kind","product","products","some","slug","options","length","multiple","option","variants","default","push","phase","preset","prompt","placeholder","help","optional","dismissOpenInputs","answeredAt","log","text","role","at","updateStep","Step","StepStatus","note","changed","title","commands","Commands","checkout/create","instructions","fresh","completion","plans","trim","map","name","guide","checkout/begin-plan","checkout/answer","answer","checkout/add-product","entry","checkout/message","agent/ack","messageId","acknowledgedAt","checkout/dismiss","params","plan","decision","decidedAt","feedback","checkout/cancel","checkout/finish","agent/intro","introducedAt","agent/hello","cwd","agent/heartbeat","agent/bye","agent/plan","markdown","revision","submittedAt","agent/add-step","detail","active","agent/step","agent/log","agent/done","summary","preview","previewUrl","proposedAt","href","source","subscribe","listener","setOnChange","CheckoutHost"],"sources":["../src/host.ts"],"sourcesContent":["import { resource } from \"@assistant-ui/tap\";\nimport {\n StatewireReject,\n getStateHost,\n useStatewireCommands,\n useStatewireState,\n} from \"statewire/host\";\nimport {\n classifyChoiceAnswer,\n currentPlan,\n initialCheckoutState,\n isAgentPresent,\n isClosed,\n isValidModelAnswer,\n parsePreviewUrl,\n} from \"./protocol\";\nimport type { Checkout } from \"./protocol\";\n\nconst reject = (reason: Checkout.RejectionReason, message: string) =>\n new StatewireReject(message, { payload: { reason } });\n\nconst useCheckoutHost = (restored: unknown) => {\n const [state] = useStatewireState<Checkout.State>(() => {\n const initial = initialCheckoutState();\n if (restored === undefined) return initial;\n const snapshot = restored as Partial<Checkout.State>;\n if (snapshot.version !== 2) return initial;\n return {\n ...initial,\n ...snapshot,\n agent: { ...initial.agent, ...snapshot.agent },\n };\n });\n\n const requireOpen = () => {\n if (state.createdAt === null) {\n throw reject(\"not-created\", \"the checkout has not been created yet\");\n }\n if (isClosed(state)) {\n throw reject(\"closed\", `the checkout is ${state.status}`);\n }\n };\n\n const requireStarted = () => {\n requireOpen();\n if (state.status === \"waiting\") {\n throw reject(\n \"planning-not-started\",\n 'ask the user to return to their browser and click \"Begin plan\"; wait with \"wait start\"',\n );\n }\n };\n\n const requireInstalling = () => {\n requireOpen();\n if (state.status !== \"installing\") {\n throw reject(\n \"plan-required\",\n \"steps run once the user has approved a plan; submit one with plan\",\n );\n }\n };\n\n const findStep = (stepId: string) => {\n const step = state.steps.find((candidate) => candidate.id === stepId);\n if (!step) throw reject(\"unknown-step\", `no step \"${stepId}\"`);\n return step;\n };\n\n const findInput = (inputId: string) => {\n const input = state.inputs.find((candidate) => candidate.id === inputId);\n if (!input) throw reject(\"unknown-input\", `no input \"${inputId}\"`);\n if (input.status !== \"open\") {\n throw reject(\"input-closed\", `input \"${inputId}\" is ${input.status}`);\n }\n return input;\n };\n\n const touchAgent = () => {\n state.agent.lastSeenAt = Date.now();\n state.agent.connected = true;\n };\n\n const ask = (seed: Checkout.InputSeed & { stepId?: string }) => {\n const kind = seed.kind ?? \"text\";\n if (kind === \"product\") {\n if (!seed.product) {\n throw reject(\"invalid-input\", \"a product input names a product\");\n }\n if (state.products.some((product) => product.slug === seed.product)) {\n throw reject(\n \"invalid-input\",\n `\"${seed.product}\" is already part of this checkout`,\n );\n }\n } else if (kind !== \"text\" && !seed.options?.length) {\n throw reject(\"invalid-input\", `a ${kind} input needs options`);\n }\n if (\n seed.multiple &&\n (kind !== \"choice\" || seed.options?.some((option) => option.variants))\n ) {\n throw reject(\n \"invalid-input\",\n \"only a choice whose options have no variants accepts several answers\",\n );\n }\n if (\n seed.default !== undefined &&\n !seed.options?.some((option) => option.id === seed.default)\n ) {\n throw reject(\n \"invalid-input\",\n `no option \"${seed.default}\" to default to`,\n );\n }\n if (seed.stepId !== undefined) findStep(seed.stepId);\n const id = `q${state.inputs.length + 1}`;\n const stepId =\n seed.stepId ?? state.steps.find((step) => step.status === \"active\")?.id;\n state.inputs.push({\n id,\n phase: state.status,\n kind,\n ...(seed.preset !== undefined && { preset: seed.preset }),\n prompt: seed.prompt,\n ...(seed.placeholder !== undefined && { placeholder: seed.placeholder }),\n ...(seed.options !== undefined && { options: seed.options }),\n ...(seed.multiple && { multiple: true }),\n ...(kind === \"product\" && { product: seed.product }),\n ...(seed.default !== undefined && { default: seed.default }),\n ...(seed.help !== undefined && { help: seed.help }),\n optional: seed.optional ?? false,\n status: \"open\",\n ...(stepId !== undefined && { stepId }),\n createdAt: Date.now(),\n });\n return { inputId: id };\n };\n\n const dismissOpenInputs = () => {\n for (const input of state.inputs) {\n if (input.status === \"open\") {\n input.status = \"dismissed\";\n input.answeredAt = Date.now();\n }\n }\n };\n\n const log = (text: string, stepId?: string) => {\n state.log.push({\n id: `l${state.log.length + 1}`,\n role: \"agent\",\n phase: state.status,\n at: Date.now(),\n text,\n ...(stepId !== undefined && { stepId }),\n });\n };\n\n const updateStep = (\n step: Checkout.Step,\n status: Checkout.StepStatus,\n note?: string,\n ) => {\n const changed = step.status !== status;\n step.status = status;\n if (note !== undefined) step.note = note;\n if (changed && (status === \"done\" || status === \"skipped\")) {\n log(\n `${status === \"done\" ? \"Completed\" : \"Skipped\"}: ${step.title}${step.note ? `\\n\\n${step.note}` : \"\"}`,\n step.id,\n );\n }\n };\n\n const commands = useStatewireCommands<Checkout.Commands>({\n \"checkout/create\": ({ id, products, instructions }) => {\n if (state.id === id) {\n throw reject(\"already-created\", \"the checkout already exists\");\n }\n const fresh = initialCheckoutState();\n if (!isAgentPresent(state)) state.agent = fresh.agent;\n state.id = id;\n state.status = fresh.status;\n delete state.completion;\n state.plans = fresh.plans;\n state.steps = fresh.steps;\n state.inputs = fresh.inputs;\n state.log = fresh.log;\n state.createdAt = Date.now();\n state.instructions = instructions?.trim() ?? \"\";\n state.products = products.map((product) => ({\n slug: product.slug,\n name: product.name,\n ...(product.guide !== undefined && { guide: product.guide }),\n }));\n },\n \"checkout/begin-plan\": () => {\n requireOpen();\n if (state.status === \"waiting\") state.status = \"planning\";\n },\n \"checkout/answer\": ({ inputId, answer, note }) => {\n requireOpen();\n const input = findInput(inputId);\n if (\n input.kind === \"choice\" &&\n classifyChoiceAnswer(input, answer) === \"invalid\"\n ) {\n throw reject(\n \"invalid-answer\",\n input.multiple\n ? `\"${answer}\" is not a JSON array of option ids`\n : `\"${answer}\" names an option without a valid variant`,\n );\n }\n if (input.kind === \"model\" && !isValidModelAnswer(input, answer)) {\n throw reject(\n \"invalid-answer\",\n \"a model answer is JSON with provider and model\",\n );\n }\n if (input.kind === \"product\") {\n throw reject(\n \"invalid-answer\",\n \"a product input is answered with checkout/add-product\",\n );\n }\n if (input.kind === \"text\" && answer.trim() === \"\") {\n throw reject(\"invalid-answer\", \"an answer cannot be empty\");\n }\n input.status = \"answered\";\n input.answeredAt = Date.now();\n input.answer = answer;\n if (note !== undefined && note.trim() !== \"\") input.note = note.trim();\n },\n \"checkout/add-product\": ({ inputId, product }) => {\n requireOpen();\n const input = findInput(inputId);\n if (input.kind !== \"product\" || input.product !== product.slug) {\n throw reject(\n \"invalid-answer\",\n `input \"${inputId}\" does not propose \"${product.slug}\"`,\n );\n }\n if (!state.products.some((entry) => entry.slug === product.slug)) {\n state.products.push({\n slug: product.slug,\n name: product.name,\n ...(product.guide !== undefined && { guide: product.guide }),\n });\n }\n input.status = \"answered\";\n input.answer = \"added\";\n input.answeredAt = Date.now();\n },\n \"checkout/message\": ({ text }) => {\n requireOpen();\n if (text.trim() === \"\")\n throw reject(\"empty-message\", \"a message cannot be empty\");\n const stepId = state.steps.find((step) => step.status === \"active\")?.id;\n state.log.push({\n id: `l${state.log.length + 1}`,\n role: \"user\",\n phase: state.status,\n ...(stepId !== undefined && { stepId }),\n at: Date.now(),\n text: text.trim(),\n });\n },\n \"agent/ack\": ({ messageId }) => {\n requireOpen();\n const message = state.log.find(\n (entry) => entry.id === messageId && entry.role === \"user\",\n );\n if (!message)\n throw reject(\"unknown-message\", `no user message \"${messageId}\"`);\n message.acknowledgedAt ??= Date.now();\n },\n \"checkout/dismiss\": ({ inputId }) => {\n requireOpen();\n const input = findInput(inputId);\n input.status = \"dismissed\";\n input.answeredAt = Date.now();\n },\n \"checkout/plan\": (params) => {\n requireOpen();\n const plan = currentPlan(state);\n if (plan === undefined) {\n throw reject(\"no-plan\", \"the agent has not proposed a plan yet\");\n }\n if (plan.status !== \"proposed\") {\n throw reject(\"plan-decided\", `the plan is already ${plan.status}`);\n }\n if (params.decision === \"approve\") {\n plan.status = \"approved\";\n plan.decidedAt = Date.now();\n state.status = \"installing\";\n return;\n }\n if (params.feedback.trim() === \"\") {\n throw reject(\"feedback-required\", \"say what should change\");\n }\n plan.status = \"changes-requested\";\n plan.decidedAt = Date.now();\n plan.feedback = params.feedback.trim();\n },\n \"checkout/cancel\": () => {\n if (state.status === \"cancelled\") return;\n state.status = \"cancelled\";\n dismissOpenInputs();\n },\n \"checkout/finish\": () => {\n requireOpen();\n if (state.completion === undefined) {\n throw reject(\n \"finish-not-proposed\",\n \"the agent has not proposed to finish yet\",\n );\n }\n dismissOpenInputs();\n state.status = \"done\";\n log(\"Setup complete.\");\n },\n \"agent/intro\": ({ kind } = {}) => {\n requireOpen();\n if (state.agent.introducedAt === null) {\n state.agent.introducedAt = Date.now();\n }\n if (kind !== undefined) state.agent.kind = kind;\n },\n \"agent/hello\": ({ cwd, kind } = {}) => {\n touchAgent();\n if (cwd !== undefined) state.agent.cwd = cwd;\n if (kind !== undefined) state.agent.kind = kind;\n },\n \"agent/heartbeat\": () => {\n touchAgent();\n },\n \"agent/bye\": () => {\n state.agent.connected = false;\n },\n \"agent/plan\": ({ markdown }) => {\n requireStarted();\n touchAgent();\n if (markdown.trim() === \"\") {\n throw reject(\"empty-plan\", \"the plan is empty\");\n }\n if (state.status === \"installing\") {\n throw reject(\"plan-decided\", \"the plan is already approved\");\n }\n const revision = state.plans.length + 1;\n state.plans.push({\n revision,\n markdown: markdown.trim(),\n status: \"proposed\",\n submittedAt: Date.now(),\n });\n return { revision };\n },\n \"agent/add-step\": ({ title, detail, product, active }) => {\n requireInstalling();\n if (\n product !== undefined &&\n !state.products.some((candidate) => candidate.slug === product)\n ) {\n throw reject(\"unknown-product\", `no product \"${product}\"`);\n }\n const id = `s${state.steps.length + 1}`;\n state.steps.push({\n id,\n title,\n ...(detail !== undefined && { detail }),\n status: active ? \"active\" : \"pending\",\n ...(product !== undefined && { product }),\n createdAt: Date.now(),\n });\n return { stepId: id };\n },\n \"agent/step\": ({ stepId, status, note }) => {\n requireInstalling();\n const step = findStep(stepId);\n updateStep(step, status, note);\n },\n \"agent/ask\": (seed) => {\n requireStarted();\n touchAgent();\n return ask(seed);\n },\n \"agent/log\": ({ text }) => {\n requireOpen();\n touchAgent();\n const stepId = state.steps.find((step) => step.status === \"active\")?.id;\n log(text, stepId);\n },\n \"agent/done\": ({ summary, preview } = {}) => {\n requireInstalling();\n const previewUrl = parsePreviewUrl(preview?.trim());\n if (preview !== undefined && !previewUrl) {\n throw reject(\n \"invalid-preview\",\n \"a preview is an http URL on localhost, such as http://localhost:3000\",\n );\n }\n touchAgent();\n for (const step of state.steps) {\n if (step.status === \"pending\" || step.status === \"active\") {\n updateStep(step, \"done\");\n }\n }\n log(summary?.trim() || \"Everything is installed.\");\n state.completion = {\n proposedAt: Date.now(),\n ...(previewUrl && { preview: previewUrl.href }),\n };\n },\n });\n\n const source = getStateHost(state)!;\n return {\n state,\n commands,\n snapshot: source.snapshot,\n subscribe: (listener: () => void) => {\n source.setOnChange(listener);\n return () => source.setOnChange(null);\n },\n };\n};\n\n/** The checkout statewire host; pass the durable object's restored snapshot. */\nexport const CheckoutHost = (restored?: unknown) =>\n resource(useCheckoutHost)(restored);\n\nexport type { Checkout } from \"./protocol\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"host.js","names":["resource","StatewireReject","getStateHost","useStatewireCommands","useStatewireState","classifyChoiceAnswer","currentPlan","initialCheckoutState","isAgentPresent","isClosed","isValidModelAnswer","parsePreviewUrl","Checkout","reject","reason","RejectionReason","message","payload","useCheckoutHost","restored","state","State","initial","undefined","snapshot","Partial","version","agent","requireOpen","createdAt","status","requireStarted","requireInstalling","findStep","stepId","step","steps","find","candidate","id","findInput","inputId","input","inputs","touchAgent","lastSeenAt","Date","now","connected","ask","seed","InputSeed","kind","product","products","some","slug","options","length","multiple","option","variants","default","push","phase","preset","prompt","placeholder","help","optional","dismissOpenInputs","answeredAt","log","text","role","at","updateStep","Step","StepStatus","note","changed","title","commands","Commands","checkout/create","instructions","fresh","completion","plans","trim","map","name","guide","checkout/begin-plan","checkout/answer","answer","checkout/add-product","entry","checkout/message","agent/ack","messageId","acknowledgedAt","checkout/dismiss","params","plan","decision","decidedAt","feedback","checkout/cancel","checkout/finish","agent/intro","introducedAt","agent/hello","cwd","agent/heartbeat","agent/bye","agent/plan","markdown","revision","submittedAt","agent/add-step","detail","active","agent/step","agent/log","agent/done","summary","preview","previewUrl","proposedAt","href","source","subscribe","listener","setOnChange","CheckoutHost"],"sources":["../src/host.ts"],"sourcesContent":["import { resource } from \"@assistant-ui/tap\";\nimport {\n StatewireReject,\n getStateHost,\n useStatewireCommands,\n useStatewireState,\n} from \"statewire/host\";\nimport {\n classifyChoiceAnswer,\n currentPlan,\n initialCheckoutState,\n isAgentPresent,\n isClosed,\n isValidModelAnswer,\n parsePreviewUrl,\n} from \"./protocol\";\nimport type { Checkout } from \"./protocol\";\n\nconst reject = (reason: Checkout.RejectionReason, message: string) =>\n new StatewireReject(message, { payload: { reason } });\n\nconst useCheckoutHost = (restored: unknown) => {\n const [state] = useStatewireState<Checkout.State>(() => {\n const initial = initialCheckoutState();\n if (restored === undefined) return initial;\n const snapshot = restored as Partial<Checkout.State>;\n if (snapshot.version !== 2) return initial;\n return {\n ...initial,\n ...snapshot,\n agent: { ...initial.agent, ...snapshot.agent },\n };\n });\n\n const requireOpen = () => {\n if (state.createdAt === null) {\n throw reject(\"not-created\", \"the setup has not been created yet\");\n }\n if (isClosed(state)) {\n throw reject(\"closed\", `the setup is ${state.status}`);\n }\n };\n\n const requireStarted = () => {\n requireOpen();\n if (state.status === \"waiting\") {\n throw reject(\n \"planning-not-started\",\n 'ask the user to return to their browser and click \"Begin plan\"; wait with \"wait start\"',\n );\n }\n };\n\n const requireInstalling = () => {\n requireOpen();\n if (state.status !== \"installing\") {\n throw reject(\n \"plan-required\",\n \"steps run once the user has approved a plan; submit one with plan\",\n );\n }\n };\n\n const findStep = (stepId: string) => {\n const step = state.steps.find((candidate) => candidate.id === stepId);\n if (!step) throw reject(\"unknown-step\", `no step \"${stepId}\"`);\n return step;\n };\n\n const findInput = (inputId: string) => {\n const input = state.inputs.find((candidate) => candidate.id === inputId);\n if (!input) throw reject(\"unknown-input\", `no input \"${inputId}\"`);\n if (input.status !== \"open\") {\n throw reject(\"input-closed\", `input \"${inputId}\" is ${input.status}`);\n }\n return input;\n };\n\n const touchAgent = () => {\n state.agent.lastSeenAt = Date.now();\n state.agent.connected = true;\n };\n\n const ask = (seed: Checkout.InputSeed & { stepId?: string }) => {\n const kind = seed.kind ?? \"text\";\n if (kind === \"product\") {\n if (!seed.product) {\n throw reject(\"invalid-input\", \"a product input names a product\");\n }\n if (state.products.some((product) => product.slug === seed.product)) {\n throw reject(\n \"invalid-input\",\n `\"${seed.product}\" is already part of this setup`,\n );\n }\n } else if (kind !== \"text\" && !seed.options?.length) {\n throw reject(\"invalid-input\", `a ${kind} input needs options`);\n }\n if (\n seed.multiple &&\n (kind !== \"choice\" || seed.options?.some((option) => option.variants))\n ) {\n throw reject(\n \"invalid-input\",\n \"only a choice whose options have no variants accepts several answers\",\n );\n }\n if (\n seed.default !== undefined &&\n !seed.options?.some((option) => option.id === seed.default)\n ) {\n throw reject(\n \"invalid-input\",\n `no option \"${seed.default}\" to default to`,\n );\n }\n if (seed.stepId !== undefined) findStep(seed.stepId);\n const id = `q${state.inputs.length + 1}`;\n const stepId =\n seed.stepId ?? state.steps.find((step) => step.status === \"active\")?.id;\n state.inputs.push({\n id,\n phase: state.status,\n kind,\n ...(seed.preset !== undefined && { preset: seed.preset }),\n prompt: seed.prompt,\n ...(seed.placeholder !== undefined && { placeholder: seed.placeholder }),\n ...(seed.options !== undefined && { options: seed.options }),\n ...(seed.multiple && { multiple: true }),\n ...(kind === \"product\" && { product: seed.product }),\n ...(seed.default !== undefined && { default: seed.default }),\n ...(seed.help !== undefined && { help: seed.help }),\n optional: seed.optional ?? false,\n status: \"open\",\n ...(stepId !== undefined && { stepId }),\n createdAt: Date.now(),\n });\n return { inputId: id };\n };\n\n const dismissOpenInputs = () => {\n for (const input of state.inputs) {\n if (input.status === \"open\") {\n input.status = \"dismissed\";\n input.answeredAt = Date.now();\n }\n }\n };\n\n const log = (text: string, stepId?: string) => {\n state.log.push({\n id: `l${state.log.length + 1}`,\n role: \"agent\",\n phase: state.status,\n at: Date.now(),\n text,\n ...(stepId !== undefined && { stepId }),\n });\n };\n\n const updateStep = (\n step: Checkout.Step,\n status: Checkout.StepStatus,\n note?: string,\n ) => {\n const changed = step.status !== status;\n step.status = status;\n if (note !== undefined) step.note = note;\n if (changed && (status === \"done\" || status === \"skipped\")) {\n log(\n `${status === \"done\" ? \"Completed\" : \"Skipped\"}: ${step.title}${step.note ? `\\n\\n${step.note}` : \"\"}`,\n step.id,\n );\n }\n };\n\n const commands = useStatewireCommands<Checkout.Commands>({\n \"checkout/create\": ({ id, products, instructions }) => {\n if (state.id === id) {\n throw reject(\"already-created\", \"the setup already exists\");\n }\n const fresh = initialCheckoutState();\n if (!isAgentPresent(state)) state.agent = fresh.agent;\n state.id = id;\n state.status = fresh.status;\n delete state.completion;\n state.plans = fresh.plans;\n state.steps = fresh.steps;\n state.inputs = fresh.inputs;\n state.log = fresh.log;\n state.createdAt = Date.now();\n state.instructions = instructions?.trim() ?? \"\";\n state.products = products.map((product) => ({\n slug: product.slug,\n name: product.name,\n ...(product.guide !== undefined && { guide: product.guide }),\n }));\n },\n \"checkout/begin-plan\": () => {\n requireOpen();\n if (state.status === \"waiting\") state.status = \"planning\";\n },\n \"checkout/answer\": ({ inputId, answer, note }) => {\n requireOpen();\n const input = findInput(inputId);\n if (\n input.kind === \"choice\" &&\n classifyChoiceAnswer(input, answer) === \"invalid\"\n ) {\n throw reject(\n \"invalid-answer\",\n input.multiple\n ? `\"${answer}\" is not a JSON array of option ids`\n : `\"${answer}\" names an option without a valid variant`,\n );\n }\n if (input.kind === \"model\" && !isValidModelAnswer(input, answer)) {\n throw reject(\n \"invalid-answer\",\n \"a model answer is JSON with provider and model\",\n );\n }\n if (input.kind === \"product\") {\n throw reject(\n \"invalid-answer\",\n \"a product input is answered with checkout/add-product\",\n );\n }\n if (input.kind === \"text\" && answer.trim() === \"\") {\n throw reject(\"invalid-answer\", \"an answer cannot be empty\");\n }\n input.status = \"answered\";\n input.answeredAt = Date.now();\n input.answer = answer;\n if (note !== undefined && note.trim() !== \"\") input.note = note.trim();\n },\n \"checkout/add-product\": ({ inputId, product }) => {\n requireOpen();\n const input = findInput(inputId);\n if (input.kind !== \"product\" || input.product !== product.slug) {\n throw reject(\n \"invalid-answer\",\n `input \"${inputId}\" does not propose \"${product.slug}\"`,\n );\n }\n if (!state.products.some((entry) => entry.slug === product.slug)) {\n state.products.push({\n slug: product.slug,\n name: product.name,\n ...(product.guide !== undefined && { guide: product.guide }),\n });\n }\n input.status = \"answered\";\n input.answer = \"added\";\n input.answeredAt = Date.now();\n },\n \"checkout/message\": ({ text }) => {\n requireOpen();\n if (text.trim() === \"\")\n throw reject(\"empty-message\", \"a message cannot be empty\");\n const stepId = state.steps.find((step) => step.status === \"active\")?.id;\n state.log.push({\n id: `l${state.log.length + 1}`,\n role: \"user\",\n phase: state.status,\n ...(stepId !== undefined && { stepId }),\n at: Date.now(),\n text: text.trim(),\n });\n },\n \"agent/ack\": ({ messageId }) => {\n requireOpen();\n const message = state.log.find(\n (entry) => entry.id === messageId && entry.role === \"user\",\n );\n if (!message)\n throw reject(\"unknown-message\", `no user message \"${messageId}\"`);\n message.acknowledgedAt ??= Date.now();\n },\n \"checkout/dismiss\": ({ inputId }) => {\n requireOpen();\n const input = findInput(inputId);\n input.status = \"dismissed\";\n input.answeredAt = Date.now();\n },\n \"checkout/plan\": (params) => {\n requireOpen();\n const plan = currentPlan(state);\n if (plan === undefined) {\n throw reject(\"no-plan\", \"the agent has not proposed a plan yet\");\n }\n if (plan.status !== \"proposed\") {\n throw reject(\"plan-decided\", `the plan is already ${plan.status}`);\n }\n if (params.decision === \"approve\") {\n plan.status = \"approved\";\n plan.decidedAt = Date.now();\n state.status = \"installing\";\n return;\n }\n if (params.feedback.trim() === \"\") {\n throw reject(\"feedback-required\", \"say what should change\");\n }\n plan.status = \"changes-requested\";\n plan.decidedAt = Date.now();\n plan.feedback = params.feedback.trim();\n },\n \"checkout/cancel\": () => {\n if (state.status === \"cancelled\") return;\n state.status = \"cancelled\";\n dismissOpenInputs();\n },\n \"checkout/finish\": () => {\n requireOpen();\n if (state.completion === undefined) {\n throw reject(\n \"finish-not-proposed\",\n \"the agent has not proposed to finish yet\",\n );\n }\n dismissOpenInputs();\n state.status = \"done\";\n log(\"Setup complete.\");\n },\n \"agent/intro\": ({ kind } = {}) => {\n requireOpen();\n if (state.agent.introducedAt === null) {\n state.agent.introducedAt = Date.now();\n }\n if (kind !== undefined) state.agent.kind = kind;\n },\n \"agent/hello\": ({ cwd, kind } = {}) => {\n touchAgent();\n if (cwd !== undefined) state.agent.cwd = cwd;\n if (kind !== undefined) state.agent.kind = kind;\n },\n \"agent/heartbeat\": () => {\n touchAgent();\n },\n \"agent/bye\": () => {\n state.agent.connected = false;\n },\n \"agent/plan\": ({ markdown }) => {\n requireStarted();\n touchAgent();\n if (markdown.trim() === \"\") {\n throw reject(\"empty-plan\", \"the plan is empty\");\n }\n if (state.status === \"installing\") {\n throw reject(\"plan-decided\", \"the plan is already approved\");\n }\n const revision = state.plans.length + 1;\n state.plans.push({\n revision,\n markdown: markdown.trim(),\n status: \"proposed\",\n submittedAt: Date.now(),\n });\n return { revision };\n },\n \"agent/add-step\": ({ title, detail, product, active }) => {\n requireInstalling();\n if (\n product !== undefined &&\n !state.products.some((candidate) => candidate.slug === product)\n ) {\n throw reject(\"unknown-product\", `no product \"${product}\"`);\n }\n const id = `s${state.steps.length + 1}`;\n state.steps.push({\n id,\n title,\n ...(detail !== undefined && { detail }),\n status: active ? \"active\" : \"pending\",\n ...(product !== undefined && { product }),\n createdAt: Date.now(),\n });\n return { stepId: id };\n },\n \"agent/step\": ({ stepId, status, note }) => {\n requireInstalling();\n const step = findStep(stepId);\n updateStep(step, status, note);\n },\n \"agent/ask\": (seed) => {\n requireStarted();\n touchAgent();\n return ask(seed);\n },\n \"agent/log\": ({ text }) => {\n requireOpen();\n touchAgent();\n const stepId = state.steps.find((step) => step.status === \"active\")?.id;\n log(text, stepId);\n },\n \"agent/done\": ({ summary, preview } = {}) => {\n requireInstalling();\n const previewUrl = parsePreviewUrl(preview?.trim());\n if (preview !== undefined && !previewUrl) {\n throw reject(\n \"invalid-preview\",\n \"a preview is an http URL on localhost, such as http://localhost:3000\",\n );\n }\n touchAgent();\n for (const step of state.steps) {\n if (step.status === \"pending\" || step.status === \"active\") {\n updateStep(step, \"done\");\n }\n }\n log(summary?.trim() || \"Everything is installed.\");\n state.completion = {\n proposedAt: Date.now(),\n ...(previewUrl && { preview: previewUrl.href }),\n };\n },\n });\n\n const source = getStateHost(state)!;\n return {\n state,\n commands,\n snapshot: source.snapshot,\n subscribe: (listener: () => void) => {\n source.setOnChange(listener);\n return () => source.setOnChange(null);\n },\n };\n};\n\n/** The setup statewire host; pass the durable object's restored snapshot. */\nexport const CheckoutHost = (restored?: unknown) =>\n resource(useCheckoutHost)(restored);\n\nexport type { Checkout } from \"./protocol\";\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "setup-agent",
3
- "version": "0.0.1",
4
- "description": "A live checkout shared between a browser and the coding agent installing the products in it, on Statewire",
3
+ "version": "0.0.3",
4
+ "description": "A live setup shared between a browser and the coding agent installing the products in it, on Statewire",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "type": "module",
15
15
  "bin": {
16
- "setup-agent": "bin/setup-agent.js"
16
+ "setup-agent": "./bin/setup-agent.js"
17
17
  },
18
18
  "exports": {
19
19
  ".": {
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@assistant-ui/tap": "^0.9.7",
47
- "statewire": "workspace:*"
47
+ "statewire": "^0.19.4"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@assistant-ui/x-buildutils": "^0.0.20",
package/src/cli.ts CHANGED
@@ -20,11 +20,11 @@ import {
20
20
  } from "./protocol";
21
21
  import type { Checkout } from "./protocol";
22
22
 
23
- const HELP = `setup-agent <url> [command]
23
+ export const HELP = `setup-agent <url> [command]
24
24
 
25
25
  Commands (the url always comes first):
26
- status Print the checkout as JSON.
27
- stream Follow the checkout: one JSON array of events per line.
26
+ status Print the setup as JSON.
27
+ stream Follow the setup: one JSON array of events per line.
28
28
  log <text> Send a message to the user: a reply, something no question or step conveys, or one wrap-up when finished.
29
29
  ack <message-id> Acknowledge a user message.
30
30
  ask <prompt> [--placeholder <text>] [--optional] [--step <step-id>] [--wait]
@@ -39,8 +39,8 @@ Commands (the url always comes first):
39
39
  --single is for options that exclude each other (one framework, one model, yes or no); the answer is then one id.
40
40
  --icons is required and names an icon for every option, from: ${OPTION_ICONS.join(", ")}.
41
41
  ask <prompt> --product <slug> [--wait]
42
- Propose adding a product to this checkout, such as one another product needs first. Answers "added"; the product and its guide then appear in "status".
43
- wait start Block until the user clicks Begin plan in their browser; after a finished or cancelled checkout, until the next one begins.
42
+ Propose adding a product to this setup, such as one another product needs first. Answers "added"; the product and its guide then appear in "status".
43
+ wait start Block until the user clicks Begin plan in their browser; after a finished or cancelled setup, until the next one begins.
44
44
  wait <input-id> Block until the user answers or dismisses an input. A multi-select answer prints as an array.
45
45
  plan (<markdown> | --file <path>) [--wait]
46
46
  Propose the plan for the user to approve. --wait blocks until they decide.
@@ -55,19 +55,19 @@ Commands (the url always comes first):
55
55
  Mark every remaining step done and propose closing; the user closes it.
56
56
  `;
57
57
 
58
- const INSTRUCTIONS = (
58
+ export const INSTRUCTIONS = (
59
59
  url: string,
60
- ) => `You are the coding agent for a checkout the user opened in their browser. The browser shows this checkout live; every command below updates it. The user watches the browser, not this terminal: once setup begins, send questions through "ask" so the user can answer in their browser.
60
+ ) => `You are the coding agent for a setup the user opened in their browser. The browser shows this setup live; every command below updates it. The user watches the browser, not this terminal: once setup begins, send questions through "ask" so the user can answer in their browser.
61
61
 
62
62
  First, start a persistent Monitor on
63
63
  npx setup-agent ${url} stream
64
- and keep it running: the browser shows you as connected only while it runs, and it is how answers reach you. The URL is the user's link to you from their browser, not one checkout: it outlives this setup, and their next setup arrives on the same stream. Each line is a JSON array of events: "planning.started", "input.answered" ({inputId, answer, note?}; answer is an array for a choice question not asked with --single), "input.dismissed", "plan.approved", "plan.changes-requested" ({feedback}), "message.created" ({messageId, text}), "finished", "cancelled", "checkout.created" ({products}).
64
+ and keep it running: the browser shows you as connected only while it runs, and it is how answers reach you. The URL is the user's link to you from their browser, not one setup: it outlives this one, and their next setup arrives on the same stream. Each line is a JSON array of events: "planning.started", "input.answered" ({inputId, answer, note?}; answer is an array for a choice question not asked with --single), "input.dismissed", "plan.approved", "plan.changes-requested" ({feedback}), "message.created" ({messageId, text}), "finished", "cancelled", "setup.created" ({products}).
65
65
 
66
66
  The user can message you at any time, including while a question is waiting. Read "message.created" events, acknowledge each with "npx setup-agent ${url} ack <message-id>", and reply using "log" with what you will do. Pending messages are replayed when the stream starts and listed in "status". A message is not an answer or plan approval; keep those decisions explicit.
67
67
 
68
68
  "log" is a message to the user, and the user reads every one: send it only to reply to their message, to tell them something no question or step conveys (a blocker, a deviation from the plan, a decision you made for them), or once as a wrap-up when the install is finished. Findings go in the plan, progress in step notes, questions in "ask"; do not narrate.
69
69
 
70
- Before planning, check the current checkout status. If it is "waiting", tell the user in your own conversation: "I'm connected. Go back to your browser and click Begin plan. You can watch my progress, answer questions, and steer me there." Then run:
70
+ Before planning, check the current setup status. If it is "waiting", tell the user in your own conversation: "I'm connected. Go back to your browser and click Begin plan. You can watch my progress, answer questions, and steer me there." Then run:
71
71
  npx setup-agent ${url} wait start
72
72
  Keep the stream running while you wait. Do not inspect the project, fetch guides, ask setup questions, or propose a plan until this command returns {"ok":true}. Only the user in the browser should start planning; never invoke the browser's start command yourself. "planning.started" also arrives on the stream. If the status is already "planning" or "installing", resume that phase without asking the user to begin again. If it is "done" or "cancelled", that setup is over: tell the user you are connected and waiting for their next setup, and run "wait start", which returns once the next one begins.
73
73
 
@@ -75,16 +75,16 @@ The remaining phases are plan, then install.
75
75
 
76
76
  1. Plan. Investigate only; change nothing yet. If the current directory is not an app to install into (a monorepo root, an empty folder, a library), ask where to install first:
77
77
  npx setup-agent ${url} ask "Which project should I install into?" --placeholder "An absolute path, or a path for a new Next.js app" --wait
78
- Read the project: its app framework and package manager, the agent framework and model provider it already uses, where components live, any existing chat route. Fetch each product's "guide" URL from the checkout below; it is the install reference. Ask for what the project does not decide, using the standard questions so the browser can render them well:
78
+ Read the project: its app framework and package manager, the agent framework and model provider it already uses, where components live, any existing chat route. Fetch each product's "guide" URL from the setup below; it is the install reference. Ask for what the project does not decide, using the standard questions so the browser can render them well:
79
79
  npx setup-agent ${url} ask --preset framework
80
80
  npx setup-agent ${url} ask --preset llm-provider
81
- If a product's guide needs another product the project does not have yet (most need assistant-ui installed and rendering a thread), do not stop and do not install it on the side. Propose adding it to this checkout:
81
+ If a product's guide needs another product the project does not have yet (most need assistant-ui installed and rendering a thread), do not stop and do not install it on the side. Propose adding it to this setup:
82
82
  npx setup-agent ${url} ask "assistant-ui is not installed in this project yet. Add it to this setup?" --product assistant-ui --wait
83
83
  On "added", run "status", fetch the new product's guide, and install it first in your plan. If the user dismisses it, follow what the guide says about the missing product.
84
84
  Skip a preset the code already answers (a chat route on the AI SDK, a provider key in .env.local) and log why. Ask everything at once, keep investigating, and block with "wait <input-id>" only when nothing else can proceed. A framework answer is "<framework>:<language>", for example "ai-sdk:typescript". A model answer is JSON {"provider","model","reasoningEffort"?}. An answer may carry a "note" from the user; follow it. A choice answer that matches none of the options is the user's own text.
85
85
  Then write the plan as markdown and submit it:
86
86
  npx setup-agent ${url} plan --file <path> --wait
87
- Use these headings, each with bullets: "## What I found" (facts as \`- **Label:** value\`: app framework, package manager, agent framework, model provider and model, where components live), "## What I will install" (packages and files, one per bullet), "## Steps" (a numbered list, one line each), and "## Open questions" only if any remain. Keep it under 30 lines; no preamble. "plan --wait" prints {"decision":"approve"} or {"decision":"revise","feedback":"..."}. On revise, address the feedback (ask again if you need to) and submit a new plan; repeat until approved. Do not change the project before approval: the checkout refuses steps until then.
87
+ Use these headings, each with bullets: "## What I found" (facts as \`- **Label:** value\`: app framework, package manager, agent framework, model provider and model, where components live), "## What I will install" (packages and files, one per bullet), "## Steps" (a numbered list, one line each), and "## Open questions" only if any remain. Keep it under 30 lines; no preamble. "plan --wait" prints {"decision":"approve"} or {"decision":"revise","feedback":"..."}. On revise, address the feedback (ask again if you need to) and submit a new plan; repeat until approved. Do not change the project before approval: the setup refuses steps until then.
88
88
 
89
89
  2. Install. Declare the steps from your plan up front, in order:
90
90
  npx setup-agent ${url} add-step "<title>" --detail "<one line>"
@@ -95,9 +95,9 @@ which writes ENV_VAR=<key> into that file. Use the answer's model in the chat ro
95
95
 
96
96
  When every product is installed and verified, run
97
97
  npx setup-agent ${url} done --summary "<one or two lines on what you installed and how to try it>" --preview http://localhost:<port>
98
- Before that, start the project's dev server in the background when the project has one, check that the page you changed loads, and pass the address as --preview (the page itself when it is not the root); leave the server running so the user can try the result. Omit --preview when there is nothing to open. This proposes closing the checkout; only the user closes it. Do not stop the stream or exit: stay idle on it, because the user may follow up with a "message.created" event. Handle it, then run "done" again. The stream prints "finished" once the user closes the checkout, and "cancelled" if they end it early. Keep the stream running after either: the user's next setup starts on this same URL and arrives as "checkout.created" with its products. Run
98
+ Before that, start the project's dev server in the background when the project has one, check that the page you changed loads, and pass the address as --preview (the page itself when it is not the root); leave the server running so the user can try the result. Omit --preview when there is nothing to open. This proposes closing the setup; only the user closes it. Do not stop the stream or exit: stay idle on it, because the user may follow up with a "message.created" event. Handle it, then run "done" again. The stream prints "finished" once the user closes the setup, and "cancelled" if they end it early. Keep the stream running after either: the user's next setup starts on this same URL and arrives as "setup.created" with its products. Run
99
99
  npx setup-agent ${url} wait start
100
- which returns once that setup begins, then run "status" for its products, guides and instructions and go through plan and install again. A "checkout.created" event while you are still working means the user replaced this setup: drop what you were doing and start over the same way.
100
+ which returns once that setup begins, then run "status" for its products, guides and instructions and go through plan and install again. A "setup.created" event while you are still working means the user replaced this setup: drop what you were doing and start over the same way.
101
101
  `;
102
102
 
103
103
  type Parsed = { positional: string[]; flags: Map<string, string | true> };
@@ -251,7 +251,7 @@ const launchAgent = async (url: string) => {
251
251
  };
252
252
 
253
253
  const requireState = (client: CheckoutClient): Checkout.State =>
254
- client.state ?? fail("the checkout has no state");
254
+ client.state ?? fail("the setup has no state");
255
255
 
256
256
  const summary = (state: Checkout.State) => {
257
257
  const plan = currentPlan(state);
@@ -304,7 +304,7 @@ type StreamEvent =
304
304
  | { event: "plan.changes-requested"; revision: number; feedback: string }
305
305
  | { event: "finished"; next: string }
306
306
  | { event: "cancelled"; next: string }
307
- | { event: "checkout.created"; products: string[] };
307
+ | { event: "setup.created"; products: string[] };
308
308
 
309
309
  const STAY_CONNECTED =
310
310
  "This setup is over, not your connection. Keep the stream running, tell the user you are connected and waiting for their next setup, and run wait start.";
@@ -376,7 +376,7 @@ export const diffEvents = (
376
376
  }
377
377
  if (after.id !== null && after.id !== before.id) {
378
378
  events.push({
379
- event: "checkout.created",
379
+ event: "setup.created",
380
380
  products: after.products.map((product) => product.slug),
381
381
  });
382
382
  }
@@ -425,7 +425,7 @@ const stream = async (client: CheckoutClient) => {
425
425
  await new Promise(() => {});
426
426
  };
427
427
 
428
- /** Resolves once a checkout is planning or installing, or once the one being waited on is cancelled; a checkout already closed when the wait begins is over, so the wait is for the next one. */
428
+ /** Resolves once a setup is planning or installing, or once the one being waited on is cancelled; a setup already closed when the wait begins is over, so the wait is for the next one. */
429
429
  export const waitForStart = (
430
430
  client: Pick<CheckoutClient, "state" | "subscribe">,
431
431
  ) =>
@@ -637,7 +637,7 @@ export const main = async (argv: readonly string[]) => {
637
637
  ...(kind !== undefined && { kind }),
638
638
  }).catch(() => {});
639
639
  process.stdout.write(INSTRUCTIONS(url));
640
- process.stdout.write("\nCurrent checkout:\n");
640
+ process.stdout.write("\nCurrent setup:\n");
641
641
  print(summary(requireState(client)));
642
642
  break;
643
643
  }
@@ -784,7 +784,7 @@ export const main = async (argv: readonly string[]) => {
784
784
  print({
785
785
  ok: true,
786
786
  status: "proposed",
787
- next: "Keep the stream running and stay idle. The user closes the checkout or sends a follow-up message.",
787
+ next: "Keep the stream running and stay idle. The user closes the setup or sends a follow-up message.",
788
788
  });
789
789
  break;
790
790
  }
package/src/client.ts CHANGED
@@ -6,7 +6,7 @@ export type CheckoutClient = StatewireClient<
6
6
  Checkout.Commands
7
7
  >;
8
8
 
9
- /** Attach to a checkout by its base URL and resolve once its state has arrived. */
9
+ /** Attach to a setup by its base URL and resolve once its state has arrived. */
10
10
  export const connectCheckout = (url: string): Promise<CheckoutClient> =>
11
11
  new Promise((resolve, reject) => {
12
12
  const client: CheckoutClient = new StatewireClient({
@@ -20,7 +20,7 @@ export const connectCheckout = (url: string): Promise<CheckoutClient> =>
20
20
  client.dispose();
21
21
  reject(
22
22
  new Error(
23
- `checkout unreachable: ${client.connection.reason}${
23
+ `setup unreachable: ${client.connection.reason}${
24
24
  client.connection.message ? ` (${client.connection.message})` : ""
25
25
  }`,
26
26
  ),
package/src/host.ts CHANGED
@@ -34,10 +34,10 @@ const useCheckoutHost = (restored: unknown) => {
34
34
 
35
35
  const requireOpen = () => {
36
36
  if (state.createdAt === null) {
37
- throw reject("not-created", "the checkout has not been created yet");
37
+ throw reject("not-created", "the setup has not been created yet");
38
38
  }
39
39
  if (isClosed(state)) {
40
- throw reject("closed", `the checkout is ${state.status}`);
40
+ throw reject("closed", `the setup is ${state.status}`);
41
41
  }
42
42
  };
43
43
 
@@ -90,7 +90,7 @@ const useCheckoutHost = (restored: unknown) => {
90
90
  if (state.products.some((product) => product.slug === seed.product)) {
91
91
  throw reject(
92
92
  "invalid-input",
93
- `"${seed.product}" is already part of this checkout`,
93
+ `"${seed.product}" is already part of this setup`,
94
94
  );
95
95
  }
96
96
  } else if (kind !== "text" && !seed.options?.length) {
@@ -177,7 +177,7 @@ const useCheckoutHost = (restored: unknown) => {
177
177
  const commands = useStatewireCommands<Checkout.Commands>({
178
178
  "checkout/create": ({ id, products, instructions }) => {
179
179
  if (state.id === id) {
180
- throw reject("already-created", "the checkout already exists");
180
+ throw reject("already-created", "the setup already exists");
181
181
  }
182
182
  const fresh = initialCheckoutState();
183
183
  if (!isAgentPresent(state)) state.agent = fresh.agent;
@@ -428,7 +428,7 @@ const useCheckoutHost = (restored: unknown) => {
428
428
  };
429
429
  };
430
430
 
431
- /** The checkout statewire host; pass the durable object's restored snapshot. */
431
+ /** The setup statewire host; pass the durable object's restored snapshot. */
432
432
  export const CheckoutHost = (restored?: unknown) =>
433
433
  resource(useCheckoutHost)(restored);
434
434