@superpanel-sdk/runtime 0.5.0 → 0.7.0

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 (189) hide show
  1. package/README.md +3 -3
  2. package/dist/builder/codegen/emit.d.ts +5 -10
  3. package/dist/builder/codegen/emit.js +100 -106
  4. package/dist/builder/codegen/generate.d.ts +3 -5
  5. package/dist/builder/codegen/generate.js +13 -32
  6. package/dist/builder/company-imports.d.ts +5 -0
  7. package/dist/builder/company-imports.js +9 -1
  8. package/dist/builder/compile/compile-sequence.d.ts +13 -11
  9. package/dist/builder/compile/compile-sequence.js +135 -83
  10. package/dist/builder/compile/function-code.d.ts +15 -9
  11. package/dist/builder/compile/function-code.js +59 -29
  12. package/dist/builder/compile/registry.d.ts +4 -4
  13. package/dist/builder/compile/registry.js +14 -12
  14. package/dist/builder/errors.d.ts +9 -0
  15. package/dist/builder/errors.js +17 -0
  16. package/dist/builder/function-params.d.ts +10 -0
  17. package/dist/builder/function-params.js +203 -0
  18. package/dist/builder/load.d.ts +17 -17
  19. package/dist/builder/load.js +27 -25
  20. package/dist/builder/paths.d.ts +6 -1
  21. package/dist/builder/paths.js +16 -2
  22. package/dist/builder/schedules.d.ts +9 -7
  23. package/dist/builder/schedules.js +20 -21
  24. package/dist/builder/typecheck.d.ts +1 -1
  25. package/dist/builder/typecheck.js +3 -6
  26. package/dist/builder/types.d.ts +41 -5
  27. package/dist/builder/types.js +19 -0
  28. package/dist/cli/calls/index.d.ts +177 -0
  29. package/dist/cli/calls/index.js +96 -0
  30. package/dist/cli/debug/index.d.ts +31 -0
  31. package/dist/cli/debug/index.js +119 -0
  32. package/dist/cli/exec/index.d.ts +39 -0
  33. package/dist/cli/exec/index.js +138 -0
  34. package/dist/cli/exec/session.d.ts +48 -0
  35. package/dist/cli/exec/session.js +82 -0
  36. package/dist/cli/exec/trace.d.ts +18 -0
  37. package/dist/cli/exec/trace.js +52 -0
  38. package/dist/cli/index.js +84 -1
  39. package/dist/cli/logs/index.d.ts +47 -0
  40. package/dist/cli/logs/index.js +113 -0
  41. package/dist/cli/mobile/index.d.ts +12 -0
  42. package/dist/cli/mobile/index.js +405 -0
  43. package/dist/cli/serve/index.d.ts +40 -4
  44. package/dist/cli/serve/index.js +167 -28
  45. package/dist/cli/turns/index.d.ts +105 -0
  46. package/dist/cli/turns/index.js +128 -0
  47. package/dist/cli/turns/run.d.ts +13 -0
  48. package/dist/cli/turns/run.js +57 -0
  49. package/dist/cli/turns/shadow.d.ts +112 -0
  50. package/dist/cli/turns/shadow.js +54 -0
  51. package/dist/cli/type-gen/index.js +0 -1
  52. package/dist/compute-host/compute-host.d.ts +5 -6
  53. package/dist/compute-host/compute-host.js +4 -13
  54. package/dist/compute-host/source-compute-host.d.ts +5 -7
  55. package/dist/compute-host/source-compute-host.js +12 -33
  56. package/dist/core/case-orchestrator/contact-hours.d.ts +26 -6
  57. package/dist/core/case-orchestrator/contact-hours.js +27 -8
  58. package/dist/core/case-orchestrator/index.d.ts +1 -4
  59. package/dist/core/case-orchestrator/index.js +1 -4
  60. package/dist/core/case-orchestrator/render.d.ts +1 -1
  61. package/dist/core/case-orchestrator/render.js +15 -10
  62. package/dist/core/case-orchestrator/types.d.ts +83 -128
  63. package/dist/core/case-orchestrator/types.js +0 -4
  64. package/dist/core/context/email-message.d.ts +40 -1
  65. package/dist/core/context/email-message.js +27 -0
  66. package/dist/core/context/email.d.ts +13 -1
  67. package/dist/core/context/email.js +2 -3
  68. package/dist/core/context/exchanges.d.ts +25 -0
  69. package/dist/core/context/exchanges.js +25 -7
  70. package/dist/core/context/instances.d.ts +31 -2
  71. package/dist/core/context/instances.js +18 -4
  72. package/dist/core/context/platform-registry.d.ts +35 -0
  73. package/dist/core/context/sms.d.ts +30 -0
  74. package/dist/core/context/sms.js +20 -0
  75. package/dist/core/context/types.d.ts +29 -1
  76. package/dist/core/conversation/index.d.ts +58 -4
  77. package/dist/core/conversation/index.js +123 -4
  78. package/dist/core/functions/errors.d.ts +74 -0
  79. package/dist/core/functions/errors.js +26 -0
  80. package/dist/core/functions/events.d.ts +23 -78
  81. package/dist/core/functions/events.js +8 -17
  82. package/dist/core/functions/schedules.d.ts +9 -10
  83. package/dist/core/functions/schedules.js +2 -2
  84. package/dist/core/functions/types.d.ts +17 -72
  85. package/dist/core/functions/types.js +1 -9
  86. package/dist/core/page/define.d.ts +230 -160
  87. package/dist/core/page/define.js +145 -104
  88. package/dist/core/page/envelope.d.ts +55 -0
  89. package/dist/core/page/envelope.js +4 -0
  90. package/dist/core/page/fns.d.ts +448 -0
  91. package/dist/core/page/fns.js +539 -0
  92. package/dist/core/page/memory-reader.d.ts +27 -0
  93. package/dist/core/page/memory-reader.js +299 -0
  94. package/dist/core/page/page-host.d.ts +20 -0
  95. package/dist/core/page/page-host.js +57 -0
  96. package/dist/core/page/records-query.d.ts +234 -0
  97. package/dist/core/page/records-query.js +780 -0
  98. package/dist/core/page/render.d.ts +67 -25
  99. package/dist/core/page/render.js +492 -272
  100. package/dist/core/page/sandbox.d.ts +85 -0
  101. package/dist/core/page/sandbox.js +357 -0
  102. package/dist/core/page/time-format.d.ts +27 -0
  103. package/dist/core/page/time-format.js +84 -0
  104. package/dist/core/page/typed.d.ts +165 -159
  105. package/dist/core/page/workspace.d.ts +25 -1
  106. package/dist/core/page/workspace.js +48 -13
  107. package/dist/core/party/schema.d.ts +74 -3
  108. package/dist/core/party/schema.js +39 -6
  109. package/dist/core/party/types.d.ts +8 -13
  110. package/dist/core/sequence/define.d.ts +1 -2
  111. package/dist/core/slot/company-metadata.d.ts +11 -22
  112. package/dist/core/slot/company-metadata.js +12 -39
  113. package/dist/core/slot/compile-schema.js +6 -6
  114. package/dist/core/slot/field.d.ts +1 -2
  115. package/dist/core/slot/field.js +0 -2
  116. package/dist/core/slot/index.d.ts +3 -3
  117. package/dist/core/slot/index.js +3 -3
  118. package/dist/core/slot/naming.d.ts +1 -1
  119. package/dist/core/slot/naming.js +1 -1
  120. package/dist/core/slot/platform-fields.d.ts +4 -4
  121. package/dist/core/slot/platform-fields.js +5 -5
  122. package/dist/core/slot/schema-compat.d.ts +8 -7
  123. package/dist/core/slot/schema-compat.js +22 -31
  124. package/dist/core/slot/schema.d.ts +3 -59
  125. package/dist/core/slot/schema.js +0 -15
  126. package/dist/core/slot/types.d.ts +4 -10
  127. package/dist/core/slot/value.d.ts +2 -0
  128. package/dist/core/slot/value.js +6 -2
  129. package/dist/dev-overlay/protocol.d.ts +38 -5
  130. package/dist/fn-context/build-context.js +10 -4
  131. package/dist/fn-context/fake/index.d.ts +2 -0
  132. package/dist/fn-context/fake/index.js +5 -3
  133. package/dist/fn-context/fn-params.d.ts +2 -5
  134. package/dist/fn-context/fn-params.js +6 -9
  135. package/dist/fn-context/index.d.ts +1 -1
  136. package/dist/fn-context/make-ctx.js +5 -4
  137. package/dist/fn-context/rpc/case-orchestrator-tools.d.ts +11 -3
  138. package/dist/fn-context/rpc/case-orchestrator-tools.js +0 -1
  139. package/dist/fn-context/rpc/errors.d.ts +13 -0
  140. package/dist/fn-context/rpc/errors.js +22 -0
  141. package/dist/fn-context/rpc/index.d.ts +1 -1
  142. package/dist/fn-context/rpc/index.js +1 -1
  143. package/dist/fn-context/rpc/integration/index.d.ts +11 -1
  144. package/dist/fn-context/rpc/log.d.ts +11 -1
  145. package/dist/fn-context/rpc/platform.d.ts +11 -1
  146. package/dist/fn-context/rpc/records.d.ts +13 -6
  147. package/dist/fn-context/rpc/records.js +2 -4
  148. package/dist/fn-context/rpc/trpc-client.d.ts +42 -0
  149. package/dist/fn-context/rpc/trpc-client.js +151 -22
  150. package/dist/fn-context/rpc/trpc-init.d.ts +19 -1
  151. package/dist/fn-context/rpc/trpc-init.js +10 -4
  152. package/dist/fn-context/rpc/trpc-router-factory.d.ts +66 -8
  153. package/dist/fn-context/run-scope.js +1 -1
  154. package/dist/fn-context/types.d.ts +20 -6
  155. package/dist/index.d.ts +11 -12
  156. package/dist/index.js +5 -8
  157. package/dist/logger.d.ts +4 -0
  158. package/dist/logger.js +8 -0
  159. package/dist/platform.d.ts +32 -33
  160. package/dist/platform.js +23 -22
  161. package/dist/testing-compile-child.d.ts +2 -0
  162. package/dist/testing-compile-child.js +20 -0
  163. package/dist/testing.d.ts +368 -0
  164. package/dist/testing.js +439 -0
  165. package/dist/voice-service/actions.d.ts +12 -35
  166. package/dist/voice-service/index.d.ts +2 -0
  167. package/dist/voice-service/index.js +2 -0
  168. package/dist/voice-service/protocol.d.ts +604 -0
  169. package/dist/voice-service/protocol.js +67 -0
  170. package/dist/voice-service/types.d.ts +16 -6
  171. package/dist/worker.d.ts +2 -1
  172. package/dist/worker.js +2 -1
  173. package/package.json +20 -16
  174. package/dist/core/case-orchestrator/follow-ups.d.ts +0 -31
  175. package/dist/core/case-orchestrator/follow-ups.js +0 -103
  176. package/dist/core/case-orchestrator/tools.d.ts +0 -29
  177. package/dist/core/case-orchestrator/tools.js +0 -31
  178. package/dist/core/case-orchestrator/validate.d.ts +0 -11
  179. package/dist/core/case-orchestrator/validate.js +0 -63
  180. package/dist/core/functions/define/bind.d.ts +0 -25
  181. package/dist/core/functions/define/bind.js +0 -14
  182. package/dist/core/functions/define/case.d.ts +0 -9
  183. package/dist/core/functions/define/case.js +0 -13
  184. package/dist/core/functions/define/events.d.ts +0 -20
  185. package/dist/core/functions/define/events.js +0 -21
  186. package/dist/core/functions/define/no-case.d.ts +0 -4
  187. package/dist/core/functions/define/no-case.js +0 -9
  188. package/dist/core/functions/func-ref.d.ts +0 -25
  189. package/dist/core/functions/func-ref.js +0 -20
@@ -0,0 +1,405 @@
1
+ /**
2
+ * `superpanel-sdk-runtime mobile` (68 §5.4): a test customer's phone in the terminal. This repository's code
3
+ * serves the environment (as `connect()` does); you are the customer: call a line and talk in text, text it,
4
+ * email it, and get what the environment sends you on its own (its texts, its emails, its calls ringing),
5
+ * answered or let ring. Plain lines are what you say on the call you are on, else a text to the line.
6
+ * Commands start with `/` (`/help`). Lines piped in run one after another, so a script or an agent can
7
+ * drive it: `printf '/call\nHi, this is Carl\n/hangup\n' | npm run mobile`.
8
+ */
9
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
10
+ import path from 'node:path';
11
+ import { createInterface } from 'node:readline';
12
+ import { connect } from '../../testing.js';
13
+ import { formatTurnLine } from '../turns/index.js';
14
+ const HELP = `What you type:
15
+ <text> say it on the call you are on; else text it to the line
16
+ /call [line] call a line (the line you text when left out)
17
+ /sms <text> text the line, on a call or not
18
+ /burst <a> | <b> | … several texts in a row, batched as a carrier's are (one run after 15 s quiet)
19
+ /email <subject> | <body> email the mailbox (a new thread)
20
+ /reply <body> answer the last email you got, in its thread
21
+ /answer [voicemail|screening|ivr] pick up the call ringing you (a person when left out)
22
+ /decline [busy] let it ring (no-answer), or be busy
23
+ /press <keys> press keys for the keypad the call asked for
24
+ /transfer answered|failed the transfer the call asked for: taken, or not
25
+ /hangup hang up
26
+ /wait sms|email|call [s] wait for the environment to send you one (60 s when left out)
27
+ /due do your case's next scheduled step now (a call or text booked for a later day)
28
+ /inbox everything sent to you since you started
29
+ /turns your case's turns: what was heard, said, pressed and written
30
+ /line <name>, /mailbox <name> where calls, texts and emails go
31
+ /whoami your number, address, line, mailbox and case
32
+ /help, /quit`;
33
+ const tty = process.stdout.isTTY === true;
34
+ const paint = (code) => (text) => (tty ? `\x1b[${code}m${text}\x1b[0m` : text);
35
+ const ai = paint('36');
36
+ const dim = paint('2');
37
+ const alert = paint('33');
38
+ const bad = paint('31');
39
+ /** The test customer this repository last used for an environment, kept in .superpanel/mobile/. */
40
+ function identity(repoRoot, environmentId, fresh, make) {
41
+ const file = path.join(repoRoot, '.superpanel', 'mobile', `${environmentId}.json`);
42
+ if (!fresh) {
43
+ try {
44
+ const kept = JSON.parse(readFileSync(file, 'utf8'));
45
+ if (typeof kept.phone === 'string' && typeof kept.email === 'string')
46
+ return { phone: kept.phone, email: kept.email };
47
+ }
48
+ catch {
49
+ // none yet
50
+ }
51
+ }
52
+ const made = make();
53
+ mkdirSync(path.dirname(file), { recursive: true });
54
+ writeFileSync(file, `${JSON.stringify(made, null, 2)}\n`);
55
+ return made;
56
+ }
57
+ export async function runMobile(repoRoot, options) {
58
+ const out = (line) => print(line);
59
+ out(dim(`connecting to ${options.env ?? 'sandbox'}: this repository's code serves it while the phone is on…`));
60
+ const sandbox = await connect({ env: options.env ?? 'sandbox', repoRoot, ...(options.url ? { url: options.url } : {}) });
61
+ const probe = sandbox.sim.customer();
62
+ const me = identity(repoRoot, sandbox.environment.id, options.new === true, () => ({ phone: probe.phone, email: probe.email }));
63
+ const customer = sandbox.sim.customer(me);
64
+ const state = {
65
+ line: options.line ?? 'Main line',
66
+ mailbox: options.mailbox ?? 'Support',
67
+ call: null,
68
+ ringing: [],
69
+ lastEmail: null,
70
+ /** Your cases, the latest last: your number and your address may be different contacts, each with its own. */
71
+ cases: [],
72
+ };
73
+ const since = new Date(Date.now() - 1_000).toISOString();
74
+ const seen = new Set();
75
+ const arrived = [];
76
+ const waiters = [];
77
+ // ── Output while you type ──────────────────────────────────────────────────────────────────────
78
+ const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: tty });
79
+ rl.setPrompt(tty ? '📱 ' : '');
80
+ function print(line) {
81
+ if (tty)
82
+ process.stdout.write('\r\x1b[K');
83
+ process.stdout.write(`${line}\n`);
84
+ if (tty && rl)
85
+ rl.prompt(true);
86
+ }
87
+ const showItem = (item) => {
88
+ if (item.kind === 'sms')
89
+ out(`${alert('💬 text')} from ${item.line ?? item.from}: ${item.body}`);
90
+ if (item.kind === 'email') {
91
+ out(`${alert('✉ email')} from ${item.mailbox ?? item.from}: ${item.subject}`);
92
+ for (const line of item.body.split('\n'))
93
+ out(` ${line}`);
94
+ out(dim(' (/reply <body> to answer)'));
95
+ }
96
+ if (item.kind === 'call')
97
+ out(`${alert('📞 ringing')} from ${item.line ?? item.from} ${dim('(/answer or /decline)')}`);
98
+ };
99
+ const take = (item, show) => {
100
+ if (seen.has(item.id))
101
+ return;
102
+ seen.add(item.id);
103
+ if (item.kind === 'call')
104
+ state.ringing.push(item);
105
+ if (item.kind === 'email')
106
+ state.lastEmail = item;
107
+ if (show)
108
+ showItem(item);
109
+ const waiter = waiters.findIndex((w) => w.kind === item.kind);
110
+ if (waiter >= 0)
111
+ waiters.splice(waiter, 1)[0].resolve(item);
112
+ else
113
+ arrived.push(item);
114
+ };
115
+ const showTurns = (turns) => {
116
+ for (const turn of turns) {
117
+ if (turn.instanceId)
118
+ state.cases = [...state.cases.filter((id) => id !== turn.instanceId), turn.instanceId];
119
+ for (const thought of turn.thoughts ?? [])
120
+ out(dim(` (thought) ${thought}`));
121
+ for (const action of turn.actions)
122
+ out(dim(` ${action.key}: ${action.status}${action.error ? ` (${action.error})` : ''}`));
123
+ for (const write of turn.writes)
124
+ out(dim(` wrote ${write.table}.${write.field} = ${JSON.stringify(write.value)}${write.state && write.state !== 'filled' ? ` (${write.state})` : ''}`));
125
+ if (turn.error)
126
+ out(bad(` ${turn.error}`));
127
+ }
128
+ };
129
+ const showOrchestrator = (orchestrator) => {
130
+ if (!orchestrator)
131
+ return;
132
+ const booked = orchestrator.scheduled.map((a) => {
133
+ const action = a;
134
+ return `${action.kind ?? 'step'} at ${action.scheduledFor ?? '?'}${action.reason ? ` (${action.reason})` : ''}`;
135
+ });
136
+ out(dim(` Orchestrator: ${orchestrator.status}${booked.length ? `; booked ${booked.join('; ')}` : ''}${orchestrator.cancelled.length ? `; cancelled ${orchestrator.cancelled.length}` : ''}`));
137
+ };
138
+ const showStep = (step) => {
139
+ for (const said of step.said)
140
+ out(`${ai('AI:')} ${said}`);
141
+ for (const event of step.events) {
142
+ if (event.kind === 'keypad')
143
+ out(alert(` ⌨ keypad ${event.key}: "${event.say}" (${event.digits.join(', ')}) — /press <keys>`));
144
+ else if (event.kind === 'transfer')
145
+ out(alert(` ↪ transfer to ${event.to} (${event.mode}) — /transfer answered|failed`));
146
+ else if (event.kind === 'voicemail')
147
+ out(alert(` 📼 voicemail left: "${event.text}"`));
148
+ else if (event.kind === 'language')
149
+ out(dim(` language: ${event.language}`));
150
+ else if (event.kind === 'far_end')
151
+ out(dim(` who answered (${event.farEnd}): ${event.decision}`));
152
+ }
153
+ showTurns(step.turns);
154
+ if (step.ended) {
155
+ out(alert(`📴 call ended (${step.ended.by})`));
156
+ showOrchestrator(step.ended.orchestrator);
157
+ state.call = null;
158
+ }
159
+ };
160
+ const showSent = (result) => {
161
+ showTurns(result.turns);
162
+ for (const reply of result.replies)
163
+ take(reply, true);
164
+ if (!result.replies.length)
165
+ out(dim(' (no reply)'));
166
+ showOrchestrator(result.orchestrator);
167
+ };
168
+ // ── What the environment sends you, as it comes ────────────────────────────────────────────────
169
+ let polling = true;
170
+ const poll = async () => {
171
+ while (polling) {
172
+ try {
173
+ for (const item of await customer.inbox(since))
174
+ take(item, true);
175
+ // A call that stopped ringing (answered elsewhere, or ended) is no longer to answer.
176
+ const ringingNow = new Set((await customer.inbox(since)).filter((i) => i.kind === 'call').map((i) => i.id));
177
+ state.ringing = state.ringing.filter((c) => ringingNow.has(c.id));
178
+ }
179
+ catch (error) {
180
+ out(bad(`inbox: ${error instanceof Error ? error.message : String(error)}`));
181
+ }
182
+ await new Promise((resolve) => setTimeout(resolve, 2_000));
183
+ }
184
+ };
185
+ const waitFor = (kind, seconds) => new Promise((resolve) => {
186
+ const ready = arrived.findIndex((item) => item.kind === kind);
187
+ if (ready >= 0)
188
+ return resolve(arrived.splice(ready, 1)[0]);
189
+ const waiter = { kind, resolve: (item) => (clearTimeout(timer), resolve(item)) };
190
+ const timer = setTimeout(() => {
191
+ waiters.splice(waiters.indexOf(waiter), 1);
192
+ resolve(null);
193
+ }, seconds * 1_000);
194
+ waiters.push(waiter);
195
+ });
196
+ /** Your open cases, the latest first: the ones you talked on here, else the ones your number and address have. */
197
+ const caseIds = async () => {
198
+ if (!state.cases.length) {
199
+ for (const lookup of [{ phone: me.phone }, { email: me.email }]) {
200
+ const { contacts } = await sandbox.ctx.api.contact.find(lookup);
201
+ for (const contact of contacts) {
202
+ const { instances } = await sandbox.ctx.api.instances.findForContact({ contactId: contact.id, activeOnly: true });
203
+ for (const instance of instances)
204
+ if (!state.cases.includes(instance.id))
205
+ state.cases.push(instance.id);
206
+ }
207
+ }
208
+ }
209
+ return [...state.cases].reverse();
210
+ };
211
+ const onCall = () => {
212
+ if (!state.call)
213
+ throw new Error('you are not on a call: /call, or /answer one ringing you');
214
+ return state.call;
215
+ };
216
+ const ringingCall = () => {
217
+ const call = state.ringing.shift();
218
+ if (!call)
219
+ throw new Error('nothing is ringing you (/wait call waits for one)');
220
+ return call;
221
+ };
222
+ // ── What you type ─────────────────────────────────────────────────────────────────────────────
223
+ const commands = {
224
+ help: async () => out(HELP),
225
+ whoami: async () => out(`you: ${me.phone}, ${me.email}\nline: ${state.line}; mailbox: ${state.mailbox}\nenvironment: ${sandbox.environment.name}${state.cases.length ? `\ncases: ${state.cases.join(', ')}` : ''}`),
226
+ line: async (rest) => {
227
+ if (!rest)
228
+ throw new Error('/line <name>');
229
+ state.line = rest;
230
+ const line = await sandbox.sim.line(rest);
231
+ out(dim(`line ${line.name}: ${line.number}`));
232
+ },
233
+ mailbox: async (rest) => {
234
+ if (!rest)
235
+ throw new Error('/mailbox <name>');
236
+ state.mailbox = rest;
237
+ const box = await sandbox.sim.mailbox(rest);
238
+ out(dim(`mailbox ${box.name}: ${box.address}`));
239
+ },
240
+ call: async (rest) => {
241
+ if (state.call)
242
+ throw new Error('you are on a call: /hangup first');
243
+ if (rest)
244
+ state.line = rest;
245
+ await sandbox.sim.line(state.line);
246
+ out(dim(`calling ${state.line}…`));
247
+ const call = await sandbox.call.inbound({ from: me.phone, to: state.line });
248
+ state.call = call;
249
+ showStep(call.opening);
250
+ },
251
+ sms: async (rest) => {
252
+ if (!rest)
253
+ throw new Error('/sms <text>');
254
+ await sandbox.sim.line(state.line);
255
+ showSent(await customer.text(rest, { to: state.line, orchestrator: 'now' }));
256
+ },
257
+ burst: async (rest) => {
258
+ const texts = rest.split('|').map((part) => part.trim()).filter(Boolean);
259
+ if (texts.length < 2)
260
+ throw new Error('/burst <text> | <text> | …');
261
+ await sandbox.sim.line(state.line);
262
+ for (const body of texts)
263
+ await customer.text(body, { to: state.line, debounce: 'real' });
264
+ out(dim(`${texts.length} texts sent; the line answers once they stop coming (about 15 s)…`));
265
+ if (!(await waitFor('sms', 90)))
266
+ throw new Error('no reply came within 90 s');
267
+ },
268
+ email: async (rest) => {
269
+ const [subject, ...body] = rest.split('|');
270
+ if (!subject?.trim() || !body.join('|').trim())
271
+ throw new Error('/email <subject> | <body>');
272
+ await sandbox.sim.mailbox(state.mailbox);
273
+ showSent(await customer.sendEmail({ to: state.mailbox, subject: subject.trim(), body: body.join('|').trim(), orchestrator: 'now' }));
274
+ },
275
+ reply: async (rest) => {
276
+ const last = state.lastEmail;
277
+ if (!last)
278
+ throw new Error('no email to answer yet: /email starts a thread');
279
+ if (!rest)
280
+ throw new Error('/reply <body>');
281
+ const subject = /^re:/i.test(last.subject) ? last.subject : `Re: ${last.subject}`;
282
+ showSent(await customer.sendEmail({ to: last.from, subject, body: rest, threadId: last.threadId, orchestrator: 'now' }));
283
+ },
284
+ answer: async (rest) => {
285
+ if (state.call)
286
+ throw new Error('you are on a call: /hangup first');
287
+ const as = (rest || 'human');
288
+ if (!['human', 'voicemail', 'screening', 'ivr'].includes(as))
289
+ throw new Error('/answer [voicemail|screening|ivr]');
290
+ const call = await customer.answer(ringingCall(), { as, orchestrator: 'now' });
291
+ state.call = call.ended ? null : call;
292
+ showStep(call.opening);
293
+ },
294
+ decline: async (rest) => {
295
+ const status = rest === 'busy' ? 'busy' : 'no-answer';
296
+ showStep(await customer.decline(ringingCall(), { status, orchestrator: 'now' }));
297
+ },
298
+ press: async (rest) => {
299
+ if (!rest)
300
+ throw new Error('/press <keys>');
301
+ showStep(await onCall().press(rest));
302
+ },
303
+ transfer: async (rest) => {
304
+ if (rest !== 'answered' && rest !== 'failed')
305
+ throw new Error('/transfer answered|failed');
306
+ showStep(await onCall().transfer(rest));
307
+ },
308
+ hangup: async () => showStep(await onCall().hangup()),
309
+ wait: async (rest) => {
310
+ const [kind, seconds] = rest.split(/\s+/);
311
+ if (kind !== 'sms' && kind !== 'email' && kind !== 'call')
312
+ throw new Error('/wait sms|email|call [seconds]');
313
+ const wait = Number(seconds) > 0 ? Number(seconds) : 60;
314
+ out(dim(`waiting up to ${wait} s for ${kind === 'sms' ? 'a text' : kind === 'call' ? 'a call' : 'an email'}…`));
315
+ if (!(await waitFor(kind, wait)))
316
+ throw new Error(`no ${kind} came within ${wait} s`);
317
+ },
318
+ due: async () => {
319
+ const cases = await caseIds();
320
+ if (!cases.length)
321
+ throw new Error('you have no open case yet: call, text or email first');
322
+ // The latest case with something scheduled: its earliest step.
323
+ for (const instanceId of cases) {
324
+ const due = await sandbox.runDue({ instanceId });
325
+ if (!due.action)
326
+ continue;
327
+ out(dim(`done now: ${due.action.kind}${due.action.reason ? ` (${due.action.reason})` : ''}, booked for ${due.action.scheduledFor}`));
328
+ return;
329
+ }
330
+ out(dim('nothing scheduled on your cases'));
331
+ },
332
+ inbox: async () => {
333
+ const items = await customer.inbox(since);
334
+ if (!items.length)
335
+ out(dim('nothing yet'));
336
+ for (const item of items)
337
+ showItem(item);
338
+ },
339
+ turns: async () => {
340
+ const cases = await caseIds();
341
+ if (!cases.length)
342
+ throw new Error('you have no case yet');
343
+ for (const instanceId of [...cases].reverse()) {
344
+ out(dim(`case ${instanceId}`));
345
+ for (const turn of await sandbox.turns({ instanceId }))
346
+ out(formatTurnLine(turn));
347
+ }
348
+ },
349
+ };
350
+ out(`📱 ${me.phone} · ${me.email} on ${sandbox.environment.name} — /help for what you can do`);
351
+ void poll();
352
+ if (tty)
353
+ rl.prompt();
354
+ let quitting = false;
355
+ for await (const raw of rl) {
356
+ const line = raw.trim();
357
+ if (!line) {
358
+ if (tty)
359
+ rl.prompt();
360
+ continue;
361
+ }
362
+ if (!tty)
363
+ out(dim(`> ${line}`));
364
+ try {
365
+ if (line.startsWith('/')) {
366
+ const [name = '', ...rest] = line.slice(1).split(' ');
367
+ if (name === 'quit' || name === 'exit') {
368
+ quitting = true;
369
+ break;
370
+ }
371
+ const command = commands[name];
372
+ if (!command)
373
+ throw new Error(`/${name}: no such command (/help)`);
374
+ await command(rest.join(' ').trim());
375
+ }
376
+ else if (state.call) {
377
+ showStep(await state.call.say(line));
378
+ }
379
+ else {
380
+ await commands.sms(line);
381
+ }
382
+ }
383
+ catch (error) {
384
+ out(bad(error instanceof Error ? error.message : String(error)));
385
+ }
386
+ if (tty)
387
+ rl.prompt();
388
+ }
389
+ // Leave nothing open: the call you are on is hung up, so its case moves on.
390
+ if (state.call) {
391
+ try {
392
+ showStep(await state.call.hangup());
393
+ }
394
+ catch {
395
+ // ended meanwhile
396
+ }
397
+ }
398
+ polling = false;
399
+ rl.close();
400
+ await sandbox.close();
401
+ if (!quitting && !tty)
402
+ out(dim('input ended'));
403
+ process.exit(0);
404
+ }
405
+ //# sourceMappingURL=index.js.map
@@ -1,7 +1,6 @@
1
1
  import type { AnyEventHandlers } from '../../core/functions/events.js';
2
2
  import type { FuncMap } from '../../core/functions/types.js';
3
- import { type CompanyPart, type SequenceBuild } from '../../builder/types.js';
4
- import type { CompiledCompanyMetadata } from '../../core/slot/schema.js';
3
+ import { type CompanyPart, type RuntimePackageSource, type SequenceBuild, type SourceMaps } from '../../builder/types.js';
5
4
  import { type OverlayAuthErrorMessage } from '../../dev-overlay/protocol.js';
6
5
  export type ServeOptions = {
7
6
  url?: string;
@@ -10,17 +9,25 @@ export type ServeOptions = {
10
9
  };
11
10
  type LoadedSequence = {
12
11
  build: SequenceBuild;
12
+ /** Its code's maps by `functionKey` (the page script's: the backend runs the page this compiled). */
13
+ sourceMaps: SourceMaps;
13
14
  functions: FuncMap;
14
15
  events: AnyEventHandlers;
15
16
  generation: number;
16
17
  };
17
18
  /** The company-level functions (22 §5), compiled without code, and their implementations. */
18
19
  type LoadedCompany = {
20
+ /** Its `schema` comes from `schema/`, apart from any sequence: a repository with no sequence still pushes it. */
19
21
  part: CompanyPart;
20
- /** From `schema/`, apart from any sequence: a repository with no sequence still pushes it. */
21
- companyMetadata: CompiledCompanyMetadata;
22
22
  functions: FuncMap;
23
23
  };
24
+ /** What one execute runs on: a sequence's functions, or the company's. */
25
+ type Executable = {
26
+ functions: FuncMap;
27
+ events: AnyEventHandlers;
28
+ runtimeVersion: string;
29
+ runtimeSource: RuntimePackageSource;
30
+ };
24
31
  export declare function overlayWebSocketUrl(raw: string): string;
25
32
  /** Not the repository's code: dependencies, git, and what compiling writes. */
26
33
  export declare function isIgnoredServePath(repoRoot: string, file: string): boolean;
@@ -73,7 +80,36 @@ export declare function reloadServeState(repoRoot: string, current: ServeState,
73
80
  * With no sequence left it is still pushed, so the environment drops the ones it had (22 §11).
74
81
  */
75
82
  export declare function pushOverlay(ws: Pick<WebSocket, 'send'>, company: LoadedCompany, sequences: Map<string, LoadedSequence>, generation: number): void;
83
+ /**
84
+ * What the company is shown of a failed function's stack (the backend's company logs): the error's first
85
+ * line and the frames of the repository's own files, by their path from its root. tsx runs the original
86
+ * files, so their lines are already the company's. Undefined when no frame is the repository's.
87
+ */
88
+ export declare function repositoryStack(error: unknown, repoRoot: string): string | undefined;
76
89
  export declare function toOverlayError(message: OverlayAuthErrorMessage): Error;
90
+ /**
91
+ * Runs each `execute` the backend sends, unless its deadline has passed: the backend has then reported
92
+ * it as failed (a paused process, a laptop that slept), and running it would repeat its effects when
93
+ * the call is retried. One already started is not interrupted.
94
+ */
95
+ export declare function attachExecuteHandler(ws: Pick<WebSocket, 'addEventListener' | 'send'>, find: (scope: string) => Executable | undefined, rpcUrl: string, repoRoot: string): void;
77
96
  export declare function runServe(repoRoot: string, options?: ServeOptions): Promise<void>;
97
+ /** A Local Serve opened from code (the integration tests' `connect`): the repository's code on one environment until `close`. */
98
+ export type OpenOverlay = {
99
+ /** Stops serving: the environment runs its release again. */
100
+ close(): Promise<void>;
101
+ /** The sequences served. */
102
+ sequences: string[];
103
+ };
104
+ /**
105
+ * The repository served on `environmentId` as `serve` serves it, once (no file watch, no reconnect): its
106
+ * code loaded through the company import graph (an import the compile refuses fails here), pushed, and
107
+ * every function the environment runs meanwhile run here. Resolves once the environment took it.
108
+ */
109
+ export declare function openOverlay(repoRoot: string, options: {
110
+ backendUrl: string;
111
+ token: string;
112
+ environmentId: string;
113
+ }): Promise<OpenOverlay>;
78
114
  export {};
79
115
  //# sourceMappingURL=index.d.ts.map