@go-go-golems/os-scripting 0.1.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.
- package/README.md +147 -0
- package/app/createAppStore.d.ts +89 -0
- package/app/createAppStore.js +90 -0
- package/app/index.d.ts +2 -0
- package/app/index.js +2 -0
- package/app/runtimeSessionLifecycleMiddleware.d.ts +6 -0
- package/app/runtimeSessionLifecycleMiddleware.js +42 -0
- package/features/runtimeSessions/capabilityPolicy.d.ts +12 -0
- package/features/runtimeSessions/capabilityPolicy.js +36 -0
- package/features/runtimeSessions/index.d.ts +3 -0
- package/features/runtimeSessions/index.js +3 -0
- package/features/runtimeSessions/runtimeSessionsSlice.d.ts +90 -0
- package/features/runtimeSessions/runtimeSessionsSlice.js +210 -0
- package/features/runtimeSessions/selectors.d.ts +16 -0
- package/features/runtimeSessions/selectors.js +40 -0
- package/hypercard/artifacts/artifactProjectionMiddleware.d.ts +1 -0
- package/hypercard/artifacts/artifactProjectionMiddleware.js +66 -0
- package/hypercard/artifacts/artifactRuntime.d.ts +22 -0
- package/hypercard/artifacts/artifactRuntime.js +137 -0
- package/hypercard/artifacts/artifactsSelectors.d.ts +10 -0
- package/hypercard/artifacts/artifactsSelectors.js +2 -0
- package/hypercard/artifacts/artifactsSlice.d.ts +38 -0
- package/hypercard/artifacts/artifactsSlice.js +94 -0
- package/hypercard/debug/RuntimeSurfaceDebugWindow.d.ts +7 -0
- package/hypercard/debug/RuntimeSurfaceDebugWindow.js +181 -0
- package/hypercard/debug/jsSessionDebugRegistry.d.ts +11 -0
- package/hypercard/debug/jsSessionDebugRegistry.js +43 -0
- package/hypercard/debug/runtimeDebugApp.d.ts +19 -0
- package/hypercard/debug/runtimeDebugApp.js +25 -0
- package/hypercard/debug/runtimeDebugRegistry.d.ts +6 -0
- package/hypercard/debug/runtimeDebugRegistry.js +49 -0
- package/hypercard/editor/CodeEditorWindow.d.ts +7 -0
- package/hypercard/editor/CodeEditorWindow.js +128 -0
- package/hypercard/editor/editorLaunch.d.ts +16 -0
- package/hypercard/editor/editorLaunch.js +58 -0
- package/hypercard/editor/runtimeSurfaceRef.d.ts +8 -0
- package/hypercard/editor/runtimeSurfaceRef.js +60 -0
- package/hypercard/index.d.ts +12 -0
- package/hypercard/index.js +12 -0
- package/hypercard/task-manager/TaskManagerWindow.d.ts +1 -0
- package/hypercard/task-manager/TaskManagerWindow.js +102 -0
- package/hypercard/task-manager/index.d.ts +6 -0
- package/hypercard/task-manager/index.js +6 -0
- package/hypercard/task-manager/jsSessionSource.d.ts +10 -0
- package/hypercard/task-manager/jsSessionSource.js +49 -0
- package/hypercard/task-manager/runtimeSessionSource.d.ts +26 -0
- package/hypercard/task-manager/runtimeSessionSource.js +123 -0
- package/hypercard/task-manager/taskManagerApp.d.ts +16 -0
- package/hypercard/task-manager/taskManagerApp.js +25 -0
- package/hypercard/task-manager/taskManagerRegistry.d.ts +10 -0
- package/hypercard/task-manager/taskManagerRegistry.js +75 -0
- package/hypercard/task-manager/types.d.ts +25 -0
- package/hypercard/task-manager/types.js +1 -0
- package/hypercard/timeline/hypercardCard.d.ts +5 -0
- package/hypercard/timeline/hypercardCard.js +104 -0
- package/index.d.ts +18 -0
- package/index.js +18 -0
- package/package.json +72 -0
- package/plugin-runtime/contracts.d.ts +116 -0
- package/plugin-runtime/contracts.js +32 -0
- package/plugin-runtime/fixtures/column-stack.vm.js +19 -0
- package/plugin-runtime/fixtures/dynamic-card.vm.js +13 -0
- package/plugin-runtime/fixtures/inventory-stack.vm.js +29 -0
- package/plugin-runtime/fixtures/kanban-card.vm.js +55 -0
- package/plugin-runtime/fixtures/loop-stack.vm.js +16 -0
- package/plugin-runtime/fixtures/patched-low-stock-handler.vm.js +3 -0
- package/plugin-runtime/fixtures/patched-low-stock-render.vm.js +5 -0
- package/plugin-runtime/index.d.ts +6 -0
- package/plugin-runtime/index.js +6 -0
- package/plugin-runtime/intentSchema.d.ts +3 -0
- package/plugin-runtime/intentSchema.js +25 -0
- package/plugin-runtime/jsEvalSupport.d.ts +6 -0
- package/plugin-runtime/jsEvalSupport.js +93 -0
- package/plugin-runtime/jsSessionService.d.ts +55 -0
- package/plugin-runtime/jsSessionService.js +136 -0
- package/plugin-runtime/quickJsSessionCore.d.ts +24 -0
- package/plugin-runtime/quickJsSessionCore.js +92 -0
- package/plugin-runtime/runtimeService.d.ts +34 -0
- package/plugin-runtime/runtimeService.js +248 -0
- package/plugin-runtime/runtimeSurfaceRegistry.d.ts +45 -0
- package/plugin-runtime/runtimeSurfaceRegistry.js +73 -0
- package/plugin-runtime/stack-bootstrap.vm.js +236 -0
- package/repl/attachedJsSessionRegistry.d.ts +25 -0
- package/repl/attachedJsSessionRegistry.js +81 -0
- package/repl/attachedRuntimeSessionRegistry.d.ts +11 -0
- package/repl/attachedRuntimeSessionRegistry.js +107 -0
- package/repl/hypercardReplDriver.d.ts +54 -0
- package/repl/hypercardReplDriver.js +600 -0
- package/repl/jsReplDriver.d.ts +8 -0
- package/repl/jsReplDriver.js +348 -0
- package/repl/jsSessionBroker.d.ts +21 -0
- package/repl/jsSessionBroker.js +75 -0
- package/repl/runtimeBroker.d.ts +43 -0
- package/repl/runtimeBroker.js +117 -0
- package/runtime-host/RuntimeSurfaceSessionHost.d.ts +8 -0
- package/runtime-host/RuntimeSurfaceSessionHost.js +384 -0
- package/runtime-host/fixtures/CardSessionHost.chat.vm.js +61 -0
- package/runtime-host/fixtures/CardSessionHost.list.vm.js +29 -0
- package/runtime-host/fixtures/CardSessionHost.nav.vm.js +101 -0
- package/runtime-host/fixtures/CardSessionHost.report.vm.js +34 -0
- package/runtime-host/pluginIntentRouting.d.ts +13 -0
- package/runtime-host/pluginIntentRouting.js +83 -0
- package/runtime-packages/index.d.ts +1 -0
- package/runtime-packages/index.js +1 -0
- package/runtime-packages/runtimePackageRegistry.d.ts +14 -0
- package/runtime-packages/runtimePackageRegistry.js +42 -0
- package/runtime-packages/ui.package.vm.js +84 -0
- package/runtime-packs/index.d.ts +1 -0
- package/runtime-packs/index.js +1 -0
- package/runtime-packs/runtimeSurfaceTypeRegistry.d.ts +20 -0
- package/runtime-packs/runtimeSurfaceTypeRegistry.js +37 -0
- package/runtime-session-manager/index.d.ts +2 -0
- package/runtime-session-manager/index.js +2 -0
- package/runtime-session-manager/runtimeOwnership.d.ts +10 -0
- package/runtime-session-manager/runtimeOwnership.js +19 -0
- package/runtime-session-manager/runtimeSessionManager.d.ts +47 -0
- package/runtime-session-manager/runtimeSessionManager.js +214 -0
- package/testRuntimeUi.d.ts +4 -0
- package/testRuntimeUi.js +39 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { getAttachedJsSession, listAttachedJsSessions } from './attachedJsSessionRegistry';
|
|
2
|
+
import { createJsSessionBroker } from './jsSessionBroker';
|
|
3
|
+
const JS_KEYWORDS = [
|
|
4
|
+
'await',
|
|
5
|
+
'break',
|
|
6
|
+
'const',
|
|
7
|
+
'continue',
|
|
8
|
+
'else',
|
|
9
|
+
'false',
|
|
10
|
+
'for',
|
|
11
|
+
'function',
|
|
12
|
+
'if',
|
|
13
|
+
'let',
|
|
14
|
+
'null',
|
|
15
|
+
'return',
|
|
16
|
+
'switch',
|
|
17
|
+
'throw',
|
|
18
|
+
'true',
|
|
19
|
+
'typeof',
|
|
20
|
+
'undefined',
|
|
21
|
+
'var',
|
|
22
|
+
'while',
|
|
23
|
+
];
|
|
24
|
+
const COMMAND_HELP = {
|
|
25
|
+
':spawn': {
|
|
26
|
+
title: ':spawn',
|
|
27
|
+
detail: 'Spawn a blank JavaScript session.',
|
|
28
|
+
usage: ':spawn [session-id]',
|
|
29
|
+
},
|
|
30
|
+
':sessions': {
|
|
31
|
+
title: ':sessions',
|
|
32
|
+
detail: 'List live JavaScript sessions, including attached runtime-backed sessions.',
|
|
33
|
+
usage: ':sessions',
|
|
34
|
+
},
|
|
35
|
+
':attach': {
|
|
36
|
+
title: ':attach',
|
|
37
|
+
detail: 'Attach to a live runtime-backed JavaScript session.',
|
|
38
|
+
usage: ':attach <session-id>',
|
|
39
|
+
},
|
|
40
|
+
':use': {
|
|
41
|
+
title: ':use',
|
|
42
|
+
detail: 'Select the active JavaScript session for plain eval lines.',
|
|
43
|
+
usage: ':use <session-id>',
|
|
44
|
+
},
|
|
45
|
+
':globals': {
|
|
46
|
+
title: ':globals',
|
|
47
|
+
detail: 'List globals in the active or provided session.',
|
|
48
|
+
usage: ':globals [session-id]',
|
|
49
|
+
},
|
|
50
|
+
':reset': {
|
|
51
|
+
title: ':reset',
|
|
52
|
+
detail: 'Reset the active or provided session back to its initial prelude.',
|
|
53
|
+
usage: ':reset [session-id]',
|
|
54
|
+
},
|
|
55
|
+
':dispose': {
|
|
56
|
+
title: ':dispose',
|
|
57
|
+
detail: 'Dispose a JavaScript session.',
|
|
58
|
+
usage: ':dispose [session-id]',
|
|
59
|
+
},
|
|
60
|
+
':help': {
|
|
61
|
+
title: ':help',
|
|
62
|
+
detail: 'Show JS REPL command help.',
|
|
63
|
+
usage: ':help [topic]',
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
function splitCommand(raw) {
|
|
67
|
+
const trimmed = raw.trim();
|
|
68
|
+
const spaceIndex = trimmed.indexOf(' ');
|
|
69
|
+
if (spaceIndex === -1) {
|
|
70
|
+
return { command: trimmed, rest: '' };
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
command: trimmed.slice(0, spaceIndex),
|
|
74
|
+
rest: trimmed.slice(spaceIndex + 1).trim(),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function nextGeneratedSessionId(counter) {
|
|
78
|
+
return `js-${counter}`;
|
|
79
|
+
}
|
|
80
|
+
function formatValue(value) {
|
|
81
|
+
if (typeof value === 'string') {
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
if (value === undefined) {
|
|
85
|
+
return 'undefined';
|
|
86
|
+
}
|
|
87
|
+
if (value === null) {
|
|
88
|
+
return 'null';
|
|
89
|
+
}
|
|
90
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
91
|
+
return String(value);
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
return JSON.stringify(value, null, 2);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return String(value);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function formatEvalLines(result) {
|
|
101
|
+
const lines = result.logs.map((text) => ({ type: 'system', text }));
|
|
102
|
+
if (result.error) {
|
|
103
|
+
lines.push({
|
|
104
|
+
type: 'error',
|
|
105
|
+
text: `${result.error.name}: ${result.error.message}`,
|
|
106
|
+
});
|
|
107
|
+
return lines;
|
|
108
|
+
}
|
|
109
|
+
lines.push({
|
|
110
|
+
type: 'output',
|
|
111
|
+
text: formatValue(result.value),
|
|
112
|
+
});
|
|
113
|
+
return lines;
|
|
114
|
+
}
|
|
115
|
+
function currentToken(input) {
|
|
116
|
+
const match = input.match(/([A-Za-z_$:][\w$:]*)$/);
|
|
117
|
+
return match?.[1] ?? '';
|
|
118
|
+
}
|
|
119
|
+
function promptForSession(sessionId, origin) {
|
|
120
|
+
if (!sessionId || !origin) {
|
|
121
|
+
return 'js>';
|
|
122
|
+
}
|
|
123
|
+
return origin === 'attached-runtime' ? `js[runtime:${sessionId}]>` : `js[${sessionId}]>`;
|
|
124
|
+
}
|
|
125
|
+
export function createJsReplDriver(options = {}) {
|
|
126
|
+
const broker = options.broker ?? createJsSessionBroker();
|
|
127
|
+
let activeSessionId = options.initialSessionId ?? null;
|
|
128
|
+
let activeOrigin = options.initialOrigin ?? null;
|
|
129
|
+
let sessionCounter = 1;
|
|
130
|
+
function requireSession(sessionIdArg) {
|
|
131
|
+
const sessionId = sessionIdArg ?? activeSessionId;
|
|
132
|
+
if (!sessionId) {
|
|
133
|
+
throw new Error('No active JS session. Use :spawn or :use <session-id> first.');
|
|
134
|
+
}
|
|
135
|
+
const spawned = broker.getSession(sessionId);
|
|
136
|
+
if (spawned) {
|
|
137
|
+
return {
|
|
138
|
+
kind: 'spawned',
|
|
139
|
+
sessionId,
|
|
140
|
+
title: broker.listSessions().find((summary) => summary.sessionId === sessionId)?.title ?? sessionId,
|
|
141
|
+
origin: 'spawned',
|
|
142
|
+
session: spawned,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const attached = getAttachedJsSession(sessionId);
|
|
146
|
+
if (attached) {
|
|
147
|
+
return {
|
|
148
|
+
kind: 'attached-runtime',
|
|
149
|
+
sessionId,
|
|
150
|
+
title: attached.summary.title,
|
|
151
|
+
origin: 'attached-runtime',
|
|
152
|
+
session: attached.handle,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
throw new Error(`Unknown JS session: ${sessionId}`);
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
async execute(raw, _context) {
|
|
159
|
+
const trimmed = raw.trim();
|
|
160
|
+
if (!trimmed) {
|
|
161
|
+
return { lines: [] };
|
|
162
|
+
}
|
|
163
|
+
if (trimmed.startsWith(':')) {
|
|
164
|
+
const { command, rest } = splitCommand(trimmed);
|
|
165
|
+
switch (command) {
|
|
166
|
+
case ':spawn': {
|
|
167
|
+
const sessionId = rest || nextGeneratedSessionId(sessionCounter++);
|
|
168
|
+
await broker.spawnSession({ sessionId, title: `JavaScript ${sessionId}` });
|
|
169
|
+
activeSessionId = sessionId;
|
|
170
|
+
activeOrigin = 'spawned';
|
|
171
|
+
return {
|
|
172
|
+
lines: [
|
|
173
|
+
{ type: 'system', text: `Spawned JS session ${sessionId}` },
|
|
174
|
+
],
|
|
175
|
+
envVars: {
|
|
176
|
+
REPL_PROMPT: promptForSession(sessionId, 'spawned'),
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
case ':sessions': {
|
|
181
|
+
const sessions = [
|
|
182
|
+
...broker.listSessions().map((summary) => ({
|
|
183
|
+
sessionId: summary.sessionId,
|
|
184
|
+
title: summary.title,
|
|
185
|
+
origin: 'spawned',
|
|
186
|
+
})),
|
|
187
|
+
...listAttachedJsSessions().map((entry) => ({
|
|
188
|
+
sessionId: entry.summary.sessionId,
|
|
189
|
+
title: entry.summary.title,
|
|
190
|
+
origin: 'attached-runtime',
|
|
191
|
+
})),
|
|
192
|
+
].sort((left, right) => left.sessionId.localeCompare(right.sessionId));
|
|
193
|
+
if (sessions.length === 0) {
|
|
194
|
+
return { lines: [{ type: 'system', text: 'No JS sessions.' }] };
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
lines: sessions.map((summary) => ({
|
|
198
|
+
type: 'output',
|
|
199
|
+
text: `${summary.sessionId}${summary.sessionId === activeSessionId ? ' *' : ''} — ` +
|
|
200
|
+
`${summary.title} [${summary.origin}]`,
|
|
201
|
+
})),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
case ':attach': {
|
|
205
|
+
if (!rest) {
|
|
206
|
+
throw new Error('Usage: :attach <session-id>');
|
|
207
|
+
}
|
|
208
|
+
const attached = getAttachedJsSession(rest);
|
|
209
|
+
if (!attached) {
|
|
210
|
+
throw new Error(`Unknown attached JS session: ${rest}`);
|
|
211
|
+
}
|
|
212
|
+
activeSessionId = rest;
|
|
213
|
+
activeOrigin = 'attached-runtime';
|
|
214
|
+
return {
|
|
215
|
+
lines: [{ type: 'system', text: `Attached JS console to runtime session ${rest}` }],
|
|
216
|
+
envVars: {
|
|
217
|
+
REPL_PROMPT: promptForSession(rest, 'attached-runtime'),
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
case ':use': {
|
|
222
|
+
if (!rest) {
|
|
223
|
+
throw new Error('Usage: :use <session-id>');
|
|
224
|
+
}
|
|
225
|
+
const active = requireSession(rest);
|
|
226
|
+
activeSessionId = rest;
|
|
227
|
+
activeOrigin = active.origin;
|
|
228
|
+
return {
|
|
229
|
+
lines: [{ type: 'system', text: `Active JS session: ${rest} [${active.origin}]` }],
|
|
230
|
+
envVars: {
|
|
231
|
+
REPL_PROMPT: promptForSession(rest, active.origin),
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
case ':globals': {
|
|
236
|
+
const { sessionId, session } = requireSession(rest || undefined);
|
|
237
|
+
return {
|
|
238
|
+
lines: [
|
|
239
|
+
{ type: 'system', text: `Globals for ${sessionId}` },
|
|
240
|
+
...session.inspectGlobals().map((name) => ({ type: 'output', text: name })),
|
|
241
|
+
],
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
case ':reset': {
|
|
245
|
+
const active = requireSession(rest || undefined);
|
|
246
|
+
if (active.kind !== 'spawned') {
|
|
247
|
+
throw new Error(`Attached JS session ${active.sessionId} cannot be reset`);
|
|
248
|
+
}
|
|
249
|
+
const { sessionId, session } = active;
|
|
250
|
+
await session.reset();
|
|
251
|
+
return {
|
|
252
|
+
lines: [{ type: 'system', text: `Reset JS session ${sessionId}` }],
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
case ':dispose': {
|
|
256
|
+
const sessionId = rest || activeSessionId;
|
|
257
|
+
if (!sessionId) {
|
|
258
|
+
throw new Error('Usage: :dispose <session-id>');
|
|
259
|
+
}
|
|
260
|
+
if (getAttachedJsSession(sessionId)) {
|
|
261
|
+
throw new Error(`Attached JS session ${sessionId} cannot be disposed from JavaScript REPL`);
|
|
262
|
+
}
|
|
263
|
+
const disposed = broker.disposeSession(sessionId);
|
|
264
|
+
if (!disposed) {
|
|
265
|
+
throw new Error(`Unknown JS session: ${sessionId}`);
|
|
266
|
+
}
|
|
267
|
+
if (activeSessionId === sessionId) {
|
|
268
|
+
activeSessionId = null;
|
|
269
|
+
activeOrigin = null;
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
lines: [{ type: 'system', text: `Disposed JS session ${sessionId}` }],
|
|
273
|
+
envVars: {
|
|
274
|
+
REPL_PROMPT: promptForSession(null, null),
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
case ':help': {
|
|
279
|
+
const topic = rest || null;
|
|
280
|
+
const entries = topic
|
|
281
|
+
? Object.values(COMMAND_HELP).filter((entry) => entry.title === topic)
|
|
282
|
+
: Object.values(COMMAND_HELP);
|
|
283
|
+
return {
|
|
284
|
+
lines: entries.flatMap((entry) => [
|
|
285
|
+
{ type: 'output', text: `${entry.title} — ${entry.detail}` },
|
|
286
|
+
...(entry.usage ? [{ type: 'system', text: ` ${entry.usage}` }] : []),
|
|
287
|
+
]),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
default:
|
|
291
|
+
throw new Error(`Unknown JS REPL command: ${command}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const { session } = requireSession();
|
|
295
|
+
return {
|
|
296
|
+
lines: formatEvalLines('eval' in session ? session.eval(trimmed) : session.evaluate(trimmed)),
|
|
297
|
+
};
|
|
298
|
+
},
|
|
299
|
+
getCompletions(input) {
|
|
300
|
+
const trimmed = input.trimStart();
|
|
301
|
+
if (trimmed.startsWith(':')) {
|
|
302
|
+
const token = currentToken(trimmed);
|
|
303
|
+
if (trimmed.startsWith(':use') ||
|
|
304
|
+
trimmed.startsWith(':attach') ||
|
|
305
|
+
trimmed.startsWith(':globals')) {
|
|
306
|
+
const partial = trimmed.split(/\s+/)[1] ?? '';
|
|
307
|
+
return [
|
|
308
|
+
...broker.listSessions().map((summary) => ({ value: summary.sessionId, detail: `${summary.title} [spawned]` })),
|
|
309
|
+
...listAttachedJsSessions().map((entry) => ({
|
|
310
|
+
value: entry.summary.sessionId,
|
|
311
|
+
detail: `${entry.summary.title} [attached-runtime]`,
|
|
312
|
+
})),
|
|
313
|
+
]
|
|
314
|
+
.filter((entry) => entry.value.startsWith(partial));
|
|
315
|
+
}
|
|
316
|
+
if (trimmed.startsWith(':dispose') || trimmed.startsWith(':reset')) {
|
|
317
|
+
const partial = trimmed.split(/\s+/)[1] ?? '';
|
|
318
|
+
return broker.listSessions()
|
|
319
|
+
.filter((summary) => summary.sessionId.startsWith(partial))
|
|
320
|
+
.map((summary) => ({ value: summary.sessionId, detail: summary.title }));
|
|
321
|
+
}
|
|
322
|
+
return Object.values(COMMAND_HELP)
|
|
323
|
+
.filter((entry) => entry.title.startsWith(token || ':'))
|
|
324
|
+
.map((entry) => ({ value: entry.title, detail: entry.detail }));
|
|
325
|
+
}
|
|
326
|
+
const token = currentToken(input);
|
|
327
|
+
let globals = [];
|
|
328
|
+
if (activeSessionId) {
|
|
329
|
+
try {
|
|
330
|
+
globals = requireSession(activeSessionId).session.inspectGlobals();
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
globals = [];
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return [
|
|
337
|
+
...JS_KEYWORDS.map((keyword) => ({ value: keyword, detail: 'js keyword' })),
|
|
338
|
+
...globals.map((name) => ({ value: name, detail: 'session global' })),
|
|
339
|
+
].filter((entry) => entry.value.startsWith(token));
|
|
340
|
+
},
|
|
341
|
+
getHelp(topic) {
|
|
342
|
+
if (!topic) {
|
|
343
|
+
return Object.values(COMMAND_HELP);
|
|
344
|
+
}
|
|
345
|
+
return Object.values(COMMAND_HELP).filter((entry) => entry.title === topic);
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type CreateJsSessionRequest, type JsEvalResult, type JsSessionServiceOptions, type JsSessionSummary } from '../plugin-runtime/jsSessionService';
|
|
2
|
+
export interface SpawnJsSessionRequest extends CreateJsSessionRequest {
|
|
3
|
+
}
|
|
4
|
+
export interface JsSessionHandle {
|
|
5
|
+
readonly sessionId: string;
|
|
6
|
+
eval(code: string): JsEvalResult;
|
|
7
|
+
inspectGlobals(): string[];
|
|
8
|
+
installPrelude(code: string): void;
|
|
9
|
+
reset(): Promise<JsSessionSummary>;
|
|
10
|
+
dispose(): boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface JsSessionBroker {
|
|
13
|
+
spawnSession(request: SpawnJsSessionRequest): Promise<JsSessionHandle>;
|
|
14
|
+
getSession(sessionId: string): JsSessionHandle | null;
|
|
15
|
+
listSessions(): JsSessionSummary[];
|
|
16
|
+
resetSession(sessionId: string): Promise<JsSessionSummary>;
|
|
17
|
+
disposeSession(sessionId: string): boolean;
|
|
18
|
+
clear(): void;
|
|
19
|
+
subscribe(listener: () => void): () => void;
|
|
20
|
+
}
|
|
21
|
+
export declare function createJsSessionBroker(options?: JsSessionServiceOptions): JsSessionBroker;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { JsSessionService, } from '../plugin-runtime/jsSessionService';
|
|
2
|
+
export function createJsSessionBroker(options = {}) {
|
|
3
|
+
const sessionService = new JsSessionService(options);
|
|
4
|
+
const handles = new Map();
|
|
5
|
+
const listeners = new Set();
|
|
6
|
+
function emit() {
|
|
7
|
+
listeners.forEach((listener) => listener());
|
|
8
|
+
}
|
|
9
|
+
function createHandle(sessionId) {
|
|
10
|
+
return {
|
|
11
|
+
sessionId,
|
|
12
|
+
eval(code) {
|
|
13
|
+
return sessionService.evaluate(sessionId, code);
|
|
14
|
+
},
|
|
15
|
+
inspectGlobals() {
|
|
16
|
+
return sessionService.getGlobalNames(sessionId);
|
|
17
|
+
},
|
|
18
|
+
installPrelude(code) {
|
|
19
|
+
sessionService.installPrelude(sessionId, code);
|
|
20
|
+
},
|
|
21
|
+
async reset() {
|
|
22
|
+
const summary = await sessionService.resetSession(sessionId);
|
|
23
|
+
emit();
|
|
24
|
+
return summary;
|
|
25
|
+
},
|
|
26
|
+
dispose() {
|
|
27
|
+
return broker.disposeSession(sessionId);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const broker = {
|
|
32
|
+
async spawnSession(request) {
|
|
33
|
+
if (handles.has(request.sessionId)) {
|
|
34
|
+
throw new Error(`JS session already exists: ${request.sessionId}`);
|
|
35
|
+
}
|
|
36
|
+
await sessionService.createSession(request);
|
|
37
|
+
const handle = createHandle(request.sessionId);
|
|
38
|
+
handles.set(request.sessionId, handle);
|
|
39
|
+
emit();
|
|
40
|
+
return handle;
|
|
41
|
+
},
|
|
42
|
+
getSession(sessionId) {
|
|
43
|
+
return handles.get(sessionId) ?? null;
|
|
44
|
+
},
|
|
45
|
+
listSessions() {
|
|
46
|
+
return sessionService.listSessions();
|
|
47
|
+
},
|
|
48
|
+
async resetSession(sessionId) {
|
|
49
|
+
const summary = await sessionService.resetSession(sessionId);
|
|
50
|
+
emit();
|
|
51
|
+
return summary;
|
|
52
|
+
},
|
|
53
|
+
disposeSession(sessionId) {
|
|
54
|
+
const disposed = sessionService.disposeSession(sessionId);
|
|
55
|
+
if (!disposed) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
handles.delete(sessionId);
|
|
59
|
+
emit();
|
|
60
|
+
return true;
|
|
61
|
+
},
|
|
62
|
+
clear() {
|
|
63
|
+
sessionService.clear();
|
|
64
|
+
handles.clear();
|
|
65
|
+
emit();
|
|
66
|
+
},
|
|
67
|
+
subscribe(listener) {
|
|
68
|
+
listeners.add(listener);
|
|
69
|
+
return () => {
|
|
70
|
+
listeners.delete(listener);
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
return broker;
|
|
75
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { RuntimeBundleMeta, RuntimeSurfaceId, RuntimeAction, SessionId, StackId } from '../plugin-runtime/contracts';
|
|
2
|
+
import { type QuickJSRuntimeServiceOptions } from '../plugin-runtime/runtimeService';
|
|
3
|
+
import { type RuntimeSessionOwnership } from '../runtime-session-manager/runtimeOwnership';
|
|
4
|
+
export interface SpawnRuntimeSessionRequest {
|
|
5
|
+
stackId: StackId;
|
|
6
|
+
sessionId: SessionId;
|
|
7
|
+
packageIds: string[];
|
|
8
|
+
bundleCode: string;
|
|
9
|
+
}
|
|
10
|
+
export interface RuntimeSessionSummary {
|
|
11
|
+
sessionId: SessionId;
|
|
12
|
+
stackId: StackId;
|
|
13
|
+
packageIds: string[];
|
|
14
|
+
surfaces: string[];
|
|
15
|
+
surfaceTypes?: Record<string, string>;
|
|
16
|
+
title: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
origin: 'spawned' | 'attached';
|
|
19
|
+
writable: boolean;
|
|
20
|
+
ownership: RuntimeSessionOwnership;
|
|
21
|
+
}
|
|
22
|
+
export interface RuntimeSessionHandle {
|
|
23
|
+
readonly sessionId: SessionId;
|
|
24
|
+
readonly stackId: StackId;
|
|
25
|
+
readonly origin: 'spawned' | 'attached';
|
|
26
|
+
readonly writable: boolean;
|
|
27
|
+
getBundleMeta(): RuntimeBundleMeta;
|
|
28
|
+
renderSurface(surfaceId: RuntimeSurfaceId, state: unknown): unknown;
|
|
29
|
+
eventSurface(surfaceId: RuntimeSurfaceId, handler: string, args: unknown, state: unknown): RuntimeAction[];
|
|
30
|
+
defineSurface(surfaceId: RuntimeSurfaceId, code: string, packId: string): RuntimeBundleMeta;
|
|
31
|
+
defineSurfaceRender(surfaceId: RuntimeSurfaceId, code: string): RuntimeBundleMeta;
|
|
32
|
+
defineSurfaceHandler(surfaceId: RuntimeSurfaceId, handler: string, code: string): RuntimeBundleMeta;
|
|
33
|
+
dispose(): boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface RuntimeBroker {
|
|
36
|
+
spawnSession(request: SpawnRuntimeSessionRequest): Promise<RuntimeSessionHandle>;
|
|
37
|
+
getSession(sessionId: SessionId): RuntimeSessionHandle | null;
|
|
38
|
+
listSessions(): RuntimeSessionSummary[];
|
|
39
|
+
disposeSession(sessionId: SessionId): boolean;
|
|
40
|
+
clear(): void;
|
|
41
|
+
subscribe(listener: () => void): () => void;
|
|
42
|
+
}
|
|
43
|
+
export declare function createRuntimeBroker(options?: QuickJSRuntimeServiceOptions): RuntimeBroker;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { QuickJSRuntimeService } from '../plugin-runtime/runtimeService';
|
|
2
|
+
import { BROKER_OWNED_RUNTIME_SESSION, } from '../runtime-session-manager/runtimeOwnership';
|
|
3
|
+
function toRuntimeSessionSummary(bundle) {
|
|
4
|
+
return {
|
|
5
|
+
sessionId: bundle.sessionId,
|
|
6
|
+
stackId: bundle.stackId,
|
|
7
|
+
packageIds: [...bundle.packageIds],
|
|
8
|
+
surfaces: [...bundle.surfaces],
|
|
9
|
+
surfaceTypes: bundle.surfaceTypes ? { ...bundle.surfaceTypes } : undefined,
|
|
10
|
+
title: bundle.title,
|
|
11
|
+
description: bundle.description,
|
|
12
|
+
origin: 'spawned',
|
|
13
|
+
writable: true,
|
|
14
|
+
ownership: BROKER_OWNED_RUNTIME_SESSION,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function createRuntimeBroker(options = {}) {
|
|
18
|
+
const runtimeService = new QuickJSRuntimeService(options);
|
|
19
|
+
const handles = new Map();
|
|
20
|
+
const summaries = new Map();
|
|
21
|
+
const listeners = new Set();
|
|
22
|
+
function emit() {
|
|
23
|
+
listeners.forEach((listener) => listener());
|
|
24
|
+
}
|
|
25
|
+
function updateBundle(bundle) {
|
|
26
|
+
summaries.set(bundle.sessionId, toRuntimeSessionSummary(bundle));
|
|
27
|
+
emit();
|
|
28
|
+
return bundle;
|
|
29
|
+
}
|
|
30
|
+
function createHandle(bundle) {
|
|
31
|
+
return {
|
|
32
|
+
sessionId: bundle.sessionId,
|
|
33
|
+
stackId: bundle.stackId,
|
|
34
|
+
origin: 'spawned',
|
|
35
|
+
writable: true,
|
|
36
|
+
getBundleMeta() {
|
|
37
|
+
return {
|
|
38
|
+
...bundle,
|
|
39
|
+
packageIds: [...bundle.packageIds],
|
|
40
|
+
surfaces: [...bundle.surfaces],
|
|
41
|
+
surfaceTypes: bundle.surfaceTypes ? { ...bundle.surfaceTypes } : undefined,
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
renderSurface(surfaceId, state) {
|
|
45
|
+
return runtimeService.renderRuntimeSurface(bundle.sessionId, surfaceId, state);
|
|
46
|
+
},
|
|
47
|
+
eventSurface(surfaceId, handler, args, state) {
|
|
48
|
+
return runtimeService.eventRuntimeSurface(bundle.sessionId, surfaceId, handler, args, state);
|
|
49
|
+
},
|
|
50
|
+
defineSurface(surfaceId, code, packId) {
|
|
51
|
+
bundle = updateBundle(runtimeService.defineRuntimeSurface(bundle.sessionId, surfaceId, code, packId));
|
|
52
|
+
return bundle;
|
|
53
|
+
},
|
|
54
|
+
defineSurfaceRender(surfaceId, code) {
|
|
55
|
+
bundle = updateBundle(runtimeService.defineRuntimeSurfaceRender(bundle.sessionId, surfaceId, code));
|
|
56
|
+
return bundle;
|
|
57
|
+
},
|
|
58
|
+
defineSurfaceHandler(surfaceId, handler, code) {
|
|
59
|
+
bundle = updateBundle(runtimeService.defineRuntimeSurfaceHandler(bundle.sessionId, surfaceId, handler, code));
|
|
60
|
+
return bundle;
|
|
61
|
+
},
|
|
62
|
+
dispose() {
|
|
63
|
+
return broker.disposeSession(bundle.sessionId);
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const broker = {
|
|
68
|
+
async spawnSession(request) {
|
|
69
|
+
if (handles.has(request.sessionId)) {
|
|
70
|
+
throw new Error(`Runtime session already exists: ${request.sessionId}`);
|
|
71
|
+
}
|
|
72
|
+
const bundle = await runtimeService.loadRuntimeBundle(request.stackId, request.sessionId, request.packageIds, request.bundleCode);
|
|
73
|
+
const handle = createHandle(bundle);
|
|
74
|
+
handles.set(request.sessionId, handle);
|
|
75
|
+
updateBundle(bundle);
|
|
76
|
+
return handle;
|
|
77
|
+
},
|
|
78
|
+
getSession(sessionId) {
|
|
79
|
+
return handles.get(sessionId) ?? null;
|
|
80
|
+
},
|
|
81
|
+
listSessions() {
|
|
82
|
+
return Array.from(summaries.values())
|
|
83
|
+
.map((summary) => ({
|
|
84
|
+
...summary,
|
|
85
|
+
packageIds: [...summary.packageIds],
|
|
86
|
+
surfaces: [...summary.surfaces],
|
|
87
|
+
surfaceTypes: summary.surfaceTypes ? { ...summary.surfaceTypes } : undefined,
|
|
88
|
+
}))
|
|
89
|
+
.sort((a, b) => a.sessionId.localeCompare(b.sessionId));
|
|
90
|
+
},
|
|
91
|
+
disposeSession(sessionId) {
|
|
92
|
+
const disposed = runtimeService.disposeSession(sessionId);
|
|
93
|
+
if (!disposed) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
handles.delete(sessionId);
|
|
97
|
+
summaries.delete(sessionId);
|
|
98
|
+
emit();
|
|
99
|
+
return true;
|
|
100
|
+
},
|
|
101
|
+
clear() {
|
|
102
|
+
for (const sessionId of Array.from(handles.keys())) {
|
|
103
|
+
runtimeService.disposeSession(sessionId);
|
|
104
|
+
}
|
|
105
|
+
handles.clear();
|
|
106
|
+
summaries.clear();
|
|
107
|
+
emit();
|
|
108
|
+
},
|
|
109
|
+
subscribe(listener) {
|
|
110
|
+
listeners.add(listener);
|
|
111
|
+
return () => {
|
|
112
|
+
listeners.delete(listener);
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
return broker;
|
|
117
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RuntimeBundleDefinition } from '@go-go-golems/os-core';
|
|
2
|
+
export interface RuntimeSurfaceSessionHostProps {
|
|
3
|
+
windowId: string;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
bundle: RuntimeBundleDefinition;
|
|
6
|
+
mode?: 'interactive' | 'preview';
|
|
7
|
+
}
|
|
8
|
+
export declare function RuntimeSurfaceSessionHost({ windowId, sessionId, bundle, mode, }: RuntimeSurfaceSessionHostProps): import("react/jsx-runtime").JSX.Element;
|