@mindstudio-ai/local-model-tunnel 0.5.25 → 0.5.27
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-VCBGX5ND.js → chunk-JRLMRABX.js} +239 -154
- package/dist/chunk-JRLMRABX.js.map +1 -0
- package/dist/{chunk-4VEZ2MRA.js → chunk-N4J5ZGX6.js} +2 -2
- package/dist/{chunk-PZ5V43RM.js → chunk-PH6M4GT4.js} +18 -12
- package/dist/chunk-PH6M4GT4.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/headless.d.ts +5 -2
- package/dist/headless.js +2 -2
- package/dist/index.js +3 -3
- package/dist/{tui-OBUBYZB6.js → tui-ZFW3NSXF.js} +6 -6
- package/package.json +1 -1
- package/dist/chunk-PZ5V43RM.js.map +0 -1
- package/dist/chunk-VCBGX5ND.js.map +0 -1
- /package/dist/{chunk-4VEZ2MRA.js.map → chunk-N4J5ZGX6.js.map} +0 -0
- /package/dist/{tui-OBUBYZB6.js.map → tui-ZFW3NSXF.js.map} +0 -0
|
@@ -88,6 +88,7 @@ function deleteLocalInterfacePath(key) {
|
|
|
88
88
|
|
|
89
89
|
// src/dev/logging/logger.ts
|
|
90
90
|
import fs from "fs";
|
|
91
|
+
import { join } from "path";
|
|
91
92
|
var LEVELS = {
|
|
92
93
|
error: 0,
|
|
93
94
|
warn: 1,
|
|
@@ -97,31 +98,28 @@ var LEVELS = {
|
|
|
97
98
|
var currentLevel = LEVELS.error;
|
|
98
99
|
var writeFn = () => {
|
|
99
100
|
};
|
|
100
|
-
function
|
|
101
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
102
|
-
}
|
|
103
|
-
function write(level, msg, data) {
|
|
101
|
+
function write(level, module, msg, data) {
|
|
104
102
|
if (LEVELS[level] > currentLevel) {
|
|
105
103
|
return;
|
|
106
104
|
}
|
|
107
|
-
const
|
|
105
|
+
const entry = { ts: Date.now(), level, module, msg };
|
|
108
106
|
if (data) {
|
|
109
|
-
|
|
107
|
+
Object.assign(entry, data);
|
|
110
108
|
}
|
|
111
|
-
writeFn(
|
|
109
|
+
writeFn(JSON.stringify(entry));
|
|
112
110
|
}
|
|
113
111
|
var log = {
|
|
114
|
-
error(msg, data) {
|
|
115
|
-
write("error", msg, data);
|
|
112
|
+
error(module, msg, data) {
|
|
113
|
+
write("error", module, msg, data);
|
|
116
114
|
},
|
|
117
|
-
warn(msg, data) {
|
|
118
|
-
write("warn", msg, data);
|
|
115
|
+
warn(module, msg, data) {
|
|
116
|
+
write("warn", module, msg, data);
|
|
119
117
|
},
|
|
120
|
-
info(msg, data) {
|
|
121
|
-
write("info", msg, data);
|
|
118
|
+
info(module, msg, data) {
|
|
119
|
+
write("info", module, msg, data);
|
|
122
120
|
},
|
|
123
|
-
debug(msg, data) {
|
|
124
|
-
write("debug", msg, data);
|
|
121
|
+
debug(module, msg, data) {
|
|
122
|
+
write("debug", module, msg, data);
|
|
125
123
|
}
|
|
126
124
|
};
|
|
127
125
|
function initLoggerHeadless(level = "info") {
|
|
@@ -136,7 +134,9 @@ function initLoggerInteractive(level = "error") {
|
|
|
136
134
|
writeFn = (line) => {
|
|
137
135
|
try {
|
|
138
136
|
if (fd === null) {
|
|
139
|
-
|
|
137
|
+
const logsDir = join(process.cwd(), ".logs");
|
|
138
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
139
|
+
fd = fs.openSync(join(logsDir, "tunnel.ndjson"), "a");
|
|
140
140
|
}
|
|
141
141
|
fs.writeSync(fd, line + "\n");
|
|
142
142
|
} catch {
|
|
@@ -162,7 +162,8 @@ function basePath(appId) {
|
|
|
162
162
|
}
|
|
163
163
|
async function apiRequest(method, url, headers, body) {
|
|
164
164
|
const start = Date.now();
|
|
165
|
-
const
|
|
165
|
+
const httpMethod = method;
|
|
166
|
+
const path2 = url.replace(getApiBaseUrl(), "").replace(/^\/_internal\/v2\/apps\/[^/]+\/dev/, "");
|
|
166
167
|
const response = await fetch(url, {
|
|
167
168
|
method,
|
|
168
169
|
headers,
|
|
@@ -170,16 +171,16 @@ async function apiRequest(method, url, headers, body) {
|
|
|
170
171
|
});
|
|
171
172
|
const duration = Date.now() - start;
|
|
172
173
|
if (response.status === 204) {
|
|
173
|
-
log.debug(
|
|
174
|
+
log.debug("api", "Request complete", { method: httpMethod, path: path2, status: 204, duration });
|
|
174
175
|
return null;
|
|
175
176
|
}
|
|
176
177
|
if (!response.ok) {
|
|
177
178
|
const error = await response.text();
|
|
178
|
-
log.error(
|
|
179
|
-
throw new ApiError(`${
|
|
179
|
+
log.error("api", "Request failed", { method: httpMethod, path: path2, status: response.status, duration, error });
|
|
180
|
+
throw new ApiError(`${httpMethod} ${path2} failed: ${response.status} ${error}`, response.status);
|
|
180
181
|
}
|
|
181
182
|
const data = await response.json();
|
|
182
|
-
log.info(
|
|
183
|
+
log.info("api", "Request complete", { method: httpMethod, path: path2, status: response.status, duration });
|
|
183
184
|
return data;
|
|
184
185
|
}
|
|
185
186
|
async function startDevSession(appId, opts) {
|
|
@@ -276,7 +277,7 @@ var DevPollError = class extends Error {
|
|
|
276
277
|
|
|
277
278
|
// src/dev/logging/ndjson-log.ts
|
|
278
279
|
import fs2 from "fs";
|
|
279
|
-
import { join } from "path";
|
|
280
|
+
import { join as join2 } from "path";
|
|
280
281
|
var NdjsonLog = class {
|
|
281
282
|
constructor(filename, maxLines = 500, keepLines = 300, maxBytes = 2 * 1024 * 1024) {
|
|
282
283
|
this.filename = filename;
|
|
@@ -291,9 +292,9 @@ var NdjsonLog = class {
|
|
|
291
292
|
init(projectRoot) {
|
|
292
293
|
this.close();
|
|
293
294
|
try {
|
|
294
|
-
const logsDir =
|
|
295
|
+
const logsDir = join2(projectRoot, ".logs");
|
|
295
296
|
fs2.mkdirSync(logsDir, { recursive: true });
|
|
296
|
-
this.logPath =
|
|
297
|
+
this.logPath = join2(logsDir, this.filename);
|
|
297
298
|
if (fs2.existsSync(this.logPath)) {
|
|
298
299
|
const content = fs2.readFileSync(this.logPath, "utf-8");
|
|
299
300
|
this.lineCount = content.split("\n").filter((l) => l.trim()).length;
|
|
@@ -301,12 +302,12 @@ var NdjsonLog = class {
|
|
|
301
302
|
this.lineCount = 0;
|
|
302
303
|
}
|
|
303
304
|
this.fd = fs2.openSync(this.logPath, "a");
|
|
304
|
-
log.
|
|
305
|
+
log.debug("logging", `${this.filename} log initialized`, {
|
|
305
306
|
path: this.logPath,
|
|
306
307
|
existingEntries: this.lineCount
|
|
307
308
|
});
|
|
308
309
|
} catch (err) {
|
|
309
|
-
log.warn(`Failed to initialize ${this.filename} log`, {
|
|
310
|
+
log.warn("logging", `Failed to initialize ${this.filename} log`, {
|
|
310
311
|
error: err instanceof Error ? err.message : String(err)
|
|
311
312
|
});
|
|
312
313
|
this.fd = null;
|
|
@@ -320,10 +321,7 @@ var NdjsonLog = class {
|
|
|
320
321
|
fs2.writeSync(this.fd, line);
|
|
321
322
|
this.lineCount++;
|
|
322
323
|
this.maybeRotate();
|
|
323
|
-
} catch
|
|
324
|
-
log.debug(`Failed to write ${this.filename} log entry`, {
|
|
325
|
-
error: err instanceof Error ? err.message : String(err)
|
|
326
|
-
});
|
|
324
|
+
} catch {
|
|
327
325
|
}
|
|
328
326
|
}
|
|
329
327
|
close() {
|
|
@@ -355,11 +353,7 @@ var NdjsonLog = class {
|
|
|
355
353
|
fs2.writeFileSync(this.logPath, kept.join("\n") + "\n", "utf-8");
|
|
356
354
|
this.fd = fs2.openSync(this.logPath, "a");
|
|
357
355
|
this.lineCount = kept.length;
|
|
358
|
-
|
|
359
|
-
} catch (err) {
|
|
360
|
-
log.debug(`${this.filename} log rotation failed`, {
|
|
361
|
-
error: err instanceof Error ? err.message : String(err)
|
|
362
|
-
});
|
|
356
|
+
} catch {
|
|
363
357
|
} finally {
|
|
364
358
|
this.rotating = false;
|
|
365
359
|
}
|
|
@@ -373,8 +367,11 @@ function initRequestLog(projectRoot) {
|
|
|
373
367
|
}
|
|
374
368
|
function logMethodExecution(entry) {
|
|
375
369
|
ndjsonLog.append({
|
|
370
|
+
ts: Date.now(),
|
|
371
|
+
level: "info",
|
|
372
|
+
module: "execution",
|
|
373
|
+
msg: entry.result.success ? "Method complete" : "Method failed",
|
|
376
374
|
type: "method",
|
|
377
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
378
375
|
requestId: entry.requestId,
|
|
379
376
|
sessionId: entry.sessionId,
|
|
380
377
|
method: entry.methodExport,
|
|
@@ -393,8 +390,11 @@ function logMethodExecution(entry) {
|
|
|
393
390
|
}
|
|
394
391
|
function logScenarioExecution(entry) {
|
|
395
392
|
ndjsonLog.append({
|
|
393
|
+
ts: Date.now(),
|
|
394
|
+
level: "info",
|
|
395
|
+
module: "execution",
|
|
396
|
+
msg: entry.result?.success ?? false ? "Scenario complete" : "Scenario failed",
|
|
396
397
|
type: "scenario",
|
|
397
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
398
398
|
sessionId: entry.sessionId,
|
|
399
399
|
scenario: {
|
|
400
400
|
id: entry.scenario.id,
|
|
@@ -480,7 +480,7 @@ var devRequestEvents = new DevEventEmitter();
|
|
|
480
480
|
// src/dev/execution/transpiler.ts
|
|
481
481
|
import { unlink, mkdir, readdir } from "fs/promises";
|
|
482
482
|
import { existsSync } from "fs";
|
|
483
|
-
import { resolve, dirname, basename, join as
|
|
483
|
+
import { resolve, dirname, basename, join as join3 } from "path";
|
|
484
484
|
import { build } from "esbuild";
|
|
485
485
|
var Transpiler = class {
|
|
486
486
|
projectRoot;
|
|
@@ -506,18 +506,16 @@ var Transpiler = class {
|
|
|
506
506
|
const start = Date.now();
|
|
507
507
|
const absolutePath = resolve(this.projectRoot, methodPath);
|
|
508
508
|
const name = basename(absolutePath).replace(/\.[^.]+$/, "");
|
|
509
|
-
log.debug("Transpiling method", { methodPath });
|
|
510
509
|
const nodeModulesDir = findNearestNodeModules(dirname(absolutePath));
|
|
511
510
|
if (!nodeModulesDir) {
|
|
512
|
-
log.error("Cannot find node_modules for method", { methodPath, searchStart: dirname(absolutePath) });
|
|
511
|
+
log.error("transpiler", "Cannot find node_modules for method", { methodPath, searchStart: dirname(absolutePath) });
|
|
513
512
|
throw new Error(
|
|
514
513
|
`No node_modules found near ${methodPath}. Run npm install first.`
|
|
515
514
|
);
|
|
516
515
|
}
|
|
517
|
-
|
|
518
|
-
const outDir = join2(nodeModulesDir, ".cache", "mindstudio-dev");
|
|
516
|
+
const outDir = join3(nodeModulesDir, ".cache", "mindstudio-dev");
|
|
519
517
|
await mkdir(outDir, { recursive: true });
|
|
520
|
-
const outfile =
|
|
518
|
+
const outfile = join3(outDir, `${name}.__ms_dev__.mjs`);
|
|
521
519
|
await build({
|
|
522
520
|
entryPoints: [absolutePath],
|
|
523
521
|
bundle: true,
|
|
@@ -530,14 +528,13 @@ var Transpiler = class {
|
|
|
530
528
|
logLevel: "silent"
|
|
531
529
|
});
|
|
532
530
|
this.outputFiles.add(outfile);
|
|
533
|
-
log.info(
|
|
531
|
+
log.info("transpiler", "Method transpiled", { duration: Date.now() - start, methodPath, outfile });
|
|
534
532
|
return outfile;
|
|
535
533
|
}
|
|
536
534
|
/**
|
|
537
535
|
* Clean up all transpiled output files.
|
|
538
536
|
*/
|
|
539
537
|
async cleanup() {
|
|
540
|
-
log.debug("Cleaning up transpiled files", { fileCount: this.outputFiles.size });
|
|
541
538
|
for (const file of this.outputFiles) {
|
|
542
539
|
await unlink(file).catch(() => {
|
|
543
540
|
});
|
|
@@ -549,11 +546,10 @@ async function removeOrphanedDevFiles(dir) {
|
|
|
549
546
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
550
547
|
for (const entry of entries) {
|
|
551
548
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
552
|
-
const fullPath =
|
|
549
|
+
const fullPath = join3(dir, entry.name);
|
|
553
550
|
if (entry.isDirectory()) {
|
|
554
551
|
await removeOrphanedDevFiles(fullPath);
|
|
555
552
|
} else if (entry.name.endsWith(".__ms_dev__.mjs")) {
|
|
556
|
-
log.debug("Removing orphaned transpiled file", { path: fullPath });
|
|
557
553
|
await unlink(fullPath).catch(() => {
|
|
558
554
|
});
|
|
559
555
|
}
|
|
@@ -562,7 +558,7 @@ async function removeOrphanedDevFiles(dir) {
|
|
|
562
558
|
function findNearestNodeModules(startDir) {
|
|
563
559
|
let dir = startDir;
|
|
564
560
|
while (true) {
|
|
565
|
-
const candidate =
|
|
561
|
+
const candidate = join3(dir, "node_modules");
|
|
566
562
|
if (existsSync(candidate)) {
|
|
567
563
|
return candidate;
|
|
568
564
|
}
|
|
@@ -576,7 +572,7 @@ function findNearestNodeModules(startDir) {
|
|
|
576
572
|
// src/dev/execution/executor.ts
|
|
577
573
|
import { fork } from "child_process";
|
|
578
574
|
import { writeFile, unlink as unlink2 } from "fs/promises";
|
|
579
|
-
import { join as
|
|
575
|
+
import { join as join4 } from "path";
|
|
580
576
|
import { tmpdir } from "os";
|
|
581
577
|
import { randomBytes } from "crypto";
|
|
582
578
|
var EXECUTION_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
@@ -675,6 +671,10 @@ async function ensureWorker(projectRoot) {
|
|
|
675
671
|
if (worker?.connected && workerProjectRoot === projectRoot) {
|
|
676
672
|
return worker;
|
|
677
673
|
}
|
|
674
|
+
if (worker || workerProjectRoot) {
|
|
675
|
+
const reason = workerProjectRoot !== projectRoot ? "project-root-changed" : "disconnected";
|
|
676
|
+
log.info("executor", "Respawning worker process", { reason });
|
|
677
|
+
}
|
|
678
678
|
if (worker) {
|
|
679
679
|
worker.removeAllListeners();
|
|
680
680
|
worker.kill();
|
|
@@ -685,14 +685,14 @@ async function ensureWorker(projectRoot) {
|
|
|
685
685
|
});
|
|
686
686
|
workerScriptPath = null;
|
|
687
687
|
}
|
|
688
|
-
const scriptPath =
|
|
688
|
+
const scriptPath = join4(
|
|
689
689
|
tmpdir(),
|
|
690
690
|
`ms-dev-worker-${randomBytes(4).toString("hex")}.mjs`
|
|
691
691
|
);
|
|
692
692
|
await writeFile(scriptPath, buildWorkerScript(), "utf-8");
|
|
693
693
|
workerScriptPath = scriptPath;
|
|
694
694
|
workerProjectRoot = projectRoot;
|
|
695
|
-
log.debug("Spawning method execution process", { cwd: projectRoot, scriptPath });
|
|
695
|
+
log.debug("executor", "Spawning method execution process", { cwd: projectRoot, scriptPath });
|
|
696
696
|
const child = fork(scriptPath, [], {
|
|
697
697
|
cwd: projectRoot,
|
|
698
698
|
stdio: ["ignore", "pipe", "pipe", "ipc"],
|
|
@@ -718,7 +718,7 @@ async function ensureWorker(projectRoot) {
|
|
|
718
718
|
req.resolve(msg);
|
|
719
719
|
});
|
|
720
720
|
child.on("exit", (code) => {
|
|
721
|
-
log.warn("Method execution process exited unexpectedly", { code });
|
|
721
|
+
log.warn("executor", "Method execution process exited unexpectedly", { code });
|
|
722
722
|
for (const [id, req] of pending) {
|
|
723
723
|
clearTimeout(req.timer);
|
|
724
724
|
req.resolve({ success: false, error: { message: `Worker process exited with code ${code}` } });
|
|
@@ -728,23 +728,33 @@ async function ensureWorker(projectRoot) {
|
|
|
728
728
|
});
|
|
729
729
|
child.stderr?.on("data", (chunk) => {
|
|
730
730
|
const text = chunk.toString().trim();
|
|
731
|
-
if (text) log.warn("Method process stderr", { text: text.slice(0, 500) });
|
|
731
|
+
if (text) log.warn("executor", "Method process stderr", { text: text.slice(0, 500) });
|
|
732
732
|
});
|
|
733
733
|
worker = child;
|
|
734
|
-
log.info("Method execution process ready", { pid: child.pid });
|
|
734
|
+
log.info("executor", "Method execution process ready", { pid: child.pid });
|
|
735
735
|
return child;
|
|
736
736
|
}
|
|
737
|
-
|
|
737
|
+
var queueTail = Promise.resolve();
|
|
738
|
+
function enqueue(fn) {
|
|
739
|
+
const task = queueTail.then(fn, fn);
|
|
740
|
+
queueTail = task.catch(() => {
|
|
741
|
+
});
|
|
742
|
+
return task;
|
|
743
|
+
}
|
|
744
|
+
function executeMethod(opts) {
|
|
745
|
+
return enqueue(() => executeMethodInWorker(opts));
|
|
746
|
+
}
|
|
747
|
+
async function executeMethodInWorker(opts) {
|
|
738
748
|
const w = await ensureWorker(opts.projectRoot);
|
|
739
749
|
const id = randomBytes(8).toString("hex");
|
|
740
|
-
log.debug("Sending method to execution process", { id, methodExport: opts.methodExport });
|
|
750
|
+
log.debug("executor", "Sending method to execution process", { id, methodExport: opts.methodExport });
|
|
741
751
|
return new Promise((resolve2) => {
|
|
742
752
|
const timer = setTimeout(() => {
|
|
743
753
|
pending.delete(id);
|
|
744
|
-
log.warn("Method execution timed out", { id, methodExport: opts.methodExport });
|
|
754
|
+
log.warn("executor", "Method execution timed out", { id, methodExport: opts.methodExport });
|
|
745
755
|
resolve2({
|
|
746
756
|
success: false,
|
|
747
|
-
error: { message: "Method execution timed out after
|
|
757
|
+
error: { message: "Method execution timed out after 30m" }
|
|
748
758
|
});
|
|
749
759
|
}, EXECUTION_TIMEOUT_MS);
|
|
750
760
|
pending.set(id, { resolve: resolve2, timer });
|
|
@@ -777,6 +787,7 @@ async function cleanupWorker() {
|
|
|
777
787
|
clearTimeout(req.timer);
|
|
778
788
|
}
|
|
779
789
|
pending.clear();
|
|
790
|
+
queueTail = Promise.resolve();
|
|
780
791
|
}
|
|
781
792
|
|
|
782
793
|
// src/api.ts
|
|
@@ -980,6 +991,7 @@ var DevRunner = class {
|
|
|
980
991
|
hadConnectionWarning = false;
|
|
981
992
|
proxyUrl;
|
|
982
993
|
proxy = null;
|
|
994
|
+
roleOverride = null;
|
|
983
995
|
// proxyUrl is sent on every poll request so the platform dashboard can
|
|
984
996
|
// show the developer's preview URL. Also included in the start request
|
|
985
997
|
// so the dashboard sees it immediately without waiting for the first poll.
|
|
@@ -994,13 +1006,13 @@ var DevRunner = class {
|
|
|
994
1006
|
if (this.isRunning) {
|
|
995
1007
|
throw new Error("DevRunner is already running");
|
|
996
1008
|
}
|
|
997
|
-
log.info("Dev session starting", { appId: this.appId, branch: this.startOpts.branch });
|
|
1009
|
+
log.info("runner", "Dev session starting", { appId: this.appId, branch: this.startOpts.branch });
|
|
998
1010
|
const session = await startDevSession(this.appId, this.startOpts);
|
|
999
1011
|
this.session = session;
|
|
1000
1012
|
this.transpiler = new Transpiler(this.projectRoot);
|
|
1001
1013
|
this.isRunning = true;
|
|
1002
1014
|
this.backoffMs = 1e3;
|
|
1003
|
-
log.info("Dev session started", { sessionId: session.sessionId, branch: session.branch });
|
|
1015
|
+
log.info("runner", "Dev session started", { sessionId: session.sessionId, branch: session.branch });
|
|
1004
1016
|
return session;
|
|
1005
1017
|
}
|
|
1006
1018
|
// Begin polling for platform method requests. Call this after all
|
|
@@ -1010,16 +1022,17 @@ var DevRunner = class {
|
|
|
1010
1022
|
this.pollLoop();
|
|
1011
1023
|
}
|
|
1012
1024
|
async stop() {
|
|
1013
|
-
log.info("Dev session stopping");
|
|
1025
|
+
log.info("runner", "Dev session stopping");
|
|
1014
1026
|
this.isRunning = false;
|
|
1015
1027
|
if (this.session) {
|
|
1016
1028
|
try {
|
|
1017
1029
|
await stopDevSession(this.appId, this.session.sessionId);
|
|
1018
1030
|
} catch (err) {
|
|
1019
|
-
log.warn("Failed to stop dev session cleanly", { error: err instanceof Error ? err.message : String(err) });
|
|
1031
|
+
log.warn("runner", "Failed to stop dev session cleanly", { error: err instanceof Error ? err.message : String(err) });
|
|
1020
1032
|
}
|
|
1021
1033
|
this.session = null;
|
|
1022
1034
|
}
|
|
1035
|
+
this.roleOverride = null;
|
|
1023
1036
|
await cleanupWorker();
|
|
1024
1037
|
if (this.transpiler) {
|
|
1025
1038
|
await this.transpiler.cleanup();
|
|
@@ -1032,15 +1045,17 @@ var DevRunner = class {
|
|
|
1032
1045
|
// Set role override for subsequent method executions.
|
|
1033
1046
|
async setImpersonation(roles) {
|
|
1034
1047
|
if (!this.session) return;
|
|
1035
|
-
log.info("Setting role override", { roles });
|
|
1048
|
+
log.info("runner", "Setting role override", { roles });
|
|
1036
1049
|
await impersonate(this.appId, this.session.sessionId, roles);
|
|
1050
|
+
this.roleOverride = roles;
|
|
1037
1051
|
await this.refreshClientContext();
|
|
1038
1052
|
}
|
|
1039
1053
|
// Clear role override — revert to session's default roles.
|
|
1040
1054
|
async clearImpersonation() {
|
|
1041
1055
|
if (!this.session) return;
|
|
1042
|
-
log.info("Clearing role override");
|
|
1056
|
+
log.info("runner", "Clearing role override");
|
|
1043
1057
|
await impersonate(this.appId, this.session.sessionId, null);
|
|
1058
|
+
this.roleOverride = null;
|
|
1044
1059
|
await this.refreshClientContext();
|
|
1045
1060
|
}
|
|
1046
1061
|
// Fetch fresh clientContext from platform and update the proxy.
|
|
@@ -1052,7 +1067,7 @@ var DevRunner = class {
|
|
|
1052
1067
|
this.session.clientContext = context;
|
|
1053
1068
|
this.proxy.updateClientContext(context);
|
|
1054
1069
|
} catch (err) {
|
|
1055
|
-
log.warn("Failed to refresh session context after role change", { error: err instanceof Error ? err.message : String(err) });
|
|
1070
|
+
log.warn("runner", "Failed to refresh session context after role change", { error: err instanceof Error ? err.message : String(err) });
|
|
1056
1071
|
}
|
|
1057
1072
|
}
|
|
1058
1073
|
// Run a method directly (not via poll loop). Used by headless stdin commands
|
|
@@ -1063,15 +1078,22 @@ var DevRunner = class {
|
|
|
1063
1078
|
}
|
|
1064
1079
|
const requestId = randomBytes2(8).toString("hex");
|
|
1065
1080
|
const startTime = Date.now();
|
|
1066
|
-
log.info("Method received
|
|
1081
|
+
log.info("runner", "Method received", { requestId, method: opts.methodExport, source: "direct", sessionId: this.session.sessionId });
|
|
1067
1082
|
try {
|
|
1068
1083
|
const authorizationToken = await fetchCallbackToken(this.appId, this.session.sessionId);
|
|
1069
1084
|
const transpiledPath = await this.transpiler.transpile(opts.methodPath);
|
|
1085
|
+
const auth = this.roleOverride ? {
|
|
1086
|
+
userId: this.session.auth.userId,
|
|
1087
|
+
roleAssignments: this.roleOverride.map((roleName) => ({
|
|
1088
|
+
userId: this.session.auth.userId,
|
|
1089
|
+
roleName
|
|
1090
|
+
}))
|
|
1091
|
+
} : this.session.auth;
|
|
1070
1092
|
const result = await executeMethod({
|
|
1071
1093
|
transpiledPath,
|
|
1072
1094
|
methodExport: opts.methodExport,
|
|
1073
1095
|
input: opts.input,
|
|
1074
|
-
auth
|
|
1096
|
+
auth,
|
|
1075
1097
|
databases: this.session.databases,
|
|
1076
1098
|
authorizationToken,
|
|
1077
1099
|
apiBaseUrl: getApiBaseUrl(),
|
|
@@ -1079,13 +1101,14 @@ var DevRunner = class {
|
|
|
1079
1101
|
});
|
|
1080
1102
|
const duration = Date.now() - startTime;
|
|
1081
1103
|
if (result.success) {
|
|
1082
|
-
log.info("Method complete", { requestId, method: opts.methodExport, duration });
|
|
1104
|
+
log.info("runner", "Method complete", { requestId, method: opts.methodExport, duration, sessionId: this.session.sessionId });
|
|
1083
1105
|
} else {
|
|
1084
|
-
log.warn("Method failed", {
|
|
1106
|
+
log.warn("runner", "Method failed", {
|
|
1085
1107
|
requestId,
|
|
1086
1108
|
method: opts.methodExport,
|
|
1087
1109
|
duration,
|
|
1088
|
-
error: result.error ? formatErrorForDisplay(result.error) : void 0
|
|
1110
|
+
error: result.error ? formatErrorForDisplay(result.error) : void 0,
|
|
1111
|
+
sessionId: this.session.sessionId
|
|
1089
1112
|
});
|
|
1090
1113
|
}
|
|
1091
1114
|
logMethodExecution({
|
|
@@ -1109,7 +1132,7 @@ var DevRunner = class {
|
|
|
1109
1132
|
} catch (err) {
|
|
1110
1133
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
1111
1134
|
const duration = Date.now() - startTime;
|
|
1112
|
-
log.error("Method error", { requestId, method: opts.methodExport, duration, error: message });
|
|
1135
|
+
log.error("runner", "Method execution error", { requestId, method: opts.methodExport, duration, error: message, sessionId: this.session.sessionId });
|
|
1113
1136
|
logMethodExecution({
|
|
1114
1137
|
requestId,
|
|
1115
1138
|
sessionId: this.session.sessionId,
|
|
@@ -1132,15 +1155,15 @@ var DevRunner = class {
|
|
|
1132
1155
|
}
|
|
1133
1156
|
const startTime = Date.now();
|
|
1134
1157
|
const scenarioName = scenario.name ?? scenario.export;
|
|
1135
|
-
log.info("Scenario starting", { id: scenario.id, name: scenarioName });
|
|
1158
|
+
log.info("runner", "Scenario starting", { id: scenario.id, name: scenarioName });
|
|
1136
1159
|
try {
|
|
1137
|
-
log.
|
|
1160
|
+
log.debug("runner", "Resetting database for scenario");
|
|
1138
1161
|
const databases = await resetDevDatabase(this.appId, this.session.sessionId, "truncate");
|
|
1139
1162
|
this.session.databases = databases;
|
|
1140
|
-
log.
|
|
1163
|
+
log.debug("runner", "Transpiling scenario", { path: scenario.path });
|
|
1141
1164
|
const transpiledPath = await this.transpiler.transpile(scenario.path);
|
|
1142
1165
|
const authorizationToken = await fetchCallbackToken(this.appId, this.session.sessionId);
|
|
1143
|
-
log.
|
|
1166
|
+
log.debug("runner", "Running scenario seed function", { export: scenario.export });
|
|
1144
1167
|
const result = await executeMethod({
|
|
1145
1168
|
transpiledPath,
|
|
1146
1169
|
methodExport: scenario.export,
|
|
@@ -1153,7 +1176,7 @@ var DevRunner = class {
|
|
|
1153
1176
|
});
|
|
1154
1177
|
if (!result.success) {
|
|
1155
1178
|
const error = result.error?.message ?? "Scenario seed failed";
|
|
1156
|
-
log.error("Scenario seed function failed", { id: scenario.id, error });
|
|
1179
|
+
log.error("runner", "Scenario seed function failed", { id: scenario.id, name: scenarioName, duration: Date.now() - startTime, error });
|
|
1157
1180
|
logScenarioExecution({
|
|
1158
1181
|
sessionId: this.session.sessionId,
|
|
1159
1182
|
scenario,
|
|
@@ -1164,12 +1187,12 @@ var DevRunner = class {
|
|
|
1164
1187
|
return { success: false, databases, error };
|
|
1165
1188
|
}
|
|
1166
1189
|
if (scenario.roles.length > 0) {
|
|
1167
|
-
log.
|
|
1190
|
+
log.debug("runner", "Setting role override for scenario", { roles: scenario.roles });
|
|
1168
1191
|
await impersonate(this.appId, this.session.sessionId, scenario.roles);
|
|
1169
1192
|
await this.refreshClientContext();
|
|
1170
1193
|
}
|
|
1171
1194
|
const duration = Date.now() - startTime;
|
|
1172
|
-
log.info("Scenario complete", { id: scenario.id, duration, roles: scenario.roles });
|
|
1195
|
+
log.info("runner", "Scenario complete", { id: scenario.id, name: scenarioName, duration, roles: scenario.roles });
|
|
1173
1196
|
logScenarioExecution({
|
|
1174
1197
|
sessionId: this.session.sessionId,
|
|
1175
1198
|
scenario,
|
|
@@ -1180,7 +1203,7 @@ var DevRunner = class {
|
|
|
1180
1203
|
return { success: true, databases };
|
|
1181
1204
|
} catch (err) {
|
|
1182
1205
|
const error = err instanceof Error ? err.message : "Unknown error";
|
|
1183
|
-
log.error("Scenario failed", { id: scenario.id, error });
|
|
1206
|
+
log.error("runner", "Scenario failed", { id: scenario.id, name: scenarioName, duration: Date.now() - startTime, error });
|
|
1184
1207
|
logScenarioExecution({
|
|
1185
1208
|
sessionId: this.session.sessionId,
|
|
1186
1209
|
scenario,
|
|
@@ -1202,7 +1225,7 @@ var DevRunner = class {
|
|
|
1202
1225
|
);
|
|
1203
1226
|
if (this.hadConnectionWarning) {
|
|
1204
1227
|
this.hadConnectionWarning = false;
|
|
1205
|
-
log.info("Connection to platform restored");
|
|
1228
|
+
log.info("runner", "Connection to platform restored");
|
|
1206
1229
|
devRequestEvents.emitConnectionRestored();
|
|
1207
1230
|
}
|
|
1208
1231
|
if (request) {
|
|
@@ -1211,31 +1234,29 @@ var DevRunner = class {
|
|
|
1211
1234
|
this.backoffMs = 1e3;
|
|
1212
1235
|
} catch (error) {
|
|
1213
1236
|
if (error instanceof DevPollError && error.statusCode === 404) {
|
|
1214
|
-
log.error("Dev session expired", { statusCode: 404 });
|
|
1237
|
+
log.error("runner", "Dev session expired", { statusCode: 404 });
|
|
1215
1238
|
devRequestEvents.emitSessionExpired();
|
|
1216
1239
|
this.isRunning = false;
|
|
1217
1240
|
return;
|
|
1218
1241
|
}
|
|
1219
1242
|
if ((error instanceof DevPollError || error instanceof ApiError) && error.statusCode === 401) {
|
|
1220
|
-
log.warn("Session token expired, re-authenticating");
|
|
1221
1243
|
const refreshed = await this.refreshAuth();
|
|
1222
1244
|
if (refreshed) {
|
|
1223
1245
|
this.backoffMs = 1e3;
|
|
1224
1246
|
continue;
|
|
1225
1247
|
}
|
|
1226
|
-
log.error("Re-authentication failed");
|
|
1248
|
+
log.error("runner", "Re-authentication failed");
|
|
1227
1249
|
devRequestEvents.emitSessionExpired();
|
|
1228
1250
|
this.isRunning = false;
|
|
1229
1251
|
return;
|
|
1230
1252
|
}
|
|
1231
1253
|
if (!this.hadConnectionWarning) {
|
|
1232
1254
|
this.hadConnectionWarning = true;
|
|
1233
|
-
log.warn("Lost connection to platform, retrying");
|
|
1255
|
+
log.warn("runner", "Lost connection to platform, retrying");
|
|
1234
1256
|
devRequestEvents.emitConnectionWarning(
|
|
1235
1257
|
"Lost connection to platform, retrying..."
|
|
1236
1258
|
);
|
|
1237
1259
|
}
|
|
1238
|
-
log.debug("Backing off", { ms: this.backoffMs });
|
|
1239
1260
|
await this.sleep(this.backoffMs);
|
|
1240
1261
|
this.backoffMs = Math.min(this.backoffMs * 2, 3e4);
|
|
1241
1262
|
}
|
|
@@ -1249,9 +1270,8 @@ var DevRunner = class {
|
|
|
1249
1270
|
method: request.methodExport,
|
|
1250
1271
|
timestamp: startTime
|
|
1251
1272
|
});
|
|
1252
|
-
log.info("Method received", { requestId: request.requestId, method: request.methodExport });
|
|
1273
|
+
log.info("runner", "Method received", { requestId: request.requestId, method: request.methodExport, source: "poll", sessionId: this.session.sessionId });
|
|
1253
1274
|
try {
|
|
1254
|
-
log.debug("Transpiling method", { path: request.methodPath });
|
|
1255
1275
|
const transpiledPath = await this.transpiler.transpile(request.methodPath);
|
|
1256
1276
|
const auth = request.roleOverride ? {
|
|
1257
1277
|
userId: this.session.auth.userId,
|
|
@@ -1287,13 +1307,14 @@ var DevRunner = class {
|
|
|
1287
1307
|
);
|
|
1288
1308
|
const duration = Date.now() - startTime;
|
|
1289
1309
|
if (result.success) {
|
|
1290
|
-
log.info("Method complete", { requestId: request.requestId, method: request.methodExport, duration });
|
|
1310
|
+
log.info("runner", "Method complete", { requestId: request.requestId, method: request.methodExport, duration, sessionId: this.session.sessionId });
|
|
1291
1311
|
} else {
|
|
1292
|
-
log.warn("Method failed", {
|
|
1312
|
+
log.warn("runner", "Method failed", {
|
|
1293
1313
|
requestId: request.requestId,
|
|
1294
1314
|
method: request.methodExport,
|
|
1295
1315
|
duration,
|
|
1296
|
-
error: result.error ? formatErrorForDisplay(result.error) : void 0
|
|
1316
|
+
error: result.error ? formatErrorForDisplay(result.error) : void 0,
|
|
1317
|
+
sessionId: this.session.sessionId
|
|
1297
1318
|
});
|
|
1298
1319
|
}
|
|
1299
1320
|
logMethodExecution({
|
|
@@ -1317,7 +1338,7 @@ var DevRunner = class {
|
|
|
1317
1338
|
} catch (error) {
|
|
1318
1339
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1319
1340
|
const duration = Date.now() - startTime;
|
|
1320
|
-
log.error("Method error", { requestId: request.requestId, method: request.methodExport, duration, error: message });
|
|
1341
|
+
log.error("runner", "Method execution error", { requestId: request.requestId, method: request.methodExport, duration, error: message, sessionId: this.session.sessionId });
|
|
1321
1342
|
try {
|
|
1322
1343
|
await submitDevResult(
|
|
1323
1344
|
this.appId,
|
|
@@ -1330,7 +1351,7 @@ var DevRunner = class {
|
|
|
1330
1351
|
}
|
|
1331
1352
|
);
|
|
1332
1353
|
} catch (submitErr) {
|
|
1333
|
-
log.error("Failed to report method error to platform", { error: submitErr instanceof Error ? submitErr.message : String(submitErr) });
|
|
1354
|
+
log.error("runner", "Failed to report method error to platform", { error: submitErr instanceof Error ? submitErr.message : String(submitErr) });
|
|
1334
1355
|
}
|
|
1335
1356
|
logMethodExecution({
|
|
1336
1357
|
requestId: request.requestId,
|
|
@@ -1361,14 +1382,14 @@ var DevRunner = class {
|
|
|
1361
1382
|
const POLL_INTERVAL = 2e3;
|
|
1362
1383
|
const MAX_ATTEMPTS = 30;
|
|
1363
1384
|
try {
|
|
1364
|
-
log.info("Session token expired, requesting re-authentication");
|
|
1385
|
+
log.info("runner", "Session token expired, requesting re-authentication");
|
|
1365
1386
|
const { url, token } = await requestDeviceAuth();
|
|
1366
1387
|
devRequestEvents.emitAuthRefreshStart(url);
|
|
1367
1388
|
try {
|
|
1368
1389
|
const open = (await import("open")).default;
|
|
1369
1390
|
await open(url);
|
|
1370
1391
|
} catch {
|
|
1371
|
-
log.warn("Could not open browser \u2014 visit URL to re-authenticate");
|
|
1392
|
+
log.warn("runner", "Could not open browser \u2014 visit URL to re-authenticate");
|
|
1372
1393
|
}
|
|
1373
1394
|
for (let i = 0; i < MAX_ATTEMPTS; i++) {
|
|
1374
1395
|
await this.sleep(POLL_INTERVAL);
|
|
@@ -1379,7 +1400,7 @@ var DevRunner = class {
|
|
|
1379
1400
|
if (result.userId) {
|
|
1380
1401
|
setUserId(result.userId);
|
|
1381
1402
|
}
|
|
1382
|
-
log.info("Re-authentication successful");
|
|
1403
|
+
log.info("runner", "Re-authentication successful");
|
|
1383
1404
|
devRequestEvents.emitAuthRefreshSuccess();
|
|
1384
1405
|
return true;
|
|
1385
1406
|
}
|
|
@@ -1387,11 +1408,11 @@ var DevRunner = class {
|
|
|
1387
1408
|
break;
|
|
1388
1409
|
}
|
|
1389
1410
|
}
|
|
1390
|
-
log.error("Re-authentication timed out or was denied");
|
|
1411
|
+
log.error("runner", "Re-authentication timed out or was denied");
|
|
1391
1412
|
devRequestEvents.emitAuthRefreshFailed();
|
|
1392
1413
|
return false;
|
|
1393
1414
|
} catch (err) {
|
|
1394
|
-
log.error("Re-authentication failed", { error: err instanceof Error ? err.message : String(err) });
|
|
1415
|
+
log.error("runner", "Re-authentication failed", { error: err instanceof Error ? err.message : String(err) });
|
|
1395
1416
|
devRequestEvents.emitAuthRefreshFailed();
|
|
1396
1417
|
return false;
|
|
1397
1418
|
}
|
|
@@ -1406,10 +1427,19 @@ var ndjsonLog2 = new NdjsonLog("browser.ndjson");
|
|
|
1406
1427
|
function initBrowserLog(projectRoot) {
|
|
1407
1428
|
ndjsonLog2.init(projectRoot);
|
|
1408
1429
|
}
|
|
1430
|
+
function inferLevel(entry) {
|
|
1431
|
+
const type = entry.type;
|
|
1432
|
+
if (type === "error") return "error";
|
|
1433
|
+
const level = entry.level;
|
|
1434
|
+
if (level === "warn" || level === "error" || level === "debug") return level;
|
|
1435
|
+
return "info";
|
|
1436
|
+
}
|
|
1409
1437
|
function appendBrowserLogEntries(entries) {
|
|
1410
1438
|
for (const entry of entries) {
|
|
1411
1439
|
ndjsonLog2.append({
|
|
1412
|
-
|
|
1440
|
+
ts: Date.now(),
|
|
1441
|
+
level: inferLevel(entry),
|
|
1442
|
+
module: "browser",
|
|
1413
1443
|
...entry
|
|
1414
1444
|
});
|
|
1415
1445
|
}
|
|
@@ -1439,14 +1469,14 @@ var ClientRegistry = class {
|
|
|
1439
1469
|
alive: true,
|
|
1440
1470
|
activeCommandId: null
|
|
1441
1471
|
});
|
|
1442
|
-
log.info("Browser client connected", { clientId: id, mode: hello.mode, url: hello.url });
|
|
1472
|
+
log.info("proxy", "Browser client connected", { clientId: id, mode: hello.mode, url: hello.url });
|
|
1443
1473
|
return id;
|
|
1444
1474
|
}
|
|
1445
1475
|
remove(id) {
|
|
1446
1476
|
const client = this.clients.get(id);
|
|
1447
1477
|
if (client) {
|
|
1448
1478
|
this.clients.delete(id);
|
|
1449
|
-
log.info("Browser client disconnected", { clientId: id, mode: client.mode });
|
|
1479
|
+
log.info("proxy", "Browser client disconnected", { clientId: id, mode: client.mode });
|
|
1450
1480
|
}
|
|
1451
1481
|
return client;
|
|
1452
1482
|
}
|
|
@@ -1502,7 +1532,7 @@ var ClientRegistry = class {
|
|
|
1502
1532
|
const removed = [];
|
|
1503
1533
|
for (const client of this.clients.values()) {
|
|
1504
1534
|
if (!client.alive) {
|
|
1505
|
-
log.warn("Browser client timed out (no pong)", { clientId: client.id, activeCommandId: client.activeCommandId });
|
|
1535
|
+
log.warn("proxy", "Browser client timed out (no pong)", { clientId: client.id, activeCommandId: client.activeCommandId });
|
|
1506
1536
|
removed.push({ clientId: client.id, activeCommandId: client.activeCommandId });
|
|
1507
1537
|
this.clients.delete(client.id);
|
|
1508
1538
|
client.ws.terminate();
|
|
@@ -1537,7 +1567,6 @@ var DevProxy = class _DevProxy {
|
|
|
1537
1567
|
static HELLO_TIMEOUT = 5e3;
|
|
1538
1568
|
updateClientContext(context) {
|
|
1539
1569
|
this.clientContext = context;
|
|
1540
|
-
log.info("Dev proxy context updated after role change");
|
|
1541
1570
|
}
|
|
1542
1571
|
/**
|
|
1543
1572
|
* Whether any browser agent is actively connected via WebSocket.
|
|
@@ -1552,13 +1581,26 @@ var DevProxy = class _DevProxy {
|
|
|
1552
1581
|
dispatchBrowserCommand(steps, timeoutMs = 12e4) {
|
|
1553
1582
|
if (!this.clients.hasConnected()) {
|
|
1554
1583
|
return Promise.reject(
|
|
1555
|
-
new Error(
|
|
1584
|
+
new Error(
|
|
1585
|
+
"No browser connected, please refresh the MindStudio preview"
|
|
1586
|
+
)
|
|
1556
1587
|
);
|
|
1557
1588
|
}
|
|
1558
1589
|
const id = randomBytes4(4).toString("hex");
|
|
1559
1590
|
return new Promise((resolve2, reject) => {
|
|
1560
|
-
this.commandQueue.push({
|
|
1561
|
-
|
|
1591
|
+
this.commandQueue.push({
|
|
1592
|
+
id,
|
|
1593
|
+
steps,
|
|
1594
|
+
timeoutMs,
|
|
1595
|
+
resolve: resolve2,
|
|
1596
|
+
reject,
|
|
1597
|
+
queuedAt: Date.now()
|
|
1598
|
+
});
|
|
1599
|
+
log.debug("proxy", "Browser command queued", {
|
|
1600
|
+
id,
|
|
1601
|
+
queueLength: this.commandQueue.length,
|
|
1602
|
+
commands: steps.map((s) => s.command)
|
|
1603
|
+
});
|
|
1562
1604
|
this.drainCommandQueue();
|
|
1563
1605
|
});
|
|
1564
1606
|
}
|
|
@@ -1571,12 +1613,22 @@ var DevProxy = class _DevProxy {
|
|
|
1571
1613
|
if (!target) break;
|
|
1572
1614
|
const queued = this.commandQueue.shift();
|
|
1573
1615
|
const { id, steps, timeoutMs, resolve: resolve2, reject } = queued;
|
|
1574
|
-
log.info("Browser command sent", {
|
|
1616
|
+
log.info("proxy", "Browser command sent", {
|
|
1617
|
+
id,
|
|
1618
|
+
clientId: target.id,
|
|
1619
|
+
mode: target.mode,
|
|
1620
|
+
stepCount: steps.length,
|
|
1621
|
+
commands: steps.map((s) => s.command),
|
|
1622
|
+
queueWaitMs: Date.now() - queued.queuedAt
|
|
1623
|
+
});
|
|
1575
1624
|
const timeout = setTimeout(() => {
|
|
1576
1625
|
this.pendingResults.delete(id);
|
|
1577
1626
|
const client = this.clients.findByCommandId(id);
|
|
1578
1627
|
if (client) client.activeCommandId = null;
|
|
1579
|
-
log.warn("Browser command timed out", {
|
|
1628
|
+
log.warn("proxy", "Browser command timed out", {
|
|
1629
|
+
id,
|
|
1630
|
+
pendingCount: this.pendingResults.size
|
|
1631
|
+
});
|
|
1580
1632
|
reject(new Error("Browser command timed out"));
|
|
1581
1633
|
this.drainCommandQueue();
|
|
1582
1634
|
}, timeoutMs);
|
|
@@ -1588,6 +1640,10 @@ var DevProxy = class _DevProxy {
|
|
|
1588
1640
|
this.pendingResults.delete(id);
|
|
1589
1641
|
clearTimeout(timeout);
|
|
1590
1642
|
target.activeCommandId = null;
|
|
1643
|
+
log.warn("proxy", "Browser command send failed", {
|
|
1644
|
+
id,
|
|
1645
|
+
clientId: target.id
|
|
1646
|
+
});
|
|
1591
1647
|
reject(new Error("Failed to send command to browser"));
|
|
1592
1648
|
}
|
|
1593
1649
|
}
|
|
@@ -1598,7 +1654,10 @@ var DevProxy = class _DevProxy {
|
|
|
1598
1654
|
broadcastToClients(action, payload) {
|
|
1599
1655
|
const msg = JSON.stringify({ type: "broadcast", action, payload });
|
|
1600
1656
|
const clients = this.clients.getAll();
|
|
1601
|
-
log.info("Broadcasting to browser clients", {
|
|
1657
|
+
log.info("proxy", "Broadcasting to browser clients", {
|
|
1658
|
+
action,
|
|
1659
|
+
clientCount: clients.length
|
|
1660
|
+
});
|
|
1602
1661
|
for (const client of clients) {
|
|
1603
1662
|
try {
|
|
1604
1663
|
client.ws.send(msg);
|
|
@@ -1629,10 +1688,13 @@ var DevProxy = class _DevProxy {
|
|
|
1629
1688
|
this.proxyPort = assignedPort;
|
|
1630
1689
|
this.startHealthCheck();
|
|
1631
1690
|
this.startPingTimer();
|
|
1632
|
-
log.info("Dev proxy started", {
|
|
1691
|
+
log.info("proxy", "Dev proxy started", {
|
|
1692
|
+
port: assignedPort,
|
|
1693
|
+
bind: this.bindAddress
|
|
1694
|
+
});
|
|
1633
1695
|
return assignedPort;
|
|
1634
1696
|
} catch {
|
|
1635
|
-
log.warn("Proxy port in use, trying next", { port });
|
|
1697
|
+
log.warn("proxy", "Proxy port in use, trying next", { port });
|
|
1636
1698
|
}
|
|
1637
1699
|
}
|
|
1638
1700
|
throw new Error("Failed to start proxy server");
|
|
@@ -1670,7 +1732,7 @@ var DevProxy = class _DevProxy {
|
|
|
1670
1732
|
this.wss = null;
|
|
1671
1733
|
}
|
|
1672
1734
|
if (this.server) {
|
|
1673
|
-
log.info("Dev proxy stopping");
|
|
1735
|
+
log.info("proxy", "Dev proxy stopping");
|
|
1674
1736
|
this.server.close();
|
|
1675
1737
|
this.server = null;
|
|
1676
1738
|
this.proxyPort = null;
|
|
@@ -1695,7 +1757,10 @@ var DevProxy = class _DevProxy {
|
|
|
1695
1757
|
let clientId = null;
|
|
1696
1758
|
const helloTimeout = setTimeout(() => {
|
|
1697
1759
|
if (!clientId) {
|
|
1698
|
-
log.warn(
|
|
1760
|
+
log.warn(
|
|
1761
|
+
"proxy",
|
|
1762
|
+
"Browser WS client did not send hello in time, closing"
|
|
1763
|
+
);
|
|
1699
1764
|
ws.close(4e3, "Hello timeout");
|
|
1700
1765
|
}
|
|
1701
1766
|
}, _DevProxy.HELLO_TIMEOUT);
|
|
@@ -1713,7 +1778,10 @@ var DevProxy = class _DevProxy {
|
|
|
1713
1778
|
}
|
|
1714
1779
|
clearTimeout(helloTimeout);
|
|
1715
1780
|
const mode = msg.mode === "iframe" ? "iframe" : "standalone";
|
|
1716
|
-
const viewport = msg.viewport || {
|
|
1781
|
+
const viewport = msg.viewport || {
|
|
1782
|
+
w: 0,
|
|
1783
|
+
h: 0
|
|
1784
|
+
};
|
|
1717
1785
|
clientId = this.clients.add(ws, {
|
|
1718
1786
|
mode,
|
|
1719
1787
|
url: String(msg.url || ""),
|
|
@@ -1741,7 +1809,10 @@ var DevProxy = class _DevProxy {
|
|
|
1741
1809
|
if (clientId) {
|
|
1742
1810
|
const client = this.clients.remove(clientId);
|
|
1743
1811
|
if (client?.activeCommandId) {
|
|
1744
|
-
this.rejectPendingCommand(
|
|
1812
|
+
this.rejectPendingCommand(
|
|
1813
|
+
client.activeCommandId,
|
|
1814
|
+
"Browser disconnected during command execution"
|
|
1815
|
+
);
|
|
1745
1816
|
}
|
|
1746
1817
|
}
|
|
1747
1818
|
});
|
|
@@ -1751,12 +1822,16 @@ var DevProxy = class _DevProxy {
|
|
|
1751
1822
|
handleCommandResult(msg) {
|
|
1752
1823
|
const id = msg.id;
|
|
1753
1824
|
if (!id) {
|
|
1754
|
-
log.warn("Browser command result received with no id");
|
|
1825
|
+
log.warn("proxy", "Browser command result received with no id");
|
|
1755
1826
|
return;
|
|
1756
1827
|
}
|
|
1757
1828
|
const pending2 = this.pendingResults.get(id);
|
|
1758
1829
|
if (pending2) {
|
|
1759
|
-
log.info("Browser command result received", {
|
|
1830
|
+
log.info("proxy", "Browser command result received", {
|
|
1831
|
+
id,
|
|
1832
|
+
stepCount: msg.steps?.length,
|
|
1833
|
+
duration: msg.duration
|
|
1834
|
+
});
|
|
1760
1835
|
clearTimeout(pending2.timeout);
|
|
1761
1836
|
this.pendingResults.delete(id);
|
|
1762
1837
|
const client = this.clients.findByCommandId(id);
|
|
@@ -1764,7 +1839,11 @@ var DevProxy = class _DevProxy {
|
|
|
1764
1839
|
pending2.resolve(msg);
|
|
1765
1840
|
this.drainCommandQueue();
|
|
1766
1841
|
} else {
|
|
1767
|
-
log.warn(
|
|
1842
|
+
log.warn(
|
|
1843
|
+
"proxy",
|
|
1844
|
+
"Browser command result received but no pending command found",
|
|
1845
|
+
{ id, pendingIds: [...this.pendingResults.keys()] }
|
|
1846
|
+
);
|
|
1768
1847
|
}
|
|
1769
1848
|
}
|
|
1770
1849
|
rejectPendingCommand(commandId, reason) {
|
|
@@ -1773,7 +1852,7 @@ var DevProxy = class _DevProxy {
|
|
|
1773
1852
|
clearTimeout(pending2.timeout);
|
|
1774
1853
|
this.pendingResults.delete(commandId);
|
|
1775
1854
|
pending2.resolve({ id: commandId, steps: [], error: reason });
|
|
1776
|
-
log.warn("Pending command rejected", { id: commandId, reason });
|
|
1855
|
+
log.warn("proxy", "Pending command rejected", { id: commandId, reason });
|
|
1777
1856
|
this.drainCommandQueue();
|
|
1778
1857
|
}
|
|
1779
1858
|
}
|
|
@@ -1785,7 +1864,10 @@ var DevProxy = class _DevProxy {
|
|
|
1785
1864
|
const removed = this.clients.sweepDead();
|
|
1786
1865
|
for (const { activeCommandId } of removed) {
|
|
1787
1866
|
if (activeCommandId) {
|
|
1788
|
-
this.rejectPendingCommand(
|
|
1867
|
+
this.rejectPendingCommand(
|
|
1868
|
+
activeCommandId,
|
|
1869
|
+
"Browser client timed out"
|
|
1870
|
+
);
|
|
1789
1871
|
}
|
|
1790
1872
|
}
|
|
1791
1873
|
this.clients.pingAll();
|
|
@@ -1809,7 +1891,7 @@ var DevProxy = class _DevProxy {
|
|
|
1809
1891
|
markUpstreamDown() {
|
|
1810
1892
|
if (!this.upstreamUp) return;
|
|
1811
1893
|
this.upstreamUp = false;
|
|
1812
|
-
log.info("Upstream dev server marked as down (explicit signal)");
|
|
1894
|
+
log.info("proxy", "Upstream dev server marked as down (explicit signal)");
|
|
1813
1895
|
this.scheduleHealthCheck(_DevProxy.HEALTH_CHECK_INTERVAL_DOWN);
|
|
1814
1896
|
}
|
|
1815
1897
|
startHealthCheck() {
|
|
@@ -1836,9 +1918,9 @@ var DevProxy = class _DevProxy {
|
|
|
1836
1918
|
this.upstreamUp = false;
|
|
1837
1919
|
}
|
|
1838
1920
|
if (wasUp && !this.upstreamUp) {
|
|
1839
|
-
log.warn("Upstream dev server is down");
|
|
1921
|
+
log.warn("proxy", "Upstream dev server is down");
|
|
1840
1922
|
} else if (!wasUp && this.upstreamUp) {
|
|
1841
|
-
log.info("Upstream dev server is back up, reloading browser");
|
|
1923
|
+
log.info("proxy", "Upstream dev server is back up, reloading browser");
|
|
1842
1924
|
this.broadcastToClients("reload");
|
|
1843
1925
|
}
|
|
1844
1926
|
const interval = this.upstreamUp ? _DevProxy.HEALTH_CHECK_INTERVAL : _DevProxy.HEALTH_CHECK_INTERVAL_DOWN;
|
|
@@ -1891,7 +1973,10 @@ var DevProxy = class _DevProxy {
|
|
|
1891
1973
|
port: this.upstreamPort,
|
|
1892
1974
|
path: clientReq.url,
|
|
1893
1975
|
method: clientReq.method,
|
|
1894
|
-
headers: {
|
|
1976
|
+
headers: {
|
|
1977
|
+
...clientReq.headers,
|
|
1978
|
+
host: `localhost:${this.upstreamPort}`
|
|
1979
|
+
}
|
|
1895
1980
|
},
|
|
1896
1981
|
(upstreamRes) => {
|
|
1897
1982
|
const contentType = upstreamRes.headers["content-type"] ?? "";
|
|
@@ -1910,7 +1995,6 @@ var DevProxy = class _DevProxy {
|
|
|
1910
1995
|
};
|
|
1911
1996
|
delete headers["content-encoding"];
|
|
1912
1997
|
delete headers["etag"];
|
|
1913
|
-
log.debug("Dev proxy injected context into HTML", { path: clientReq.url, size: html.length });
|
|
1914
1998
|
clientRes.writeHead(upstreamRes.statusCode ?? 200, headers);
|
|
1915
1999
|
clientRes.end(html);
|
|
1916
2000
|
});
|
|
@@ -1927,7 +2011,10 @@ var DevProxy = class _DevProxy {
|
|
|
1927
2011
|
}
|
|
1928
2012
|
);
|
|
1929
2013
|
upstreamReq.on("error", (err) => {
|
|
1930
|
-
log.warn("Dev proxy cannot reach dev server", {
|
|
2014
|
+
log.warn("proxy", "Dev proxy cannot reach dev server", {
|
|
2015
|
+
path: clientReq.url,
|
|
2016
|
+
error: err.message
|
|
2017
|
+
});
|
|
1931
2018
|
clientRes.writeHead(502);
|
|
1932
2019
|
clientRes.end(`Proxy error: ${err.message}`);
|
|
1933
2020
|
});
|
|
@@ -1994,7 +2081,9 @@ var DevProxy = class _DevProxy {
|
|
|
1994
2081
|
clientRes.end(body);
|
|
1995
2082
|
} catch (err) {
|
|
1996
2083
|
clientRes.writeHead(502, cors);
|
|
1997
|
-
clientRes.end(
|
|
2084
|
+
clientRes.end(
|
|
2085
|
+
`Font proxy error: ${err instanceof Error ? err.message : String(err)}`
|
|
2086
|
+
);
|
|
1998
2087
|
}
|
|
1999
2088
|
}
|
|
2000
2089
|
/**
|
|
@@ -2016,7 +2105,6 @@ ${agentScript}`;
|
|
|
2016
2105
|
// Upstream WebSocket forwarding (HMR etc.)
|
|
2017
2106
|
// ---------------------------------------------------------------------------
|
|
2018
2107
|
handleUpstreamUpgrade(clientReq, clientSocket, head) {
|
|
2019
|
-
log.debug("Dev proxy WebSocket upgrade (upstream)", { path: clientReq.url });
|
|
2020
2108
|
const options = {
|
|
2021
2109
|
hostname: "127.0.0.1",
|
|
2022
2110
|
port: this.upstreamPort,
|
|
@@ -2056,13 +2144,10 @@ ${agentScript}`;
|
|
|
2056
2144
|
|
|
2057
2145
|
// src/dev/config/app-config.ts
|
|
2058
2146
|
import { readFileSync, existsSync as existsSync2 } from "fs";
|
|
2059
|
-
import { join as
|
|
2147
|
+
import { join as join5, dirname as dirname2 } from "path";
|
|
2060
2148
|
function detectAppConfig(cwd = process.cwd()) {
|
|
2061
|
-
const appJsonPath =
|
|
2062
|
-
if (!existsSync2(appJsonPath))
|
|
2063
|
-
log.debug("mindstudio.json not found", { path: appJsonPath });
|
|
2064
|
-
return null;
|
|
2065
|
-
}
|
|
2149
|
+
const appJsonPath = join5(cwd, "mindstudio.json");
|
|
2150
|
+
if (!existsSync2(appJsonPath)) return null;
|
|
2066
2151
|
try {
|
|
2067
2152
|
const raw = readFileSync(appJsonPath, "utf-8");
|
|
2068
2153
|
const parsed = JSON.parse(raw);
|
|
@@ -2079,7 +2164,7 @@ function detectAppConfig(cwd = process.cwd()) {
|
|
|
2079
2164
|
scenarios: parsed.scenarios ?? [],
|
|
2080
2165
|
interfaces: parsed.interfaces ?? []
|
|
2081
2166
|
};
|
|
2082
|
-
log.info("Loaded mindstudio.json", {
|
|
2167
|
+
log.info("config", "Loaded mindstudio.json", {
|
|
2083
2168
|
appId: config2.appId,
|
|
2084
2169
|
roles: config2.roles.length,
|
|
2085
2170
|
methods: config2.methods.length,
|
|
@@ -2089,7 +2174,7 @@ function detectAppConfig(cwd = process.cwd()) {
|
|
|
2089
2174
|
});
|
|
2090
2175
|
return config2;
|
|
2091
2176
|
} catch (err) {
|
|
2092
|
-
log.warn("Failed to parse mindstudio.json", { error: err instanceof Error ? err.message : String(err) });
|
|
2177
|
+
log.warn("config", "Failed to parse mindstudio.json", { error: err instanceof Error ? err.message : String(err) });
|
|
2093
2178
|
return null;
|
|
2094
2179
|
}
|
|
2095
2180
|
}
|
|
@@ -2100,7 +2185,7 @@ function getWebInterfaceConfig(appConfig, cwd = process.cwd()) {
|
|
|
2100
2185
|
if (!webInterface) {
|
|
2101
2186
|
return null;
|
|
2102
2187
|
}
|
|
2103
|
-
const configPath =
|
|
2188
|
+
const configPath = join5(cwd, webInterface.path);
|
|
2104
2189
|
if (!existsSync2(configPath)) {
|
|
2105
2190
|
return null;
|
|
2106
2191
|
}
|
|
@@ -2126,14 +2211,14 @@ function getWebProjectDir(appConfig, cwd = process.cwd()) {
|
|
|
2126
2211
|
if (!webInterface) {
|
|
2127
2212
|
return null;
|
|
2128
2213
|
}
|
|
2129
|
-
return dirname2(
|
|
2214
|
+
return dirname2(join5(cwd, webInterface.path));
|
|
2130
2215
|
}
|
|
2131
2216
|
function readTableSources(appConfig, cwd = process.cwd()) {
|
|
2132
2217
|
const results = [];
|
|
2133
2218
|
for (const table of appConfig.tables) {
|
|
2134
|
-
const filePath =
|
|
2219
|
+
const filePath = join5(cwd, table.path);
|
|
2135
2220
|
if (!existsSync2(filePath)) {
|
|
2136
|
-
log.warn("Table source file not found", { table: table.export, path: table.path });
|
|
2221
|
+
log.warn("config", "Table source file not found", { table: table.export, path: table.path });
|
|
2137
2222
|
continue;
|
|
2138
2223
|
}
|
|
2139
2224
|
try {
|
|
@@ -2141,11 +2226,11 @@ function readTableSources(appConfig, cwd = process.cwd()) {
|
|
|
2141
2226
|
const name = table.export;
|
|
2142
2227
|
results.push({ name, source });
|
|
2143
2228
|
} catch (err) {
|
|
2144
|
-
log.warn("Table source file unreadable", { table: table.export, path: table.path, error: err instanceof Error ? err.message : String(err) });
|
|
2229
|
+
log.warn("config", "Table source file unreadable", { table: table.export, path: table.path, error: err instanceof Error ? err.message : String(err) });
|
|
2145
2230
|
}
|
|
2146
2231
|
}
|
|
2147
2232
|
if (results.length < appConfig.tables.length) {
|
|
2148
|
-
log.warn("
|
|
2233
|
+
log.warn("config", "Table source files missing", { missing: appConfig.tables.length - results.length, found: results.length, expected: appConfig.tables.length });
|
|
2149
2234
|
}
|
|
2150
2235
|
return results;
|
|
2151
2236
|
}
|
|
@@ -2155,9 +2240,9 @@ function findDirsNeedingInstall(appConfig, cwd = process.cwd()) {
|
|
|
2155
2240
|
const firstMethodPath = appConfig.methods[0].path;
|
|
2156
2241
|
const parts = firstMethodPath.split("/");
|
|
2157
2242
|
for (let i = parts.length - 1; i >= 1; i--) {
|
|
2158
|
-
const candidate =
|
|
2159
|
-
if (existsSync2(
|
|
2160
|
-
if (!existsSync2(
|
|
2243
|
+
const candidate = join5(cwd, ...parts.slice(0, i));
|
|
2244
|
+
if (existsSync2(join5(candidate, "package.json"))) {
|
|
2245
|
+
if (!existsSync2(join5(candidate, "node_modules"))) {
|
|
2161
2246
|
dirs.push(candidate);
|
|
2162
2247
|
}
|
|
2163
2248
|
break;
|
|
@@ -2165,8 +2250,8 @@ function findDirsNeedingInstall(appConfig, cwd = process.cwd()) {
|
|
|
2165
2250
|
}
|
|
2166
2251
|
}
|
|
2167
2252
|
const webProjectDir = getWebProjectDir(appConfig, cwd);
|
|
2168
|
-
if (webProjectDir && existsSync2(
|
|
2169
|
-
if (!existsSync2(
|
|
2253
|
+
if (webProjectDir && existsSync2(join5(webProjectDir, "package.json"))) {
|
|
2254
|
+
if (!existsSync2(join5(webProjectDir, "node_modules"))) {
|
|
2170
2255
|
dirs.push(webProjectDir);
|
|
2171
2256
|
}
|
|
2172
2257
|
}
|
|
@@ -2195,11 +2280,11 @@ function detectGitBranch() {
|
|
|
2195
2280
|
|
|
2196
2281
|
// src/dev/config/table-watcher.ts
|
|
2197
2282
|
import { watch } from "chokidar";
|
|
2198
|
-
import { join as
|
|
2283
|
+
import { join as join6, dirname as dirname3, basename as basename2 } from "path";
|
|
2199
2284
|
function watchTableFiles(tables, cwd, onChanged) {
|
|
2200
2285
|
if (tables.length === 0) return () => {
|
|
2201
2286
|
};
|
|
2202
|
-
const filePaths = tables.map((t) =>
|
|
2287
|
+
const filePaths = tables.map((t) => join6(cwd, t.path));
|
|
2203
2288
|
let syncTimer;
|
|
2204
2289
|
const watcher = watch(filePaths, {
|
|
2205
2290
|
ignoreInitial: true,
|
|
@@ -2212,13 +2297,13 @@ function watchTableFiles(tables, cwd, onChanged) {
|
|
|
2212
2297
|
});
|
|
2213
2298
|
const dirToFiles = /* @__PURE__ */ new Map();
|
|
2214
2299
|
for (const table of tables) {
|
|
2215
|
-
const absPath =
|
|
2300
|
+
const absPath = join6(cwd, table.path);
|
|
2216
2301
|
const dir = dirname3(absPath);
|
|
2217
2302
|
const file = basename2(absPath);
|
|
2218
2303
|
if (!dirToFiles.has(dir)) dirToFiles.set(dir, /* @__PURE__ */ new Set());
|
|
2219
2304
|
dirToFiles.get(dir).add(file);
|
|
2220
2305
|
}
|
|
2221
|
-
log.info("Watching table source files", {
|
|
2306
|
+
log.info("config", "Watching table source files", {
|
|
2222
2307
|
dirs: dirToFiles.size,
|
|
2223
2308
|
tables: tables.length
|
|
2224
2309
|
});
|
|
@@ -2230,9 +2315,9 @@ function watchTableFiles(tables, cwd, onChanged) {
|
|
|
2230
2315
|
|
|
2231
2316
|
// src/dev/config/config-watcher.ts
|
|
2232
2317
|
import { watch as watch2 } from "chokidar";
|
|
2233
|
-
import { join as
|
|
2318
|
+
import { join as join7 } from "path";
|
|
2234
2319
|
function watchConfigFile(cwd, onChanged) {
|
|
2235
|
-
const configPath =
|
|
2320
|
+
const configPath = join7(cwd, "mindstudio.json");
|
|
2236
2321
|
let debounceTimer;
|
|
2237
2322
|
const watcher = watch2(configPath, {
|
|
2238
2323
|
ignoreInitial: true,
|
|
@@ -2244,7 +2329,7 @@ function watchConfigFile(cwd, onChanged) {
|
|
|
2244
2329
|
onChanged();
|
|
2245
2330
|
}, 500);
|
|
2246
2331
|
});
|
|
2247
|
-
log.info("Watching mindstudio.json for changes", { path: configPath });
|
|
2332
|
+
log.info("config", "Watching mindstudio.json for changes", { path: configPath });
|
|
2248
2333
|
return () => {
|
|
2249
2334
|
clearTimeout(debounceTimer);
|
|
2250
2335
|
watcher.close();
|
|
@@ -2299,4 +2384,4 @@ export {
|
|
|
2299
2384
|
watchTableFiles,
|
|
2300
2385
|
watchConfigFile
|
|
2301
2386
|
};
|
|
2302
|
-
//# sourceMappingURL=chunk-
|
|
2387
|
+
//# sourceMappingURL=chunk-JRLMRABX.js.map
|