setup-agent 0.0.3 → 0.0.5

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/dist/host.js CHANGED
@@ -1,4 +1,4 @@
1
- import { classifyChoiceAnswer, currentPlan, initialCheckoutState, isAgentPresent, isClosed, isValidModelAnswer, parsePreviewUrl } from "./protocol.js";
1
+ import { classifyChoiceAnswer, currentPlan, initialCheckoutState, isAgentPresent, isClosed, isValidEntryPointAnswer, isValidEntryPointInput, isValidModelAnswer, parseEntryPointOptions, parsePreviewUrl } from "./protocol.js";
2
2
  import { resource } from "@assistant-ui/tap";
3
3
  import { StatewireReject, getStateHost, useStatewireCommands, useStatewireState } from "statewire/host";
4
4
  //#region src/host.ts
@@ -36,7 +36,7 @@ const useCheckoutHost = (restored) => {
36
36
  return step;
37
37
  };
38
38
  const findInput = (inputId) => {
39
- const input = state.inputs.find((candidate_0) => candidate_0.id === inputId);
39
+ const input = state.inputs.find((candidate) => candidate.id === inputId);
40
40
  if (!input) throw reject("unknown-input", `no input "${inputId}"`);
41
41
  if (input.status !== "open") throw reject("input-closed", `input "${inputId}" is ${input.status}`);
42
42
  return input;
@@ -47,15 +47,20 @@ const useCheckoutHost = (restored) => {
47
47
  };
48
48
  const ask = (seed) => {
49
49
  const kind = seed.kind ?? "text";
50
+ const options = kind === "entry-point" ? parseEntryPointOptions(seed.options) : seed.options;
51
+ if (kind === "entry-point" && !isValidEntryPointInput(seed)) throw reject("invalid-input", "an entry-point input needs a prompt and 1–3 distinct options with descriptions, form factors, placements and triggers; at most one is recommended");
50
52
  if (kind === "product") {
51
53
  if (!seed.product) throw reject("invalid-input", "a product input names a product");
52
54
  if (state.products.some((product) => product.slug === seed.product)) throw reject("invalid-input", `"${seed.product}" is already part of this setup`);
53
55
  } else if (kind !== "text" && !seed.options?.length) throw reject("invalid-input", `a ${kind} input needs options`);
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
- 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`);
56
+ if (seed.multiple && (kind !== "choice" || seed.options?.some((option) => option.variants?.length))) throw reject("invalid-input", "only a choice whose options have no variants accepts several answers");
57
+ const unique = (ids) => new Set(ids).size === ids.length;
58
+ if (!unique(seed.options?.map((option) => option.id) ?? []) || seed.options?.some((option) => !unique(option.variants?.map((variant) => variant.id) ?? []))) throw reject("invalid-input", "option and variant ids must be unique");
59
+ if (seed.options?.some((option) => option.id.includes(":") || option.variants?.some((variant) => variant.id.includes(":")))) throw reject("invalid-input", "option and variant ids cannot contain \":\", which separates them in an answer");
60
+ if (seed.default !== void 0 && !seed.options?.some((option) => option.id === seed.default)) throw reject("invalid-input", `no option "${seed.default}" to default to`);
56
61
  if (seed.stepId !== void 0) findStep(seed.stepId);
57
62
  const id = `q${state.inputs.length + 1}`;
58
- const stepId_0 = seed.stepId ?? state.steps.find((step_0) => step_0.status === "active")?.id;
63
+ const stepId = seed.stepId ?? state.steps.find((step) => step.status === "active")?.id;
59
64
  state.inputs.push({
60
65
  id,
61
66
  phase: state.status,
@@ -63,46 +68,46 @@ const useCheckoutHost = (restored) => {
63
68
  ...seed.preset !== void 0 && { preset: seed.preset },
64
69
  prompt: seed.prompt,
65
70
  ...seed.placeholder !== void 0 && { placeholder: seed.placeholder },
66
- ...seed.options !== void 0 && { options: seed.options },
71
+ ...options !== void 0 && { options },
67
72
  ...seed.multiple && { multiple: true },
68
73
  ...kind === "product" && { product: seed.product },
69
74
  ...seed.default !== void 0 && { default: seed.default },
70
75
  ...seed.help !== void 0 && { help: seed.help },
71
76
  optional: seed.optional ?? false,
72
77
  status: "open",
73
- ...stepId_0 !== void 0 && { stepId: stepId_0 },
78
+ ...stepId !== void 0 && { stepId },
74
79
  createdAt: Date.now()
75
80
  });
76
81
  return { inputId: id };
77
82
  };
78
83
  const dismissOpenInputs = () => {
79
- for (const input_0 of state.inputs) if (input_0.status === "open") {
80
- input_0.status = "dismissed";
81
- input_0.answeredAt = Date.now();
84
+ for (const input of state.inputs) if (input.status === "open") {
85
+ input.status = "dismissed";
86
+ input.answeredAt = Date.now();
82
87
  }
83
88
  };
84
- const log = (text, stepId_1) => {
89
+ const log = (text, stepId) => {
85
90
  state.log.push({
86
91
  id: `l${state.log.length + 1}`,
87
92
  role: "agent",
88
93
  phase: state.status,
89
94
  at: Date.now(),
90
95
  text,
91
- ...stepId_1 !== void 0 && { stepId: stepId_1 }
96
+ ...stepId !== void 0 && { stepId }
92
97
  });
93
98
  };
94
- const updateStep = (step_1, status, note) => {
95
- const changed = step_1.status !== status;
96
- step_1.status = status;
97
- if (note !== void 0) step_1.note = note;
98
- if (changed && (status === "done" || status === "skipped")) log(`${status === "done" ? "Completed" : "Skipped"}: ${step_1.title}${step_1.note ? `\n\n${step_1.note}` : ""}`, step_1.id);
99
+ const updateStep = (step, status, note) => {
100
+ const changed = step.status !== status;
101
+ step.status = status;
102
+ if (note !== void 0) step.note = note;
103
+ if (changed && (status === "done" || status === "skipped")) log(`${status === "done" ? "Completed" : "Skipped"}: ${step.title}${step.note ? `\n\n${step.note}` : ""}`, step.id);
99
104
  };
100
105
  const commands = useStatewireCommands({
101
- "checkout/create": ({ id: id_0, products, instructions }) => {
102
- if (state.id === id_0) throw reject("already-created", "the setup already exists");
106
+ "checkout/create": ({ id, products, instructions }) => {
107
+ if (state.id === id) throw reject("already-created", "the setup already exists");
103
108
  const fresh = initialCheckoutState();
104
109
  if (!isAgentPresent(state)) state.agent = fresh.agent;
105
- state.id = id_0;
110
+ state.id = id;
106
111
  state.status = fresh.status;
107
112
  delete state.completion;
108
113
  state.plans = fresh.plans;
@@ -111,65 +116,66 @@ const useCheckoutHost = (restored) => {
111
116
  state.log = fresh.log;
112
117
  state.createdAt = Date.now();
113
118
  state.instructions = instructions?.trim() ?? "";
114
- state.products = products.map((product_0) => ({
115
- slug: product_0.slug,
116
- name: product_0.name,
117
- ...product_0.guide !== void 0 && { guide: product_0.guide }
119
+ state.products = products.map((product) => ({
120
+ slug: product.slug,
121
+ name: product.name,
122
+ ...product.guide !== void 0 && { guide: product.guide }
118
123
  }));
119
124
  },
120
125
  "checkout/begin-plan": () => {
121
126
  requireOpen();
122
127
  if (state.status === "waiting") state.status = "planning";
123
128
  },
124
- "checkout/answer": ({ inputId: inputId_0, answer, note: note_0 }) => {
129
+ "checkout/answer": ({ inputId, answer, note }) => {
125
130
  requireOpen();
126
- const input_1 = findInput(inputId_0);
127
- if (input_1.kind === "choice" && classifyChoiceAnswer(input_1, answer) === "invalid") throw reject("invalid-answer", input_1.multiple ? `"${answer}" is not a JSON array of option ids` : `"${answer}" names an option without a valid variant`);
128
- if (input_1.kind === "model" && !isValidModelAnswer(input_1, answer)) throw reject("invalid-answer", "a model answer is JSON with provider and model");
129
- if (input_1.kind === "product") throw reject("invalid-answer", "a product input is answered with checkout/add-product");
130
- if (input_1.kind === "text" && answer.trim() === "") throw reject("invalid-answer", "an answer cannot be empty");
131
- input_1.status = "answered";
132
- input_1.answeredAt = Date.now();
133
- input_1.answer = answer;
134
- if (note_0 !== void 0 && note_0.trim() !== "") input_1.note = note_0.trim();
131
+ const input = findInput(inputId);
132
+ if (input.kind === "choice" && classifyChoiceAnswer(input, answer) === "invalid") throw reject("invalid-answer", input.multiple ? `"${answer}" is not a JSON array of option ids` : `"${answer}" names an option without a valid variant`);
133
+ if (input.kind === "entry-point" && !isValidEntryPointAnswer(input, answer)) throw reject("invalid-answer", "an entry-point answer is an option id");
134
+ if (input.kind === "model" && !isValidModelAnswer(input, answer)) throw reject("invalid-answer", "a model answer is JSON with provider and model");
135
+ if (input.kind === "product") throw reject("invalid-answer", "a product input is answered with checkout/add-product");
136
+ if (input.kind === "text" && answer.trim() === "") throw reject("invalid-answer", "an answer cannot be empty");
137
+ input.status = "answered";
138
+ input.answeredAt = Date.now();
139
+ input.answer = answer;
140
+ if (note !== void 0 && note.trim() !== "") input.note = note.trim();
135
141
  },
136
- "checkout/add-product": ({ inputId: inputId_1, product: product_1 }) => {
142
+ "checkout/add-product": ({ inputId, product }) => {
137
143
  requireOpen();
138
- const input_2 = findInput(inputId_1);
139
- if (input_2.kind !== "product" || input_2.product !== product_1.slug) throw reject("invalid-answer", `input "${inputId_1}" does not propose "${product_1.slug}"`);
140
- if (!state.products.some((entry) => entry.slug === product_1.slug)) state.products.push({
141
- slug: product_1.slug,
142
- name: product_1.name,
143
- ...product_1.guide !== void 0 && { guide: product_1.guide }
144
+ const input = findInput(inputId);
145
+ if (input.kind !== "product" || input.product !== product.slug) throw reject("invalid-answer", `input "${inputId}" does not propose "${product.slug}"`);
146
+ if (!state.products.some((entry) => entry.slug === product.slug)) state.products.push({
147
+ slug: product.slug,
148
+ name: product.name,
149
+ ...product.guide !== void 0 && { guide: product.guide }
144
150
  });
145
- input_2.status = "answered";
146
- input_2.answer = "added";
147
- input_2.answeredAt = Date.now();
151
+ input.status = "answered";
152
+ input.answer = "added";
153
+ input.answeredAt = Date.now();
148
154
  },
149
- "checkout/message": ({ text: text_0 }) => {
155
+ "checkout/message": ({ text }) => {
150
156
  requireOpen();
151
- if (text_0.trim() === "") throw reject("empty-message", "a message cannot be empty");
152
- const stepId_2 = state.steps.find((step_2) => step_2.status === "active")?.id;
157
+ if (text.trim() === "") throw reject("empty-message", "a message cannot be empty");
158
+ const stepId = state.steps.find((step) => step.status === "active")?.id;
153
159
  state.log.push({
154
160
  id: `l${state.log.length + 1}`,
155
161
  role: "user",
156
162
  phase: state.status,
157
- ...stepId_2 !== void 0 && { stepId: stepId_2 },
163
+ ...stepId !== void 0 && { stepId },
158
164
  at: Date.now(),
159
- text: text_0.trim()
165
+ text: text.trim()
160
166
  });
161
167
  },
162
168
  "agent/ack": ({ messageId }) => {
163
169
  requireOpen();
164
- const message = state.log.find((entry_0) => entry_0.id === messageId && entry_0.role === "user");
170
+ const message = state.log.find((entry) => entry.id === messageId && entry.role === "user");
165
171
  if (!message) throw reject("unknown-message", `no user message "${messageId}"`);
166
172
  message.acknowledgedAt ??= Date.now();
167
173
  },
168
- "checkout/dismiss": ({ inputId: inputId_2 }) => {
174
+ "checkout/dismiss": ({ inputId }) => {
169
175
  requireOpen();
170
- const input_3 = findInput(inputId_2);
171
- input_3.status = "dismissed";
172
- input_3.answeredAt = Date.now();
176
+ const input = findInput(inputId);
177
+ input.status = "dismissed";
178
+ input.answeredAt = Date.now();
173
179
  },
174
180
  "checkout/plan": (params) => {
175
181
  requireOpen();
@@ -187,8 +193,9 @@ const useCheckoutHost = (restored) => {
187
193
  plan.decidedAt = Date.now();
188
194
  plan.feedback = params.feedback.trim();
189
195
  },
190
- "checkout/cancel": () => {
191
- if (state.status === "cancelled") return;
196
+ "checkout/cancel": ({ id } = {}) => {
197
+ if (state.createdAt === null || isClosed(state)) return;
198
+ if (id !== void 0 && id !== state.id) return;
192
199
  state.status = "cancelled";
193
200
  dismissOpenInputs();
194
201
  },
@@ -199,15 +206,15 @@ const useCheckoutHost = (restored) => {
199
206
  state.status = "done";
200
207
  log("Setup complete.");
201
208
  },
202
- "agent/intro": ({ kind: kind_0 } = {}) => {
209
+ "agent/intro": ({ kind } = {}) => {
203
210
  requireOpen();
204
211
  if (state.agent.introducedAt === null) state.agent.introducedAt = Date.now();
205
- if (kind_0 !== void 0) state.agent.kind = kind_0;
212
+ if (kind !== void 0) state.agent.kind = kind;
206
213
  },
207
- "agent/hello": ({ cwd, kind: kind_1 } = {}) => {
214
+ "agent/hello": ({ cwd, kind } = {}) => {
208
215
  touchAgent();
209
216
  if (cwd !== void 0) state.agent.cwd = cwd;
210
- if (kind_1 !== void 0) state.agent.kind = kind_1;
217
+ if (kind !== void 0) state.agent.kind = kind;
211
218
  },
212
219
  "agent/heartbeat": () => {
213
220
  touchAgent();
@@ -229,42 +236,42 @@ const useCheckoutHost = (restored) => {
229
236
  });
230
237
  return { revision };
231
238
  },
232
- "agent/add-step": ({ title, detail, product: product_2, active }) => {
239
+ "agent/add-step": ({ title, detail, product, active }) => {
233
240
  requireInstalling();
234
- if (product_2 !== void 0 && !state.products.some((candidate_1) => candidate_1.slug === product_2)) throw reject("unknown-product", `no product "${product_2}"`);
235
- const id_1 = `s${state.steps.length + 1}`;
241
+ if (product !== void 0 && product.trim() === "") throw reject("invalid-input", "a step's product identifier cannot be empty");
242
+ const id = `s${state.steps.length + 1}`;
236
243
  state.steps.push({
237
- id: id_1,
244
+ id,
238
245
  title,
239
246
  ...detail !== void 0 && { detail },
240
247
  status: active ? "active" : "pending",
241
- ...product_2 !== void 0 && { product: product_2 },
248
+ ...product !== void 0 && { product },
242
249
  createdAt: Date.now()
243
250
  });
244
- return { stepId: id_1 };
251
+ return { stepId: id };
245
252
  },
246
- "agent/step": ({ stepId: stepId_3, status: status_0, note: note_1 }) => {
253
+ "agent/step": ({ stepId, status, note }) => {
247
254
  requireInstalling();
248
- const step_3 = findStep(stepId_3);
249
- updateStep(step_3, status_0, note_1);
255
+ const step = findStep(stepId);
256
+ updateStep(step, status, note);
250
257
  },
251
- "agent/ask": (seed_0) => {
258
+ "agent/ask": (seed) => {
252
259
  requireStarted();
253
260
  touchAgent();
254
- return ask(seed_0);
261
+ return ask(seed);
255
262
  },
256
- "agent/log": ({ text: text_1 }) => {
263
+ "agent/log": ({ text }) => {
257
264
  requireOpen();
258
265
  touchAgent();
259
- const stepId_4 = state.steps.find((step_4) => step_4.status === "active")?.id;
260
- log(text_1, stepId_4);
266
+ const stepId = state.steps.find((step) => step.status === "active")?.id;
267
+ log(text, stepId);
261
268
  },
262
269
  "agent/done": ({ summary, preview } = {}) => {
263
270
  requireInstalling();
264
271
  const previewUrl = parsePreviewUrl(preview?.trim());
265
272
  if (preview !== void 0 && !previewUrl) throw reject("invalid-preview", "a preview is an http URL on localhost, such as http://localhost:3000");
266
273
  touchAgent();
267
- for (const step_5 of state.steps) if (step_5.status === "pending" || step_5.status === "active") updateStep(step_5, "done");
274
+ for (const step of state.steps) if (step.status === "pending" || step.status === "active") updateStep(step, "done");
268
275
  log(summary?.trim() || "Everything is installed.");
269
276
  state.completion = {
270
277
  proposedAt: Date.now(),
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 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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"}
1
+ {"version":3,"file":"host.js","names":[],"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 isValidEntryPointAnswer,\n isValidEntryPointInput,\n parseEntryPointOptions,\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 const options =\n kind === \"entry-point\"\n ? parseEntryPointOptions(seed.options)\n : seed.options;\n if (kind === \"entry-point\" && !isValidEntryPointInput(seed)) {\n throw reject(\n \"invalid-input\",\n \"an entry-point input needs a prompt and 1–3 distinct options with descriptions, form factors, placements and triggers; at most one is recommended\",\n );\n }\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\" ||\n seed.options?.some((option) => option.variants?.length))\n ) {\n throw reject(\n \"invalid-input\",\n \"only a choice whose options have no variants accepts several answers\",\n );\n }\n const unique = (ids: readonly string[]) => new Set(ids).size === ids.length;\n if (\n !unique(seed.options?.map((option) => option.id) ?? []) ||\n seed.options?.some(\n (option) =>\n !unique(option.variants?.map((variant) => variant.id) ?? []),\n )\n ) {\n throw reject(\"invalid-input\", \"option and variant ids must be unique\");\n }\n if (\n seed.options?.some(\n (option) =>\n option.id.includes(\":\") ||\n option.variants?.some((variant) => variant.id.includes(\":\")),\n )\n ) {\n throw reject(\n \"invalid-input\",\n 'option and variant ids cannot contain \":\", which separates them in an answer',\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 ...(options !== undefined && { 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 (\n input.kind === \"entry-point\" &&\n !isValidEntryPointAnswer(input, answer)\n ) {\n throw reject(\"invalid-answer\", \"an entry-point answer is an option id\");\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\": ({ id } = {}) => {\n if (state.createdAt === null || isClosed(state)) return;\n if (id !== undefined && id !== state.id) 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 (product !== undefined && product.trim() === \"\") {\n throw reject(\n \"invalid-input\",\n \"a step's product identifier cannot be empty\",\n );\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":";;;;AAqBA,MAAM,UAAU,QAAkC,YAChD,IAAI,gBAAgB,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AAEtD,MAAM,mBAAmB,aAAsB;CAC7C,MAAM,CAAC,SAAS,wBAAwC;EACtD,MAAM,UAAU,qBAAqB;EACrC,IAAI,aAAa,KAAA,GAAW,OAAO;EACnC,MAAM,WAAW;EACjB,IAAI,SAAS,YAAY,GAAG,OAAO;EACnC,OAAO;GACL,GAAG;GACH,GAAG;GACH,OAAO;IAAE,GAAG,QAAQ;IAAO,GAAG,SAAS;GAAM;EAC/C;CACF,CAAC;CAED,MAAM,oBAAoB;EACxB,IAAI,MAAM,cAAc,MACtB,MAAM,OAAO,eAAe,oCAAoC;EAElE,IAAI,SAAS,KAAK,GAChB,MAAM,OAAO,UAAU,gBAAgB,MAAM,QAAQ;CAEzD;CAEA,MAAM,uBAAuB;EAC3B,YAAY;EACZ,IAAI,MAAM,WAAW,WACnB,MAAM,OACJ,wBACA,4FACF;CAEJ;CAEA,MAAM,0BAA0B;EAC9B,YAAY;EACZ,IAAI,MAAM,WAAW,cACnB,MAAM,OACJ,iBACA,mEACF;CAEJ;CAEA,MAAM,YAAY,WAAmB;EACnC,MAAM,OAAO,MAAM,MAAM,MAAM,cAAc,UAAU,OAAO,MAAM;EACpE,IAAI,CAAC,MAAM,MAAM,OAAO,gBAAgB,YAAY,OAAO,EAAE;EAC7D,OAAO;CACT;CAEA,MAAM,aAAa,YAAoB;EACrC,MAAM,QAAQ,MAAM,OAAO,MAAM,cAAc,UAAU,OAAO,OAAO;EACvE,IAAI,CAAC,OAAO,MAAM,OAAO,iBAAiB,aAAa,QAAQ,EAAE;EACjE,IAAI,MAAM,WAAW,QACnB,MAAM,OAAO,gBAAgB,UAAU,QAAQ,OAAO,MAAM,QAAQ;EAEtE,OAAO;CACT;CAEA,MAAM,mBAAmB;EACvB,MAAM,MAAM,aAAa,KAAK,IAAI;EAClC,MAAM,MAAM,YAAY;CAC1B;CAEA,MAAM,OAAO,SAAmD;EAC9D,MAAM,OAAO,KAAK,QAAQ;EAC1B,MAAM,UACJ,SAAS,gBACL,uBAAuB,KAAK,OAAO,IACnC,KAAK;EACX,IAAI,SAAS,iBAAiB,CAAC,uBAAuB,IAAI,GACxD,MAAM,OACJ,iBACA,mJACF;EAEF,IAAI,SAAS,WAAW;GACtB,IAAI,CAAC,KAAK,SACR,MAAM,OAAO,iBAAiB,iCAAiC;GAEjE,IAAI,MAAM,SAAS,MAAM,YAAY,QAAQ,SAAS,KAAK,OAAO,GAChE,MAAM,OACJ,iBACA,IAAI,KAAK,QAAQ,gCACnB;EAEJ,OAAO,IAAI,SAAS,UAAU,CAAC,KAAK,SAAS,QAC3C,MAAM,OAAO,iBAAiB,KAAK,KAAK,qBAAqB;EAE/D,IACE,KAAK,aACJ,SAAS,YACR,KAAK,SAAS,MAAM,WAAW,OAAO,UAAU,MAAM,IAExD,MAAM,OACJ,iBACA,sEACF;EAEF,MAAM,UAAU,QAA2B,IAAI,IAAI,GAAG,CAAC,CAAC,SAAS,IAAI;EACrE,IACE,CAAC,OAAO,KAAK,SAAS,KAAK,WAAW,OAAO,EAAE,KAAK,CAAC,CAAC,KACtD,KAAK,SAAS,MACX,WACC,CAAC,OAAO,OAAO,UAAU,KAAK,YAAY,QAAQ,EAAE,KAAK,CAAC,CAAC,CAC/D,GAEA,MAAM,OAAO,iBAAiB,uCAAuC;EAEvE,IACE,KAAK,SAAS,MACX,WACC,OAAO,GAAG,SAAS,GAAG,KACtB,OAAO,UAAU,MAAM,YAAY,QAAQ,GAAG,SAAS,GAAG,CAAC,CAC/D,GAEA,MAAM,OACJ,iBACA,gFACF;EAEF,IACE,KAAK,YAAY,KAAA,KACjB,CAAC,KAAK,SAAS,MAAM,WAAW,OAAO,OAAO,KAAK,OAAO,GAE1D,MAAM,OACJ,iBACA,cAAc,KAAK,QAAQ,gBAC7B;EAEF,IAAI,KAAK,WAAW,KAAA,GAAW,SAAS,KAAK,MAAM;EACnD,MAAM,KAAK,IAAI,MAAM,OAAO,SAAS;EACrC,MAAM,SACJ,KAAK,UAAU,MAAM,MAAM,MAAM,SAAS,KAAK,WAAW,QAAQ,CAAC,EAAE;EACvE,MAAM,OAAO,KAAK;GAChB;GACA,OAAO,MAAM;GACb;GACA,GAAI,KAAK,WAAW,KAAA,KAAa,EAAE,QAAQ,KAAK,OAAO;GACvD,QAAQ,KAAK;GACb,GAAI,KAAK,gBAAgB,KAAA,KAAa,EAAE,aAAa,KAAK,YAAY;GACtE,GAAI,YAAY,KAAA,KAAa,EAAE,QAAQ;GACvC,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK;GACtC,GAAI,SAAS,aAAa,EAAE,SAAS,KAAK,QAAQ;GAClD,GAAI,KAAK,YAAY,KAAA,KAAa,EAAE,SAAS,KAAK,QAAQ;GAC1D,GAAI,KAAK,SAAS,KAAA,KAAa,EAAE,MAAM,KAAK,KAAK;GACjD,UAAU,KAAK,YAAY;GAC3B,QAAQ;GACR,GAAI,WAAW,KAAA,KAAa,EAAE,OAAO;GACrC,WAAW,KAAK,IAAI;EACtB,CAAC;EACD,OAAO,EAAE,SAAS,GAAG;CACvB;CAEA,MAAM,0BAA0B;EAC9B,KAAK,MAAM,SAAS,MAAM,QACxB,IAAI,MAAM,WAAW,QAAQ;GAC3B,MAAM,SAAS;GACf,MAAM,aAAa,KAAK,IAAI;EAC9B;CAEJ;CAEA,MAAM,OAAO,MAAc,WAAoB;EAC7C,MAAM,IAAI,KAAK;GACb,IAAI,IAAI,MAAM,IAAI,SAAS;GAC3B,MAAM;GACN,OAAO,MAAM;GACb,IAAI,KAAK,IAAI;GACb;GACA,GAAI,WAAW,KAAA,KAAa,EAAE,OAAO;EACvC,CAAC;CACH;CAEA,MAAM,cACJ,MACA,QACA,SACG;EACH,MAAM,UAAU,KAAK,WAAW;EAChC,KAAK,SAAS;EACd,IAAI,SAAS,KAAA,GAAW,KAAK,OAAO;EACpC,IAAI,YAAY,WAAW,UAAU,WAAW,YAC9C,IACE,GAAG,WAAW,SAAS,cAAc,UAAU,IAAI,KAAK,QAAQ,KAAK,OAAO,OAAO,KAAK,SAAS,MACjG,KAAK,EACP;CAEJ;CAEA,MAAM,WAAW,qBAAwC;EACvD,oBAAoB,EAAE,IAAI,UAAU,mBAAmB;GACrD,IAAI,MAAM,OAAO,IACf,MAAM,OAAO,mBAAmB,0BAA0B;GAE5D,MAAM,QAAQ,qBAAqB;GACnC,IAAI,CAAC,eAAe,KAAK,GAAG,MAAM,QAAQ,MAAM;GAChD,MAAM,KAAK;GACX,MAAM,SAAS,MAAM;GACrB,OAAO,MAAM;GACb,MAAM,QAAQ,MAAM;GACpB,MAAM,QAAQ,MAAM;GACpB,MAAM,SAAS,MAAM;GACrB,MAAM,MAAM,MAAM;GAClB,MAAM,YAAY,KAAK,IAAI;GAC3B,MAAM,eAAe,cAAc,KAAK,KAAK;GAC7C,MAAM,WAAW,SAAS,KAAK,aAAa;IAC1C,MAAM,QAAQ;IACd,MAAM,QAAQ;IACd,GAAI,QAAQ,UAAU,KAAA,KAAa,EAAE,OAAO,QAAQ,MAAM;GAC5D,EAAE;EACJ;EACA,6BAA6B;GAC3B,YAAY;GACZ,IAAI,MAAM,WAAW,WAAW,MAAM,SAAS;EACjD;EACA,oBAAoB,EAAE,SAAS,QAAQ,WAAW;GAChD,YAAY;GACZ,MAAM,QAAQ,UAAU,OAAO;GAC/B,IACE,MAAM,SAAS,YACf,qBAAqB,OAAO,MAAM,MAAM,WAExC,MAAM,OACJ,kBACA,MAAM,WACF,IAAI,OAAO,uCACX,IAAI,OAAO,0CACjB;GAEF,IACE,MAAM,SAAS,iBACf,CAAC,wBAAwB,OAAO,MAAM,GAEtC,MAAM,OAAO,kBAAkB,uCAAuC;GAExE,IAAI,MAAM,SAAS,WAAW,CAAC,mBAAmB,OAAO,MAAM,GAC7D,MAAM,OACJ,kBACA,gDACF;GAEF,IAAI,MAAM,SAAS,WACjB,MAAM,OACJ,kBACA,uDACF;GAEF,IAAI,MAAM,SAAS,UAAU,OAAO,KAAK,MAAM,IAC7C,MAAM,OAAO,kBAAkB,2BAA2B;GAE5D,MAAM,SAAS;GACf,MAAM,aAAa,KAAK,IAAI;GAC5B,MAAM,SAAS;GACf,IAAI,SAAS,KAAA,KAAa,KAAK,KAAK,MAAM,IAAI,MAAM,OAAO,KAAK,KAAK;EACvE;EACA,yBAAyB,EAAE,SAAS,cAAc;GAChD,YAAY;GACZ,MAAM,QAAQ,UAAU,OAAO;GAC/B,IAAI,MAAM,SAAS,aAAa,MAAM,YAAY,QAAQ,MACxD,MAAM,OACJ,kBACA,UAAU,QAAQ,sBAAsB,QAAQ,KAAK,EACvD;GAEF,IAAI,CAAC,MAAM,SAAS,MAAM,UAAU,MAAM,SAAS,QAAQ,IAAI,GAC7D,MAAM,SAAS,KAAK;IAClB,MAAM,QAAQ;IACd,MAAM,QAAQ;IACd,GAAI,QAAQ,UAAU,KAAA,KAAa,EAAE,OAAO,QAAQ,MAAM;GAC5D,CAAC;GAEH,MAAM,SAAS;GACf,MAAM,SAAS;GACf,MAAM,aAAa,KAAK,IAAI;EAC9B;EACA,qBAAqB,EAAE,WAAW;GAChC,YAAY;GACZ,IAAI,KAAK,KAAK,MAAM,IAClB,MAAM,OAAO,iBAAiB,2BAA2B;GAC3D,MAAM,SAAS,MAAM,MAAM,MAAM,SAAS,KAAK,WAAW,QAAQ,CAAC,EAAE;GACrE,MAAM,IAAI,KAAK;IACb,IAAI,IAAI,MAAM,IAAI,SAAS;IAC3B,MAAM;IACN,OAAO,MAAM;IACb,GAAI,WAAW,KAAA,KAAa,EAAE,OAAO;IACrC,IAAI,KAAK,IAAI;IACb,MAAM,KAAK,KAAK;GAClB,CAAC;EACH;EACA,cAAc,EAAE,gBAAgB;GAC9B,YAAY;GACZ,MAAM,UAAU,MAAM,IAAI,MACvB,UAAU,MAAM,OAAO,aAAa,MAAM,SAAS,MACtD;GACA,IAAI,CAAC,SACH,MAAM,OAAO,mBAAmB,oBAAoB,UAAU,EAAE;GAClE,QAAQ,mBAAmB,KAAK,IAAI;EACtC;EACA,qBAAqB,EAAE,cAAc;GACnC,YAAY;GACZ,MAAM,QAAQ,UAAU,OAAO;GAC/B,MAAM,SAAS;GACf,MAAM,aAAa,KAAK,IAAI;EAC9B;EACA,kBAAkB,WAAW;GAC3B,YAAY;GACZ,MAAM,OAAO,YAAY,KAAK;GAC9B,IAAI,SAAS,KAAA,GACX,MAAM,OAAO,WAAW,uCAAuC;GAEjE,IAAI,KAAK,WAAW,YAClB,MAAM,OAAO,gBAAgB,uBAAuB,KAAK,QAAQ;GAEnE,IAAI,OAAO,aAAa,WAAW;IACjC,KAAK,SAAS;IACd,KAAK,YAAY,KAAK,IAAI;IAC1B,MAAM,SAAS;IACf;GACF;GACA,IAAI,OAAO,SAAS,KAAK,MAAM,IAC7B,MAAM,OAAO,qBAAqB,wBAAwB;GAE5D,KAAK,SAAS;GACd,KAAK,YAAY,KAAK,IAAI;GAC1B,KAAK,WAAW,OAAO,SAAS,KAAK;EACvC;EACA,oBAAoB,EAAE,OAAO,CAAC,MAAM;GAClC,IAAI,MAAM,cAAc,QAAQ,SAAS,KAAK,GAAG;GACjD,IAAI,OAAO,KAAA,KAAa,OAAO,MAAM,IAAI;GACzC,MAAM,SAAS;GACf,kBAAkB;EACpB;EACA,yBAAyB;GACvB,YAAY;GACZ,IAAI,MAAM,eAAe,KAAA,GACvB,MAAM,OACJ,uBACA,0CACF;GAEF,kBAAkB;GAClB,MAAM,SAAS;GACf,IAAI,iBAAiB;EACvB;EACA,gBAAgB,EAAE,SAAS,CAAC,MAAM;GAChC,YAAY;GACZ,IAAI,MAAM,MAAM,iBAAiB,MAC/B,MAAM,MAAM,eAAe,KAAK,IAAI;GAEtC,IAAI,SAAS,KAAA,GAAW,MAAM,MAAM,OAAO;EAC7C;EACA,gBAAgB,EAAE,KAAK,SAAS,CAAC,MAAM;GACrC,WAAW;GACX,IAAI,QAAQ,KAAA,GAAW,MAAM,MAAM,MAAM;GACzC,IAAI,SAAS,KAAA,GAAW,MAAM,MAAM,OAAO;EAC7C;EACA,yBAAyB;GACvB,WAAW;EACb;EACA,mBAAmB;GACjB,MAAM,MAAM,YAAY;EAC1B;EACA,eAAe,EAAE,eAAe;GAC9B,eAAe;GACf,WAAW;GACX,IAAI,SAAS,KAAK,MAAM,IACtB,MAAM,OAAO,cAAc,mBAAmB;GAEhD,IAAI,MAAM,WAAW,cACnB,MAAM,OAAO,gBAAgB,8BAA8B;GAE7D,MAAM,WAAW,MAAM,MAAM,SAAS;GACtC,MAAM,MAAM,KAAK;IACf;IACA,UAAU,SAAS,KAAK;IACxB,QAAQ;IACR,aAAa,KAAK,IAAI;GACxB,CAAC;GACD,OAAO,EAAE,SAAS;EACpB;EACA,mBAAmB,EAAE,OAAO,QAAQ,SAAS,aAAa;GACxD,kBAAkB;GAClB,IAAI,YAAY,KAAA,KAAa,QAAQ,KAAK,MAAM,IAC9C,MAAM,OACJ,iBACA,6CACF;GAEF,MAAM,KAAK,IAAI,MAAM,MAAM,SAAS;GACpC,MAAM,MAAM,KAAK;IACf;IACA;IACA,GAAI,WAAW,KAAA,KAAa,EAAE,OAAO;IACrC,QAAQ,SAAS,WAAW;IAC5B,GAAI,YAAY,KAAA,KAAa,EAAE,QAAQ;IACvC,WAAW,KAAK,IAAI;GACtB,CAAC;GACD,OAAO,EAAE,QAAQ,GAAG;EACtB;EACA,eAAe,EAAE,QAAQ,QAAQ,WAAW;GAC1C,kBAAkB;GAClB,MAAM,OAAO,SAAS,MAAM;GAC5B,WAAW,MAAM,QAAQ,IAAI;EAC/B;EACA,cAAc,SAAS;GACrB,eAAe;GACf,WAAW;GACX,OAAO,IAAI,IAAI;EACjB;EACA,cAAc,EAAE,WAAW;GACzB,YAAY;GACZ,WAAW;GACX,MAAM,SAAS,MAAM,MAAM,MAAM,SAAS,KAAK,WAAW,QAAQ,CAAC,EAAE;GACrE,IAAI,MAAM,MAAM;EAClB;EACA,eAAe,EAAE,SAAS,YAAY,CAAC,MAAM;GAC3C,kBAAkB;GAClB,MAAM,aAAa,gBAAgB,SAAS,KAAK,CAAC;GAClD,IAAI,YAAY,KAAA,KAAa,CAAC,YAC5B,MAAM,OACJ,mBACA,sEACF;GAEF,WAAW;GACX,KAAK,MAAM,QAAQ,MAAM,OACvB,IAAI,KAAK,WAAW,aAAa,KAAK,WAAW,UAC/C,WAAW,MAAM,MAAM;GAG3B,IAAI,SAAS,KAAK,KAAK,0BAA0B;GACjD,MAAM,aAAa;IACjB,YAAY,KAAK,IAAI;IACrB,GAAI,cAAc,EAAE,SAAS,WAAW,KAAK;GAC/C;EACF;CACF,CAAC;CAED,MAAM,SAAS,aAAa,KAAK;CACjC,OAAO;EACL;EACA;EACA,UAAU,OAAO;EACjB,YAAY,aAAyB;GACnC,OAAO,YAAY,QAAQ;GAC3B,aAAa,OAAO,YAAY,IAAI;EACtC;CACF;AACF;;AAGA,MAAa,gBAAgB,aAC3B,SAAS,eAAe,CAAC,CAAC,QAAQ"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
- import { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, Checkout, OPTION_ICONS, OptionIcon, classifyChoiceAnswer, currentPlan, finishProposed, followedUpSinceProposal, initialCheckoutState, isAgentPresent, isClosed, isOptionIcon, isValidModelAnswer, openInputs, parseChoiceAnswer, parseModelAnswer, parseMultipleAnswer, parsePreviewUrl, planNeedsReview, stepProgress } from "./protocol.js";
2
- import { CheckoutClient, connectCheckout } from "./client.js";
3
- import { INPUT_PRESETS, PresetId, PresetOverrides, isPresetId, presetInput } from "./presets.js";
4
- export { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, type Checkout, type CheckoutClient, INPUT_PRESETS, OPTION_ICONS, type OptionIcon, type PresetId, type PresetOverrides, classifyChoiceAnswer, connectCheckout, currentPlan, finishProposed, followedUpSinceProposal, initialCheckoutState, isAgentPresent, isClosed, isOptionIcon, isPresetId, isValidModelAnswer, openInputs, parseChoiceAnswer, parseModelAnswer, parseMultipleAnswer, parsePreviewUrl, planNeedsReview, presetInput, stepProgress };
1
+ export type { Checkout, OptionIcon } from "./protocol.js";
2
+ export { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, OPTION_ICONS, classifyChoiceAnswer, currentPlan, initialCheckoutState, finishProposed, followedUpSinceProposal, isAgentPresent, isClosed, isOptionIcon, isValidModelAnswer, isValidEntryPointAnswer, isValidEntryPointInput, openInputs, parseChoiceAnswer, parseEntryPointOptions, parseModelAnswer, parseMultipleAnswer, parsePreviewUrl, planNeedsReview, stepProgress, } from "./protocol.js";
3
+ export { INPUT_PRESETS, isPresetId, presetInput } from "./presets.js";
4
+ export type { PresetId, PresetOverrides } from "./presets.js";
5
+ export { connectCheckout } from "./client.js";
6
+ export type { CheckoutClient } from "./client.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,sBAAmB;AACvD,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,uBAAuB,EACvB,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,kBAAkB,EAClB,uBAAuB,EACvB,sBAAsB,EACtB,UAAU,EACV,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,YAAY,GACb,sBAAmB;AACpB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,qBAAkB;AACnE,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,qBAAkB;AAC3D,OAAO,EAAE,eAAe,EAAE,oBAAiB;AAC3C,YAAY,EAAE,cAAc,EAAE,oBAAiB"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { connectCheckout } from "./client.js";
2
2
  import { INPUT_PRESETS, isPresetId, presetInput } from "./presets.js";
3
- import { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, OPTION_ICONS, classifyChoiceAnswer, currentPlan, finishProposed, followedUpSinceProposal, initialCheckoutState, isAgentPresent, isClosed, isOptionIcon, isValidModelAnswer, openInputs, parseChoiceAnswer, parseModelAnswer, parseMultipleAnswer, parsePreviewUrl, planNeedsReview, stepProgress } from "./protocol.js";
4
- export { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, INPUT_PRESETS, OPTION_ICONS, classifyChoiceAnswer, connectCheckout, currentPlan, finishProposed, followedUpSinceProposal, initialCheckoutState, isAgentPresent, isClosed, isOptionIcon, isPresetId, isValidModelAnswer, openInputs, parseChoiceAnswer, parseModelAnswer, parseMultipleAnswer, parsePreviewUrl, planNeedsReview, presetInput, stepProgress };
3
+ import { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, OPTION_ICONS, classifyChoiceAnswer, currentPlan, finishProposed, followedUpSinceProposal, initialCheckoutState, isAgentPresent, isClosed, isOptionIcon, isValidEntryPointAnswer, isValidEntryPointInput, isValidModelAnswer, openInputs, parseChoiceAnswer, parseEntryPointOptions, parseModelAnswer, parseMultipleAnswer, parsePreviewUrl, planNeedsReview, stepProgress } from "./protocol.js";
4
+ export { AGENT_HEARTBEAT_MS, AGENT_PRESENCE_MS, INPUT_PRESETS, OPTION_ICONS, classifyChoiceAnswer, connectCheckout, currentPlan, finishProposed, followedUpSinceProposal, initialCheckoutState, isAgentPresent, isClosed, isOptionIcon, isPresetId, isValidEntryPointAnswer, isValidEntryPointInput, isValidModelAnswer, openInputs, parseChoiceAnswer, parseEntryPointOptions, parseModelAnswer, parseMultipleAnswer, parsePreviewUrl, planNeedsReview, presetInput, stepProgress };
package/dist/presets.d.ts CHANGED
@@ -1,32 +1,30 @@
1
- import { Checkout } from "./protocol.js";
2
- //#region src/presets.d.ts
3
- type PresetId = "framework" | "llm-provider" | "project";
4
- type PresetOverrides = {
5
- /** Restrict the options to these ids, in this order; one id locks the choice in. */
6
- only?: readonly string[];
7
- /** Options the agent found itself, listed before the preset's own. */
8
- found?: readonly {
9
- id: string;
10
- description?: string;
11
- }[];
12
- default?: string;
13
- optional?: boolean;
14
- stepId?: string;
1
+ import type { Checkout } from "./protocol.js";
2
+ export type PresetId = "framework" | "llm-provider" | "project";
3
+ export type PresetOverrides = {
4
+ /** Restrict the options to these ids, in this order; one id locks the choice in. */
5
+ only?: readonly string[];
6
+ /** Options the agent found itself, listed before the preset's own; `only` leaves them out. */
7
+ found?: readonly {
8
+ id: string;
9
+ description?: string;
10
+ }[];
11
+ default?: string;
12
+ optional?: boolean;
13
+ stepId?: string;
15
14
  };
16
15
  type Preset = {
17
- kind: Checkout.InputKind;
18
- prompt: string;
19
- options: Checkout.ChoiceOption[];
20
- default?: string;
21
- help: Checkout.InputHelp;
16
+ kind: Checkout.InputKind;
17
+ prompt: string;
18
+ options: Checkout.ChoiceOption[];
19
+ default?: string;
20
+ help: Checkout.InputHelp;
22
21
  };
23
22
  /** The standard inputs an agent can ask with `ask --preset <id>`. */
24
- declare const INPUT_PRESETS: Record<PresetId, Preset>;
25
- declare const isPresetId: (id: string) => id is PresetId;
23
+ export declare const INPUT_PRESETS: Record<PresetId, Preset>;
24
+ export declare const isPresetId: (id: string) => id is PresetId;
26
25
  /** Build the seed for a standard input, throwing on an unknown preset, option or default. */
27
- declare const presetInput: (id: PresetId, overrides?: PresetOverrides) => Checkout.InputSeed & {
28
- stepId?: string;
26
+ export declare const presetInput: (id: PresetId, overrides?: PresetOverrides) => Checkout.InputSeed & {
27
+ stepId?: string;
29
28
  };
30
- //#endregion
31
- export { INPUT_PRESETS, PresetId, PresetOverrides, isPresetId, presetInput };
29
+ export {};
32
30
  //# sourceMappingURL=presets.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"presets.d.ts","names":[],"sources":["../src/presets.ts"],"mappings":";;KAOY;KAEA;;EAEV;;EAEA;IAAmB;IAAY;;EAC/B;EACA;EACA;;KAGG;EACH,MAAM,SAAS;EACf;EACA,SAAS,SAAS;EAClB;EACA,MAAM,SAAS;;;cAIJ,eAAe,OAAO,UAAU;cAiHhC,aAAc,eAAa,MAAM;;cAGjC,cACX,IAAI,UACJ,YAAW,oBACV,SAAS;EAAc"}
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../src/presets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,sBAAmB;AAO3C,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,cAAc,GAAG,SAAS,CAAC;AAEhE,MAAM,MAAM,eAAe,GAAG;IAC5B,oFAAoF;IACpF,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,8FAA8F;IAC9F,KAAK,CAAC,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,KAAK,MAAM,GAAG;IACZ,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,QAAQ,CAAC,YAAY,EAAE,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC;CAC1B,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CA+GlD,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,IAAI,MAAM,KAAG,EAAE,IAAI,QACZ,CAAC;AAEnC,6FAA6F;AAC7F,eAAO,MAAM,WAAW,GACtB,IAAI,QAAQ,EACZ,YAAW,eAAoB,KAC9B,QAAQ,CAAC,SAAS,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAqCxC,CAAC"}
package/dist/presets.js CHANGED
@@ -139,7 +139,7 @@ const INPUT_PRESETS = {
139
139
  }
140
140
  }
141
141
  };
142
- const isPresetId = (id) => id in INPUT_PRESETS;
142
+ const isPresetId = (id) => Object.hasOwn(INPUT_PRESETS, id);
143
143
  /** Build the seed for a standard input, throwing on an unknown preset, option or default. */
144
144
  const presetInput = (id, overrides = {}) => {
145
145
  const preset = INPUT_PRESETS[id];
@@ -148,13 +148,14 @@ const presetInput = (id, overrides = {}) => {
148
148
  if (!option) throw new Error(`preset "${id}" has no option "${optionId}"`);
149
149
  return option;
150
150
  }) : preset.options;
151
- const options = [...(overrides.found ?? []).map((option) => {
151
+ const found = (overrides.found ?? []).map((option) => {
152
152
  if (option.id.includes(":") || preset.options.some((o) => o.id === option.id)) throw new Error(`preset "${id}" cannot list "${option.id}" as an option`);
153
153
  return {
154
154
  ...option,
155
155
  label: option.id
156
156
  };
157
- }), ...listed];
157
+ });
158
+ const options = overrides.only ? listed : [...found, ...listed];
158
159
  const fallback = options.some((o) => o.id === preset.default) ? preset.default : void 0;
159
160
  const chosen = overrides.default ?? fallback;
160
161
  if (chosen !== void 0 && !options.some((o) => o.id === chosen)) throw new Error(`preset "${id}" has no option "${chosen}" to default to`);
@@ -1 +1 @@
1
- {"version":3,"file":"presets.js","names":["Checkout","TYPESCRIPT","id","label","PYTHON","DOCS","PresetId","PresetOverrides","only","found","description","default","optional","stepId","Preset","kind","InputKind","prompt","options","ChoiceOption","help","InputHelp","INPUT_PRESETS","Record","framework","icon","variants","summary","href","project","isPresetId","presetInput","overrides","InputSeed","preset","listed","map","optionId","option","find","c","Error","includes","some","o","fallback","undefined","chosen"],"sources":["../src/presets.ts"],"sourcesContent":["import type { Checkout } from \"./protocol\";\n\nconst TYPESCRIPT = { id: \"typescript\", label: \"TypeScript\" };\nconst PYTHON = { id: \"python\", label: \"Python\" };\n\nconst DOCS = \"https://www.assistant-ui.com\";\n\nexport type PresetId = \"framework\" | \"llm-provider\" | \"project\";\n\nexport type PresetOverrides = {\n /** Restrict the options to these ids, in this order; one id locks the choice in. */\n only?: readonly string[];\n /** Options the agent found itself, listed before the preset's own. */\n found?: readonly { id: string; description?: string }[];\n default?: string;\n optional?: boolean;\n stepId?: string;\n};\n\ntype Preset = {\n kind: Checkout.InputKind;\n prompt: string;\n options: Checkout.ChoiceOption[];\n default?: string;\n help: Checkout.InputHelp;\n};\n\n/** The standard inputs an agent can ask with `ask --preset <id>`. */\nexport const INPUT_PRESETS: Record<PresetId, Preset> = {\n framework: {\n kind: \"choice\",\n prompt: \"Which agent framework runs your backend?\",\n options: [\n {\n id: \"ai-sdk\",\n label: \"Vercel AI SDK\",\n description: \"Shortest path for Next.js and React\",\n icon: \"vercel\",\n variants: [TYPESCRIPT],\n },\n {\n id: \"mastra\",\n label: \"Mastra\",\n description: \"Workflows, memory and tools\",\n icon: \"mastra\",\n variants: [TYPESCRIPT],\n },\n {\n id: \"langgraph\",\n label: \"LangGraph\",\n description: \"Stateful graphs, Python or TypeScript\",\n icon: \"langgraph\",\n variants: [PYTHON, TYPESCRIPT],\n },\n ],\n help: {\n summary:\n \"Already on one of these? Pick it. Starting fresh in Next.js, the AI SDK is the shortest path. Mastra adds workflows and memory in TypeScript. LangGraph fits a Python backend or multi-step graphs that pause for a human.\",\n href: `${DOCS}/docs/runtimes/pick-a-runtime`,\n },\n },\n \"llm-provider\": {\n kind: \"model\",\n prompt: \"Which model should the assistant use?\",\n options: [\n {\n id: \"openai\",\n label: \"OpenAI\",\n description: \"OPENAI_API_KEY\",\n icon: \"openai\",\n },\n {\n id: \"anthropic\",\n label: \"Anthropic\",\n description: \"ANTHROPIC_API_KEY\",\n icon: \"anthropic\",\n },\n {\n id: \"google\",\n label: \"Google Gemini\",\n description: \"GOOGLE_GENERATIVE_AI_API_KEY\",\n icon: \"google\",\n },\n {\n id: \"openrouter\",\n label: \"OpenRouter\",\n description: \"OPENROUTER_API_KEY\",\n icon: \"openrouter\",\n },\n { id: \"xai\", label: \"xAI\", description: \"XAI_API_KEY\", icon: \"xai\" },\n {\n id: \"mistral\",\n label: \"Mistral\",\n description: \"MISTRAL_API_KEY\",\n icon: \"mistral\",\n },\n {\n id: \"deepseek\",\n label: \"DeepSeek\",\n description: \"DEEPSEEK_API_KEY\",\n icon: \"deepseek\",\n },\n { id: \"groq\", label: \"Groq\", description: \"GROQ_API_KEY\", icon: \"groq\" },\n {\n id: \"fireworks\",\n label: \"Fireworks AI\",\n description: \"FIREWORKS_API_KEY\",\n icon: \"fireworks\",\n },\n ],\n default: \"openai\",\n help: {\n summary:\n \"Any of them works with every framework here. Pick the one whose API key you already have, or OpenRouter to reach many models with one key.\",\n },\n },\n project: {\n kind: \"choice\",\n prompt: \"Which project should this go into?\",\n options: [\n {\n id: \"new\",\n label: \"New project\",\n description: \"Start a fresh React app here\",\n variants: [\n { id: \"next\", label: \"Next.js\" },\n { id: \"vite\", label: \"Vite\" },\n { id: \"react-router\", label: \"React Router\" },\n { id: \"tanstack-start\", label: \"TanStack Start\" },\n { id: \"expo\", label: \"Expo\" },\n ],\n },\n ],\n help: {\n summary:\n \"Pick the app the assistant should live in. For a new project, Next.js is the shortest path: the assistant-ui CLI scaffolds the chat route with it. Pick Vite, React Router or TanStack Start when the rest of your stack already uses them, and Expo for a native app.\",\n href: `${DOCS}/docs/installation`,\n },\n },\n};\n\nexport const isPresetId = (id: string): id is PresetId => id in INPUT_PRESETS;\n\n/** Build the seed for a standard input, throwing on an unknown preset, option or default. */\nexport const presetInput = (\n id: PresetId,\n overrides: PresetOverrides = {},\n): Checkout.InputSeed & { stepId?: string } => {\n const preset = INPUT_PRESETS[id];\n const listed = overrides.only\n ? overrides.only.map((optionId) => {\n const option = preset.options.find((c) => c.id === optionId);\n if (!option)\n throw new Error(`preset \"${id}\" has no option \"${optionId}\"`);\n return option;\n })\n : preset.options;\n const found = (overrides.found ?? []).map((option) => {\n if (\n option.id.includes(\":\") ||\n preset.options.some((o) => o.id === option.id)\n ) {\n throw new Error(`preset \"${id}\" cannot list \"${option.id}\" as an option`);\n }\n return { ...option, label: option.id };\n });\n const options = [...found, ...listed];\n const fallback = options.some((o) => o.id === preset.default)\n ? preset.default\n : undefined;\n const chosen = overrides.default ?? fallback;\n if (chosen !== undefined && !options.some((o) => o.id === chosen)) {\n throw new Error(`preset \"${id}\" has no option \"${chosen}\" to default to`);\n }\n return {\n kind: preset.kind,\n preset: id,\n prompt: preset.prompt,\n options,\n ...(chosen !== undefined && { default: chosen }),\n help: preset.help,\n optional: overrides.optional ?? false,\n ...(overrides.stepId !== undefined && { stepId: overrides.stepId }),\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"presets.js","names":[],"sources":["../src/presets.ts"],"sourcesContent":["import type { Checkout } from \"./protocol\";\n\nconst TYPESCRIPT = { id: \"typescript\", label: \"TypeScript\" };\nconst PYTHON = { id: \"python\", label: \"Python\" };\n\nconst DOCS = \"https://www.assistant-ui.com\";\n\nexport type PresetId = \"framework\" | \"llm-provider\" | \"project\";\n\nexport type PresetOverrides = {\n /** Restrict the options to these ids, in this order; one id locks the choice in. */\n only?: readonly string[];\n /** Options the agent found itself, listed before the preset's own; `only` leaves them out. */\n found?: readonly { id: string; description?: string }[];\n default?: string;\n optional?: boolean;\n stepId?: string;\n};\n\ntype Preset = {\n kind: Checkout.InputKind;\n prompt: string;\n options: Checkout.ChoiceOption[];\n default?: string;\n help: Checkout.InputHelp;\n};\n\n/** The standard inputs an agent can ask with `ask --preset <id>`. */\nexport const INPUT_PRESETS: Record<PresetId, Preset> = {\n framework: {\n kind: \"choice\",\n prompt: \"Which agent framework runs your backend?\",\n options: [\n {\n id: \"ai-sdk\",\n label: \"Vercel AI SDK\",\n description: \"Shortest path for Next.js and React\",\n icon: \"vercel\",\n variants: [TYPESCRIPT],\n },\n {\n id: \"mastra\",\n label: \"Mastra\",\n description: \"Workflows, memory and tools\",\n icon: \"mastra\",\n variants: [TYPESCRIPT],\n },\n {\n id: \"langgraph\",\n label: \"LangGraph\",\n description: \"Stateful graphs, Python or TypeScript\",\n icon: \"langgraph\",\n variants: [PYTHON, TYPESCRIPT],\n },\n ],\n help: {\n summary:\n \"Already on one of these? Pick it. Starting fresh in Next.js, the AI SDK is the shortest path. Mastra adds workflows and memory in TypeScript. LangGraph fits a Python backend or multi-step graphs that pause for a human.\",\n href: `${DOCS}/docs/runtimes/pick-a-runtime`,\n },\n },\n \"llm-provider\": {\n kind: \"model\",\n prompt: \"Which model should the assistant use?\",\n options: [\n {\n id: \"openai\",\n label: \"OpenAI\",\n description: \"OPENAI_API_KEY\",\n icon: \"openai\",\n },\n {\n id: \"anthropic\",\n label: \"Anthropic\",\n description: \"ANTHROPIC_API_KEY\",\n icon: \"anthropic\",\n },\n {\n id: \"google\",\n label: \"Google Gemini\",\n description: \"GOOGLE_GENERATIVE_AI_API_KEY\",\n icon: \"google\",\n },\n {\n id: \"openrouter\",\n label: \"OpenRouter\",\n description: \"OPENROUTER_API_KEY\",\n icon: \"openrouter\",\n },\n { id: \"xai\", label: \"xAI\", description: \"XAI_API_KEY\", icon: \"xai\" },\n {\n id: \"mistral\",\n label: \"Mistral\",\n description: \"MISTRAL_API_KEY\",\n icon: \"mistral\",\n },\n {\n id: \"deepseek\",\n label: \"DeepSeek\",\n description: \"DEEPSEEK_API_KEY\",\n icon: \"deepseek\",\n },\n { id: \"groq\", label: \"Groq\", description: \"GROQ_API_KEY\", icon: \"groq\" },\n {\n id: \"fireworks\",\n label: \"Fireworks AI\",\n description: \"FIREWORKS_API_KEY\",\n icon: \"fireworks\",\n },\n ],\n default: \"openai\",\n help: {\n summary:\n \"Any of them works with every framework here. Pick the one whose API key you already have, or OpenRouter to reach many models with one key.\",\n },\n },\n project: {\n kind: \"choice\",\n prompt: \"Which project should this go into?\",\n options: [\n {\n id: \"new\",\n label: \"New project\",\n description: \"Start a fresh React app here\",\n variants: [\n { id: \"next\", label: \"Next.js\" },\n { id: \"vite\", label: \"Vite\" },\n { id: \"react-router\", label: \"React Router\" },\n { id: \"tanstack-start\", label: \"TanStack Start\" },\n { id: \"expo\", label: \"Expo\" },\n ],\n },\n ],\n help: {\n summary:\n \"Pick the app the assistant should live in. For a new project, Next.js is the shortest path: the assistant-ui CLI scaffolds the chat route with it. Pick Vite, React Router or TanStack Start when the rest of your stack already uses them, and Expo for a native app.\",\n href: `${DOCS}/docs/installation`,\n },\n },\n};\n\nexport const isPresetId = (id: string): id is PresetId =>\n Object.hasOwn(INPUT_PRESETS, id);\n\n/** Build the seed for a standard input, throwing on an unknown preset, option or default. */\nexport const presetInput = (\n id: PresetId,\n overrides: PresetOverrides = {},\n): Checkout.InputSeed & { stepId?: string } => {\n const preset = INPUT_PRESETS[id];\n const listed = overrides.only\n ? overrides.only.map((optionId) => {\n const option = preset.options.find((c) => c.id === optionId);\n if (!option)\n throw new Error(`preset \"${id}\" has no option \"${optionId}\"`);\n return option;\n })\n : preset.options;\n const found = (overrides.found ?? []).map((option) => {\n if (\n option.id.includes(\":\") ||\n preset.options.some((o) => o.id === option.id)\n ) {\n throw new Error(`preset \"${id}\" cannot list \"${option.id}\" as an option`);\n }\n return { ...option, label: option.id };\n });\n const options = overrides.only ? listed : [...found, ...listed];\n const fallback = options.some((o) => o.id === preset.default)\n ? preset.default\n : undefined;\n const chosen = overrides.default ?? fallback;\n if (chosen !== undefined && !options.some((o) => o.id === chosen)) {\n throw new Error(`preset \"${id}\" has no option \"${chosen}\" to default to`);\n }\n return {\n kind: preset.kind,\n preset: id,\n prompt: preset.prompt,\n options,\n ...(chosen !== undefined && { default: chosen }),\n help: preset.help,\n optional: overrides.optional ?? false,\n ...(overrides.stepId !== undefined && { stepId: overrides.stepId }),\n };\n};\n"],"mappings":"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"}