setup-agent 0.0.2 → 0.0.4
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/LICENSE +21 -0
- package/README.md +10 -6
- package/dist/cli.d.ts +51 -45
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +56 -30
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +4 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +13 -5
- package/dist/client.js.map +1 -1
- package/dist/host.d.ts +8 -10
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +79 -76
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.d.ts.map +1 -0
- package/dist/presets.d.ts +23 -25
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +4 -3
- package/dist/presets.js.map +1 -1
- package/dist/protocol.d.ts +260 -262
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +5 -3
- package/dist/protocol.js.map +1 -1
- package/package.json +21 -14
- package/src/cli.test.ts +464 -0
- package/src/cli.ts +80 -40
- package/src/client.test.ts +54 -0
- package/src/client.ts +14 -6
- package/src/host.test.ts +883 -0
- package/src/host.ts +30 -7
- package/src/presets.ts +4 -3
- package/src/protocol.ts +10 -3
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
|
|
23
|
-
if (isClosed(state)) throw reject("closed", `the
|
|
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();
|
|
@@ -36,7 +36,7 @@ const useCheckoutHost = (restored) => {
|
|
|
36
36
|
return step;
|
|
37
37
|
};
|
|
38
38
|
const findInput = (inputId) => {
|
|
39
|
-
const input = state.inputs.find((
|
|
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;
|
|
@@ -49,13 +49,16 @@ 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
|
|
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
|
-
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
|
-
|
|
54
|
+
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");
|
|
55
|
+
const unique = (ids) => new Set(ids).size === ids.length;
|
|
56
|
+
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");
|
|
57
|
+
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");
|
|
58
|
+
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
59
|
if (seed.stepId !== void 0) findStep(seed.stepId);
|
|
57
60
|
const id = `q${state.inputs.length + 1}`;
|
|
58
|
-
const
|
|
61
|
+
const stepId = seed.stepId ?? state.steps.find((step) => step.status === "active")?.id;
|
|
59
62
|
state.inputs.push({
|
|
60
63
|
id,
|
|
61
64
|
phase: state.status,
|
|
@@ -70,39 +73,39 @@ const useCheckoutHost = (restored) => {
|
|
|
70
73
|
...seed.help !== void 0 && { help: seed.help },
|
|
71
74
|
optional: seed.optional ?? false,
|
|
72
75
|
status: "open",
|
|
73
|
-
...
|
|
76
|
+
...stepId !== void 0 && { stepId },
|
|
74
77
|
createdAt: Date.now()
|
|
75
78
|
});
|
|
76
79
|
return { inputId: id };
|
|
77
80
|
};
|
|
78
81
|
const dismissOpenInputs = () => {
|
|
79
|
-
for (const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
for (const input of state.inputs) if (input.status === "open") {
|
|
83
|
+
input.status = "dismissed";
|
|
84
|
+
input.answeredAt = Date.now();
|
|
82
85
|
}
|
|
83
86
|
};
|
|
84
|
-
const log = (text,
|
|
87
|
+
const log = (text, stepId) => {
|
|
85
88
|
state.log.push({
|
|
86
89
|
id: `l${state.log.length + 1}`,
|
|
87
90
|
role: "agent",
|
|
88
91
|
phase: state.status,
|
|
89
92
|
at: Date.now(),
|
|
90
93
|
text,
|
|
91
|
-
...
|
|
94
|
+
...stepId !== void 0 && { stepId }
|
|
92
95
|
});
|
|
93
96
|
};
|
|
94
|
-
const updateStep = (
|
|
95
|
-
const changed =
|
|
96
|
-
|
|
97
|
-
if (note !== void 0)
|
|
98
|
-
if (changed && (status === "done" || status === "skipped")) log(`${status === "done" ? "Completed" : "Skipped"}: ${
|
|
97
|
+
const updateStep = (step, status, note) => {
|
|
98
|
+
const changed = step.status !== status;
|
|
99
|
+
step.status = status;
|
|
100
|
+
if (note !== void 0) step.note = note;
|
|
101
|
+
if (changed && (status === "done" || status === "skipped")) log(`${status === "done" ? "Completed" : "Skipped"}: ${step.title}${step.note ? `\n\n${step.note}` : ""}`, step.id);
|
|
99
102
|
};
|
|
100
103
|
const commands = useStatewireCommands({
|
|
101
|
-
"checkout/create": ({ id
|
|
102
|
-
if (state.id ===
|
|
104
|
+
"checkout/create": ({ id, products, instructions }) => {
|
|
105
|
+
if (state.id === id) throw reject("already-created", "the setup already exists");
|
|
103
106
|
const fresh = initialCheckoutState();
|
|
104
107
|
if (!isAgentPresent(state)) state.agent = fresh.agent;
|
|
105
|
-
state.id =
|
|
108
|
+
state.id = id;
|
|
106
109
|
state.status = fresh.status;
|
|
107
110
|
delete state.completion;
|
|
108
111
|
state.plans = fresh.plans;
|
|
@@ -111,65 +114,65 @@ const useCheckoutHost = (restored) => {
|
|
|
111
114
|
state.log = fresh.log;
|
|
112
115
|
state.createdAt = Date.now();
|
|
113
116
|
state.instructions = instructions?.trim() ?? "";
|
|
114
|
-
state.products = products.map((
|
|
115
|
-
slug:
|
|
116
|
-
name:
|
|
117
|
-
...
|
|
117
|
+
state.products = products.map((product) => ({
|
|
118
|
+
slug: product.slug,
|
|
119
|
+
name: product.name,
|
|
120
|
+
...product.guide !== void 0 && { guide: product.guide }
|
|
118
121
|
}));
|
|
119
122
|
},
|
|
120
123
|
"checkout/begin-plan": () => {
|
|
121
124
|
requireOpen();
|
|
122
125
|
if (state.status === "waiting") state.status = "planning";
|
|
123
126
|
},
|
|
124
|
-
"checkout/answer": ({ inputId
|
|
127
|
+
"checkout/answer": ({ inputId, answer, note }) => {
|
|
125
128
|
requireOpen();
|
|
126
|
-
const
|
|
127
|
-
if (
|
|
128
|
-
if (
|
|
129
|
-
if (
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (
|
|
129
|
+
const input = findInput(inputId);
|
|
130
|
+
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`);
|
|
131
|
+
if (input.kind === "model" && !isValidModelAnswer(input, answer)) throw reject("invalid-answer", "a model answer is JSON with provider and model");
|
|
132
|
+
if (input.kind === "product") throw reject("invalid-answer", "a product input is answered with checkout/add-product");
|
|
133
|
+
if (input.kind === "text" && answer.trim() === "") throw reject("invalid-answer", "an answer cannot be empty");
|
|
134
|
+
input.status = "answered";
|
|
135
|
+
input.answeredAt = Date.now();
|
|
136
|
+
input.answer = answer;
|
|
137
|
+
if (note !== void 0 && note.trim() !== "") input.note = note.trim();
|
|
135
138
|
},
|
|
136
|
-
"checkout/add-product": ({ inputId
|
|
139
|
+
"checkout/add-product": ({ inputId, product }) => {
|
|
137
140
|
requireOpen();
|
|
138
|
-
const
|
|
139
|
-
if (
|
|
140
|
-
if (!state.products.some((entry) => entry.slug ===
|
|
141
|
-
slug:
|
|
142
|
-
name:
|
|
143
|
-
...
|
|
141
|
+
const input = findInput(inputId);
|
|
142
|
+
if (input.kind !== "product" || input.product !== product.slug) throw reject("invalid-answer", `input "${inputId}" does not propose "${product.slug}"`);
|
|
143
|
+
if (!state.products.some((entry) => entry.slug === product.slug)) state.products.push({
|
|
144
|
+
slug: product.slug,
|
|
145
|
+
name: product.name,
|
|
146
|
+
...product.guide !== void 0 && { guide: product.guide }
|
|
144
147
|
});
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
input.status = "answered";
|
|
149
|
+
input.answer = "added";
|
|
150
|
+
input.answeredAt = Date.now();
|
|
148
151
|
},
|
|
149
|
-
"checkout/message": ({ text
|
|
152
|
+
"checkout/message": ({ text }) => {
|
|
150
153
|
requireOpen();
|
|
151
|
-
if (
|
|
152
|
-
const
|
|
154
|
+
if (text.trim() === "") throw reject("empty-message", "a message cannot be empty");
|
|
155
|
+
const stepId = state.steps.find((step) => step.status === "active")?.id;
|
|
153
156
|
state.log.push({
|
|
154
157
|
id: `l${state.log.length + 1}`,
|
|
155
158
|
role: "user",
|
|
156
159
|
phase: state.status,
|
|
157
|
-
...
|
|
160
|
+
...stepId !== void 0 && { stepId },
|
|
158
161
|
at: Date.now(),
|
|
159
|
-
text:
|
|
162
|
+
text: text.trim()
|
|
160
163
|
});
|
|
161
164
|
},
|
|
162
165
|
"agent/ack": ({ messageId }) => {
|
|
163
166
|
requireOpen();
|
|
164
|
-
const message = state.log.find((
|
|
167
|
+
const message = state.log.find((entry) => entry.id === messageId && entry.role === "user");
|
|
165
168
|
if (!message) throw reject("unknown-message", `no user message "${messageId}"`);
|
|
166
169
|
message.acknowledgedAt ??= Date.now();
|
|
167
170
|
},
|
|
168
|
-
"checkout/dismiss": ({ inputId
|
|
171
|
+
"checkout/dismiss": ({ inputId }) => {
|
|
169
172
|
requireOpen();
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
+
const input = findInput(inputId);
|
|
174
|
+
input.status = "dismissed";
|
|
175
|
+
input.answeredAt = Date.now();
|
|
173
176
|
},
|
|
174
177
|
"checkout/plan": (params) => {
|
|
175
178
|
requireOpen();
|
|
@@ -188,7 +191,7 @@ const useCheckoutHost = (restored) => {
|
|
|
188
191
|
plan.feedback = params.feedback.trim();
|
|
189
192
|
},
|
|
190
193
|
"checkout/cancel": () => {
|
|
191
|
-
if (state.
|
|
194
|
+
if (state.createdAt === null || isClosed(state)) return;
|
|
192
195
|
state.status = "cancelled";
|
|
193
196
|
dismissOpenInputs();
|
|
194
197
|
},
|
|
@@ -199,15 +202,15 @@ const useCheckoutHost = (restored) => {
|
|
|
199
202
|
state.status = "done";
|
|
200
203
|
log("Setup complete.");
|
|
201
204
|
},
|
|
202
|
-
"agent/intro": ({ kind
|
|
205
|
+
"agent/intro": ({ kind } = {}) => {
|
|
203
206
|
requireOpen();
|
|
204
207
|
if (state.agent.introducedAt === null) state.agent.introducedAt = Date.now();
|
|
205
|
-
if (
|
|
208
|
+
if (kind !== void 0) state.agent.kind = kind;
|
|
206
209
|
},
|
|
207
|
-
"agent/hello": ({ cwd, kind
|
|
210
|
+
"agent/hello": ({ cwd, kind } = {}) => {
|
|
208
211
|
touchAgent();
|
|
209
212
|
if (cwd !== void 0) state.agent.cwd = cwd;
|
|
210
|
-
if (
|
|
213
|
+
if (kind !== void 0) state.agent.kind = kind;
|
|
211
214
|
},
|
|
212
215
|
"agent/heartbeat": () => {
|
|
213
216
|
touchAgent();
|
|
@@ -229,42 +232,42 @@ const useCheckoutHost = (restored) => {
|
|
|
229
232
|
});
|
|
230
233
|
return { revision };
|
|
231
234
|
},
|
|
232
|
-
"agent/add-step": ({ title, detail, product
|
|
235
|
+
"agent/add-step": ({ title, detail, product, active }) => {
|
|
233
236
|
requireInstalling();
|
|
234
|
-
if (
|
|
235
|
-
const
|
|
237
|
+
if (product !== void 0 && !state.products.some((candidate) => candidate.slug === product)) throw reject("unknown-product", `no product "${product}"`);
|
|
238
|
+
const id = `s${state.steps.length + 1}`;
|
|
236
239
|
state.steps.push({
|
|
237
|
-
id
|
|
240
|
+
id,
|
|
238
241
|
title,
|
|
239
242
|
...detail !== void 0 && { detail },
|
|
240
243
|
status: active ? "active" : "pending",
|
|
241
|
-
...
|
|
244
|
+
...product !== void 0 && { product },
|
|
242
245
|
createdAt: Date.now()
|
|
243
246
|
});
|
|
244
|
-
return { stepId:
|
|
247
|
+
return { stepId: id };
|
|
245
248
|
},
|
|
246
|
-
"agent/step": ({ stepId
|
|
249
|
+
"agent/step": ({ stepId, status, note }) => {
|
|
247
250
|
requireInstalling();
|
|
248
|
-
const
|
|
249
|
-
updateStep(
|
|
251
|
+
const step = findStep(stepId);
|
|
252
|
+
updateStep(step, status, note);
|
|
250
253
|
},
|
|
251
|
-
"agent/ask": (
|
|
254
|
+
"agent/ask": (seed) => {
|
|
252
255
|
requireStarted();
|
|
253
256
|
touchAgent();
|
|
254
|
-
return ask(
|
|
257
|
+
return ask(seed);
|
|
255
258
|
},
|
|
256
|
-
"agent/log": ({ text
|
|
259
|
+
"agent/log": ({ text }) => {
|
|
257
260
|
requireOpen();
|
|
258
261
|
touchAgent();
|
|
259
|
-
const
|
|
260
|
-
log(
|
|
262
|
+
const stepId = state.steps.find((step) => step.status === "active")?.id;
|
|
263
|
+
log(text, stepId);
|
|
261
264
|
},
|
|
262
265
|
"agent/done": ({ summary, preview } = {}) => {
|
|
263
266
|
requireInstalling();
|
|
264
267
|
const previewUrl = parsePreviewUrl(preview?.trim());
|
|
265
268
|
if (preview !== void 0 && !previewUrl) throw reject("invalid-preview", "a preview is an http URL on localhost, such as http://localhost:3000");
|
|
266
269
|
touchAgent();
|
|
267
|
-
for (const
|
|
270
|
+
for (const step of state.steps) if (step.status === "pending" || step.status === "active") updateStep(step, "done");
|
|
268
271
|
log(summary?.trim() || "Everything is installed.");
|
|
269
272
|
state.completion = {
|
|
270
273
|
proposedAt: Date.now(),
|
|
@@ -283,7 +286,7 @@ const useCheckoutHost = (restored) => {
|
|
|
283
286
|
}
|
|
284
287
|
};
|
|
285
288
|
};
|
|
286
|
-
/** The
|
|
289
|
+
/** The setup statewire host; pass the durable object's restored snapshot. */
|
|
287
290
|
const CheckoutHost = (restored) => resource(useCheckoutHost)(restored);
|
|
288
291
|
//#endregion
|
|
289
292
|
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":[],"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\" ||\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 ...(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.createdAt === null || isClosed(state)) 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/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
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, openInputs, parseChoiceAnswer, 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,UAAU,EACV,iBAAiB,EACjB,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/presets.d.ts
CHANGED
|
@@ -1,32 +1,30 @@
|
|
|
1
|
-
import { Checkout } from "./protocol.js";
|
|
2
|
-
|
|
3
|
-
type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
26
|
+
export declare const presetInput: (id: PresetId, overrides?: PresetOverrides) => Checkout.InputSeed & {
|
|
27
|
+
stepId?: string;
|
|
29
28
|
};
|
|
30
|
-
|
|
31
|
-
export { INPUT_PRESETS, PresetId, PresetOverrides, isPresetId, presetInput };
|
|
29
|
+
export {};
|
|
32
30
|
//# sourceMappingURL=presets.d.ts.map
|
package/dist/presets.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presets.d.ts","
|
|
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
|
|
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
|
|
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
|
-
})
|
|
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`);
|
package/dist/presets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presets.js","names":[
|
|
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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"}
|