@mindstudio-ai/local-model-tunnel 0.5.21 → 0.5.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-YTHFWJU6.js → chunk-ALEWMAQ4.js} +219 -236
- package/dist/chunk-ALEWMAQ4.js.map +1 -0
- package/dist/{chunk-VJTZSOKC.js → chunk-HZZVPY7J.js} +293 -188
- package/dist/chunk-HZZVPY7J.js.map +1 -0
- package/dist/{chunk-TMIU4S53.js → chunk-KOEOMG4Z.js} +2 -2
- package/dist/cli.js +1 -1
- package/dist/headless.js +2 -2
- package/dist/index.js +3 -3
- package/dist/{tui-TUREGKKI.js → tui-JG3DY3LK.js} +8 -12
- package/dist/{tui-TUREGKKI.js.map → tui-JG3DY3LK.js.map} +1 -1
- package/package.json +4 -2
- package/dist/chunk-VJTZSOKC.js.map +0 -1
- package/dist/chunk-YTHFWJU6.js.map +0 -1
- /package/dist/{chunk-TMIU4S53.js.map → chunk-KOEOMG4Z.js.map} +0 -0
|
@@ -22,19 +22,29 @@ import {
|
|
|
22
22
|
syncSchema,
|
|
23
23
|
watchConfigFile,
|
|
24
24
|
watchTableFiles
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-HZZVPY7J.js";
|
|
26
|
+
|
|
27
|
+
// src/dev/ipc.ts
|
|
28
|
+
function emitEvent(event, data) {
|
|
29
|
+
process.stdout.write(JSON.stringify({ event, ...data }) + "\n");
|
|
30
|
+
}
|
|
31
|
+
function emitResponse(action, requestId, status, data) {
|
|
32
|
+
process.stdout.write(
|
|
33
|
+
JSON.stringify({ event: action, requestId, status, ...data }) + "\n"
|
|
34
|
+
);
|
|
35
|
+
}
|
|
26
36
|
|
|
27
37
|
// src/dev/session-events.ts
|
|
28
|
-
function subscribeDevEvents(
|
|
38
|
+
function subscribeDevEvents(shutdown) {
|
|
29
39
|
const unsubs = [];
|
|
30
40
|
unsubs.push(
|
|
31
41
|
devRequestEvents.onStart((event) => {
|
|
32
|
-
|
|
42
|
+
emitEvent("platform-method-started", { id: event.id, method: event.method });
|
|
33
43
|
})
|
|
34
44
|
);
|
|
35
45
|
unsubs.push(
|
|
36
46
|
devRequestEvents.onComplete((event) => {
|
|
37
|
-
|
|
47
|
+
emitEvent("platform-method-completed", {
|
|
38
48
|
id: event.id,
|
|
39
49
|
success: event.success,
|
|
40
50
|
duration: event.duration,
|
|
@@ -44,248 +54,211 @@ function subscribeDevEvents(emit2, shutdown) {
|
|
|
44
54
|
);
|
|
45
55
|
unsubs.push(
|
|
46
56
|
devRequestEvents.onConnectionWarning((message) => {
|
|
47
|
-
|
|
57
|
+
emitEvent("connection-lost", { message });
|
|
48
58
|
})
|
|
49
59
|
);
|
|
50
60
|
unsubs.push(
|
|
51
61
|
devRequestEvents.onConnectionRestored(() => {
|
|
52
|
-
|
|
62
|
+
emitEvent("connection-restored");
|
|
53
63
|
})
|
|
54
64
|
);
|
|
55
65
|
unsubs.push(
|
|
56
66
|
devRequestEvents.onSessionExpired(() => {
|
|
57
|
-
|
|
67
|
+
emitEvent("session-expired");
|
|
58
68
|
shutdown().then(() => process.exit(1));
|
|
59
69
|
})
|
|
60
70
|
);
|
|
61
71
|
unsubs.push(
|
|
62
72
|
devRequestEvents.onAuthRefreshStart((url) => {
|
|
63
|
-
|
|
73
|
+
emitEvent("auth-refresh-start", { url });
|
|
64
74
|
})
|
|
65
75
|
);
|
|
66
76
|
unsubs.push(
|
|
67
77
|
devRequestEvents.onAuthRefreshSuccess(() => {
|
|
68
|
-
|
|
78
|
+
emitEvent("auth-refresh-success");
|
|
69
79
|
})
|
|
70
80
|
);
|
|
71
81
|
unsubs.push(
|
|
72
82
|
devRequestEvents.onAuthRefreshFailed(() => {
|
|
73
|
-
|
|
74
|
-
})
|
|
75
|
-
);
|
|
76
|
-
unsubs.push(
|
|
77
|
-
devRequestEvents.onImpersonate((event) => {
|
|
78
|
-
emit2("impersonation-changed", { roles: event.roles });
|
|
79
|
-
})
|
|
80
|
-
);
|
|
81
|
-
unsubs.push(
|
|
82
|
-
devRequestEvents.onScenarioStart((event) => {
|
|
83
|
-
emit2("scenario-started", { id: event.id, name: event.name });
|
|
84
|
-
})
|
|
85
|
-
);
|
|
86
|
-
unsubs.push(
|
|
87
|
-
devRequestEvents.onScenarioComplete((event) => {
|
|
88
|
-
emit2("scenario-completed", {
|
|
89
|
-
id: event.id,
|
|
90
|
-
success: event.success,
|
|
91
|
-
duration: event.duration,
|
|
92
|
-
roles: event.roles,
|
|
93
|
-
...event.error ? { error: event.error } : {}
|
|
94
|
-
});
|
|
83
|
+
emitEvent("auth-refresh-failed");
|
|
95
84
|
})
|
|
96
85
|
);
|
|
97
86
|
return unsubs;
|
|
98
87
|
}
|
|
99
88
|
|
|
100
89
|
// src/dev/stdin-commands/run-scenario.ts
|
|
101
|
-
async function handleRunScenario(
|
|
102
|
-
if (!state.runner)
|
|
103
|
-
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const freshConfig = detectAppConfig(cwd) ?? state.appConfig;
|
|
90
|
+
async function handleRunScenario(ctx, cmd) {
|
|
91
|
+
if (!ctx.state.runner) throw new Error("No active session");
|
|
92
|
+
const freshConfig = detectAppConfig(ctx.cwd) ?? ctx.state.appConfig;
|
|
107
93
|
const scenario = freshConfig?.scenarios.find((s) => s.id === cmd.scenarioId);
|
|
108
|
-
if (!scenario) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
94
|
+
if (!scenario) throw new Error(`Unknown scenario: ${cmd.scenarioId}`);
|
|
95
|
+
const scenarioName = scenario.name ?? scenario.export;
|
|
96
|
+
ctx.started({ scenarioId: scenario.id, name: scenarioName });
|
|
97
|
+
const result = await ctx.state.runner.runScenario(scenario);
|
|
98
|
+
return {
|
|
99
|
+
success: result.success,
|
|
100
|
+
scenarioId: scenario.id,
|
|
101
|
+
name: scenarioName,
|
|
102
|
+
...result.error ? { error: result.error } : {}
|
|
103
|
+
};
|
|
113
104
|
}
|
|
114
105
|
|
|
115
106
|
// src/dev/stdin-commands/run-method.ts
|
|
116
|
-
async function handleRunMethod(
|
|
117
|
-
if (!state.runner)
|
|
118
|
-
emit2("command-error", { message: "No active session" });
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
107
|
+
async function handleRunMethod(ctx, cmd) {
|
|
108
|
+
if (!ctx.state.runner) throw new Error("No active session");
|
|
121
109
|
const methodName = cmd.method;
|
|
122
|
-
if (!methodName)
|
|
123
|
-
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
const freshConfig = detectAppConfig(cwd) ?? state.appConfig;
|
|
110
|
+
if (!methodName) throw new Error('run-method requires "method" (export name or ID)');
|
|
111
|
+
const freshConfig = detectAppConfig(ctx.cwd) ?? ctx.state.appConfig;
|
|
127
112
|
const method = freshConfig?.methods.find((m) => m.export === methodName) ?? freshConfig?.methods.find((m) => m.id === methodName);
|
|
128
|
-
if (!method) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
emit2("method-run-started", { method: method.export });
|
|
133
|
-
const result = await state.runner.runMethod({
|
|
113
|
+
if (!method) throw new Error(`Unknown method: ${methodName}`);
|
|
114
|
+
ctx.started({ method: method.export });
|
|
115
|
+
const result = await ctx.state.runner.runMethod({
|
|
134
116
|
methodExport: method.export,
|
|
135
117
|
methodPath: method.path,
|
|
136
118
|
input: cmd.input ?? {}
|
|
137
119
|
});
|
|
138
|
-
|
|
139
|
-
method: method.export,
|
|
120
|
+
return {
|
|
140
121
|
success: result.success,
|
|
122
|
+
method: method.export,
|
|
141
123
|
output: result.output ?? null,
|
|
142
124
|
error: result.error ?? null,
|
|
143
125
|
stdout: result.stdout ?? [],
|
|
144
126
|
duration: result.duration
|
|
145
|
-
}
|
|
127
|
+
};
|
|
146
128
|
}
|
|
147
129
|
|
|
148
130
|
// src/dev/stdin-commands/impersonate.ts
|
|
149
|
-
async function handleImpersonate(
|
|
150
|
-
if (!state.runner)
|
|
151
|
-
emit2("command-error", { message: "No active session" });
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
131
|
+
async function handleImpersonate(ctx, cmd) {
|
|
132
|
+
if (!ctx.state.runner) throw new Error("No active session");
|
|
154
133
|
const roles = cmd.roles;
|
|
155
|
-
if (!Array.isArray(roles))
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
await state.runner.setImpersonation(roles);
|
|
134
|
+
if (!Array.isArray(roles)) throw new Error("impersonate requires roles array");
|
|
135
|
+
await ctx.state.runner.setImpersonation(roles);
|
|
136
|
+
return { roles };
|
|
160
137
|
}
|
|
161
|
-
async function handleClearImpersonation(
|
|
162
|
-
if (!state.runner)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
await state.runner.clearImpersonation();
|
|
138
|
+
async function handleClearImpersonation(ctx) {
|
|
139
|
+
if (!ctx.state.runner) throw new Error("No active session");
|
|
140
|
+
await ctx.state.runner.clearImpersonation();
|
|
141
|
+
return { roles: null };
|
|
167
142
|
}
|
|
168
143
|
|
|
169
144
|
// src/dev/stdin-commands/browser.ts
|
|
170
|
-
async function handleBrowser(
|
|
171
|
-
if (!state.proxy)
|
|
172
|
-
emit2("command-error", { message: "No active proxy \u2014 browser commands require a web interface" });
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
145
|
+
async function handleBrowser(ctx, cmd) {
|
|
146
|
+
if (!ctx.state.proxy) throw new Error("No active proxy \u2014 browser commands require a web interface");
|
|
175
147
|
const steps = cmd.steps;
|
|
176
148
|
if (!Array.isArray(steps) || steps.length === 0) {
|
|
177
|
-
|
|
178
|
-
return;
|
|
149
|
+
throw new Error('browser action requires a non-empty "steps" array');
|
|
179
150
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
});
|
|
151
|
+
const preparedSteps = await injectScreenshotUploads(ctx, steps);
|
|
152
|
+
const result = await ctx.state.proxy.dispatchBrowserCommand(preparedSteps);
|
|
153
|
+
const resultSteps = result.steps ?? [];
|
|
154
|
+
for (const step of resultSteps) {
|
|
155
|
+
const stepResult = step.result;
|
|
156
|
+
if (stepResult?.uploaded && stepResult?._publicUrl) {
|
|
157
|
+
stepResult.url = stepResult._publicUrl;
|
|
158
|
+
delete stepResult.uploaded;
|
|
159
|
+
delete stepResult._publicUrl;
|
|
160
|
+
delete stepResult.image;
|
|
161
|
+
}
|
|
192
162
|
}
|
|
163
|
+
return {
|
|
164
|
+
steps: resultSteps,
|
|
165
|
+
snapshot: result.snapshot,
|
|
166
|
+
logs: result.logs,
|
|
167
|
+
duration: result.duration
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
async function injectScreenshotUploads(ctx, steps) {
|
|
171
|
+
const session = ctx.state.runner?.getSession();
|
|
172
|
+
const appId = ctx.state.appConfig?.appId;
|
|
173
|
+
if (!session || !appId) return steps;
|
|
174
|
+
const prepared = [];
|
|
175
|
+
for (const step of steps) {
|
|
176
|
+
if (step.command === "screenshot") {
|
|
177
|
+
try {
|
|
178
|
+
const { uploadUrl, uploadFields, publicUrl } = await getUploadUrl(
|
|
179
|
+
appId,
|
|
180
|
+
session.sessionId,
|
|
181
|
+
"jpg",
|
|
182
|
+
"image/jpeg"
|
|
183
|
+
);
|
|
184
|
+
prepared.push({ ...step, uploadUrl, uploadFields, _publicUrl: publicUrl });
|
|
185
|
+
} catch {
|
|
186
|
+
prepared.push(step);
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
prepared.push(step);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return prepared;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
// src/dev/stdin-commands/screenshot.ts
|
|
196
|
-
async function handleScreenshot(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
width: 0,
|
|
201
|
-
height: 0,
|
|
202
|
-
duration: 0,
|
|
203
|
-
error: message
|
|
204
|
-
});
|
|
205
|
-
};
|
|
206
|
-
if (!state.proxy) {
|
|
207
|
-
fail("No active proxy");
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
if (!state.proxy.isBrowserConnected()) {
|
|
211
|
-
fail("No browser connected, please refresh the MindStudio preview");
|
|
212
|
-
return;
|
|
196
|
+
async function handleScreenshot(ctx, cmd) {
|
|
197
|
+
if (!ctx.state.proxy) throw new Error("No active proxy");
|
|
198
|
+
if (!ctx.state.proxy.isBrowserConnected()) {
|
|
199
|
+
throw new Error("No browser connected, please refresh the MindStudio preview");
|
|
213
200
|
}
|
|
214
|
-
if (!state.runner?.getSession() || !state.appConfig?.appId) {
|
|
215
|
-
|
|
216
|
-
return;
|
|
201
|
+
if (!ctx.state.runner?.getSession() || !ctx.state.appConfig?.appId) {
|
|
202
|
+
throw new Error("No active session");
|
|
217
203
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const form = new FormData();
|
|
235
|
-
for (const [key, value] of Object.entries(uploadFields)) {
|
|
236
|
-
form.append(key, value);
|
|
237
|
-
}
|
|
238
|
-
form.append("file", new Blob([imageBuffer], { type: "image/jpeg" }), "screenshot.jpg");
|
|
239
|
-
const uploadResult = await fetch(uploadUrl, { method: "POST", body: form });
|
|
240
|
-
if (!uploadResult.ok) {
|
|
241
|
-
fail(`S3 upload failed: ${uploadResult.status}`);
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
emit2("screenshot-completed", {
|
|
245
|
-
url: publicUrl,
|
|
246
|
-
width: stepResult.width,
|
|
247
|
-
height: stepResult.height,
|
|
248
|
-
duration: Date.now() - startTime
|
|
249
|
-
});
|
|
250
|
-
} catch (err) {
|
|
251
|
-
fail(err instanceof Error ? err.message : "Screenshot failed");
|
|
204
|
+
const startTime = Date.now();
|
|
205
|
+
const session = ctx.state.runner.getSession();
|
|
206
|
+
const { uploadUrl, uploadFields, publicUrl } = await getUploadUrl(
|
|
207
|
+
ctx.state.appConfig.appId,
|
|
208
|
+
session.sessionId,
|
|
209
|
+
"jpg",
|
|
210
|
+
"image/jpeg"
|
|
211
|
+
);
|
|
212
|
+
const fullPage = cmd.fullPage !== false;
|
|
213
|
+
const result = await ctx.state.proxy.dispatchBrowserCommand(
|
|
214
|
+
[{ command: "screenshot", fullPage, scrollToTop: true, uploadUrl, uploadFields }],
|
|
215
|
+
12e4
|
|
216
|
+
);
|
|
217
|
+
const stepResult = result.steps?.[0]?.result;
|
|
218
|
+
if (!stepResult?.uploaded) {
|
|
219
|
+
throw new Error("Screenshot capture or upload failed");
|
|
252
220
|
}
|
|
221
|
+
return {
|
|
222
|
+
url: publicUrl,
|
|
223
|
+
width: stepResult.width,
|
|
224
|
+
height: stepResult.height,
|
|
225
|
+
duration: Date.now() - startTime
|
|
226
|
+
};
|
|
253
227
|
}
|
|
254
228
|
|
|
255
229
|
// src/dev/stdin-commands/dev-server-restarting.ts
|
|
256
|
-
async function handleDevServerRestarting(
|
|
257
|
-
if (!state.proxy)
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
state.proxy.markUpstreamDown();
|
|
262
|
-
emit2("dev-server-restarting-ack", {});
|
|
230
|
+
async function handleDevServerRestarting(ctx) {
|
|
231
|
+
if (!ctx.state.proxy) throw new Error("No active proxy");
|
|
232
|
+
ctx.state.proxy.markUpstreamDown();
|
|
233
|
+
return {};
|
|
263
234
|
}
|
|
264
235
|
|
|
265
236
|
// src/dev/stdin-commands/browser-status.ts
|
|
266
|
-
function handleBrowserStatus(
|
|
267
|
-
|
|
268
|
-
connected: state.proxy?.isBrowserConnected() ?? false
|
|
269
|
-
});
|
|
237
|
+
async function handleBrowserStatus(ctx) {
|
|
238
|
+
return { connected: ctx.state.proxy?.isBrowserConnected() ?? false };
|
|
270
239
|
}
|
|
271
240
|
|
|
272
241
|
// src/dev/stdin-commands/reset-browser.ts
|
|
273
|
-
function handleResetBrowser(
|
|
274
|
-
if (!state.proxy)
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
if (!state.proxy.isBrowserConnected()) {
|
|
279
|
-
emit2("command-error", { message: "No browser connected" });
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
state.proxy.dispatchBrowserCommand([{ command: "reload" }]).catch(() => {
|
|
283
|
-
});
|
|
284
|
-
emit2("reset-browser-completed", {});
|
|
242
|
+
async function handleResetBrowser(ctx) {
|
|
243
|
+
if (!ctx.state.proxy) throw new Error("No active proxy");
|
|
244
|
+
if (!ctx.state.proxy.isBrowserConnected()) throw new Error("No browser connected");
|
|
245
|
+
ctx.state.proxy.broadcastToClients("reload");
|
|
246
|
+
return {};
|
|
285
247
|
}
|
|
286
248
|
|
|
287
249
|
// src/dev/stdin-commands/index.ts
|
|
288
|
-
|
|
250
|
+
var handlers = {
|
|
251
|
+
"run-method": handleRunMethod,
|
|
252
|
+
"run-scenario": handleRunScenario,
|
|
253
|
+
"impersonate": handleImpersonate,
|
|
254
|
+
"clear-impersonation": handleClearImpersonation,
|
|
255
|
+
"browser": handleBrowser,
|
|
256
|
+
"screenshot": handleScreenshot,
|
|
257
|
+
"browser-status": handleBrowserStatus,
|
|
258
|
+
"reset-browser": handleResetBrowser,
|
|
259
|
+
"dev-server-restarting": handleDevServerRestarting
|
|
260
|
+
};
|
|
261
|
+
function setupStdinCommands(state, cwd) {
|
|
289
262
|
if (!process.stdin.readable) return;
|
|
290
263
|
let buffer = "";
|
|
291
264
|
process.stdin.setEncoding("utf-8");
|
|
@@ -296,53 +269,58 @@ function setupStdinCommands(state, cwd, emit2) {
|
|
|
296
269
|
const line = buffer.slice(0, idx).trim();
|
|
297
270
|
buffer = buffer.slice(idx + 1);
|
|
298
271
|
if (!line) continue;
|
|
272
|
+
let cmd;
|
|
299
273
|
try {
|
|
300
|
-
|
|
301
|
-
handleStdinCommand(cmd, state, cwd, emit2);
|
|
274
|
+
cmd = JSON.parse(line);
|
|
302
275
|
} catch {
|
|
303
|
-
|
|
276
|
+
log.warn("Invalid JSON on stdin", { preview: line.slice(0, 100) });
|
|
277
|
+
continue;
|
|
304
278
|
}
|
|
279
|
+
handleStdinCommand(cmd, state, cwd);
|
|
305
280
|
}
|
|
306
281
|
});
|
|
307
282
|
}
|
|
308
|
-
async function handleStdinCommand(cmd, state, cwd
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
283
|
+
async function handleStdinCommand(cmd, state, cwd) {
|
|
284
|
+
const { requestId, action } = cmd;
|
|
285
|
+
if (!requestId) {
|
|
286
|
+
log.warn("Command rejected: missing requestId", { action });
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const handler = handlers[action];
|
|
290
|
+
if (!handler) {
|
|
291
|
+
emitResponse(action ?? "unknown", requestId, "completed", {
|
|
292
|
+
success: false,
|
|
293
|
+
error: `Unknown action: ${action}`
|
|
294
|
+
});
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const ctx = {
|
|
298
|
+
state,
|
|
299
|
+
cwd,
|
|
300
|
+
requestId,
|
|
301
|
+
started: (data) => emitResponse(action, requestId, "started", data)
|
|
302
|
+
};
|
|
303
|
+
try {
|
|
304
|
+
const result = await handler(ctx, cmd);
|
|
305
|
+
emitResponse(action, requestId, "completed", result);
|
|
306
|
+
} catch (err) {
|
|
307
|
+
emitResponse(action, requestId, "completed", {
|
|
308
|
+
success: false,
|
|
309
|
+
error: err instanceof Error ? err.message : String(err)
|
|
310
|
+
});
|
|
330
311
|
}
|
|
331
312
|
}
|
|
332
313
|
|
|
333
314
|
// src/headless.ts
|
|
334
|
-
function emit(event, data) {
|
|
335
|
-
process.stdout.write(JSON.stringify({ event, ...data }) + "\n");
|
|
336
|
-
}
|
|
337
315
|
async function startSession(cwd, opts, state, shutdown) {
|
|
338
316
|
const bindAddress = opts.bindAddress ?? "127.0.0.1";
|
|
339
317
|
const appConfig = detectAppConfig(cwd);
|
|
340
318
|
if (!appConfig) {
|
|
341
|
-
|
|
319
|
+
emitEvent("config-error", { message: "No valid mindstudio.json found in " + cwd });
|
|
342
320
|
return false;
|
|
343
321
|
}
|
|
344
322
|
if (!appConfig.appId) {
|
|
345
|
-
|
|
323
|
+
emitEvent("config-error", { message: 'Missing "appId" in mindstudio.json' });
|
|
346
324
|
return false;
|
|
347
325
|
}
|
|
348
326
|
state.appConfig = appConfig;
|
|
@@ -351,7 +329,7 @@ async function startSession(cwd, opts, state, shutdown) {
|
|
|
351
329
|
const webConfig = getWebInterfaceConfig(appConfig, cwd);
|
|
352
330
|
devPort = webConfig?.devPort ?? null;
|
|
353
331
|
}
|
|
354
|
-
|
|
332
|
+
emitEvent("session-starting", { appId: appConfig.appId, name: appConfig.name });
|
|
355
333
|
try {
|
|
356
334
|
const branch = detectGitBranch();
|
|
357
335
|
const runner = new DevRunner(appConfig.appId, cwd, {
|
|
@@ -368,7 +346,7 @@ async function startSession(cwd, opts, state, shutdown) {
|
|
|
368
346
|
if (tableSources.length > 0) {
|
|
369
347
|
const syncResult = await syncSchema(appConfig.appId, session.sessionId, tableSources);
|
|
370
348
|
session.databases = syncResult.databases;
|
|
371
|
-
|
|
349
|
+
emitEvent("schema-sync-completed", {
|
|
372
350
|
created: syncResult.created,
|
|
373
351
|
altered: syncResult.altered,
|
|
374
352
|
errors: syncResult.errors
|
|
@@ -379,29 +357,32 @@ async function startSession(cwd, opts, state, shutdown) {
|
|
|
379
357
|
});
|
|
380
358
|
}
|
|
381
359
|
} catch (err) {
|
|
382
|
-
|
|
360
|
+
emitEvent("schema-sync-completed", {
|
|
383
361
|
created: [],
|
|
384
362
|
altered: [],
|
|
385
363
|
errors: [err instanceof Error ? err.message : "Schema sync failed"]
|
|
386
364
|
});
|
|
387
365
|
}
|
|
388
366
|
}
|
|
389
|
-
let proxyPort = null;
|
|
390
367
|
if (devPort !== null && session.clientContext) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
368
|
+
if (state.proxy) {
|
|
369
|
+
state.proxy.updateClientContext(session.clientContext);
|
|
370
|
+
} else {
|
|
371
|
+
const proxy = new DevProxy(devPort, session.clientContext, bindAddress, opts.browserAgentUrl);
|
|
372
|
+
const preferred = opts.proxyPort ?? stablePort(appConfig.appId);
|
|
373
|
+
const proxyPort = await proxy.start(preferred);
|
|
374
|
+
state.proxy = proxy;
|
|
375
|
+
state.proxyPort = proxyPort;
|
|
376
|
+
}
|
|
377
|
+
runner.setProxyUrl(`http://${bindAddress === "0.0.0.0" ? "localhost" : bindAddress}:${state.proxyPort}`);
|
|
378
|
+
runner.setProxy(state.proxy);
|
|
397
379
|
}
|
|
398
|
-
|
|
399
|
-
emit("session-started", {
|
|
380
|
+
emitEvent("session-started", {
|
|
400
381
|
sessionId: session.sessionId,
|
|
401
382
|
releaseId: session.releaseId,
|
|
402
383
|
branch: session.branch,
|
|
403
|
-
proxyPort,
|
|
404
|
-
proxyUrl: proxyPort ? `http://${bindAddress === "0.0.0.0" ? "localhost" : bindAddress}:${proxyPort}/` : null,
|
|
384
|
+
proxyPort: state.proxyPort,
|
|
385
|
+
proxyUrl: state.proxyPort ? `http://${bindAddress === "0.0.0.0" ? "localhost" : bindAddress}:${state.proxyPort}/` : null,
|
|
405
386
|
webInterfaceUrl: session.webInterfaceUrl,
|
|
406
387
|
roles: appConfig.roles.map((r) => ({ id: r.id, name: r.name ?? r.id, description: r.description })),
|
|
407
388
|
scenarios: appConfig.scenarios.map((s) => ({
|
|
@@ -412,11 +393,11 @@ async function startSession(cwd, opts, state, shutdown) {
|
|
|
412
393
|
roles: s.roles
|
|
413
394
|
}))
|
|
414
395
|
});
|
|
415
|
-
state.unsubscribers.push(...subscribeDevEvents(
|
|
396
|
+
state.unsubscribers.push(...subscribeDevEvents(shutdown));
|
|
416
397
|
setupTableWatchers(cwd, state);
|
|
417
398
|
return true;
|
|
418
399
|
} catch (err) {
|
|
419
|
-
|
|
400
|
+
emitEvent("config-error", {
|
|
420
401
|
message: err instanceof Error ? err.message : "Failed to start session"
|
|
421
402
|
});
|
|
422
403
|
return false;
|
|
@@ -428,14 +409,14 @@ function setupTableWatchers(cwd, state) {
|
|
|
428
409
|
if (!state.runner || !state.appConfig?.appId) return;
|
|
429
410
|
const session = state.runner.getSession();
|
|
430
411
|
if (!session) return;
|
|
431
|
-
|
|
412
|
+
emitEvent("schema-sync-started");
|
|
432
413
|
log.info("Table source file changed, syncing schema");
|
|
433
414
|
try {
|
|
434
415
|
const tableSources = readTableSources(state.appConfig, cwd);
|
|
435
416
|
if (tableSources.length > 0) {
|
|
436
417
|
const result = await syncSchema(state.appConfig.appId, session.sessionId, tableSources);
|
|
437
418
|
session.databases = result.databases;
|
|
438
|
-
|
|
419
|
+
emitEvent("schema-sync-completed", {
|
|
439
420
|
created: result.created,
|
|
440
421
|
altered: result.altered,
|
|
441
422
|
errors: result.errors
|
|
@@ -448,18 +429,15 @@ function setupTableWatchers(cwd, state) {
|
|
|
448
429
|
}
|
|
449
430
|
} catch (err) {
|
|
450
431
|
const message = err instanceof Error ? err.message : "Schema sync failed";
|
|
451
|
-
|
|
432
|
+
emitEvent("schema-sync-completed", { created: [], altered: [], errors: [message] });
|
|
452
433
|
log.warn("Schema sync failed", { error: message });
|
|
453
434
|
}
|
|
454
435
|
});
|
|
455
436
|
state.unsubscribers.push(cleanup);
|
|
456
437
|
}
|
|
457
|
-
async function
|
|
438
|
+
async function teardownRunner(state) {
|
|
458
439
|
for (const unsub of state.unsubscribers) unsub();
|
|
459
440
|
state.unsubscribers = [];
|
|
460
|
-
state.proxy?.stop();
|
|
461
|
-
state.proxy = null;
|
|
462
|
-
state.proxyPort = null;
|
|
463
441
|
if (state.runner) {
|
|
464
442
|
await state.runner.stop().catch(() => {
|
|
465
443
|
});
|
|
@@ -468,6 +446,12 @@ async function teardownSession(state) {
|
|
|
468
446
|
closeRequestLog();
|
|
469
447
|
closeBrowserLog();
|
|
470
448
|
}
|
|
449
|
+
async function teardownAll(state) {
|
|
450
|
+
await teardownRunner(state);
|
|
451
|
+
state.proxy?.stop();
|
|
452
|
+
state.proxy = null;
|
|
453
|
+
state.proxyPort = null;
|
|
454
|
+
}
|
|
471
455
|
async function startHeadless(opts = {}) {
|
|
472
456
|
initLoggerHeadless(opts.logLevel ?? "info");
|
|
473
457
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -496,10 +480,10 @@ async function startHeadless(opts = {}) {
|
|
|
496
480
|
const shutdown = async () => {
|
|
497
481
|
if (stopping) return;
|
|
498
482
|
stopping = true;
|
|
499
|
-
|
|
483
|
+
emitEvent("session-stopping");
|
|
500
484
|
cleanupConfigWatcher?.();
|
|
501
|
-
await
|
|
502
|
-
|
|
485
|
+
await teardownAll(state);
|
|
486
|
+
emitEvent("session-stopped");
|
|
503
487
|
};
|
|
504
488
|
process.on("SIGTERM", () => {
|
|
505
489
|
shutdown().then(() => process.exit(0));
|
|
@@ -511,18 +495,17 @@ async function startHeadless(opts = {}) {
|
|
|
511
495
|
if (!ok) {
|
|
512
496
|
process.exit(1);
|
|
513
497
|
}
|
|
514
|
-
setupStdinCommands(state, cwd
|
|
498
|
+
setupStdinCommands(state, cwd);
|
|
515
499
|
cleanupConfigWatcher = watchConfigFile(cwd, async () => {
|
|
516
500
|
if (stopping || restarting) return;
|
|
517
501
|
restarting = true;
|
|
518
502
|
try {
|
|
519
503
|
log.info("mindstudio.json changed, restarting dev session");
|
|
520
|
-
|
|
521
|
-
await
|
|
504
|
+
emitEvent("config-changed");
|
|
505
|
+
await teardownRunner(state);
|
|
522
506
|
const ok2 = await startSession(cwd, opts, state, shutdown);
|
|
523
507
|
if (ok2 && state.proxy) {
|
|
524
|
-
state.proxy.
|
|
525
|
-
});
|
|
508
|
+
state.proxy.broadcastToClients("reload");
|
|
526
509
|
}
|
|
527
510
|
} finally {
|
|
528
511
|
restarting = false;
|
|
@@ -535,4 +518,4 @@ async function startHeadless(opts = {}) {
|
|
|
535
518
|
export {
|
|
536
519
|
startHeadless
|
|
537
520
|
};
|
|
538
|
-
//# sourceMappingURL=chunk-
|
|
521
|
+
//# sourceMappingURL=chunk-ALEWMAQ4.js.map
|