@runtypelabs/cli 2.59.0 → 2.59.2

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.
Files changed (84) hide show
  1. package/dist/chunk-0-T9NWjH.js +3899 -0
  2. package/dist/chunk-0x7UKC1U.js +17 -0
  3. package/dist/chunk-2ySxXtcg.js +418 -0
  4. package/dist/chunk-3An_BRHv.js +501 -0
  5. package/dist/chunk-6uTxgdiY.js +16218 -0
  6. package/dist/chunk-87nRnNRG.js +1363 -0
  7. package/dist/chunk-B3wo8uRx.js +322 -0
  8. package/dist/chunk-BGp3ZdBy.js +560 -0
  9. package/dist/chunk-BJQv9Elp.js +107 -0
  10. package/dist/chunk-BOpO6xE-.js +6053 -0
  11. package/dist/chunk-BQXXCL8B.js +102 -0
  12. package/dist/chunk-BV-8isOd.js +34 -0
  13. package/dist/chunk-BVMhjGVe.js +826 -0
  14. package/dist/chunk-BW2fg46T.js +141 -0
  15. package/dist/chunk-BWJdY5Ag.js +36 -0
  16. package/dist/chunk-BYJOrLhh.js +302 -0
  17. package/dist/chunk-BYab8EVL.js +127 -0
  18. package/dist/chunk-BfEKvp7h.js +82 -0
  19. package/dist/chunk-BhgLSfH6.js +1123 -0
  20. package/dist/chunk-BirUxTtH.js +296 -0
  21. package/dist/chunk-BmB6UVZ9.js +1559 -0
  22. package/dist/chunk-BnWhIqW1.js +109 -0
  23. package/dist/chunk-Bpfnfk0t.js +123 -0
  24. package/dist/chunk-BrhgdnM3.js +108 -0
  25. package/dist/chunk-Bt0XAgX5.js +327 -0
  26. package/dist/chunk-Bu0FJr6S.js +84 -0
  27. package/dist/chunk-BvEoKpN3.js +255 -0
  28. package/dist/chunk-BxNJgYbE.js +28 -0
  29. package/dist/chunk-C1OFhd7F.js +6131 -0
  30. package/dist/chunk-C7e7WwbU.js +331 -0
  31. package/dist/chunk-CDTYP6bN.js +558 -0
  32. package/dist/chunk-CGDQoqDC.js +24 -0
  33. package/dist/chunk-CJraEBVd.js +127 -0
  34. package/dist/chunk-CQrNhOtv.js +40 -0
  35. package/dist/chunk-CW6AzBH6.js +128 -0
  36. package/dist/chunk-CXgL4w-9.js +317 -0
  37. package/dist/chunk-CZrqoPyf.js +233 -0
  38. package/dist/chunk-CcFCyqbB.js +83 -0
  39. package/dist/chunk-CdaIvO-y.js +485 -0
  40. package/dist/chunk-CehSXtgK.js +624 -0
  41. package/dist/chunk-Ciqkop65.js +218 -0
  42. package/dist/chunk-CoAWdSiE.js +43 -0
  43. package/dist/chunk-Crb4y7t4.js +830 -0
  44. package/dist/chunk-CrlqIfmb.js +337 -0
  45. package/dist/chunk-Cs4pwCQ-.js +324 -0
  46. package/dist/chunk-CwM_9y01.js +519 -0
  47. package/dist/chunk-CyRbIJg8.js +440 -0
  48. package/dist/chunk-D0OHsyMy.js +212 -0
  49. package/dist/chunk-D3y2bURs.js +1347 -0
  50. package/dist/chunk-D4_mJk03.js +2338 -0
  51. package/dist/chunk-D6C8rned.js +578 -0
  52. package/dist/chunk-D9ZbUnks.js +11 -0
  53. package/dist/chunk-DEPmLshX.js +834 -0
  54. package/dist/chunk-DFj1UPB2.js +330 -0
  55. package/dist/chunk-DGdFdZVf.js +197 -0
  56. package/dist/chunk-DGv7X9S_.js +63 -0
  57. package/dist/chunk-DJQ47WYZ.js +664 -0
  58. package/dist/chunk-DJw3O61j.js +1974 -0
  59. package/dist/chunk-DK4FbWVK.js +100 -0
  60. package/dist/chunk-DSRYhUus.js +993 -0
  61. package/dist/chunk-DajZ9Eg4.js +299 -0
  62. package/dist/chunk-Df4dunvU.js +195 -0
  63. package/dist/chunk-DjNTPhfO.js +481 -0
  64. package/dist/chunk-Dn8eaJuu.js +1042 -0
  65. package/dist/chunk-DwDWqSxM.js +637 -0
  66. package/dist/chunk-Dz7cEtxl.js +84 -0
  67. package/dist/chunk-E_0bgjrS.js +359 -0
  68. package/dist/chunk-KPYN2vUp.js +776 -0
  69. package/dist/chunk-KcqPQJBi.js +86 -0
  70. package/dist/chunk-N9H0JnoG.js +349 -0
  71. package/dist/chunk-NONFcXkn.js +728 -0
  72. package/dist/chunk-PrFjcpmH.js +256 -0
  73. package/dist/chunk-QSfNYiw9.js +11 -0
  74. package/dist/chunk-R265bzj9.js +226 -0
  75. package/dist/chunk-X9V_r6jM.js +166 -0
  76. package/dist/chunk-ZBtPUtyA.js +55 -0
  77. package/dist/chunk-ZCTfmOs7.js +4939 -0
  78. package/dist/chunk-h6oavj0t.js +97 -0
  79. package/dist/chunk-i7DwkvvD.js +42 -0
  80. package/dist/chunk-mG-4eZ6S.js +203 -0
  81. package/dist/chunk-o0ygUbCf.js +313 -0
  82. package/dist/chunk-ucu5lrDZ.js +5715 -0
  83. package/dist/index.js +11367 -73764
  84. package/package.json +6 -6
@@ -0,0 +1,1974 @@
1
+ #!/usr/bin/env node
2
+ import { b as CredentialStore, t as isTTY } from "./index.js";
3
+ import { p as buildDashboardUrl } from "./chunk-ucu5lrDZ.js";
4
+ import { i as getApiUrl, l as getRuntimeApiKey, n as ApiKeyValidationUnavailableError, r as createCliClient, s as getDashboardUrl, t as ApiKeyManager } from "./chunk-BW2fg46T.js";
5
+ import { n as ensureAuth } from "./chunk-CJraEBVd.js";
6
+ import { a as printJson } from "./chunk-CcFCyqbB.js";
7
+ import { t as getCurrentAccountId } from "./chunk-BV-8isOd.js";
8
+ import { Command } from "commander";
9
+ import chalk from "chalk";
10
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
11
+ import path from "node:path";
12
+ import readline from "readline";
13
+ import open from "open";
14
+ import { createServer } from "node:http";
15
+ import { execFileSync } from "child_process";
16
+ import { createServer as createServer$1 } from "node:net";
17
+ // ../../node_modules/.pnpm/@runtypelabs+persona@4.34.0/node_modules/@runtypelabs/persona/dist/codegen.js
18
+ function e(e) {
19
+ return e?.cdnBase?.trim().replace(/\/+$/, ``).replace(/[\\'"<>`]/g, (e) => `%${e.charCodeAt(0).toString(16).toUpperCase().padStart(2, `0`)}`) || `https://cdn.jsdelivr.net/npm/@runtypelabs/persona@4.34.0/dist`;
20
+ }
21
+ function t(e) {
22
+ if (e !== void 0) return typeof e == `string` ? e : Array.isArray(e) ? `[${e.map((e) => e.toString()).join(`, `)}]` : e.toString();
23
+ }
24
+ function n(e) {
25
+ if (e) return {
26
+ getHeaders: t(e.getHeaders),
27
+ onFeedback: t(e.onFeedback),
28
+ onCopy: t(e.onCopy),
29
+ requestMiddleware: t(e.requestMiddleware),
30
+ actionHandlers: t(e.actionHandlers),
31
+ actionParsers: t(e.actionParsers),
32
+ postprocessMessage: t(e.postprocessMessage),
33
+ contextProviders: t(e.contextProviders),
34
+ streamParser: t(e.streamParser)
35
+ };
36
+ }
37
+ const r = `({ text, message }: any) => {
38
+ const jsonSource = (message as any).rawContent || text || message.content;
39
+ if (!jsonSource || typeof jsonSource !== 'string') return null;
40
+ let cleanJson = jsonSource
41
+ .replace(/^\`\`\`(?:json)?\\s*\\n?/, '')
42
+ .replace(/\\n?\`\`\`\\s*$/, '')
43
+ .trim();
44
+ if (!cleanJson.startsWith('{') || !cleanJson.endsWith('}')) return null;
45
+ try {
46
+ const parsed = JSON.parse(cleanJson);
47
+ if (parsed.action) return { type: parsed.action, payload: parsed };
48
+ } catch (e) { return null; }
49
+ return null;
50
+ }`;
51
+ const i = `function(ctx) {
52
+ var jsonSource = ctx.message.rawContent || ctx.text || ctx.message.content;
53
+ if (!jsonSource || typeof jsonSource !== 'string') return null;
54
+ var cleanJson = jsonSource
55
+ .replace(/^\`\`\`(?:json)?\\s*\\n?/, '')
56
+ .replace(/\\n?\`\`\`\\s*$/, '')
57
+ .trim();
58
+ if (!cleanJson.startsWith('{') || !cleanJson.endsWith('}')) return null;
59
+ try {
60
+ var parsed = JSON.parse(cleanJson);
61
+ if (parsed.action) return { type: parsed.action, payload: parsed };
62
+ } catch (e) { return null; }
63
+ return null;
64
+ }`;
65
+ const a = `(action: any, context: any) => {
66
+ if (action.type !== 'nav_then_click') return;
67
+ const payload = action.payload || action.raw || {};
68
+ const url = payload?.page;
69
+ const text = payload?.on_load_text || 'Navigating...';
70
+ if (!url) return { handled: true, displayText: text };
71
+ const messageId = context.message?.id;
72
+ const processedActions = JSON.parse(localStorage.getItem(PROCESSED_ACTIONS_KEY) || '[]');
73
+ const actionKey = \`nav_\${messageId}_\${url}\`;
74
+ if (processedActions.includes(actionKey)) {
75
+ return { handled: true, displayText: text };
76
+ }
77
+ processedActions.push(actionKey);
78
+ localStorage.setItem(PROCESSED_ACTIONS_KEY, JSON.stringify(processedActions));
79
+ const targetUrl = url.startsWith('http') ? url : new URL(url, window.location.origin).toString();
80
+ window.location.href = targetUrl;
81
+ return { handled: true, displayText: text };
82
+ }`;
83
+ const o = `function(action, context) {
84
+ if (action.type !== 'nav_then_click') return;
85
+ var payload = action.payload || action.raw || {};
86
+ var url = payload.page;
87
+ var text = payload.on_load_text || 'Navigating...';
88
+ if (!url) return { handled: true, displayText: text };
89
+ var messageId = context.message ? context.message.id : null;
90
+ var processedActions = JSON.parse(localStorage.getItem(PROCESSED_ACTIONS_KEY) || '[]');
91
+ var actionKey = 'nav_' + messageId + '_' + url;
92
+ if (processedActions.includes(actionKey)) {
93
+ return { handled: true, displayText: text };
94
+ }
95
+ processedActions.push(actionKey);
96
+ localStorage.setItem(PROCESSED_ACTIONS_KEY, JSON.stringify(processedActions));
97
+ var targetUrl = url.startsWith('http') ? url : new URL(url, window.location.origin).toString();
98
+ window.location.href = targetUrl;
99
+ return { handled: true, displayText: text };
100
+ }`;
101
+ function s(e) {
102
+ if (!e) return null;
103
+ let t = e.toString();
104
+ return t.includes(`createJsonStreamParser`) || t.includes(`partial-json`) ? `json` : t.includes(`createRegexJsonParser`) || t.includes(`regex`) ? `regex-json` : t.includes(`createXmlParser`) || t.includes(`<text>`) ? `xml` : null;
105
+ }
106
+ function c(e) {
107
+ return e.parserType ?? s(e.streamParser) ?? `plain`;
108
+ }
109
+ function l(e, t) {
110
+ let n = [];
111
+ return e.toolCall && (n.push(`${t}toolCall: {`), Object.entries(e.toolCall).forEach(([e, r]) => {
112
+ typeof r == `string` && n.push(`${t} ${e}: "${r}",`);
113
+ }), n.push(`${t}},`)), n;
114
+ }
115
+ function u(e, t, n) {
116
+ let r = [], i = e.messageActions && Object.entries(e.messageActions).some(([e, t]) => e !== `onFeedback` && e !== `onCopy` && t !== void 0), a = n?.onFeedback || n?.onCopy;
117
+ return (i || a) && (r.push(`${t}messageActions: {`), e.messageActions && Object.entries(e.messageActions).forEach(([e, n]) => {
118
+ e !== `onFeedback` && e !== `onCopy` && (typeof n == `string` ? r.push(`${t} ${e}: "${n}",`) : typeof n == `boolean` && r.push(`${t} ${e}: ${n},`));
119
+ }), n?.onFeedback && r.push(`${t} onFeedback: ${n.onFeedback},`), n?.onCopy && r.push(`${t} onCopy: ${n.onCopy},`), r.push(`${t}},`)), r;
120
+ }
121
+ function d(e, t) {
122
+ let n = [];
123
+ if (e.markdown) {
124
+ let r = e.markdown.options && Object.keys(e.markdown.options).length > 0, i = e.markdown.disableDefaultStyles !== void 0;
125
+ (r || i) && (n.push(`${t}markdown: {`), r && (n.push(`${t} options: {`), Object.entries(e.markdown.options).forEach(([e, r]) => {
126
+ typeof r == `string` ? n.push(`${t} ${e}: "${r}",`) : typeof r == `boolean` && n.push(`${t} ${e}: ${r},`);
127
+ }), n.push(`${t} },`)), i && n.push(`${t} disableDefaultStyles: ${e.markdown.disableDefaultStyles},`), n.push(`${t}},`));
128
+ }
129
+ return n;
130
+ }
131
+ function f(e, t) {
132
+ if (!e.suggestions) return [];
133
+ let n = JSON.stringify(e.suggestions, null, 2).split(`
134
+ `);
135
+ return n.map((e, r) => `${r === 0 ? `${t}suggestions: ` : t}${e}${r === n.length - 1 ? `,` : ``}`);
136
+ }
137
+ function p(e, t) {
138
+ let n = [];
139
+ if (e.layout) {
140
+ let r = e.layout.header && Object.keys(e.layout.header).some((e) => e !== `render`), i = e.layout.messages && Object.keys(e.layout.messages).some((e) => e !== `renderUserMessage` && e !== `renderAssistantMessage`);
141
+ (r || i) && (n.push(`${t}layout: {`), r && (n.push(`${t} header: {`), Object.entries(e.layout.header).forEach(([e, r]) => {
142
+ e !== `render` && (typeof r == `string` ? n.push(`${t} ${e}: "${r}",`) : typeof r == `boolean` && n.push(`${t} ${e}: ${r},`));
143
+ }), n.push(`${t} },`)), i && (n.push(`${t} messages: {`), Object.entries(e.layout.messages).forEach(([e, r]) => {
144
+ e !== `renderUserMessage` && e !== `renderAssistantMessage` && (e === `avatar` && typeof r == `object` && r ? (n.push(`${t} avatar: {`), Object.entries(r).forEach(([e, r]) => {
145
+ typeof r == `string` ? n.push(`${t} ${e}: "${r}",`) : typeof r == `boolean` && n.push(`${t} ${e}: ${r},`);
146
+ }), n.push(`${t} },`)) : e === `timestamp` && typeof r == `object` && r ? Object.entries(r).some(([e]) => e !== `format`) && (n.push(`${t} timestamp: {`), Object.entries(r).forEach(([e, r]) => {
147
+ e !== `format` && (typeof r == `string` ? n.push(`${t} ${e}: "${r}",`) : typeof r == `boolean` && n.push(`${t} ${e}: ${r},`));
148
+ }), n.push(`${t} },`)) : typeof r == `string` ? n.push(`${t} ${e}: "${r}",`) : typeof r == `boolean` && n.push(`${t} ${e}: ${r},`));
149
+ }), n.push(`${t} },`)), n.push(`${t}},`));
150
+ }
151
+ return n;
152
+ }
153
+ function m(e, t) {
154
+ let n = [];
155
+ return e && (e.getHeaders && n.push(`${t}getHeaders: ${e.getHeaders},`), e.requestMiddleware && n.push(`${t}requestMiddleware: ${e.requestMiddleware},`), e.actionParsers && n.push(`${t}actionParsers: ${e.actionParsers},`), e.actionHandlers && n.push(`${t}actionHandlers: ${e.actionHandlers},`), e.contextProviders && n.push(`${t}contextProviders: ${e.contextProviders},`), e.streamParser && n.push(`${t}streamParser: ${e.streamParser},`)), n;
156
+ }
157
+ function h(e, t) {
158
+ return JSON.stringify(e, null, t).replace(/</g, `\\u003c`).replace(/\u2028/g, `\\u2028`).replace(/\u2029/g, `\\u2029`);
159
+ }
160
+ function g(e, t, n) {
161
+ Object.entries(t).forEach(([t, r]) => {
162
+ if (r === void 0 || typeof r == `function`) return;
163
+ let i = /^[a-zA-Z_$][\w$]*$/.test(t) ? t : h(t);
164
+ Array.isArray(r) ? e.push(`${n}${i}: ${h(r)},`) : r && typeof r == `object` ? (e.push(`${n}${i}: {`), g(e, r, `${n} `), e.push(`${n}},`)) : e.push(`${n}${i}: ${h(r)},`);
165
+ });
166
+ }
167
+ function _(e, t, n, r) {
168
+ n && (e.push(`${r}${t}: {`), g(e, n, `${r} `), e.push(`${r}},`));
169
+ }
170
+ function v(e, t, n) {
171
+ _(e, `copy`, t.copy, n), _(e, `welcome`, t.welcome, n), t.approval === !1 ? e.push(`${n}approval: false,`) : _(e, `approval`, t.approval, n);
172
+ }
173
+ function y(e, t, n) {
174
+ t.future?.v5Defaults !== void 0 && e.push(`${n}future: { v5Defaults: ${t.future.v5Defaults === !0} },`), _(e, `brand`, t.brand, n), _(e, `darkTheme`, t.darkTheme, n), t.colorScheme !== void 0 && e.push(`${n}colorScheme: ${JSON.stringify(t.colorScheme)},`);
175
+ }
176
+ function b(e) {
177
+ return (e?.target ?? `body`).replace(/\\/g, `\\\\`).replace(/'/g, `\\'`);
178
+ }
179
+ function x(e, t = `esm`, r) {
180
+ if (r?.authoredOverrides) return S(e, t, r);
181
+ let i = { ...e };
182
+ delete i.postprocessMessage, delete i.initialMessages;
183
+ let a = r ? {
184
+ ...r,
185
+ hooks: n(r.hooks)
186
+ } : void 0, o;
187
+ o = t === `esm` ? C(i, a) : t === `script-installer` ? D(i, a) : t === `script-advanced` ? k(i, a) : t === `react-component` ? w(i, a) : t === `react-advanced` ? T(i, a) : O(i, a);
188
+ let s = [
189
+ [`welcome.icon`, e.welcome?.icon],
190
+ [`approval.formatDescription`, e.approval?.formatDescription],
191
+ [`approval.onDecision`, e.approval?.onDecision]
192
+ ].filter(([, e]) => typeof e == `function`).map(([e]) => e);
193
+ if (s.length === 0) return o;
194
+ let c = `Function-valued ${s.join(`, `)} omitted; reattach in host JavaScript.`;
195
+ return t.startsWith(`script-`) ? `<!-- ${c} -->\n${o}` : `// ${c}\n${o}`;
196
+ }
197
+ function S(t, r, i) {
198
+ let a = [], o = Symbol(`omit`), s = /* @__PURE__ */ new Set([
199
+ `storageAdapter`,
200
+ `targetProviders`,
201
+ `components`,
202
+ `voiceRecognition.provider`,
203
+ `voiceRecognition.provider.custom`
204
+ ]), c = (e) => typeof e == `function` || !!e && typeof e == `object` && Object.values(e).some(c), l = (e, t) => {
205
+ if (typeof e == `function` || typeof e == `symbol` || e === void 0) return a.push(t), o;
206
+ if (!e || typeof e != `object`) return e;
207
+ if (s.has(t) && c(e) || !Array.isArray(e) && Object.getPrototypeOf(e) !== Object.prototype) return a.push(t), o;
208
+ if (Array.isArray(e)) {
209
+ let n = e.map((e, n) => l(e, `${t}.${n}`));
210
+ return n.includes(o) ? (a.push(t), o) : n;
211
+ }
212
+ return Object.fromEntries(Object.entries(e).flatMap(([e, n]) => {
213
+ let r = l(n, t ? `${t}.${e}` : e);
214
+ return r === o ? [] : [[e, r]];
215
+ }));
216
+ }, u = l(t, ``), d = n(i.hooks), f = r === `script-installer`, p = h(u, 2);
217
+ if (d) {
218
+ let e = m(d, ` `);
219
+ d.postprocessMessage && e.push(` postprocessMessage: ${d.postprocessMessage},`);
220
+ let t = [`onFeedback`, `onCopy`];
221
+ if (t.some((e) => d[e])) {
222
+ if (!f) {
223
+ let e = { ...u };
224
+ delete e.messageActions, p = h(e, 2);
225
+ }
226
+ e.push(` messageActions: {`), e.push(...Object.entries(u.messageActions ?? {}).map(([e, t]) => ` ${h(e)}: ${h(t)},`));
227
+ for (let n of t) d[n] && e.push(` ${n}: ${d[n]},`);
228
+ e.push(` },`);
229
+ }
230
+ if (f) a.push(...Object.keys(d).map((e) => `hooks.${e}`));
231
+ else if (e.length) {
232
+ let t = p.slice(1, -1).trim();
233
+ p = `{\n${t}${t ? `,` : ``}\n${e.join(`
234
+ `)}\n}`;
235
+ }
236
+ }
237
+ r.startsWith(`script-`) && (p = p.replace(/<\/script/gi, `<\\/script`));
238
+ let g, _ = h(i.target ?? `body`), v = i.windowKey ? `, windowKey: ${h(i.windowKey)}` : ``, y = e(i);
239
+ if (g = f ? `<script src="${y}/install.global.js" data-config='${h(i.target || i.windowKey ? {
240
+ config: u,
241
+ ...i.target ? { target: i.target } : {},
242
+ ...i.windowKey ? { windowKey: i.windowKey } : {}
243
+ } : u).replace(/&/g, `&amp;`).replace(/'/g, `&#39;`).replace(/"/g, `&quot;`)}'><\/script>` : r.startsWith(`script-`) ? `<link rel="stylesheet" href="${y}/widget.css" />\n<script src="${y}/index.global.js"><\/script>\n<script>\nvar config = ${p};\nvar handle = window.AgentWidget.initAgentWidget({ target: ${_}${v}, config });\n<\/script>` : r.startsWith(`react-`) ? `'use client';\nimport { useEffect } from 'react';\nimport '@runtypelabs/persona/widget.css';\nimport { initAgentWidget } from '@runtypelabs/persona';\n\nexport function ChatWidget() {\n useEffect(() => {\n const handle = initAgentWidget({ target: ${_}${v}, config: ${p} });\n return () => handle.destroy();\n }, []);\n return null;\n}` : `import '@runtypelabs/persona/widget.css';\nimport { initAgentWidget } from '@runtypelabs/persona';\n\ninitAgentWidget({ target: ${_}${v}, config: ${p} });`, !a.length) return g;
244
+ let b = `Omitted non-serializable inputs: ${[...new Set(a)].join(`, `)}. Reattach functions/custom implementations in host JavaScript; closures cannot be reconstructed.`;
245
+ return r.startsWith(`script-`) ? `<!-- ${b.replace(/--/g, `—`).replace(/</g, `&lt;`).replace(/>/g, `&gt;`)} -->\n${g}` : `// ${b.replace(/[\r\n\u2028\u2029]/g, ` `)}\n${g}`;
246
+ }
247
+ function C(e, t) {
248
+ let n = t?.hooks, r = c(e), i = r !== `plain`, a = [
249
+ `import '@runtypelabs/persona/widget.css';`,
250
+ `import { initAgentWidget, markdownPostprocessor } from '@runtypelabs/persona';`,
251
+ ``,
252
+ `initAgentWidget({`,
253
+ ` target: '${b(t)}',`,
254
+ ` config: {`
255
+ ];
256
+ return e.apiUrl && a.push(` apiUrl: "${e.apiUrl}",`), e.clientToken && a.push(` clientToken: "${e.clientToken}",`), e.agentId && a.push(` agentId: "${e.agentId}",`), e.target && a.push(` target: "${e.target}",`), e.flowId && a.push(` flowId: "${e.flowId}",`), i && a.push(` parserType: "${r}",`), y(a, e, ` `), e.theme && typeof e.theme == `object` && Object.keys(e.theme).length > 0 && _(a, `theme`, e.theme, ` `), e.launcher && _(a, `launcher`, e.launcher, ` `), v(a, e, ` `), e.sendButton && (a.push(` sendButton: {`), Object.entries(e.sendButton).forEach(([e, t]) => {
257
+ typeof t == `string` ? a.push(` ${e}: "${t}",`) : typeof t == `boolean` && a.push(` ${e}: ${t},`);
258
+ }), a.push(` },`)), e.voiceRecognition && (a.push(` voiceRecognition: {`), Object.entries(e.voiceRecognition).forEach(([e, t]) => {
259
+ typeof t == `string` ? a.push(` ${e}: "${t}",`) : (typeof t == `boolean` || typeof t == `number`) && a.push(` ${e}: ${t},`);
260
+ }), a.push(` },`)), e.statusIndicator && (a.push(` statusIndicator: {`), Object.entries(e.statusIndicator).forEach(([e, t]) => {
261
+ typeof t == `string` ? a.push(` ${e}: "${t}",`) : typeof t == `boolean` && a.push(` ${e}: ${t},`);
262
+ }), a.push(` },`)), e.features && (a.push(` features: {`), Object.entries(e.features).forEach(([e, t]) => {
263
+ a.push(` ${e}: ${t},`);
264
+ }), a.push(` },`)), a.push(...f(e, ` `)), e.suggestionChips && e.suggestionChips.length > 0 && (a.push(` suggestionChips: [`), e.suggestionChips.forEach((e) => {
265
+ a.push(` "${e}",`);
266
+ }), a.push(` ],`)), e.suggestionChipsConfig && (a.push(` suggestionChipsConfig: {`), e.suggestionChipsConfig.fontFamily && a.push(` fontFamily: "${e.suggestionChipsConfig.fontFamily}",`), e.suggestionChipsConfig.fontWeight && a.push(` fontWeight: "${e.suggestionChipsConfig.fontWeight}",`), e.suggestionChipsConfig.paddingX && a.push(` paddingX: "${e.suggestionChipsConfig.paddingX}",`), e.suggestionChipsConfig.paddingY && a.push(` paddingY: "${e.suggestionChipsConfig.paddingY}",`), a.push(` },`)), a.push(...l(e, ` `)), a.push(...u(e, ` `, n)), a.push(...d(e, ` `)), a.push(...p(e, ` `)), a.push(...m(n, ` `)), e.debug && a.push(` debug: ${e.debug},`), n?.postprocessMessage ? a.push(` postprocessMessage: ${n.postprocessMessage}`) : a.push(` postprocessMessage: ({ text }) => markdownPostprocessor(text)`), a.push(` }`), a.push(`});`), a.join(`
267
+ `);
268
+ }
269
+ function w(e, t) {
270
+ let n = t?.hooks, r = c(e), i = r !== `plain`, a = [
271
+ `// ChatWidget.tsx`,
272
+ `'use client'; // Required for Next.js - remove for Vite/CRA`,
273
+ ``,
274
+ `import { useEffect } from 'react';`,
275
+ `import '@runtypelabs/persona/widget.css';`,
276
+ `import { initAgentWidget, markdownPostprocessor } from '@runtypelabs/persona';`,
277
+ `import type { AgentWidgetInitHandle } from '@runtypelabs/persona';`,
278
+ ``,
279
+ `export function ChatWidget() {`,
280
+ ` useEffect(() => {`,
281
+ ` let handle: AgentWidgetInitHandle | null = null;`,
282
+ ``,
283
+ ` handle = initAgentWidget({`,
284
+ ` target: '${b(t)}',`,
285
+ ` config: {`
286
+ ];
287
+ return e.apiUrl && a.push(` apiUrl: "${e.apiUrl}",`), e.clientToken && a.push(` clientToken: "${e.clientToken}",`), e.agentId && a.push(` agentId: "${e.agentId}",`), e.target && a.push(` target: "${e.target}",`), e.flowId && a.push(` flowId: "${e.flowId}",`), i && a.push(` parserType: "${r}",`), y(a, e, ` `), e.theme && typeof e.theme == `object` && Object.keys(e.theme).length > 0 && _(a, `theme`, e.theme, ` `), e.launcher && _(a, `launcher`, e.launcher, ` `), v(a, e, ` `), e.sendButton && (a.push(` sendButton: {`), Object.entries(e.sendButton).forEach(([e, t]) => {
288
+ typeof t == `string` ? a.push(` ${e}: "${t}",`) : typeof t == `boolean` && a.push(` ${e}: ${t},`);
289
+ }), a.push(` },`)), e.voiceRecognition && (a.push(` voiceRecognition: {`), Object.entries(e.voiceRecognition).forEach(([e, t]) => {
290
+ typeof t == `string` ? a.push(` ${e}: "${t}",`) : (typeof t == `boolean` || typeof t == `number`) && a.push(` ${e}: ${t},`);
291
+ }), a.push(` },`)), e.statusIndicator && (a.push(` statusIndicator: {`), Object.entries(e.statusIndicator).forEach(([e, t]) => {
292
+ typeof t == `string` ? a.push(` ${e}: "${t}",`) : typeof t == `boolean` && a.push(` ${e}: ${t},`);
293
+ }), a.push(` },`)), e.features && (a.push(` features: {`), Object.entries(e.features).forEach(([e, t]) => {
294
+ a.push(` ${e}: ${t},`);
295
+ }), a.push(` },`)), a.push(...f(e, ` `)), e.suggestionChips && e.suggestionChips.length > 0 && (a.push(` suggestionChips: [`), e.suggestionChips.forEach((e) => {
296
+ a.push(` "${e}",`);
297
+ }), a.push(` ],`)), e.suggestionChipsConfig && (a.push(` suggestionChipsConfig: {`), e.suggestionChipsConfig.fontFamily && a.push(` fontFamily: "${e.suggestionChipsConfig.fontFamily}",`), e.suggestionChipsConfig.fontWeight && a.push(` fontWeight: "${e.suggestionChipsConfig.fontWeight}",`), e.suggestionChipsConfig.paddingX && a.push(` paddingX: "${e.suggestionChipsConfig.paddingX}",`), e.suggestionChipsConfig.paddingY && a.push(` paddingY: "${e.suggestionChipsConfig.paddingY}",`), a.push(` },`)), a.push(...l(e, ` `)), a.push(...u(e, ` `, n)), a.push(...d(e, ` `)), a.push(...p(e, ` `)), a.push(...m(n, ` `)), e.debug && a.push(` debug: ${e.debug},`), n?.postprocessMessage ? a.push(` postprocessMessage: ${n.postprocessMessage}`) : a.push(` postprocessMessage: ({ text }) => markdownPostprocessor(text)`), a.push(` }`), a.push(` });`), a.push(``), a.push(` // Cleanup on unmount`), a.push(` return () => {`), a.push(` if (handle) {`), a.push(` handle.destroy();`), a.push(` }`), a.push(` };`), a.push(` }, []);`), a.push(``), a.push(` return null; // Widget injects itself into the DOM`), a.push(`}`), a.push(``), a.push(`// Usage in your app:`), a.push(`// import { ChatWidget } from './components/ChatWidget';`), a.push(`//`), a.push(`// export default function App() {`), a.push(`// return (`), a.push(`// <div>`), a.push(`// {/* Your app content */}`), a.push(`// <ChatWidget />`), a.push(`// </div>`), a.push(`// );`), a.push(`// }`), a.join(`
298
+ `);
299
+ }
300
+ function T(e, t) {
301
+ let n = t?.hooks, i = [
302
+ `// ChatWidgetAdvanced.tsx`,
303
+ `'use client'; // Required for Next.js - remove for Vite/CRA`,
304
+ ``,
305
+ `import { useEffect } from 'react';`,
306
+ `import '@runtypelabs/persona/widget.css';`,
307
+ `import {`,
308
+ ` initAgentWidget,`,
309
+ ` createFlexibleJsonStreamParser,`,
310
+ ` defaultJsonActionParser,`,
311
+ ` defaultActionHandlers,`,
312
+ ` markdownPostprocessor`,
313
+ `} from '@runtypelabs/persona';`,
314
+ `import type { AgentWidgetInitHandle } from '@runtypelabs/persona';`,
315
+ ``,
316
+ `const STORAGE_KEY = 'chat-widget-state';`,
317
+ `const PROCESSED_ACTIONS_KEY = 'chat-widget-processed-actions';`,
318
+ ``,
319
+ `// Types for DOM elements`,
320
+ `interface PageElement {`,
321
+ ` type: string;`,
322
+ ` tagName: string;`,
323
+ ` selector: string;`,
324
+ ` innerText: string;`,
325
+ ` href?: string;`,
326
+ `}`,
327
+ ``,
328
+ `interface DOMContext {`,
329
+ ` page_elements: PageElement[];`,
330
+ ` page_element_count: number;`,
331
+ ` element_types: Record<string, number>;`,
332
+ ` page_url: string;`,
333
+ ` page_title: string;`,
334
+ ` timestamp: string;`,
335
+ `}`,
336
+ ``,
337
+ `// DOM context provider - extracts page elements for AI context`,
338
+ `const collectDOMContext = (): DOMContext => {`,
339
+ ` const selectors = {`,
340
+ ` products: '[data-product-id], .product-card, .product-item, [role="article"]',`,
341
+ ` buttons: 'button, [role="button"], .btn',`,
342
+ ` links: 'a[href]',`,
343
+ ` inputs: 'input, textarea, select'`,
344
+ ` };`,
345
+ ``,
346
+ ` const elements: PageElement[] = [];`,
347
+ ` Object.entries(selectors).forEach(([type, selector]) => {`,
348
+ ` document.querySelectorAll(selector).forEach((element) => {`,
349
+ ` if (!(element instanceof HTMLElement)) return;`,
350
+ ` `,
351
+ ` // Exclude elements within the widget`,
352
+ ` const widgetHost = element.closest('.persona-host');`,
353
+ ` if (widgetHost) return;`,
354
+ ` `,
355
+ ` const text = element.innerText?.trim();`,
356
+ ` if (!text) return;`,
357
+ ``,
358
+ ` const selectorString =`,
359
+ " element.id ? `#${element.id}` :",
360
+ " element.getAttribute('data-testid') ? `[data-testid=\"${element.getAttribute('data-testid')}\"]` :",
361
+ " element.getAttribute('data-product-id') ? `[data-product-id=\"${element.getAttribute('data-product-id')}\"]` :",
362
+ ` element.tagName.toLowerCase();`,
363
+ ``,
364
+ ` const elementData: PageElement = {`,
365
+ ` type,`,
366
+ ` tagName: element.tagName.toLowerCase(),`,
367
+ ` selector: selectorString,`,
368
+ ` innerText: text.substring(0, 200)`,
369
+ ` };`,
370
+ ``,
371
+ ` if (type === 'links' && element instanceof HTMLAnchorElement && element.href) {`,
372
+ ` elementData.href = element.href;`,
373
+ ` }`,
374
+ ``,
375
+ ` elements.push(elementData);`,
376
+ ` });`,
377
+ ` });`,
378
+ ``,
379
+ ` const counts = elements.reduce((acc, el) => {`,
380
+ ` acc[el.type] = (acc[el.type] || 0) + 1;`,
381
+ ` return acc;`,
382
+ ` }, {} as Record<string, number>);`,
383
+ ``,
384
+ ` return {`,
385
+ ` page_elements: elements.slice(0, 50),`,
386
+ ` page_element_count: elements.length,`,
387
+ ` element_types: counts,`,
388
+ ` page_url: window.location.href,`,
389
+ ` page_title: document.title,`,
390
+ ` timestamp: new Date().toISOString()`,
391
+ ` };`,
392
+ `};`,
393
+ ``,
394
+ `export function ChatWidgetAdvanced() {`,
395
+ ` useEffect(() => {`,
396
+ ` let handle: AgentWidgetInitHandle | null = null;`,
397
+ ``,
398
+ ` // Load saved state`,
399
+ ` const loadSavedMessages = () => {`,
400
+ ` const savedState = localStorage.getItem(STORAGE_KEY);`,
401
+ ` if (savedState) {`,
402
+ ` try {`,
403
+ ` const { messages } = JSON.parse(savedState);`,
404
+ ` return messages || [];`,
405
+ ` } catch (e) {`,
406
+ ` console.error('Failed to load saved state:', e);`,
407
+ ` }`,
408
+ ` }`,
409
+ ` return [];`,
410
+ ` };`,
411
+ ``,
412
+ ` handle = initAgentWidget({`,
413
+ ` target: '${b(t)}',`,
414
+ ` config: {`
415
+ ];
416
+ return e.apiUrl && i.push(` apiUrl: "${e.apiUrl}",`), e.clientToken && i.push(` clientToken: "${e.clientToken}",`), e.agentId && i.push(` agentId: "${e.agentId}",`), e.target && i.push(` target: "${e.target}",`), e.flowId && i.push(` flowId: "${e.flowId}",`), y(i, e, ` `), e.theme && typeof e.theme == `object` && Object.keys(e.theme).length > 0 && _(i, `theme`, e.theme, ` `), e.launcher && _(i, `launcher`, e.launcher, ` `), v(i, e, ` `), e.sendButton && (i.push(` sendButton: {`), Object.entries(e.sendButton).forEach(([e, t]) => {
417
+ typeof t == `string` ? i.push(` ${e}: "${t}",`) : typeof t == `boolean` && i.push(` ${e}: ${t},`);
418
+ }), i.push(` },`)), e.voiceRecognition && (i.push(` voiceRecognition: {`), Object.entries(e.voiceRecognition).forEach(([e, t]) => {
419
+ typeof t == `string` ? i.push(` ${e}: "${t}",`) : (typeof t == `boolean` || typeof t == `number`) && i.push(` ${e}: ${t},`);
420
+ }), i.push(` },`)), e.statusIndicator && (i.push(` statusIndicator: {`), Object.entries(e.statusIndicator).forEach(([e, t]) => {
421
+ typeof t == `string` ? i.push(` ${e}: "${t}",`) : typeof t == `boolean` && i.push(` ${e}: ${t},`);
422
+ }), i.push(` },`)), e.features && (i.push(` features: {`), Object.entries(e.features).forEach(([e, t]) => {
423
+ i.push(` ${e}: ${t},`);
424
+ }), i.push(` },`)), i.push(...f(e, ` `)), e.suggestionChips && e.suggestionChips.length > 0 && (i.push(` suggestionChips: [`), e.suggestionChips.forEach((e) => {
425
+ i.push(` "${e}",`);
426
+ }), i.push(` ],`)), e.suggestionChipsConfig && (i.push(` suggestionChipsConfig: {`), e.suggestionChipsConfig.fontFamily && i.push(` fontFamily: "${e.suggestionChipsConfig.fontFamily}",`), e.suggestionChipsConfig.fontWeight && i.push(` fontWeight: "${e.suggestionChipsConfig.fontWeight}",`), e.suggestionChipsConfig.paddingX && i.push(` paddingX: "${e.suggestionChipsConfig.paddingX}",`), e.suggestionChipsConfig.paddingY && i.push(` paddingY: "${e.suggestionChipsConfig.paddingY}",`), i.push(` },`)), i.push(...l(e, ` `)), i.push(...u(e, ` `, n)), i.push(...d(e, ` `)), i.push(...p(e, ` `)), n?.getHeaders && i.push(` getHeaders: ${n.getHeaders},`), n?.contextProviders && i.push(` contextProviders: ${n.contextProviders},`), e.debug && i.push(` debug: ${e.debug},`), i.push(` initialMessages: loadSavedMessages(),`), n?.streamParser ? i.push(` streamParser: ${n.streamParser},`) : (i.push(` // Flexible JSON stream parser for handling structured actions`), i.push(` streamParser: () => createFlexibleJsonStreamParser((parsed: any) => {
427
+ if (!parsed || typeof parsed !== 'object') return null;
428
+ if (parsed.action === 'nav_then_click') return 'Navigating...';
429
+ if (parsed.action === 'message') return parsed.text || '';
430
+ if (parsed.action === 'message_and_click') return parsed.text || 'Processing...';
431
+ return parsed.text || null;
432
+ }),`)), n?.actionParsers ? (i.push(` // Action parsers (custom merged with defaults)`), i.push(` actionParsers: [...(${n.actionParsers}), defaultJsonActionParser,`), i.push(` // Built-in parser for markdown-wrapped JSON`), i.push(` ${r}`), i.push(` ],`)) : (i.push(` // Action parsers to detect JSON actions in responses`), i.push(` actionParsers: [`), i.push(` defaultJsonActionParser,`), i.push(` // Parser for markdown-wrapped JSON`), i.push(` ${r}`), i.push(` ],`)), n?.actionHandlers ? (i.push(` // Action handlers (custom merged with defaults)`), i.push(` actionHandlers: [...(${n.actionHandlers}),`), i.push(` defaultActionHandlers.message,`), i.push(` defaultActionHandlers.messageAndClick,`), i.push(` // Built-in handler for nav_then_click action`), i.push(` ${a}`), i.push(` ],`)) : (i.push(` // Action handlers for navigation and other actions`), i.push(` actionHandlers: [`), i.push(` defaultActionHandlers.message,`), i.push(` defaultActionHandlers.messageAndClick,`), i.push(` // Handler for nav_then_click action`), i.push(` ${a}`), i.push(` ],`)), n?.postprocessMessage ? i.push(` postprocessMessage: ${n.postprocessMessage},`) : i.push(` postprocessMessage: ({ text }) => markdownPostprocessor(text),`), n?.requestMiddleware ? (i.push(` // Request middleware (custom merged with DOM context)`), i.push(` requestMiddleware: ({ payload, config }) => {`), i.push(` const customResult = (${n.requestMiddleware})({ payload, config });`), i.push(` const merged = customResult || payload;`), i.push(` return {`), i.push(` ...merged,`), i.push(` metadata: { ...merged.metadata, ...collectDOMContext() }`), i.push(` };`), i.push(` }`)) : (i.push(` requestMiddleware: ({ payload }) => {`), i.push(` return {`), i.push(` ...payload,`), i.push(` metadata: collectDOMContext()`), i.push(` };`), i.push(` }`)), i.push(` }`), i.push(` });`), i.push(``), i.push(` // Save state on message events`), i.push(` const handleMessage = () => {`), i.push(` const session = handle?.getSession?.();`), i.push(` if (session) {`), i.push(` localStorage.setItem(STORAGE_KEY, JSON.stringify({`), i.push(` messages: session.messages,`), i.push(` timestamp: new Date().toISOString()`), i.push(` }));`), i.push(` }`), i.push(` };`), i.push(``), i.push(` // Clear state on clear chat`), i.push(` const handleClearChat = () => {`), i.push(` localStorage.removeItem(STORAGE_KEY);`), i.push(` localStorage.removeItem(PROCESSED_ACTIONS_KEY);`), i.push(` };`), i.push(``), i.push(` window.addEventListener('persona:message', handleMessage);`), i.push(` window.addEventListener('persona:clear-chat', handleClearChat);`), i.push(``), i.push(` // Cleanup on unmount`), i.push(` return () => {`), i.push(` window.removeEventListener('persona:message', handleMessage);`), i.push(` window.removeEventListener('persona:clear-chat', handleClearChat);`), i.push(` if (handle) {`), i.push(` handle.destroy();`), i.push(` }`), i.push(` };`), i.push(` }, []);`), i.push(``), i.push(` return null; // Widget injects itself into the DOM`), i.push(`}`), i.push(``), i.push(`// Usage: Collects DOM context for AI-powered navigation`), i.push(`// Features:`), i.push(`// - Extracts page elements (products, buttons, links)`), i.push(`// - Persists chat history across page loads`), i.push(`// - Handles navigation actions (nav_then_click)`), i.push(`// - Processes structured JSON actions from AI`), i.push(`//`), i.push(`// Example usage in Next.js:`), i.push(`// import { ChatWidgetAdvanced } from './components/ChatWidgetAdvanced';`), i.push(`//`), i.push(`// export default function RootLayout({ children }) {`), i.push(`// return (`), i.push(`// <html lang="en">`), i.push(`// <body>`), i.push(`// {children}`), i.push(`// <ChatWidgetAdvanced />`), i.push(`// </body>`), i.push(`// </html>`), i.push(`// );`), i.push(`// }`), i.join(`
433
+ `);
434
+ }
435
+ function E(e) {
436
+ let t = c(e), n = t !== `plain`, r = {};
437
+ if (e.apiUrl && (r.apiUrl = e.apiUrl), e.clientToken && (r.clientToken = e.clientToken), e.agentId && (r.agentId = e.agentId), e.target && (r.target = e.target), e.flowId && (r.flowId = e.flowId), n && (r.parserType = t), e.future?.v5Defaults !== void 0 && (r.future = { v5Defaults: e.future.v5Defaults === !0 }), e.theme && (r.theme = e.theme), e.brand && (r.brand = e.brand), e.darkTheme && (r.darkTheme = e.darkTheme), e.colorScheme !== void 0 && (r.colorScheme = e.colorScheme), e.launcher && (r.launcher = e.launcher), e.copy && (r.copy = e.copy), e.welcome !== void 0 && (r.welcome = e.welcome), e.approval !== void 0 && (r.approval = e.approval), e.sendButton && (r.sendButton = e.sendButton), e.voiceRecognition && (r.voiceRecognition = e.voiceRecognition), e.statusIndicator && (r.statusIndicator = e.statusIndicator), e.features && (r.features = e.features), e.suggestions && (r.suggestions = e.suggestions), e.suggestionChips?.length > 0 && (r.suggestionChips = e.suggestionChips), e.suggestionChipsConfig && (r.suggestionChipsConfig = e.suggestionChipsConfig), e.debug && (r.debug = e.debug), e.toolCall) {
438
+ let t = {};
439
+ Object.entries(e.toolCall).forEach(([e, n]) => {
440
+ typeof n == `string` && (t[e] = n);
441
+ }), Object.keys(t).length > 0 && (r.toolCall = t);
442
+ }
443
+ if (e.messageActions) {
444
+ let t = {};
445
+ Object.entries(e.messageActions).forEach(([e, n]) => {
446
+ e !== `onFeedback` && e !== `onCopy` && n !== void 0 && (typeof n == `string` || typeof n == `boolean`) && (t[e] = n);
447
+ }), Object.keys(t).length > 0 && (r.messageActions = t);
448
+ }
449
+ if (e.markdown) {
450
+ let t = {};
451
+ e.markdown.options && (t.options = e.markdown.options), e.markdown.disableDefaultStyles !== void 0 && (t.disableDefaultStyles = e.markdown.disableDefaultStyles), Object.keys(t).length > 0 && (r.markdown = t);
452
+ }
453
+ if (e.layout) {
454
+ let t = {};
455
+ if (e.layout.header) {
456
+ let n = {};
457
+ Object.entries(e.layout.header).forEach(([e, t]) => {
458
+ e !== `render` && (typeof t == `string` || typeof t == `boolean`) && (n[e] = t);
459
+ }), Object.keys(n).length > 0 && (t.header = n);
460
+ }
461
+ if (e.layout.messages) {
462
+ let n = {};
463
+ Object.entries(e.layout.messages).forEach(([e, t]) => {
464
+ if (e !== `renderUserMessage` && e !== `renderAssistantMessage`) {
465
+ if (e === `avatar` && typeof t == `object` && t) n.avatar = t;
466
+ else if (e === `timestamp` && typeof t == `object` && t) {
467
+ let e = {};
468
+ Object.entries(t).forEach(([t, n]) => {
469
+ t !== `format` && (typeof n == `string` || typeof n == `boolean`) && (e[t] = n);
470
+ }), Object.keys(e).length > 0 && (n.timestamp = e);
471
+ } else (typeof t == `string` || typeof t == `boolean`) && (n[e] = t);
472
+ }
473
+ }), Object.keys(n).length > 0 && (t.messages = n);
474
+ }
475
+ Object.keys(t).length > 0 && (r.layout = t);
476
+ }
477
+ return r;
478
+ }
479
+ function D(t, n) {
480
+ let r = E(t), i = n?.windowKey || n?.target ? {
481
+ config: r,
482
+ ...n?.windowKey ? { windowKey: n.windowKey } : {},
483
+ ...n?.target ? { target: n.target } : {}
484
+ } : r, a = JSON.stringify(i).replace(/&/g, `&amp;`).replace(/</g, `&lt;`).replace(/>/g, `&gt;`).replace(/'/g, `&#39;`);
485
+ return `<script src="${e(n)}/install.global.js" data-config='${a}'><\/script>`;
486
+ }
487
+ function O(t, n) {
488
+ let r = n?.hooks, i = c(t), a = i !== `plain`, o = e(n), s = [
489
+ `<!-- Load CSS -->`,
490
+ `<link rel="stylesheet" href="${o}/widget.css" />`,
491
+ ``,
492
+ `<!-- Load JavaScript -->`,
493
+ `<script src="${o}/index.global.js"><\/script>`,
494
+ ``,
495
+ `<!-- Initialize widget -->`,
496
+ `<script>`,
497
+ ` var handle = window.AgentWidget.initAgentWidget({`,
498
+ ` target: '${b(n)}',`,
499
+ ...n?.windowKey ? [` windowKey: '${n.windowKey}',`] : [],
500
+ ` config: {`
501
+ ];
502
+ return t.apiUrl && s.push(` apiUrl: "${t.apiUrl}",`), t.clientToken && s.push(` clientToken: "${t.clientToken}",`), t.agentId && s.push(` agentId: "${t.agentId}",`), t.target && s.push(` target: "${t.target}",`), t.flowId && s.push(` flowId: "${t.flowId}",`), a && s.push(` parserType: "${i}",`), y(s, t, ` `), t.theme && typeof t.theme == `object` && Object.keys(t.theme).length > 0 && _(s, `theme`, t.theme, ` `), t.launcher && _(s, `launcher`, t.launcher, ` `), v(s, t, ` `), t.sendButton && (s.push(` sendButton: {`), Object.entries(t.sendButton).forEach(([e, t]) => {
503
+ typeof t == `string` ? s.push(` ${e}: "${t}",`) : typeof t == `boolean` && s.push(` ${e}: ${t},`);
504
+ }), s.push(` },`)), t.voiceRecognition && (s.push(` voiceRecognition: {`), Object.entries(t.voiceRecognition).forEach(([e, t]) => {
505
+ typeof t == `string` ? s.push(` ${e}: "${t}",`) : (typeof t == `boolean` || typeof t == `number`) && s.push(` ${e}: ${t},`);
506
+ }), s.push(` },`)), t.statusIndicator && (s.push(` statusIndicator: {`), Object.entries(t.statusIndicator).forEach(([e, t]) => {
507
+ typeof t == `string` ? s.push(` ${e}: "${t}",`) : typeof t == `boolean` && s.push(` ${e}: ${t},`);
508
+ }), s.push(` },`)), t.features && (s.push(` features: {`), Object.entries(t.features).forEach(([e, t]) => {
509
+ s.push(` ${e}: ${t},`);
510
+ }), s.push(` },`)), s.push(...f(t, ` `)), t.suggestionChips && t.suggestionChips.length > 0 && (s.push(` suggestionChips: [`), t.suggestionChips.forEach((e) => {
511
+ s.push(` "${e}",`);
512
+ }), s.push(` ],`)), t.suggestionChipsConfig && (s.push(` suggestionChipsConfig: {`), t.suggestionChipsConfig.fontFamily && s.push(` fontFamily: "${t.suggestionChipsConfig.fontFamily}",`), t.suggestionChipsConfig.fontWeight && s.push(` fontWeight: "${t.suggestionChipsConfig.fontWeight}",`), t.suggestionChipsConfig.paddingX && s.push(` paddingX: "${t.suggestionChipsConfig.paddingX}",`), t.suggestionChipsConfig.paddingY && s.push(` paddingY: "${t.suggestionChipsConfig.paddingY}",`), s.push(` },`)), s.push(...l(t, ` `)), s.push(...u(t, ` `, r)), s.push(...d(t, ` `)), s.push(...p(t, ` `)), s.push(...m(r, ` `)), t.debug && s.push(` debug: ${t.debug},`), r?.postprocessMessage ? s.push(` postprocessMessage: ${r.postprocessMessage}`) : s.push(` postprocessMessage: ({ text }) => window.AgentWidget.markdownPostprocessor(text)`), s.push(` }`), s.push(` });`), s.push(`<\/script>`), s.join(`
513
+ `);
514
+ }
515
+ function k(t, n) {
516
+ let r = n?.hooks, a = [
517
+ `<script>`,
518
+ `(function() {`,
519
+ ` 'use strict';`,
520
+ ``,
521
+ ` // Configuration`,
522
+ ` var CONFIG = ${h(E(t), 2).split(`
523
+ `).map((e, t) => t === 0 ? e : ` ` + e).join(`
524
+ `)};`,
525
+ ``,
526
+ ` // Constants`,
527
+ ` var CDN_BASE = '${e(n)}';`,
528
+ ` var STORAGE_KEY = 'chat-widget-state';`,
529
+ ` var PROCESSED_ACTIONS_KEY = 'chat-widget-processed-actions';`,
530
+ ``,
531
+ ` // DOM context provider - extracts page elements for AI context`,
532
+ ` var domContextProvider = function() {`,
533
+ ` var selectors = {`,
534
+ ` products: '[data-product-id], .product-card, .product-item, [role="article"]',`,
535
+ ` buttons: 'button, [role="button"], .btn',`,
536
+ ` links: 'a[href]',`,
537
+ ` inputs: 'input, textarea, select'`,
538
+ ` };`,
539
+ ``,
540
+ ` var elements = [];`,
541
+ ` Object.entries(selectors).forEach(function(entry) {`,
542
+ ` var type = entry[0], selector = entry[1];`,
543
+ ` document.querySelectorAll(selector).forEach(function(element) {`,
544
+ ` if (!(element instanceof HTMLElement)) return;`,
545
+ ` var widgetHost = element.closest('.persona-host');`,
546
+ ` if (widgetHost) return;`,
547
+ ` var text = element.innerText ? element.innerText.trim() : '';`,
548
+ ` if (!text) return;`,
549
+ ``,
550
+ ` var selectorString = element.id ? '#' + element.id :`,
551
+ ` element.getAttribute('data-testid') ? '[data-testid="' + element.getAttribute('data-testid') + '"]' :`,
552
+ ` element.getAttribute('data-product-id') ? '[data-product-id="' + element.getAttribute('data-product-id') + '"]' :`,
553
+ ` element.tagName.toLowerCase();`,
554
+ ``,
555
+ ` var elementData = {`,
556
+ ` type: type,`,
557
+ ` tagName: element.tagName.toLowerCase(),`,
558
+ ` selector: selectorString,`,
559
+ ` innerText: text.substring(0, 200)`,
560
+ ` };`,
561
+ ``,
562
+ ` if (type === 'links' && element instanceof HTMLAnchorElement && element.href) {`,
563
+ ` elementData.href = element.href;`,
564
+ ` }`,
565
+ ` elements.push(elementData);`,
566
+ ` });`,
567
+ ` });`,
568
+ ``,
569
+ ` var counts = elements.reduce(function(acc, el) {`,
570
+ ` acc[el.type] = (acc[el.type] || 0) + 1;`,
571
+ ` return acc;`,
572
+ ` }, {});`,
573
+ ``,
574
+ ` return {`,
575
+ ` page_elements: elements.slice(0, 50),`,
576
+ ` page_element_count: elements.length,`,
577
+ ` element_types: counts,`,
578
+ ` page_url: window.location.href,`,
579
+ ` page_title: document.title,`,
580
+ ` timestamp: new Date().toISOString()`,
581
+ ` };`,
582
+ ` };`,
583
+ ``,
584
+ ` // Load CSS dynamically`,
585
+ ` var loadCSS = function() {`,
586
+ ` if (document.querySelector('link[data-persona]')) return;`,
587
+ ` var link = document.createElement('link');`,
588
+ ` link.rel = 'stylesheet';`,
589
+ ` link.href = CDN_BASE + '/widget.css';`,
590
+ ` link.setAttribute('data-persona', 'true');`,
591
+ ` document.head.appendChild(link);`,
592
+ ` };`,
593
+ ``,
594
+ ` // Load JS dynamically`,
595
+ ` var loadJS = function(callback) {`,
596
+ ` if (window.AgentWidget) { callback(); return; }`,
597
+ ` var script = document.createElement('script');`,
598
+ ` script.src = CDN_BASE + '/index.global.js';`,
599
+ ` script.onload = callback;`,
600
+ ` script.onerror = function() { console.error('Failed to load AgentWidget'); };`,
601
+ ` document.head.appendChild(script);`,
602
+ ` };`,
603
+ ``,
604
+ ` // Create widget config with advanced features`,
605
+ ` var createWidgetConfig = function(agentWidget) {`,
606
+ ` var widgetConfig = Object.assign({}, CONFIG);`,
607
+ ``
608
+ ];
609
+ return r?.getHeaders && (a.push(` widgetConfig.getHeaders = ${r.getHeaders};`), a.push(``)), r?.contextProviders && (a.push(` widgetConfig.contextProviders = ${r.contextProviders};`), a.push(``)), r?.streamParser ? a.push(` widgetConfig.streamParser = ${r.streamParser};`) : (a.push(` // Flexible JSON stream parser for handling structured actions`), a.push(` widgetConfig.streamParser = function() {`), a.push(` return agentWidget.createFlexibleJsonStreamParser(function(parsed) {
610
+ if (!parsed || typeof parsed !== 'object') return null;
611
+ if (parsed.action === 'nav_then_click') return 'Navigating...';
612
+ if (parsed.action === 'message') return parsed.text || '';
613
+ if (parsed.action === 'message_and_click') return parsed.text || 'Processing...';
614
+ return parsed.text || null;
615
+ });`), a.push(` };`)), a.push(``), r?.actionParsers ? (a.push(` // Action parsers (custom merged with defaults)`), a.push(` var customParsers = ${r.actionParsers};`), a.push(` widgetConfig.actionParsers = customParsers.concat([`), a.push(` agentWidget.defaultJsonActionParser,`), a.push(` ${i}`), a.push(` ]);`)) : (a.push(` // Action parsers to detect JSON actions in responses`), a.push(` widgetConfig.actionParsers = [`), a.push(` agentWidget.defaultJsonActionParser,`), a.push(` ${i}`), a.push(` ];`)), a.push(``), r?.actionHandlers ? (a.push(` // Action handlers (custom merged with defaults)`), a.push(` var customHandlers = ${r.actionHandlers};`), a.push(` widgetConfig.actionHandlers = customHandlers.concat([`), a.push(` agentWidget.defaultActionHandlers.message,`), a.push(` agentWidget.defaultActionHandlers.messageAndClick,`), a.push(` ${o}`), a.push(` ]);`)) : (a.push(` // Action handlers for navigation and other actions`), a.push(` widgetConfig.actionHandlers = [`), a.push(` agentWidget.defaultActionHandlers.message,`), a.push(` agentWidget.defaultActionHandlers.messageAndClick,`), a.push(` ${o}`), a.push(` ];`)), a.push(``), r?.requestMiddleware ? (a.push(` // Request middleware (custom merged with DOM context)`), a.push(` widgetConfig.requestMiddleware = function(ctx) {`), a.push(` var customResult = (${r.requestMiddleware})(ctx);`), a.push(` var merged = customResult || ctx.payload;`), a.push(` return Object.assign({}, merged, { metadata: Object.assign({}, merged.metadata, domContextProvider()) });`), a.push(` };`)) : (a.push(` // Send DOM context with each request`), a.push(` widgetConfig.requestMiddleware = function(ctx) {`), a.push(` return Object.assign({}, ctx.payload, { metadata: domContextProvider() });`), a.push(` };`)), a.push(``), r?.postprocessMessage ? a.push(` widgetConfig.postprocessMessage = ${r.postprocessMessage};`) : (a.push(` // Markdown postprocessor`), a.push(` widgetConfig.postprocessMessage = function(ctx) {`), a.push(` return agentWidget.markdownPostprocessor(ctx.text);`), a.push(` };`)), a.push(``), (r?.onFeedback || r?.onCopy) && (a.push(` // Message action callbacks`), a.push(` widgetConfig.messageActions = widgetConfig.messageActions || {};`), r?.onFeedback && a.push(` widgetConfig.messageActions.onFeedback = ${r.onFeedback};`), r?.onCopy && a.push(` widgetConfig.messageActions.onCopy = ${r.onCopy};`), a.push(``)), a.push(` return widgetConfig;`, ` };`, ``, ` // Initialize widget`, ` var init = function() {`, ` var agentWidget = window.AgentWidget;`, ` if (!agentWidget) {`, ` console.error('AgentWidget not loaded');`, ` return;`, ` }`, ``, ` var widgetConfig = createWidgetConfig(agentWidget);`, ``, ` // Load saved state`, ` var savedState = localStorage.getItem(STORAGE_KEY);`, ` if (savedState) {`, ` try {`, ` var parsed = JSON.parse(savedState);`, ` widgetConfig.initialMessages = parsed.messages || [];`, ` } catch (e) {`, ` console.error('Failed to load saved state:', e);`, ` }`, ` }`, ``, ` // Initialize widget`, ` var handle = agentWidget.initAgentWidget({`, ` target: '${b(n)}',`, ` useShadowDom: false,`, ...n?.windowKey ? [` windowKey: '${n.windowKey}',`] : [], ` config: widgetConfig`, ` });`, ``, ` // Save state on message events`, ` window.addEventListener('persona:message', function() {`, ` var session = handle.getSession ? handle.getSession() : null;`, ` if (session) {`, ` localStorage.setItem(STORAGE_KEY, JSON.stringify({`, ` messages: session.messages,`, ` timestamp: new Date().toISOString()`, ` }));`, ` }`, ` });`, ``, ` // Clear state on clear chat`, ` window.addEventListener('persona:clear-chat', function() {`, ` localStorage.removeItem(STORAGE_KEY);`, ` localStorage.removeItem(PROCESSED_ACTIONS_KEY);`, ` });`, ` };`, ``, ` // Wait for framework hydration to complete (Next.js, Nuxt, etc.)`, ` // This prevents the framework from removing dynamically added CSS during reconciliation`, ` var waitForHydration = function(callback) {`, ` var executed = false;`, ` `, ` var execute = function() {`, ` if (executed) return;`, ` executed = true;`, ` callback();`, ` };`, ``, ` var afterDom = function() {`, ` // Strategy 1: Use requestIdleCallback if available (best for detecting idle after hydration)`, ` if (typeof requestIdleCallback !== 'undefined') {`, ` requestIdleCallback(function() {`, ` // Double requestAnimationFrame ensures at least one full paint cycle completed`, ` requestAnimationFrame(function() {`, ` requestAnimationFrame(execute);`, ` });`, ` }, { timeout: 3000 }); // Max wait 3 seconds, then proceed anyway`, ` } else {`, ` // Strategy 2: Fallback for Safari (no requestIdleCallback)`, ` // 300ms is typically enough for hydration on most pages`, ` setTimeout(execute, 300);`, ` }`, ` };`, ``, ` if (document.readyState === 'loading') {`, ` document.addEventListener('DOMContentLoaded', afterDom);`, ` } else {`, ` // DOM already ready, but still wait for potential hydration`, ` afterDom();`, ` }`, ` };`, ``, ` // Boot sequence: wait for hydration, then load CSS and JS, then initialize`, ` // This prevents Next.js/Nuxt/etc. from removing dynamically added CSS during reconciliation`, ` waitForHydration(function() {`, ` loadCSS();`, ` loadJS(function() {`, ` init();`, ` });`, ` });`, `})();`, `<\/script>`), a.join(`
616
+ `);
617
+ }
618
+ // src/lib/persona-snippets.ts
619
+ /**
620
+ * Map a CLI format to its real Persona generator format. Each maps to its true
621
+ * equivalent: `script-manual` → Persona's CDN global-script `script-manual` (not
622
+ * the heavy DOM-collecting `script-advanced`), `esm` → the npm/ESM init, `react`
623
+ * → the React component.
624
+ */
625
+ function mapCliFormatToPersona(cli) {
626
+ switch (cli) {
627
+ case "script-installer": return "script-installer";
628
+ case "script-manual": return "script-manual";
629
+ case "esm": return "esm";
630
+ case "react": return "react-component";
631
+ default: return cli;
632
+ }
633
+ }
634
+ function parsePersonaInitFormat(raw) {
635
+ const normalized = raw.trim().toLowerCase();
636
+ if (normalized === "script-installer" || normalized === "script-manual" || normalized === "esm" || normalized === "react") return normalized;
637
+ throw new Error(`Invalid --format "${raw}". Use: script-installer | script-manual | esm | react`);
638
+ }
639
+ /**
640
+ * Build the widget config the generator serializes. Kept minimal — the CLI only
641
+ * collects token + apiUrl; `parserType: 'json'` matches the platform default for
642
+ * agent/flow surfaces. The mount target is passed via generator options (not the
643
+ * widget config) because it is an init/installer argument, not a config field.
644
+ */
645
+ function buildWidgetConfig(input) {
646
+ return {
647
+ apiUrl: input.apiUrl,
648
+ clientToken: input.clientToken,
649
+ parserType: "json",
650
+ future: { v5Defaults: true }
651
+ };
652
+ }
653
+ function generatePersonaInitSnippet(input, personaFormat) {
654
+ const widgetConfig = buildWidgetConfig(input);
655
+ const target = input.targetSelector?.trim();
656
+ return x(widgetConfig, personaFormat, target ? { target } : void 0);
657
+ }
658
+ /**
659
+ * Return the version-pinned Persona CDN installer URL from the same generator
660
+ * used for regular snippets. Keeping demo HTML on this path avoids introducing
661
+ * a second manually-maintained Persona version string in the CLI.
662
+ */
663
+ function getPersonaInstallerScriptUrl() {
664
+ return x({
665
+ apiUrl: "https://api.runtype.com",
666
+ clientToken: "ct_test_demo_00000000000000000000000000000000",
667
+ parserType: "json"
668
+ }, "script-installer").match(/<script\s+src="([^"]+)"/)?.[1] ?? "https://cdn.jsdelivr.net/npm/@runtypelabs/persona/dist/install.global.js";
669
+ }
670
+ // src/lib/persona-init.ts
671
+ const PERSONA_INIT_AGENT_MODEL = "nemotron-3-ultra-550b-a55b";
672
+ const PERSONA_INIT_DEMO_SYSTEM_PROMPT = `
673
+ # Persona Playground Agent
674
+
675
+ ## Role
676
+
677
+ You are the assistant inside a local Persona Playground generated by the Runtype CLI.
678
+
679
+ ## Goal
680
+
681
+ Give the user one quick, visible "aha" moment: inspect the local page, request approval for a browser-side update, make the page change, and leave them with native next-step pills.
682
+
683
+ ## Available page tools
684
+
685
+ - \`get_playground_state\` — read the current accent, headline, note, status, checklist, URL, and allowed origins.
686
+ - \`update_playground\` — update the accent, headline, note, status, or checklist on the local HTML page.
687
+
688
+ ## Demo behavior
689
+
690
+ 1. Use \`get_playground_state\` before describing the current page or proposing a specific page change.
691
+ 2. Use \`update_playground\` when the user asks you to change the page.
692
+ 3. Explain, only when relevant, that mutating page updates require the user to approve Persona's tool-call bubble.
693
+ 4. After every successful \`update_playground\` call, follow \`responseGuidance\` exactly: do not send a visible response yet; first call \`suggest_replies\` with \`responseGuidance.suggestedReplies\`.
694
+ 5. When \`suggest_replies\` returns, send \`responseGuidance.assistantMessage\` exactly once as your only visible confirmation, then stop.
695
+ 6. Do not ask "what would you like to do next?" in prose; the Persona widget renders \`suggest_replies\` as tappable next-step pills.
696
+
697
+ ## Response style
698
+
699
+ - Be concise, direct, and practical.
700
+ - Prefer showing the page changing over explaining how it works.
701
+ - If the user asks how to customize the demo, point them to \`index.html\`, the generated README, and the Runtype dashboard link.
702
+ `.trim();
703
+ /**
704
+ * Resolve API key without browser login (CI / --json / non-TTY).
705
+ */
706
+ async function getPersonaInitApiKeyNonInteractive(apiUrl) {
707
+ const envApiKey = getRuntimeApiKey();
708
+ if (envApiKey) try {
709
+ if (await new ApiKeyManager().validateApiKey(envApiKey, apiUrl)) return envApiKey;
710
+ } catch (error) {
711
+ if (error instanceof ApiKeyValidationUnavailableError) console.error(error.message);
712
+ }
713
+ return new CredentialStore().getApiKey();
714
+ }
715
+ async function runPersonaInit(options) {
716
+ const client = createCliClient(options.apiKey, options.apiUrl);
717
+ const demoEnabled = !!options.demo;
718
+ const agent = await client.post("/agents", {
719
+ name: options.agentName,
720
+ description: options.agentDescription || "A Persona test agent",
721
+ config: {
722
+ model: PERSONA_INIT_AGENT_MODEL,
723
+ systemPrompt: demoEnabled ? PERSONA_INIT_DEMO_SYSTEM_PROMPT : "You are a helpful assistant."
724
+ }
725
+ });
726
+ let demoResources;
727
+ if (options.demo) {
728
+ const product = await client.post("/products", {
729
+ name: `${options.agentName} Persona Playground`,
730
+ description: "Local Persona WebMCP playground generated by the Runtype CLI.",
731
+ icon: "✨",
732
+ spec: {
733
+ productGoal: "Demonstrate a Persona widget operating a host page through WebMCP tools.",
734
+ targetAudience: "Developers testing Persona locally.",
735
+ productStage: "prototype"
736
+ }
737
+ });
738
+ const capability = await client.post(`/products/${product.id}/capabilities`, {
739
+ agentId: agent.id,
740
+ capabilityName: "Persona Playground Assistant",
741
+ capabilityDescription: "Agent that can read and update the local Persona Playground page through WebMCP tools.",
742
+ enabled: true
743
+ });
744
+ const allowlist = options.demo.origins.map((origin) => ({
745
+ origin,
746
+ tools: options.demo?.toolNames ?? []
747
+ }));
748
+ const surface = await client.post(`/products/${product.id}/surfaces`, {
749
+ name: "Local Persona Playground",
750
+ type: "chat",
751
+ status: "active",
752
+ behavior: {
753
+ type: "chat",
754
+ welcomeMessage: "Try asking me to inspect or update this local page. I can use WebMCP tools registered by the HTML file.",
755
+ webmcp: {
756
+ enabled: true,
757
+ allowlist,
758
+ requireConfirmFor: ["update_playground"]
759
+ }
760
+ }
761
+ });
762
+ const surfaceItem = await client.post(`/products/${product.id}/surfaces/${surface.id}/items`, {
763
+ capabilityId: capability.id,
764
+ exposedName: "Persona Playground Assistant",
765
+ exposedDescription: "Operate the generated local HTML playground.",
766
+ enabled: true,
767
+ isEntryPoint: true
768
+ });
769
+ demoResources = {
770
+ productId: product.id,
771
+ capabilityId: capability.id,
772
+ surfaceId: surface.id,
773
+ surfaceItemId: surfaceItem.id
774
+ };
775
+ }
776
+ const tokenBody = {
777
+ name: options.tokenName,
778
+ flowIds: [],
779
+ agentIds: [agent.id],
780
+ allowedOrigins: options.allowedOrigins,
781
+ environment: options.environment,
782
+ rateLimitPerMinute: 10,
783
+ rateLimitPerHour: 100,
784
+ maxMessagesPerSession: 100,
785
+ sessionIdleTimeoutMinutes: 30,
786
+ ...demoResources ? { productSurfaceId: demoResources.surfaceId } : {}
787
+ };
788
+ const tokenRes = await client.post("/client-tokens", tokenBody);
789
+ const personaFormat = mapCliFormatToPersona(options.format);
790
+ const code = generatePersonaInitSnippet({
791
+ clientToken: tokenRes.token,
792
+ apiUrl: options.apiUrl,
793
+ targetSelector: options.targetSelector
794
+ }, personaFormat);
795
+ const dashboardBase = getDashboardUrl();
796
+ const account = await getCurrentAccountId({
797
+ apiKey: options.apiKey,
798
+ apiUrl: options.apiUrl
799
+ });
800
+ const dashboardUrl = buildDashboardUrl({
801
+ baseUrl: dashboardBase,
802
+ path: `/agents/${agent.id}`,
803
+ account
804
+ });
805
+ return {
806
+ status: "created",
807
+ agent: {
808
+ id: agent.id,
809
+ name: agent.name
810
+ },
811
+ clientToken: {
812
+ id: tokenRes.clientToken.id,
813
+ name: tokenRes.clientToken.name,
814
+ token: tokenRes.token,
815
+ environment: options.environment
816
+ },
817
+ snippet: {
818
+ format: options.format,
819
+ code
820
+ },
821
+ dashboardUrl,
822
+ warnings: tokenRes.warnings,
823
+ ...demoResources ? { demo: demoResources } : {}
824
+ };
825
+ }
826
+ // src/lib/persona-demo.ts
827
+ const PERSONA_DEMO_DEFAULT_DIR = "persona-demo";
828
+ const PERSONA_DEMO_DEFAULT_PORT = 43110;
829
+ const PERSONA_DEMO_TOOL_NAMES = ["get_playground_state", "update_playground"];
830
+ function unique(values) {
831
+ return [...new Set(values.filter(Boolean))];
832
+ }
833
+ function buildPersonaDemoOrigins(port) {
834
+ return [`http://localhost:${port}`, `http://127.0.0.1:${port}`];
835
+ }
836
+ /**
837
+ * In demo mode, prefer exact local origins over `*` so the generated token is
838
+ * usable immediately without leaving a broader browser-origin scope than the
839
+ * demo needs. Explicit non-wildcard origins are preserved for callers that pass
840
+ * their own site origins.
841
+ */
842
+ function mergePersonaDemoAllowedOrigins(requestedOrigins, demoOrigins) {
843
+ const explicitOrigins = (requestedOrigins ?? []).filter((origin) => origin !== "*");
844
+ return unique([...demoOrigins, ...explicitOrigins]);
845
+ }
846
+ function resolvePersonaDemoDirectory(input) {
847
+ return path.resolve(process.cwd(), input?.trim() || "persona-demo");
848
+ }
849
+ function personaDemoIndexPath(directory) {
850
+ return path.join(directory, "index.html");
851
+ }
852
+ function personaDemoReadmePath(directory) {
853
+ return path.join(directory, "README.md");
854
+ }
855
+ function ensurePersonaDemoCanWrite(directory, force) {
856
+ const existingFiles = [personaDemoIndexPath(directory), personaDemoReadmePath(directory)].filter((filePath) => existsSync(filePath));
857
+ if (existingFiles.length > 0 && !force) throw new Error(`Demo file already exists at ${existingFiles.join(", ")}. Pass --force to overwrite demo files or choose --demo-dir.`);
858
+ }
859
+ function writePersonaDemoPage(options) {
860
+ ensurePersonaDemoCanWrite(options.directory, options.force);
861
+ mkdirSync(options.directory, { recursive: true });
862
+ const filePath = personaDemoIndexPath(options.directory);
863
+ const readmePath = personaDemoReadmePath(options.directory);
864
+ writeFileSync(filePath, `${options.html.trim()}\n`, "utf8");
865
+ writeFileSync(readmePath, `${options.readme.trim()}\n`, "utf8");
866
+ return {
867
+ directory: options.directory,
868
+ filePath,
869
+ readmePath
870
+ };
871
+ }
872
+ async function findAvailablePersonaDemoPort(preferredPort = PERSONA_DEMO_DEFAULT_PORT) {
873
+ for (let offset = 0; offset < 50; offset += 1) {
874
+ const port = preferredPort + offset;
875
+ if (await canListenOnPort(port)) return port;
876
+ }
877
+ throw new Error(`Could not find an available local port starting at ${preferredPort}`);
878
+ }
879
+ function canListenOnPort(port) {
880
+ return new Promise((resolve) => {
881
+ const server = createServer$1();
882
+ server.once("error", () => {
883
+ resolve(false);
884
+ });
885
+ server.once("listening", () => {
886
+ server.close(() => resolve(true));
887
+ });
888
+ server.listen(port, "127.0.0.1");
889
+ });
890
+ }
891
+ async function startPersonaDemoServer(options) {
892
+ const filePath = personaDemoIndexPath(options.directory);
893
+ const server = createServer((req, res) => {
894
+ const url = req.url?.split("?")[0] ?? "/";
895
+ if (url !== "/" && url !== "/index.html") {
896
+ res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
897
+ res.end("Not found");
898
+ return;
899
+ }
900
+ res.writeHead(200, {
901
+ "content-type": "text/html; charset=utf-8",
902
+ "cache-control": "no-store"
903
+ });
904
+ res.end(readIndexHtml(filePath));
905
+ });
906
+ await listen(server, options.port);
907
+ return {
908
+ url: `http://127.0.0.1:${options.port}/`,
909
+ port: options.port,
910
+ close: () => new Promise((resolve, reject) => {
911
+ server.close((error) => {
912
+ if (error) reject(error);
913
+ else resolve();
914
+ });
915
+ })
916
+ };
917
+ }
918
+ function readIndexHtml(filePath) {
919
+ return readFileSync(filePath, "utf8");
920
+ }
921
+ function listen(server, port) {
922
+ return new Promise((resolve, reject) => {
923
+ server.once("error", reject);
924
+ server.listen(port, "127.0.0.1", () => {
925
+ server.off("error", reject);
926
+ resolve();
927
+ });
928
+ });
929
+ }
930
+ function escapeHtml(value) {
931
+ return value.replace(/[&<>"']/g, (char) => {
932
+ switch (char) {
933
+ case "&": return "&amp;";
934
+ case "<": return "&lt;";
935
+ case ">": return "&gt;";
936
+ case "\"": return "&quot;";
937
+ case "'": return "&#39;";
938
+ default: return char;
939
+ }
940
+ });
941
+ }
942
+ function inlineJson(value) {
943
+ return JSON.stringify(value, null, 2).replace(/<\//g, "<\\/");
944
+ }
945
+ function generatePersonaDemoHtml(input) {
946
+ const installerScriptUrl = input.installerScriptUrl ?? getPersonaInstallerScriptUrl();
947
+ const pageData = {
948
+ apiUrl: input.apiUrl,
949
+ clientToken: input.clientToken,
950
+ clientTokenId: input.clientTokenId,
951
+ agentId: input.agentId,
952
+ agentName: input.agentName,
953
+ dashboardUrl: input.dashboardUrl,
954
+ environment: input.environment,
955
+ localUrl: input.localUrl,
956
+ allowedOrigins: input.allowedOrigins
957
+ };
958
+ return `<!doctype html>
959
+ <html lang="en">
960
+ <head>
961
+ <meta charset="utf-8" />
962
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
963
+ <title>Persona Local Playground</title>
964
+ <style>
965
+ :root {
966
+ --paper: #fcfcfa;
967
+ --paper-deep: #f4f0e8;
968
+ --ink: #24211f;
969
+ --muted: #67615b;
970
+ --line: #d9d2c7;
971
+ --line-strong: #201a16;
972
+ --accent: #4c00ff;
973
+ --accent-soft: color-mix(in srgb, var(--accent) 8%, var(--paper));
974
+ --live: #b6ff00;
975
+ --success: #178c4b;
976
+ --radius: 6px;
977
+ }
978
+ * { box-sizing: border-box; }
979
+ body {
980
+ margin: 0;
981
+ min-height: 100vh;
982
+ color: var(--ink);
983
+ font-family: Asap, Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
984
+ background:
985
+ linear-gradient(90deg, rgba(36, 33, 31, .045) 1px, transparent 1px) 0 0 / 40px 40px,
986
+ linear-gradient(rgba(36, 33, 31, .04) 1px, transparent 1px) 0 0 / 40px 40px,
987
+ radial-gradient(circle at 18% 0%, var(--accent-soft), transparent 30rem),
988
+ var(--paper);
989
+ }
990
+ button { font: inherit; }
991
+ .shell {
992
+ width: min(920px, calc(100% - 32px));
993
+ margin: 0 auto;
994
+ padding: clamp(28px, 6vw, 64px) 0 112px;
995
+ }
996
+ .panel {
997
+ border: 1px solid var(--line-strong);
998
+ border-radius: var(--radius);
999
+ background: color-mix(in srgb, var(--paper) 91%, white);
1000
+ box-shadow: 7px 7px 0 rgba(36, 33, 31, .12);
1001
+ padding: clamp(18px, 3vw, 28px);
1002
+ }
1003
+ .demo-grid {
1004
+ display: grid;
1005
+ grid-template-columns: minmax(0, 1fr) minmax(260px, 340px);
1006
+ gap: 18px;
1007
+ align-items: stretch;
1008
+ }
1009
+ .preview {
1010
+ min-height: 280px;
1011
+ display: grid;
1012
+ align-content: space-between;
1013
+ gap: 24px;
1014
+ border: 1px solid var(--line-strong);
1015
+ border-radius: var(--radius);
1016
+ padding: clamp(20px, 3vw, 28px);
1017
+ color: white;
1018
+ background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 86%, black), var(--accent));
1019
+ }
1020
+ .preview h2 {
1021
+ margin: 0;
1022
+ max-width: 560px;
1023
+ font-family: Newsreader, Georgia, "Times New Roman", serif;
1024
+ font-size: clamp(30px, 5vw, 54px);
1025
+ line-height: .96;
1026
+ letter-spacing: -0.04em;
1027
+ }
1028
+ .preview p { max-width: 560px; margin: 12px 0 0; opacity: .9; line-height: 1.6; }
1029
+ .status-pill {
1030
+ justify-self: start;
1031
+ border: 1px solid rgba(255,255,255,.75);
1032
+ border-radius: 999px;
1033
+ padding: 7px 10px;
1034
+ font-size: 12px;
1035
+ font-weight: 800;
1036
+ }
1037
+ .section-title {
1038
+ margin: 0 0 12px;
1039
+ color: var(--muted);
1040
+ font-size: 12px;
1041
+ font-weight: 800;
1042
+ letter-spacing: .12em;
1043
+ text-transform: uppercase;
1044
+ }
1045
+ .tasks { display: grid; gap: 9px; }
1046
+ .task {
1047
+ display: flex;
1048
+ align-items: center;
1049
+ gap: 10px;
1050
+ padding: 11px 12px;
1051
+ border: 1px solid var(--line);
1052
+ border-radius: var(--radius);
1053
+ background: var(--paper);
1054
+ }
1055
+ .task-dot {
1056
+ display: grid;
1057
+ place-items: center;
1058
+ width: 23px;
1059
+ height: 23px;
1060
+ border-radius: 999px;
1061
+ color: var(--paper);
1062
+ background: #9c958e;
1063
+ font-size: 13px;
1064
+ font-weight: 900;
1065
+ }
1066
+ .task[data-done="true"] .task-dot { background: var(--success); }
1067
+ .task[data-done="true"] .task-label { color: var(--muted); text-decoration: line-through; }
1068
+ .activity {
1069
+ margin-top: 18px;
1070
+ padding-top: 16px;
1071
+ border-top: 1px solid var(--line);
1072
+ }
1073
+ .log { display: grid; gap: 8px; max-height: 120px; overflow: auto; }
1074
+ .log-row {
1075
+ color: #4b463f;
1076
+ font-size: 12px;
1077
+ line-height: 1.45;
1078
+ }
1079
+ .log-row strong { color: var(--ink); }
1080
+ .action-bar {
1081
+ display: flex;
1082
+ flex-wrap: wrap;
1083
+ gap: 10px;
1084
+ align-items: center;
1085
+ margin-top: 18px;
1086
+ padding-top: 18px;
1087
+ border-top: 1px solid var(--line);
1088
+ }
1089
+ .cta {
1090
+ display: inline-flex;
1091
+ align-items: center;
1092
+ justify-content: center;
1093
+ gap: 8px;
1094
+ min-height: 42px;
1095
+ border: 1px solid var(--line-strong);
1096
+ border-radius: var(--radius);
1097
+ padding: 10px 13px;
1098
+ color: var(--ink);
1099
+ font-size: 14px;
1100
+ font-weight: 800;
1101
+ text-decoration: none;
1102
+ cursor: pointer;
1103
+ }
1104
+ .cta-primary { background: var(--accent); color: white; }
1105
+ .cta-secondary { background: var(--paper); }
1106
+ .cta-arrow { font-size: 18px; line-height: 1; }
1107
+ details {
1108
+ margin-top: 18px;
1109
+ color: var(--muted);
1110
+ font-size: 13px;
1111
+ }
1112
+ summary {
1113
+ cursor: pointer;
1114
+ width: fit-content;
1115
+ color: var(--ink);
1116
+ font-weight: 700;
1117
+ }
1118
+ .details-grid {
1119
+ display: grid;
1120
+ grid-template-columns: 90px minmax(0, 1fr);
1121
+ gap: 8px 14px;
1122
+ margin-top: 12px;
1123
+ }
1124
+ code {
1125
+ color: #2f2a26;
1126
+ word-break: break-word;
1127
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
1128
+ font-size: 12px;
1129
+ }
1130
+ @media (max-width: 820px) {
1131
+ .demo-grid { grid-template-columns: 1fr; }
1132
+ .shell { width: min(100% - 24px, 720px); }
1133
+ }
1134
+ @media (prefers-reduced-motion: reduce) {
1135
+ *, *::before, *::after { transition: none !important; }
1136
+ }
1137
+ </style>
1138
+ </head>
1139
+ <body>
1140
+ <!-- Edit this file directly, refresh the browser, and keep customizing your Persona starter. -->
1141
+ <main class="shell">
1142
+ <section class="panel" aria-label="Persona demo workspace">
1143
+ <div class="demo-grid">
1144
+ <div class="preview" id="accent-preview">
1145
+ <div>
1146
+ <h2 id="playground-headline">Local tools. Visible results.</h2>
1147
+ <p id="playground-note">Persona can read this page, request approval, and update the UI from the browser.</p>
1148
+ </div>
1149
+ <span class="status-pill" id="status-pill">Waiting for your first task</span>
1150
+ </div>
1151
+
1152
+ <div>
1153
+ <p class="section-title">Checklist</p>
1154
+ <div class="tasks" id="task-list"></div>
1155
+
1156
+ <div class="activity">
1157
+ <p class="section-title">Activity</p>
1158
+ <div class="log" id="tool-log"></div>
1159
+ </div>
1160
+ </div>
1161
+ </div>
1162
+
1163
+ <div class="action-bar" aria-label="Next actions">
1164
+ <button class="cta cta-primary" id="use-persona-widget" type="button">
1165
+ <span id="use-persona-label">Use Persona widget</span> <span class="cta-arrow" aria-hidden="true">↘</span>
1166
+ </button>
1167
+ <a class="cta cta-secondary" href="${escapeHtml(input.dashboardUrl)}" target="_blank" rel="noreferrer">Customize your agent</a>
1168
+ </div>
1169
+
1170
+ <details>
1171
+ <summary>Technical details</summary>
1172
+ <div class="details-grid">
1173
+ <span>Agent</span><code>${escapeHtml(input.agentId)}</code>
1174
+ <span>Token</span><code>${escapeHtml(input.clientTokenId)} (${escapeHtml(input.environment)})</code>
1175
+ <span>Edit</span><code>index.html</code>
1176
+ <span>Origin</span><code>${escapeHtml(input.localUrl)}</code>
1177
+ <span>Tools</span><code>${PERSONA_DEMO_TOOL_NAMES.join(", ")}</code>
1178
+ <span>Dashboard</span><code>${escapeHtml(input.dashboardUrl)}</code>
1179
+ </div>
1180
+ </details>
1181
+ </section>
1182
+ </main>
1183
+
1184
+ <div id="persona-root"></div>
1185
+
1186
+ <!-- 1. Runtime values injected by the CLI. -->
1187
+ <script>
1188
+ window.__RUNTYPE_PERSONA_DEMO__ = ${inlineJson(pageData)};
1189
+ <\/script>
1190
+ <!-- 2. Minimal WebMCP host bridge for this dependency-free page. -->
1191
+ <script>
1192
+ (function installMinimalModelContext() {
1193
+ if (document.modelContext && typeof document.modelContext.getTools === 'function') {
1194
+ return;
1195
+ }
1196
+ const tools = new Map();
1197
+ document.modelContext = {
1198
+ registerTool(tool) {
1199
+ if (!tool || !tool.name || typeof tool.execute !== 'function') {
1200
+ throw new Error('registerTool requires a name and execute function');
1201
+ }
1202
+ tools.set(tool.name, tool);
1203
+ },
1204
+ async getTools() {
1205
+ return Array.from(tools.values()).map((tool) => ({
1206
+ name: tool.name,
1207
+ title: tool.title || '',
1208
+ description: tool.description || '',
1209
+ inputSchema: JSON.stringify(tool.inputSchema || { type: 'object', properties: {} }),
1210
+ }));
1211
+ },
1212
+ async executeTool(info, input) {
1213
+ const name = typeof info === 'string' ? info : info && info.name;
1214
+ const tool = tools.get(name);
1215
+ if (!tool) throw new Error('Tool not registered: ' + name);
1216
+ const args = typeof input === 'string' ? JSON.parse(input) : (input ?? {});
1217
+ const result = await tool.execute(args, {
1218
+ requestUserInteraction: async (callback) => callback(),
1219
+ });
1220
+ return result === undefined ? null : JSON.stringify(result);
1221
+ },
1222
+ };
1223
+ })();
1224
+
1225
+ const demo = window.__RUNTYPE_PERSONA_DEMO__;
1226
+ /* 3. Page state the tools can read and update. */
1227
+ const accentMap = {
1228
+ violet: '#4c00ff',
1229
+ green: '#178c4b',
1230
+ cyan: '#008ca8',
1231
+ orange: '#d95f02',
1232
+ };
1233
+ const state = {
1234
+ accent: 'violet',
1235
+ headline: 'Local tools. Visible results.',
1236
+ note: 'Persona can read this page, request approval, and update the UI from the browser.',
1237
+ status: 'Waiting for your first task',
1238
+ tasks: [
1239
+ { id: 'widget', label: 'Widget installed', done: true },
1240
+ { id: 'webmcp', label: 'WebMCP tools connected', done: true },
1241
+ { id: 'setup', label: 'Setup complete', done: false },
1242
+ ],
1243
+ };
1244
+
1245
+ function escapeText(value) {
1246
+ return String(value).replace(/[&<>]/g, (char) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' })[char]);
1247
+ }
1248
+
1249
+ function logTool(kind, message) {
1250
+ const log = document.getElementById('tool-log');
1251
+ const row = document.createElement('div');
1252
+ row.className = 'log-row';
1253
+ row.innerHTML = '<strong>' + escapeText(kind) + '</strong> · ' + escapeText(message);
1254
+ log.prepend(row);
1255
+ }
1256
+
1257
+ function snapshot() {
1258
+ return {
1259
+ accent: state.accent,
1260
+ accentHex: accentMap[state.accent],
1261
+ headline: state.headline,
1262
+ note: state.note,
1263
+ status: state.status,
1264
+ tasks: state.tasks,
1265
+ pageUrl: location.href,
1266
+ allowedOrigins: demo.allowedOrigins,
1267
+ };
1268
+ }
1269
+
1270
+ function render() {
1271
+ document.documentElement.style.setProperty('--accent', accentMap[state.accent]);
1272
+ document.getElementById('playground-headline').textContent = state.headline;
1273
+ document.getElementById('playground-note').textContent = state.note;
1274
+ document.getElementById('status-pill').textContent = state.status;
1275
+ const setupDone = state.tasks.some((task) => task.id === 'setup' && task.done);
1276
+ const ctaLabel = document.getElementById('use-persona-label');
1277
+ if (ctaLabel) ctaLabel.textContent = setupDone ? 'Try another page change' : 'Use Persona widget';
1278
+ const taskList = document.getElementById('task-list');
1279
+ taskList.innerHTML = '';
1280
+ for (const task of state.tasks) {
1281
+ const row = document.createElement('div');
1282
+ row.className = 'task';
1283
+ row.dataset.done = String(task.done);
1284
+ row.innerHTML = '<span class="task-dot">✓</span><span class="task-label"></span>';
1285
+ row.querySelector('.task-label').textContent = task.label;
1286
+ taskList.appendChild(row);
1287
+ }
1288
+ }
1289
+
1290
+ function taskLabel(id) {
1291
+ const task = state.tasks.find((item) => item.id === id);
1292
+ return task ? task.label : id;
1293
+ }
1294
+
1295
+ function buildAssistantMessage(args, changed) {
1296
+ if (!changed.length) return 'Done — no visible page changes were needed.';
1297
+
1298
+ const updates = [];
1299
+ if (args.accent && accentMap[args.accent]) updates.push('the page is now ' + args.accent);
1300
+ if (typeof args.headline === 'string' && args.headline.trim()) updates.push('the headline changed');
1301
+ if (typeof args.note === 'string' && args.note.trim()) updates.push('the note changed');
1302
+ if (typeof args.status === 'string' && args.status.trim()) updates.push('the status changed');
1303
+ if (Array.isArray(args.completedTaskIds) && args.completedTaskIds.length) {
1304
+ const labels = args.completedTaskIds.map((id) => taskLabel(id));
1305
+ updates.push(
1306
+ labels.length === 1 ? labels[0] + ' is checked' : labels.join(', ') + ' are checked'
1307
+ );
1308
+ }
1309
+
1310
+ return 'Done — ' + updates.join(' and ') + '.';
1311
+ }
1312
+
1313
+ function buildSuggestedReplies() {
1314
+ return [
1315
+ 'Change the headline',
1316
+ 'Add another page tool',
1317
+ 'Show the embed code',
1318
+ 'Customize the agent',
1319
+ ];
1320
+ }
1321
+
1322
+ /* 4. WebMCP tools exposed to the Persona widget. */
1323
+ document.modelContext.registerTool({
1324
+ name: 'get_playground_state',
1325
+ title: 'Read playground state',
1326
+ description: 'Read the current local Persona playground state: accent, headline, note, status, checklist, URL, and allowed origins. Use this before proposing page changes.',
1327
+ inputSchema: { type: 'object', properties: {} },
1328
+ annotations: { readOnlyHint: true },
1329
+ async execute() {
1330
+ logTool('Read page state', 'Persona inspected the local HTML page');
1331
+ return snapshot();
1332
+ },
1333
+ });
1334
+
1335
+ document.modelContext.registerTool({
1336
+ name: 'update_playground',
1337
+ title: 'Update playground UI',
1338
+ description: 'Update this local HTML playground. Can set accent to violet, green, cyan, or orange; rewrite the headline or note; set a short status; and mark checklist tasks complete by id.',
1339
+ inputSchema: {
1340
+ type: 'object',
1341
+ properties: {
1342
+ accent: { type: 'string', enum: Object.keys(accentMap), description: 'Accent color to apply.' },
1343
+ headline: { type: 'string', description: 'New hero headline.' },
1344
+ note: { type: 'string', description: 'New explanatory note under the headline.' },
1345
+ status: { type: 'string', description: 'Short status shown inside the demo card.' },
1346
+ completedTaskIds: {
1347
+ type: 'array',
1348
+ items: { type: 'string', enum: state.tasks.map((task) => task.id) },
1349
+ description: 'Checklist task ids to mark complete.',
1350
+ },
1351
+ },
1352
+ },
1353
+ async execute(args) {
1354
+ const changed = [];
1355
+ if (args.accent && accentMap[args.accent]) {
1356
+ state.accent = args.accent;
1357
+ changed.push('accent');
1358
+ }
1359
+ if (typeof args.headline === 'string' && args.headline.trim()) {
1360
+ state.headline = args.headline.trim();
1361
+ changed.push('headline');
1362
+ }
1363
+ if (typeof args.note === 'string' && args.note.trim()) {
1364
+ state.note = args.note.trim();
1365
+ changed.push('note');
1366
+ }
1367
+ if (typeof args.status === 'string' && args.status.trim()) {
1368
+ state.status = args.status.trim();
1369
+ changed.push('status');
1370
+ }
1371
+ if (Array.isArray(args.completedTaskIds) && args.completedTaskIds.length) {
1372
+ const completed = new Set(args.completedTaskIds);
1373
+ state.tasks = state.tasks.map((task) => completed.has(task.id) ? { ...task, done: true } : task);
1374
+ changed.push('checklist');
1375
+ }
1376
+ render();
1377
+ logTool('Updated page', changed.length ? changed.join(' + ') : 'No visible changes requested');
1378
+ return {
1379
+ ok: true,
1380
+ changed,
1381
+ responseGuidance: {
1382
+ assistantMessage: buildAssistantMessage(args, changed),
1383
+ suggestedReplies: buildSuggestedReplies(),
1384
+ instructions: 'Do not send a visible assistant message yet. First call suggest_replies with suggestedReplies. After suggest_replies returns, say assistantMessage exactly once, then stop.',
1385
+ },
1386
+ state: snapshot(),
1387
+ };
1388
+ },
1389
+ });
1390
+
1391
+ /* 5. Persona widget config. The installer below reads window.siteAgentConfig. */
1392
+ window.siteAgentConfig = {
1393
+ target: '#persona-root',
1394
+ windowKey: 'personaPlayground',
1395
+ config: {
1396
+ apiUrl: demo.apiUrl,
1397
+ clientToken: demo.clientToken,
1398
+ parserType: 'json',
1399
+ future: { v5Defaults: true },
1400
+ launcher: {
1401
+ enabled: true,
1402
+ autoExpand: true,
1403
+ title: 'Persona Playground',
1404
+ subtitle: 'Local WebMCP demo',
1405
+ width: '430px',
1406
+ },
1407
+ copy: {
1408
+ welcomeTitle: 'Persona Playground',
1409
+ welcomeSubtitle: 'Use the page tools, then pick a next step.',
1410
+ inputPlaceholder: 'Ask Persona to update this page…',
1411
+ },
1412
+ suggestionChips: [
1413
+ 'Turn this page green and mark Setup complete.',
1414
+ 'What page tools are available?',
1415
+ 'Show the embed code.',
1416
+ ],
1417
+ features: {
1418
+ showToolCalls: true,
1419
+ showEventStreamToggle: true,
1420
+ suggestReplies: {
1421
+ enabled: true,
1422
+ expose: true,
1423
+ },
1424
+ },
1425
+ webmcp: {
1426
+ enabled: true,
1427
+ autoApprove(info) {
1428
+ const ok = info && info.toolName === 'get_playground_state';
1429
+ logTool('Approval gate', (info ? info.toolName : 'unknown') + (ok ? ' auto-approved' : ' awaiting approval'));
1430
+ return ok;
1431
+ },
1432
+ },
1433
+ },
1434
+ onChatReady(handle) {
1435
+ window.personaPlayground = handle;
1436
+ logTool('Widget ready', 'Persona handle is available at window.personaPlayground');
1437
+ setTimeout(() => {
1438
+ if (handle && typeof handle.open === 'function') handle.open();
1439
+ }, 0);
1440
+ },
1441
+ onError(info) {
1442
+ logTool('Persona error', info && info.phase ? info.phase : 'unknown phase');
1443
+ },
1444
+ };
1445
+
1446
+ document.getElementById('use-persona-widget').addEventListener('click', () => {
1447
+ const handle = window.personaPlayground;
1448
+ if (handle && typeof handle.open === 'function') handle.open();
1449
+ if (handle && typeof handle.setMessage === 'function') {
1450
+ handle.setMessage('Turn this page green and mark Setup complete.');
1451
+ }
1452
+ logTool('CTA', 'Opened Persona widget');
1453
+ });
1454
+
1455
+ render();
1456
+ logTool('Tools connected', 'Registered get_playground_state and update_playground');
1457
+ <\/script>
1458
+ <!-- 6. Version-pinned Persona installer from @runtypelabs/persona. -->
1459
+ <script src="${escapeHtml(installerScriptUrl)}"><\/script>
1460
+ </body>
1461
+ </html>`;
1462
+ }
1463
+ function generatePersonaDemoReadme(input) {
1464
+ const port = (() => {
1465
+ try {
1466
+ return new URL(input.localUrl).port || String(43110);
1467
+ } catch {
1468
+ return String(PERSONA_DEMO_DEFAULT_PORT);
1469
+ }
1470
+ })();
1471
+ return `# Persona Playground
1472
+
1473
+ This folder was generated by \`runtype persona init\`.
1474
+
1475
+ ## Next steps
1476
+
1477
+ 1. Use the widget: ${input.localUrl}
1478
+ 2. Customize your agent: ${input.dashboardUrl}
1479
+ 3. Edit the page: ./index.html
1480
+
1481
+ ## Run locally
1482
+
1483
+ \`\`\`bash
1484
+ python3 -m http.server ${port} --bind 127.0.0.1
1485
+ \`\`\`
1486
+
1487
+ Then open ${input.localUrl}.
1488
+
1489
+ ## What this starter demonstrates
1490
+
1491
+ - \`window.siteAgentConfig\` installs Persona with your client token.
1492
+ - \`document.modelContext\` exposes two local WebMCP tools:
1493
+ - \`get_playground_state\` reads the page state.
1494
+ - \`update_playground\` changes the accent, copy, status, and checklist after approval.
1495
+ - Persona native suggested replies render next-step pills after a successful tool call.
1496
+
1497
+ ## Add another page tool
1498
+
1499
+ 1. Add a new \`document.modelContext.registerTool({ ... })\` block in \`index.html\`.
1500
+ 2. Add the tool name to the WebMCP allowlist for this surface in the Runtype dashboard.
1501
+ 3. Refresh the page and ask Persona to use the new tool.
1502
+
1503
+ ## Demo resources
1504
+
1505
+ - Agent: ${input.agentId}
1506
+ - Client token: ${input.clientTokenId} (${input.environment})
1507
+ - Allowed origins: ${input.allowedOrigins.join(", ")}
1508
+ `;
1509
+ }
1510
+ // src/commands/persona.ts
1511
+ const FORMAT_CYCLE = [
1512
+ "script-installer",
1513
+ "react",
1514
+ "script-manual",
1515
+ "esm"
1516
+ ];
1517
+ function defaultTokenName(agentName) {
1518
+ return `${agentName} Widget`;
1519
+ }
1520
+ function parsePort(raw, fallback) {
1521
+ if (!raw) return fallback;
1522
+ const port = Number(raw);
1523
+ if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(`Invalid port "${raw}". Use a number between 1 and 65535.`);
1524
+ return port;
1525
+ }
1526
+ async function promptLine(rl, question, defaultValue) {
1527
+ const hint = defaultValue ? chalk.dim(` (${defaultValue})`) : "";
1528
+ const trimmed = (await new Promise((resolve) => {
1529
+ rl.question(`${question}${hint}: `, resolve);
1530
+ })).trim();
1531
+ if (!trimmed && defaultValue !== void 0) return defaultValue;
1532
+ return trimmed;
1533
+ }
1534
+ async function promptConfirm(rl, message, defaultYes = false) {
1535
+ const hint = defaultYes ? chalk.dim(" (Y/n)") : chalk.dim(" (y/N)");
1536
+ const t = (await new Promise((resolve) => {
1537
+ rl.question(`${message}${hint}: `, resolve);
1538
+ })).trim().toLowerCase();
1539
+ if (t === "") return defaultYes;
1540
+ return t === "y" || t === "yes";
1541
+ }
1542
+ function copyToClipboard(text) {
1543
+ if (process.platform === "darwin") {
1544
+ execFileSync("pbcopy", [], { input: text });
1545
+ return;
1546
+ }
1547
+ if (process.platform === "win32") {
1548
+ execFileSync("clip", [], {
1549
+ shell: true,
1550
+ input: text
1551
+ });
1552
+ return;
1553
+ }
1554
+ try {
1555
+ execFileSync("xclip", ["-selection", "clipboard"], { input: text });
1556
+ } catch {
1557
+ execFileSync("wl-copy", [], { input: text });
1558
+ }
1559
+ }
1560
+ function isProbablyLocalDev(origins) {
1561
+ return origins.some((o) => o === "http://localhost:*" || o.startsWith("http://localhost:") || o.startsWith("http://127.0.0.1:"));
1562
+ }
1563
+ function snippetWithFormat(format, token, apiUrl, targetSelector) {
1564
+ const personaFmt = mapCliFormatToPersona(format);
1565
+ return {
1566
+ format,
1567
+ code: generatePersonaInitSnippet({
1568
+ clientToken: token,
1569
+ apiUrl,
1570
+ targetSelector
1571
+ }, personaFmt)
1572
+ };
1573
+ }
1574
+ function printDemoNextSteps(demo, dashboardUrl) {
1575
+ console.log(chalk.cyan("\nNext steps:"));
1576
+ console.log(` 1. Use the widget: ${chalk.green(demo.url)}`);
1577
+ console.log(` 2. Customize your agent: ${dashboardUrl}`);
1578
+ console.log(` 3. Edit the page: ${demo.filePath}`);
1579
+ if (demo.readmePath) console.log(chalk.dim(` Guide: ${demo.readmePath}`));
1580
+ }
1581
+ async function runSuccessKeyLoop(initial, apiUrl, targetSelector, demo) {
1582
+ if (!process.stdin.isTTY) return;
1583
+ let currentFormat = initial.snippet.format;
1584
+ let currentCode = initial.snippet.code;
1585
+ const token = initial.clientToken.token;
1586
+ console.log(chalk.cyan("\nPersona setup complete\n"));
1587
+ console.log(`${chalk.bold("Agent ID:")} ${initial.agent.id}`);
1588
+ console.log(`${chalk.bold("Client Token ID:")} ${initial.clientToken.id}`);
1589
+ console.log(`${chalk.bold("Client Token:")} ${chalk.yellow(token)}`);
1590
+ console.log(`${chalk.bold("Snippet format:")} ${currentFormat}`);
1591
+ console.log(chalk.dim(`\nDashboard: ${initial.dashboardUrl}`));
1592
+ if (demo) {
1593
+ console.log(`${chalk.bold("Local demo:")} ${chalk.green(demo.url)}`);
1594
+ console.log(chalk.dim(`HTML: ${demo.filePath}`));
1595
+ if (demo.readmePath) console.log(chalk.dim(`Guide: ${demo.readmePath}`));
1596
+ }
1597
+ if (initial.warnings?.length) for (const w of initial.warnings) console.log(chalk.yellow(`⚠ ${w.message}`));
1598
+ if (demo) printDemoNextSteps(demo, initial.dashboardUrl);
1599
+ console.log(chalk.cyan("\nActions:"));
1600
+ console.log(` ${chalk.green("c")} Copy snippet to clipboard`);
1601
+ console.log(` ${chalk.green("p")} Print snippet to the terminal`);
1602
+ console.log(` ${chalk.green("f")} Switch snippet format`);
1603
+ console.log(` ${chalk.green("o")} Open dashboard in browser`);
1604
+ if (demo) console.log(` ${chalk.green("d")} Open local demo in browser`);
1605
+ console.log(` ${chalk.green("q")} Quit`);
1606
+ console.log(chalk.dim("\nType a letter and press Enter. Press Ctrl+C to quit."));
1607
+ const actionKeys = demo ? "c/p/f/o/d/q" : "c/p/f/o/q";
1608
+ const rl = readline.createInterface({
1609
+ input: process.stdin,
1610
+ output: process.stdout,
1611
+ terminal: true
1612
+ });
1613
+ await new Promise((resolve) => {
1614
+ let finished = false;
1615
+ const finish = () => {
1616
+ if (finished) return;
1617
+ finished = true;
1618
+ rl.close();
1619
+ resolve();
1620
+ };
1621
+ const runAction = async (name) => {
1622
+ if (name === "q") {
1623
+ finish();
1624
+ return;
1625
+ }
1626
+ if (name === "c") {
1627
+ try {
1628
+ copyToClipboard(currentCode);
1629
+ console.log(chalk.green("Snippet copied to clipboard."));
1630
+ } catch {
1631
+ console.log(chalk.red("Could not copy (install xclip/wl-copy on Linux, or use p)."));
1632
+ }
1633
+ return;
1634
+ }
1635
+ if (name === "p") {
1636
+ console.log(chalk.cyan("\n--- snippet ---\n"));
1637
+ console.log(currentCode);
1638
+ console.log(chalk.cyan("\n--- end snippet ---\n"));
1639
+ return;
1640
+ }
1641
+ if (name === "f") {
1642
+ const idx = FORMAT_CYCLE.indexOf(currentFormat);
1643
+ const nextSnippet = snippetWithFormat(FORMAT_CYCLE[(idx + 1) % FORMAT_CYCLE.length] ?? "script-installer", token, apiUrl, targetSelector);
1644
+ currentFormat = nextSnippet.format;
1645
+ currentCode = nextSnippet.code;
1646
+ console.log(chalk.dim(`Format: ${currentFormat}`));
1647
+ return;
1648
+ }
1649
+ if (name === "o") {
1650
+ try {
1651
+ await open(initial.dashboardUrl);
1652
+ } catch {
1653
+ console.log(chalk.red("Could not open browser."));
1654
+ }
1655
+ return;
1656
+ }
1657
+ if (name === "d" && demo) {
1658
+ try {
1659
+ await open(demo.url);
1660
+ } catch {
1661
+ console.log(chalk.red("Could not open browser."));
1662
+ }
1663
+ return;
1664
+ }
1665
+ console.log(chalk.yellow(`Choose one of: ${actionKeys}`));
1666
+ };
1667
+ const ask = () => {
1668
+ if (finished) return;
1669
+ rl.question(chalk.dim(`\nChoose an action (${actionKeys}): `), (answer) => {
1670
+ const names = answer.trim().toLowerCase().split(/[\s,]+/).filter(Boolean).map((part) => part.charAt(0));
1671
+ (async () => {
1672
+ for (const name of names) {
1673
+ await runAction(name);
1674
+ if (finished) return;
1675
+ }
1676
+ if (!finished) ask();
1677
+ })();
1678
+ });
1679
+ };
1680
+ rl.on("SIGINT", finish);
1681
+ ask();
1682
+ });
1683
+ }
1684
+ const personaCommand = new Command("persona").description("Set up Persona: create an agent, client token, and embed snippet");
1685
+ personaCommand.command("init").description("Create agent + client token and output a Persona embed snippet").option("--agent-name <name>", "Name for the new agent").option("--agent-description <text", "Optional agent description").option("--token-name <name>", "Name for the new client token").option("--environment <env>", "Token environment: test or live", "test").option("--origins <origins...>", "Allowed origins (default: * — broad; prefer explicit URLs in production)").option("--format <fmt>", "Snippet format: script-installer | script-manual | esm | react", "script-installer").option("--print-snippet", "Print the generated snippet to stdout (non-interactive summary mode)").option("--target-selector <selector>", "CSS selector for widget mount target (omit to use body)").option("--demo", "Create a dependency-free local Persona Playground HTML demo").option("--no-demo", "Skip the local Persona Playground demo").option("--demo-dir <path>", `Directory for the local demo (default: ${PERSONA_DEMO_DEFAULT_DIR})`).option("--demo-port <port>", `Preferred localhost port for the local demo (default: ${PERSONA_DEMO_DEFAULT_PORT})`).option("--open", "Open the local demo in a browser when demo mode is enabled").option("--no-open", "Do not open the local demo automatically").option("--force", "Overwrite existing demo files").option("--api-url <url>", "Override API base URL").option("--json", "Structured JSON output (includes snippet body)").option("--yes", "Accept defaults in interactive prompts where sensible").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options, cmd) => {
1686
+ const globals = cmd.optsWithGlobals();
1687
+ const jsonMode = !!(options.json ?? globals.json);
1688
+ const tty = isTTY(options);
1689
+ let formatCli;
1690
+ try {
1691
+ formatCli = parsePersonaInitFormat(options.format);
1692
+ } catch (e) {
1693
+ const msg = e instanceof Error ? e.message : String(e);
1694
+ if (jsonMode) console.log(JSON.stringify({
1695
+ status: "error",
1696
+ error: msg,
1697
+ code: "INVALID_FORMAT"
1698
+ }, null, 2));
1699
+ else console.error(chalk.red(msg));
1700
+ process.exit(1);
1701
+ }
1702
+ const apiUrl = options.apiUrl || globals.apiUrl || getApiUrl();
1703
+ const targetSelector = options.targetSelector || void 0;
1704
+ let preferredDemoPort;
1705
+ try {
1706
+ preferredDemoPort = parsePort(options.demoPort, PERSONA_DEMO_DEFAULT_PORT);
1707
+ } catch (e) {
1708
+ const msg = e instanceof Error ? e.message : String(e);
1709
+ if (jsonMode) console.log(JSON.stringify({
1710
+ status: "error",
1711
+ error: msg,
1712
+ code: "INVALID_DEMO_PORT"
1713
+ }, null, 2));
1714
+ else console.error(chalk.red(msg));
1715
+ process.exit(1);
1716
+ }
1717
+ let env;
1718
+ if (options.environment === "test" || options.environment === "live") env = options.environment;
1719
+ else {
1720
+ const msg = "Error: --environment must be \"test\" or \"live\"";
1721
+ if (jsonMode) console.log(JSON.stringify({
1722
+ status: "error",
1723
+ error: msg,
1724
+ code: "INVALID_ENV"
1725
+ }, null, 2));
1726
+ else console.error(chalk.red(msg));
1727
+ process.exit(1);
1728
+ }
1729
+ const interactive = tty && !jsonMode;
1730
+ let agentName = options.agentName?.trim();
1731
+ let tokenName = options.tokenName?.trim();
1732
+ let allowedOrigins = options.origins;
1733
+ let chosenEnv = env;
1734
+ let demoRequested = options.demo === true;
1735
+ let demoDirectory;
1736
+ let demoPort;
1737
+ let demoOrigins;
1738
+ let demoLocalUrl;
1739
+ let forceDemoWrite = !!options.force;
1740
+ const rl = interactive && process.stdin.isTTY && process.stdout.isTTY ? readline.createInterface({
1741
+ input: process.stdin,
1742
+ output: process.stdout
1743
+ }) : null;
1744
+ if (!interactive) {
1745
+ if (!agentName) {
1746
+ const msg = "Non-interactive mode requires --agent-name";
1747
+ if (jsonMode) console.log(JSON.stringify({
1748
+ status: "error",
1749
+ error: msg,
1750
+ code: "MISSING_AGENT_NAME"
1751
+ }, null, 2));
1752
+ else console.error(chalk.red(msg));
1753
+ process.exit(1);
1754
+ }
1755
+ if (!tokenName) tokenName = defaultTokenName(agentName);
1756
+ if (!allowedOrigins || allowedOrigins.length === 0) allowedOrigins = ["*"];
1757
+ } else {
1758
+ if (!agentName) {
1759
+ agentName = await promptLine(rl, "Agent name", "Persona Test");
1760
+ if (!agentName) {
1761
+ console.error(chalk.red("Agent name is required"));
1762
+ process.exit(1);
1763
+ }
1764
+ }
1765
+ if (!tokenName) {
1766
+ const def = options.yes ? defaultTokenName(agentName) : defaultTokenName(agentName);
1767
+ tokenName = await promptLine(rl, "Client token name", def);
1768
+ if (!tokenName) tokenName = def;
1769
+ }
1770
+ if (options.demo === void 0 && !options.yes) demoRequested = await promptConfirm(rl, "Create and open a local Persona Playground HTML demo?", true);
1771
+ else if (options.demo === void 0 && options.yes) demoRequested = true;
1772
+ if (demoRequested) {
1773
+ demoDirectory = resolvePersonaDemoDirectory(options.demoDir);
1774
+ try {
1775
+ ensurePersonaDemoCanWrite(demoDirectory, forceDemoWrite);
1776
+ } catch (e) {
1777
+ if (!await promptConfirm(rl, `${e instanceof Error ? e.message : String(e)} Overwrite it?`, false)) {
1778
+ console.log(chalk.gray("Aborted."));
1779
+ rl.close();
1780
+ process.exit(0);
1781
+ }
1782
+ forceDemoWrite = true;
1783
+ }
1784
+ demoPort = await findAvailablePersonaDemoPort(preferredDemoPort);
1785
+ demoOrigins = buildPersonaDemoOrigins(demoPort);
1786
+ demoLocalUrl = `http://127.0.0.1:${demoPort}/`;
1787
+ }
1788
+ if (!allowedOrigins?.length) {
1789
+ const defaultOrigins = demoOrigins?.join(" ") ?? "*";
1790
+ if (options.yes) allowedOrigins = demoOrigins ?? ["*"];
1791
+ else {
1792
+ const originsInput = await promptLine(rl, "Allowed origins (space-separated URLs, or * for any)", defaultOrigins);
1793
+ allowedOrigins = originsInput === "*" ? ["*"] : originsInput.split(/\s+/).filter(Boolean);
1794
+ }
1795
+ }
1796
+ if (chosenEnv === "live") {
1797
+ if (!await promptConfirm(rl, chalk.yellow("Create a LIVE token? This is shown only once."), false)) {
1798
+ console.log(chalk.gray("Aborted."));
1799
+ rl.close();
1800
+ process.exit(0);
1801
+ }
1802
+ } else if (!options.yes) {
1803
+ if (await promptLine(rl, "Environment (test/live)", "test") === "live") {
1804
+ if (!await promptConfirm(rl, chalk.yellow("Create a LIVE token?"), false)) chosenEnv = "test";
1805
+ else chosenEnv = "live";
1806
+ }
1807
+ }
1808
+ }
1809
+ if (demoRequested && !demoDirectory) {
1810
+ demoDirectory = resolvePersonaDemoDirectory(options.demoDir);
1811
+ try {
1812
+ ensurePersonaDemoCanWrite(demoDirectory, forceDemoWrite);
1813
+ } catch (e) {
1814
+ const message = e instanceof Error ? e.message : String(e);
1815
+ if (jsonMode) console.log(JSON.stringify({
1816
+ status: "error",
1817
+ error: message,
1818
+ code: "DEMO_EXISTS"
1819
+ }, null, 2));
1820
+ else console.error(chalk.red(message));
1821
+ process.exit(1);
1822
+ }
1823
+ demoPort = await findAvailablePersonaDemoPort(preferredDemoPort);
1824
+ demoOrigins = buildPersonaDemoOrigins(demoPort);
1825
+ demoLocalUrl = `http://127.0.0.1:${demoPort}/`;
1826
+ }
1827
+ if (demoRequested && demoOrigins) allowedOrigins = mergePersonaDemoAllowedOrigins(allowedOrigins, demoOrigins);
1828
+ if (rl) rl.close();
1829
+ if (allowedOrigins.includes("*") && chosenEnv === "live" && !isProbablyLocalDev(allowedOrigins)) console.log(chalk.yellow("Warning: allowedOrigins is \"*\" for a live token. Prefer explicit https:// origins in production."));
1830
+ let apiKey;
1831
+ if (jsonMode || !tty) {
1832
+ apiKey = await getPersonaInitApiKeyNonInteractive(apiUrl);
1833
+ if (!apiKey) {
1834
+ if (jsonMode) console.log(JSON.stringify({
1835
+ status: "unauthenticated",
1836
+ message: "Set RUNTYPE_API_KEY or run `runtype auth login` (uses saved credentials)"
1837
+ }, null, 2));
1838
+ else console.error(chalk.red("Authentication required: set RUNTYPE_API_KEY or run `runtype auth login` for stored credentials"));
1839
+ process.exit(1);
1840
+ }
1841
+ } else {
1842
+ apiKey = await ensureAuth({ apiUrl });
1843
+ if (!apiKey) process.exit(1);
1844
+ }
1845
+ let success;
1846
+ try {
1847
+ success = await runPersonaInit({
1848
+ apiKey,
1849
+ apiUrl,
1850
+ agentName,
1851
+ agentDescription: options.agentDescription,
1852
+ tokenName,
1853
+ environment: chosenEnv,
1854
+ allowedOrigins,
1855
+ format: formatCli,
1856
+ targetSelector,
1857
+ ...demoRequested && demoOrigins ? { demo: {
1858
+ origins: demoOrigins,
1859
+ toolNames: [...PERSONA_DEMO_TOOL_NAMES]
1860
+ } } : {}
1861
+ });
1862
+ } catch (err) {
1863
+ const message = err instanceof Error ? err.message : String(err);
1864
+ if (jsonMode) console.log(JSON.stringify({
1865
+ status: "error",
1866
+ error: message,
1867
+ code: "API_ERROR"
1868
+ }, null, 2));
1869
+ else {
1870
+ console.error(chalk.red("Persona init failed"));
1871
+ console.error(chalk.red(message));
1872
+ }
1873
+ process.exit(1);
1874
+ }
1875
+ let demoOutput;
1876
+ let demoServer;
1877
+ if (demoRequested && demoDirectory && demoPort && demoOrigins && demoLocalUrl) {
1878
+ const html = generatePersonaDemoHtml({
1879
+ apiUrl,
1880
+ clientToken: success.clientToken.token,
1881
+ clientTokenId: success.clientToken.id,
1882
+ agentId: success.agent.id,
1883
+ agentName: success.agent.name,
1884
+ dashboardUrl: success.dashboardUrl,
1885
+ environment: success.clientToken.environment,
1886
+ localUrl: demoLocalUrl,
1887
+ allowedOrigins: demoOrigins
1888
+ });
1889
+ const readme = generatePersonaDemoReadme({
1890
+ apiUrl,
1891
+ clientToken: success.clientToken.token,
1892
+ clientTokenId: success.clientToken.id,
1893
+ agentId: success.agent.id,
1894
+ agentName: success.agent.name,
1895
+ dashboardUrl: success.dashboardUrl,
1896
+ environment: success.clientToken.environment,
1897
+ localUrl: demoLocalUrl,
1898
+ allowedOrigins: demoOrigins
1899
+ });
1900
+ demoOutput = {
1901
+ ...writePersonaDemoPage({
1902
+ directory: demoDirectory,
1903
+ html,
1904
+ readme,
1905
+ force: forceDemoWrite
1906
+ }),
1907
+ localUrl: demoLocalUrl,
1908
+ port: demoPort,
1909
+ allowedOrigins: demoOrigins,
1910
+ serverRunning: false
1911
+ };
1912
+ if (!jsonMode && options.open !== false && (interactive || options.open === true)) try {
1913
+ demoServer = await startPersonaDemoServer({
1914
+ directory: demoDirectory,
1915
+ port: demoPort
1916
+ });
1917
+ demoOutput.serverRunning = true;
1918
+ demoOutput.localUrl = demoServer.url;
1919
+ await open(demoServer.url);
1920
+ } catch (e) {
1921
+ demoOutput.serverError = e instanceof Error ? e.message : String(e);
1922
+ if (!jsonMode) console.log(chalk.yellow(`Could not start/open local demo server: ${demoOutput.serverError}`));
1923
+ }
1924
+ }
1925
+ const output = demoOutput ? {
1926
+ ...success,
1927
+ demo: {
1928
+ ...success.demo,
1929
+ ...demoOutput
1930
+ }
1931
+ } : success;
1932
+ if (jsonMode) {
1933
+ printJson(output);
1934
+ return;
1935
+ }
1936
+ if (!tty || options.printSnippet) {
1937
+ console.log(chalk.green("\nPersona setup complete\n"));
1938
+ console.log(`${chalk.bold("Agent ID:")} ${output.agent.id}`);
1939
+ console.log(`${chalk.bold("Client Token ID:")} ${output.clientToken.id}`);
1940
+ console.log(`${chalk.bold("Client Token:")} ${chalk.yellow(output.clientToken.token)}`);
1941
+ console.log(`${chalk.bold("Format:")} ${output.snippet.format}`);
1942
+ console.log(chalk.dim(`\nDashboard: ${output.dashboardUrl}`));
1943
+ if (demoOutput) {
1944
+ console.log(`${chalk.bold("Demo HTML:")} ${demoOutput.filePath}`);
1945
+ console.log(`${chalk.bold("Demo README:")} ${demoOutput.readmePath}`);
1946
+ console.log(`${chalk.bold("Demo URL:")} ${chalk.green(demoOutput.localUrl)}`);
1947
+ if (demoOutput.serverRunning) console.log(chalk.dim("Local demo server is running. Press Ctrl+C to stop it."));
1948
+ else console.log(chalk.dim(`Run: cd ${demoOutput.directory} && python3 -m http.server ${demoOutput.port} --bind 127.0.0.1`));
1949
+ printDemoNextSteps({
1950
+ url: demoOutput.localUrl,
1951
+ filePath: demoOutput.filePath,
1952
+ readmePath: demoOutput.readmePath
1953
+ }, output.dashboardUrl);
1954
+ }
1955
+ if (success.warnings?.length) for (const w of success.warnings) console.log(chalk.yellow(`⚠ ${w.message}`));
1956
+ if (!demoOutput) console.log(chalk.cyan("\nRecommended next step: npm install @runtypelabs/persona"));
1957
+ if (options.printSnippet) {
1958
+ console.log(chalk.cyan("\n--- snippet ---\n"));
1959
+ console.log(success.snippet.code);
1960
+ } else console.log(chalk.dim("\nUse --print-snippet to print the embed code, or run interactively for copy shortcuts."));
1961
+ return;
1962
+ }
1963
+ try {
1964
+ await runSuccessKeyLoop(success, apiUrl, targetSelector, demoOutput ? {
1965
+ url: demoOutput.localUrl,
1966
+ filePath: demoOutput.filePath,
1967
+ readmePath: demoOutput.readmePath
1968
+ } : void 0);
1969
+ } finally {
1970
+ await demoServer?.close().catch(() => {});
1971
+ }
1972
+ });
1973
+ export { personaCommand };
1974
+