@treeseed/core 0.8.8 → 0.8.10
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/components/SiteTitle.astro +2 -2
- package/dist/components/content/ContentStatusLegend.astro +4 -4
- package/dist/components/docs/BookFontControls.astro +9 -9
- package/dist/components/docs/DesktopSidebarToggle.astro +8 -8
- package/dist/components/docs/Footer.astro +7 -91
- package/dist/components/docs/Header.astro +9 -2
- package/dist/components/docs/PageTitle.astro +1 -1
- package/dist/components/docs/ThemeSelect.astro +3 -1
- package/dist/components/forms/ContactForm.astro +21 -21
- package/dist/components/forms/FooterSubscribeForm.astro +9 -9
- package/dist/components/site/BookList.astro +7 -7
- package/dist/components/site/CTASection.astro +4 -4
- package/dist/components/site/ChronicleList.astro +6 -6
- package/dist/components/site/Hero.astro +3 -3
- package/dist/components/site/PathCard.astro +5 -5
- package/dist/components/site/ProfileList.astro +5 -5
- package/dist/components/site/RouteNotFound.astro +6 -6
- package/dist/components/site/SectionIntro.astro +3 -3
- package/dist/components/site/StageBanner.astro +2 -2
- package/dist/components/site/TrustCallout.astro +3 -3
- package/dist/components/ui/data/ActionList.astro +51 -0
- package/dist/components/ui/data/Badge.astro +19 -0
- package/dist/components/ui/data/DataTable.astro +51 -0
- package/dist/components/ui/data/KeyValueList.astro +28 -0
- package/dist/components/ui/data/MetricCard.astro +25 -0
- package/dist/components/ui/data/MetricGrid.astro +27 -0
- package/dist/components/ui/data/StatusPill.astro +20 -0
- package/dist/components/ui/forms/Button.astro +52 -0
- package/dist/components/ui/forms/Field.astro +39 -0
- package/dist/components/ui/forms/FormActions.astro +12 -0
- package/dist/components/ui/forms/PasswordMeter.astro +80 -0
- package/dist/components/ui/forms/RadioGroup.astro +55 -0
- package/dist/components/ui/forms/Select.astro +44 -0
- package/dist/components/ui/forms/TextInput.astro +58 -0
- package/dist/components/ui/forms/Textarea.astro +45 -0
- package/dist/components/ui/layout/PageHeader.astro +45 -0
- package/dist/components/ui/shell/AppShell.astro +112 -0
- package/dist/components/ui/shell/BottomNav.astro +35 -0
- package/dist/components/ui/shell/ProjectHeader.astro +66 -0
- package/dist/components/ui/shell/PublicFooter.astro +39 -0
- package/dist/components/ui/shell/PublicShell.astro +179 -0
- package/dist/components/ui/shell/RailNav.astro +35 -0
- package/dist/components/ui/shell/TopBar.astro +52 -0
- package/dist/components/ui/surface/Card.astro +46 -0
- package/dist/components/ui/surface/EmptyState.astro +45 -0
- package/dist/components/ui/surface/Panel.astro +54 -0
- package/dist/components/ui/theme/ThemeMenu.astro +32 -0
- package/dist/components/ui/theme/ThemePreviewSwatch.astro +18 -0
- package/dist/components/ui/theme/ThemeScript.astro +111 -0
- package/dist/components/ui/theme/ThemeSelector.astro +202 -0
- package/dist/components/ui/types.js +0 -0
- package/dist/dev-watch.d.ts +6 -2
- package/dist/dev-watch.js +12 -3
- package/dist/dev.d.ts +10 -2
- package/dist/dev.js +352 -68
- package/dist/layouts/AuthoredEntryLayout.astro +27 -27
- package/dist/layouts/BookLayout.astro +10 -10
- package/dist/layouts/ContentLayout.astro +4 -4
- package/dist/layouts/MainLayout.astro +66 -193
- package/dist/layouts/NoteLayout.astro +6 -6
- package/dist/layouts/ProfileLayout.astro +17 -17
- package/dist/middleware/starlightRouteData.js +20 -14
- package/dist/pages/404.astro +8 -8
- package/dist/pages/[slug].astro +1 -1
- package/dist/pages/books/[slug].astro +5 -5
- package/dist/pages/contact.astro +4 -4
- package/dist/pages/docs-runtime/[...slug].astro +12 -12
- package/dist/pages/docs-runtime/index.astro +13 -13
- package/dist/pages/index.astro +28 -28
- package/dist/pages/ui/index.astro +216 -0
- package/dist/scripts/dev-platform.js +7 -1
- package/dist/site.js +53 -5
- package/dist/styles/app-shell.css +597 -0
- package/dist/styles/forms.css +258 -0
- package/dist/styles/global.css +125 -120
- package/dist/styles/prose.css +11 -11
- package/dist/styles/theme.css +177 -0
- package/dist/styles/tokens.css +62 -22
- package/dist/styles/ui.css +551 -0
- package/dist/utils/color-schemes/cedar.js +53 -0
- package/dist/utils/color-schemes/fern.js +53 -0
- package/dist/utils/color-schemes/index.js +13 -0
- package/dist/utils/color-schemes/lichen.js +53 -0
- package/dist/utils/color-schemes/shared.js +33 -0
- package/dist/utils/color-schemes/tidepool.js +53 -0
- package/dist/utils/content-status.js +5 -5
- package/dist/utils/site-config.js +2 -2
- package/dist/utils/starlight-nav.js +13 -7
- package/dist/utils/theme.js +133 -41
- package/package.json +36 -2
package/dist/dev.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { spawn, spawnSync } from "node:child_process";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
|
-
import { dirname, resolve, sep } from "node:path";
|
|
4
|
+
import { dirname, isAbsolute, resolve, sep } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { setTimeout as delay } from "node:timers/promises";
|
|
7
7
|
import {
|
|
@@ -33,9 +33,13 @@ const TREESEED_DEFAULT_MAILPIT_UI_PORT = 8025;
|
|
|
33
33
|
const DEV_RELOAD_FILE = "public/__treeseed/dev-reload.json";
|
|
34
34
|
const DEV_RUNTIME_FILE = ".treeseed/generated/dev/runtime.json";
|
|
35
35
|
const DEFAULT_READINESS_TIMEOUT_MS = 9e4;
|
|
36
|
+
const DEFAULT_SETUP_STEP_TIMEOUT_MS = 3e5;
|
|
36
37
|
const DEFAULT_PROCESS_READY_GRACE_MS = 1200;
|
|
37
38
|
const DEFAULT_SHUTDOWN_GRACE_MS = 2500;
|
|
38
39
|
const DEFAULT_KILL_GRACE_MS = 500;
|
|
40
|
+
const INITIAL_RESTART_BACKOFF_MS = 1e3;
|
|
41
|
+
const MAX_RESTART_BACKOFF_MS = 15e3;
|
|
42
|
+
const SETUP_RETRY_BACKOFF_MS = 3e3;
|
|
39
43
|
function resolvePackageRoot(packageName, tenantRoot) {
|
|
40
44
|
const resolvedPath = require2.resolve(packageName, {
|
|
41
45
|
paths: [tenantRoot, packageRoot, process.cwd()]
|
|
@@ -50,6 +54,22 @@ function resolvePackageRoot(packageName, tenantRoot) {
|
|
|
50
54
|
}
|
|
51
55
|
return currentDir;
|
|
52
56
|
}
|
|
57
|
+
function resolveOptionalPackageRoot(packageName, tenantRoot) {
|
|
58
|
+
try {
|
|
59
|
+
return resolvePackageRoot(packageName, tenantRoot);
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function resolvePackageRootEnvOverride(env, envName, tenantRoot) {
|
|
65
|
+
const value = env[envName]?.trim();
|
|
66
|
+
if (!value) return null;
|
|
67
|
+
const root = isAbsolute(value) ? value : resolve(tenantRoot, value);
|
|
68
|
+
if (!existsSync(resolve(root, "package.json"))) {
|
|
69
|
+
throw new Error(`${envName} must point to a package root containing package.json.`);
|
|
70
|
+
}
|
|
71
|
+
return root;
|
|
72
|
+
}
|
|
53
73
|
function resolveNodeEntrypoint(packageDir, sourceRelativePath, distRelativePath) {
|
|
54
74
|
const sourcePath = resolve(packageDir, sourceRelativePath);
|
|
55
75
|
const runTsPath = resolve(packageDir, "scripts", "run-ts.mjs");
|
|
@@ -149,6 +169,33 @@ function browserHost(host) {
|
|
|
149
169
|
function webUrlFor(host, port) {
|
|
150
170
|
return `http://${browserHost(host)}:${port}`;
|
|
151
171
|
}
|
|
172
|
+
function surfaceCommandIds(surface) {
|
|
173
|
+
switch (surface) {
|
|
174
|
+
case "web":
|
|
175
|
+
return ["web"];
|
|
176
|
+
case "api":
|
|
177
|
+
return ["api"];
|
|
178
|
+
case "manager":
|
|
179
|
+
return ["manager"];
|
|
180
|
+
case "worker":
|
|
181
|
+
return ["worker"];
|
|
182
|
+
case "agents":
|
|
183
|
+
return ["agents"];
|
|
184
|
+
case "services":
|
|
185
|
+
return ["api", "manager", "worker", "agents"];
|
|
186
|
+
case "integrated":
|
|
187
|
+
default:
|
|
188
|
+
return ["web", "api"];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function nodeLocalRuntime(label) {
|
|
192
|
+
return {
|
|
193
|
+
requested: "local",
|
|
194
|
+
provider: "local",
|
|
195
|
+
selected: "node-local",
|
|
196
|
+
reason: `${label} runs as a local Node.js process.`
|
|
197
|
+
};
|
|
198
|
+
}
|
|
152
199
|
function dockerComposeIsAvailable(env) {
|
|
153
200
|
const docker = resolveTreeseedToolBinary("docker", { env });
|
|
154
201
|
if (!docker) return false;
|
|
@@ -203,7 +250,7 @@ function createTreeseedIntegratedDevResetPlan(options) {
|
|
|
203
250
|
]
|
|
204
251
|
};
|
|
205
252
|
}
|
|
206
|
-
function createWatchEntries(tenantRoot,
|
|
253
|
+
function createWatchEntries(tenantRoot, roots) {
|
|
207
254
|
const entries = [
|
|
208
255
|
{ kind: "tenant", root: resolve(tenantRoot, "src") },
|
|
209
256
|
{ kind: "tenant", root: resolve(tenantRoot, "content") },
|
|
@@ -217,20 +264,34 @@ function createWatchEntries(tenantRoot, sdkPackageRoot) {
|
|
|
217
264
|
];
|
|
218
265
|
if (!packageRoot.split(sep).includes("node_modules")) {
|
|
219
266
|
entries.push(
|
|
220
|
-
{ kind: "
|
|
221
|
-
{ kind: "
|
|
222
|
-
{ kind: "
|
|
223
|
-
{ kind: "
|
|
224
|
-
{ kind: "
|
|
267
|
+
{ kind: "core", root: resolve(packageRoot, "src") },
|
|
268
|
+
{ kind: "core", root: resolve(packageRoot, "scripts", "build-tenant-worker.ts") },
|
|
269
|
+
{ kind: "core", root: resolve(packageRoot, "scripts", "run-ts.mjs") },
|
|
270
|
+
{ kind: "core", root: resolve(packageRoot, "package.json") },
|
|
271
|
+
{ kind: "core", root: resolve(packageRoot, "src", "dev.ts"), restartRequired: true },
|
|
272
|
+
{ kind: "core", root: resolve(packageRoot, "scripts", "dev-platform.ts"), restartRequired: true }
|
|
225
273
|
);
|
|
226
274
|
}
|
|
227
|
-
if (!sdkPackageRoot.split(sep).includes("node_modules")) {
|
|
275
|
+
if (!roots.sdkPackageRoot.split(sep).includes("node_modules")) {
|
|
228
276
|
entries.push(
|
|
229
|
-
{ kind: "sdk", root: resolve(sdkPackageRoot, "src") },
|
|
230
|
-
{ kind: "sdk", root: resolve(sdkPackageRoot, "scripts", "tenant-astro-command.ts") },
|
|
231
|
-
{ kind: "sdk", root: resolve(sdkPackageRoot, "scripts", "tenant-d1-migrate-local.ts") },
|
|
232
|
-
{ kind: "sdk", root: resolve(sdkPackageRoot, "scripts", "run-ts.mjs") },
|
|
233
|
-
{ kind: "sdk", root: resolve(sdkPackageRoot, "package.json") }
|
|
277
|
+
{ kind: "sdk", root: resolve(roots.sdkPackageRoot, "src") },
|
|
278
|
+
{ kind: "sdk", root: resolve(roots.sdkPackageRoot, "scripts", "tenant-astro-command.ts") },
|
|
279
|
+
{ kind: "sdk", root: resolve(roots.sdkPackageRoot, "scripts", "tenant-d1-migrate-local.ts") },
|
|
280
|
+
{ kind: "sdk", root: resolve(roots.sdkPackageRoot, "scripts", "run-ts.mjs") },
|
|
281
|
+
{ kind: "sdk", root: resolve(roots.sdkPackageRoot, "package.json") }
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
if (roots.agentPackageRoot && !roots.agentPackageRoot.split(sep).includes("node_modules")) {
|
|
285
|
+
entries.push(
|
|
286
|
+
{ kind: "agent", root: resolve(roots.agentPackageRoot, "src") },
|
|
287
|
+
{ kind: "agent", root: resolve(roots.agentPackageRoot, "package.json") },
|
|
288
|
+
{ kind: "agent", root: resolve(roots.agentPackageRoot, "scripts", "run-ts.mjs") }
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
if (roots.cliPackageRoot && !roots.cliPackageRoot.split(sep).includes("node_modules")) {
|
|
292
|
+
entries.push(
|
|
293
|
+
{ kind: "cli", root: resolve(roots.cliPackageRoot, "src", "cli", "handlers", "dev.ts"), restartRequired: true },
|
|
294
|
+
{ kind: "cli", root: resolve(roots.cliPackageRoot, "dist", "cli", "handlers", "dev.js"), restartRequired: true }
|
|
234
295
|
);
|
|
235
296
|
}
|
|
236
297
|
return entries;
|
|
@@ -247,6 +308,9 @@ function createSetupSteps(tenantRoot, setupMode, sdkPackageRoot, planLike, env,
|
|
|
247
308
|
}
|
|
248
309
|
];
|
|
249
310
|
}
|
|
311
|
+
const hasWebCommand = planLike.commands.some((command) => command.id === "web");
|
|
312
|
+
const hasLocalRuntimeCommand = planLike.commands.some((command) => command.id !== "web");
|
|
313
|
+
const needsCloudflareLocalRuntime = usesCloudflareWebRuntime || hasLocalRuntimeCommand;
|
|
250
314
|
const coreScripts = [
|
|
251
315
|
["starlight-patch", "Patch Starlight content path", "scripts/patch-starlight-content-path.ts", "dist/scripts/patch-starlight-content-path.js"],
|
|
252
316
|
["books", "Generate book/public artifacts", "scripts/aggregate-book.ts", "dist/scripts/aggregate-book.js"],
|
|
@@ -271,18 +335,20 @@ function createSetupSteps(tenantRoot, setupMode, sdkPackageRoot, planLike, env,
|
|
|
271
335
|
{
|
|
272
336
|
id: "wrangler",
|
|
273
337
|
label: "Verify Wrangler executable",
|
|
274
|
-
required:
|
|
338
|
+
required: needsCloudflareLocalRuntime,
|
|
275
339
|
status: "planned",
|
|
276
340
|
detail: resolveTreeseedToolBinary("wrangler", { env }) ?? void 0
|
|
277
341
|
},
|
|
278
|
-
...
|
|
342
|
+
...needsCloudflareLocalRuntime ? [
|
|
279
343
|
{
|
|
280
344
|
id: "wrangler-config",
|
|
281
345
|
label: "Generate local Wrangler config",
|
|
282
346
|
required: true,
|
|
283
347
|
status: "planned",
|
|
284
348
|
detail: generatedLocalWranglerPath(tenantRoot)
|
|
285
|
-
}
|
|
349
|
+
}
|
|
350
|
+
] : [],
|
|
351
|
+
...usesCloudflareWebRuntime && hasWebCommand ? [
|
|
286
352
|
{
|
|
287
353
|
id: "web-build",
|
|
288
354
|
label: "Build local Cloudflare web runtime",
|
|
@@ -292,7 +358,7 @@ function createSetupSteps(tenantRoot, setupMode, sdkPackageRoot, planLike, env,
|
|
|
292
358
|
status: tenantBuild ? "planned" : "failed",
|
|
293
359
|
detail: tenantBuild ? void 0 : "Unable to resolve the tenant build script."
|
|
294
360
|
}
|
|
295
|
-
] : coreScripts.map(([id, label, source, dist]) => {
|
|
361
|
+
] : hasWebCommand ? coreScripts.map(([id, label, source, dist]) => {
|
|
296
362
|
const script = resolveOptionalScriptEntrypoint(packageRoot, source, dist);
|
|
297
363
|
return {
|
|
298
364
|
id,
|
|
@@ -303,7 +369,7 @@ function createSetupSteps(tenantRoot, setupMode, sdkPackageRoot, planLike, env,
|
|
|
303
369
|
status: script ? "planned" : "skipped",
|
|
304
370
|
detail: script ? void 0 : `Script not found at ${source}.`
|
|
305
371
|
};
|
|
306
|
-
}),
|
|
372
|
+
}) : [],
|
|
307
373
|
{
|
|
308
374
|
id: "mailpit",
|
|
309
375
|
label: mailpitEnabled ? "Start Mailpit email runtime" : "Disable Mailpit email runtime",
|
|
@@ -314,7 +380,7 @@ function createSetupSteps(tenantRoot, setupMode, sdkPackageRoot, planLike, env,
|
|
|
314
380
|
detail: mailpitEnabled ? mailpit ? "Mailpit SMTP will listen on 127.0.0.1:1025 and the web UI on http://127.0.0.1:8025." : "Unable to resolve the Mailpit startup script." : "Docker Compose is unavailable, so Mailpit is disabled for this local dev run."
|
|
315
381
|
}
|
|
316
382
|
];
|
|
317
|
-
if (
|
|
383
|
+
if (needsCloudflareLocalRuntime && existsSync(resolve(tenantRoot, "migrations"))) {
|
|
318
384
|
const migrate = resolveOptionalScriptEntrypoint(
|
|
319
385
|
sdkPackageRoot,
|
|
320
386
|
"scripts/tenant-d1-migrate-local.ts",
|
|
@@ -332,6 +398,56 @@ function createSetupSteps(tenantRoot, setupMode, sdkPackageRoot, planLike, env,
|
|
|
332
398
|
}
|
|
333
399
|
return steps;
|
|
334
400
|
}
|
|
401
|
+
function createAgentCommand(id, tenantRoot, agentPackageRoot, sharedEnv, apiHost, apiPort) {
|
|
402
|
+
const configs = {
|
|
403
|
+
api: {
|
|
404
|
+
label: "Treeseed API",
|
|
405
|
+
source: "src/api/server.ts",
|
|
406
|
+
dist: "dist/api/server.js",
|
|
407
|
+
extraEnv: {
|
|
408
|
+
HOST: apiHost,
|
|
409
|
+
PORT: String(apiPort),
|
|
410
|
+
TREESEED_API_REPO_ROOT: tenantRoot
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
manager: {
|
|
414
|
+
label: "Workday Manager",
|
|
415
|
+
source: "src/services/workday-manager.ts",
|
|
416
|
+
dist: "dist/services/workday-manager.js",
|
|
417
|
+
extraEnv: {}
|
|
418
|
+
},
|
|
419
|
+
worker: {
|
|
420
|
+
label: "Worker Runner",
|
|
421
|
+
source: "src/services/worker.ts",
|
|
422
|
+
dist: "dist/services/worker.js",
|
|
423
|
+
extraEnv: {}
|
|
424
|
+
},
|
|
425
|
+
agents: {
|
|
426
|
+
label: "Agents Loop",
|
|
427
|
+
source: "src/services/agents.ts",
|
|
428
|
+
dist: "dist/services/agents.js",
|
|
429
|
+
extraEnv: {}
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
const config = configs[id];
|
|
433
|
+
const entrypoint = resolveNodeEntrypoint(agentPackageRoot, config.source, config.dist);
|
|
434
|
+
return {
|
|
435
|
+
id,
|
|
436
|
+
label: config.label,
|
|
437
|
+
command: entrypoint.command,
|
|
438
|
+
args: entrypoint.args,
|
|
439
|
+
cwd: tenantRoot,
|
|
440
|
+
env: {
|
|
441
|
+
...sharedEnv,
|
|
442
|
+
TREESEED_AGENT_REPO_ROOT: tenantRoot,
|
|
443
|
+
TREESEED_AGENT_D1_DATABASE: sharedEnv.TREESEED_API_D1_DATABASE_NAME ?? "SITE_DATA_DB",
|
|
444
|
+
TREESEED_AGENT_D1_PERSIST_TO: sharedEnv.TREESEED_API_D1_LOCAL_PERSIST_TO,
|
|
445
|
+
TREESEED_ENVIRONMENT: sharedEnv.TREESEED_ENVIRONMENT ?? "local",
|
|
446
|
+
...config.extraEnv
|
|
447
|
+
},
|
|
448
|
+
localRuntime: nodeLocalRuntime(config.label)
|
|
449
|
+
};
|
|
450
|
+
}
|
|
335
451
|
function createTreeseedIntegratedDevPlan(options = {}) {
|
|
336
452
|
const tenantRoot = resolve(options.cwd ?? process.cwd());
|
|
337
453
|
const surface = options.surface ?? "integrated";
|
|
@@ -348,8 +464,11 @@ function createTreeseedIntegratedDevPlan(options = {}) {
|
|
|
348
464
|
const projectId = options.projectId ?? mergedEnv.TREESEED_PROJECT_ID;
|
|
349
465
|
const teamId = options.teamId ?? mergedEnv.TREESEED_HOSTING_TEAM_ID;
|
|
350
466
|
const apiBaseUrl = options.apiHost != null || options.apiPort != null ? `http://${apiHost}:${apiPort}` : mergedEnv.TREESEED_API_BASE_URL?.trim() || `http://${apiHost}:${apiPort}`;
|
|
351
|
-
const
|
|
467
|
+
const selectedCommandIds = surfaceCommandIds(surface);
|
|
468
|
+
const webUrl = selectedCommandIds.includes("web") ? webUrlFor(webHost, webPort) : null;
|
|
352
469
|
const sdkPackageRoot = resolvePackageRoot("@treeseed/sdk", tenantRoot);
|
|
470
|
+
const agentPackageRoot = resolvePackageRootEnvOverride(mergedEnv, "TREESEED_AGENT_PACKAGE_ROOT", tenantRoot) ?? resolveOptionalPackageRoot("@treeseed/agent", tenantRoot);
|
|
471
|
+
const cliPackageRoot = resolveOptionalPackageRoot("@treeseed/cli", tenantRoot);
|
|
353
472
|
const deployConfig = loadDevDeployConfig(tenantRoot);
|
|
354
473
|
const webLocalRuntime = selectWebLocalRuntime(deployConfig?.surfaces?.web, fallbackWebProviderFromDeployConfig(deployConfig));
|
|
355
474
|
const usesCloudflareWebRuntime = webLocalRuntime.selected === "cloudflare-wrangler-local";
|
|
@@ -372,7 +491,7 @@ function createTreeseedIntegratedDevPlan(options = {}) {
|
|
|
372
491
|
String(webPort)
|
|
373
492
|
]
|
|
374
493
|
};
|
|
375
|
-
const watchEntries = watch ? createWatchEntries(tenantRoot, sdkPackageRoot) : [];
|
|
494
|
+
const watchEntries = watch ? createWatchEntries(tenantRoot, { sdkPackageRoot, agentPackageRoot, cliPackageRoot }) : [];
|
|
376
495
|
const mailpitEnabled = dockerComposeIsAvailable(mergedEnv);
|
|
377
496
|
const resetRequested = options.reset === true;
|
|
378
497
|
const sharedEnv = {
|
|
@@ -406,7 +525,7 @@ function createTreeseedIntegratedDevPlan(options = {}) {
|
|
|
406
525
|
sharedEnv.TREESEED_PUBLIC_DEV_WATCH_RELOAD = sharedEnv.TREESEED_PUBLIC_DEV_WATCH_RELOAD || "true";
|
|
407
526
|
}
|
|
408
527
|
const commands = [];
|
|
409
|
-
if (
|
|
528
|
+
if (selectedCommandIds.includes("web")) {
|
|
410
529
|
commands.push({
|
|
411
530
|
id: "web",
|
|
412
531
|
label: usesCloudflareWebRuntime ? "Cloudflare Wrangler UI" : "Astro UI",
|
|
@@ -417,14 +536,21 @@ function createTreeseedIntegratedDevPlan(options = {}) {
|
|
|
417
536
|
localRuntime: webLocalRuntime
|
|
418
537
|
});
|
|
419
538
|
}
|
|
539
|
+
if (selectedCommandIds.some((id) => id !== "web") && !agentPackageRoot) {
|
|
540
|
+
throw new Error("Unable to resolve @treeseed/agent for local API or agent service surfaces.");
|
|
541
|
+
}
|
|
542
|
+
for (const id of selectedCommandIds) {
|
|
543
|
+
if (id === "web") continue;
|
|
544
|
+
commands.push(createAgentCommand(id, tenantRoot, agentPackageRoot, sharedEnv, apiHost, apiPort));
|
|
545
|
+
}
|
|
420
546
|
const readyChecks = commands.map((command) => {
|
|
421
|
-
if (command.id === "web") {
|
|
547
|
+
if (command.id === "web" || command.id === "api") {
|
|
422
548
|
return {
|
|
423
549
|
id: command.id,
|
|
424
550
|
label: command.label,
|
|
425
551
|
required: true,
|
|
426
552
|
strategy: "http",
|
|
427
|
-
url: webUrl ?? void 0
|
|
553
|
+
url: command.id === "web" ? webUrl ?? void 0 : `${apiBaseUrl.replace(/\/+$/u, "")}/healthz`
|
|
428
554
|
};
|
|
429
555
|
}
|
|
430
556
|
return {
|
|
@@ -457,7 +583,18 @@ function createTreeseedIntegratedDevPlan(options = {}) {
|
|
|
457
583
|
watchEntries,
|
|
458
584
|
commands,
|
|
459
585
|
localRuntimes: {
|
|
460
|
-
web: webLocalRuntime
|
|
586
|
+
...commands.some((command) => command.id === "web") ? { web: webLocalRuntime } : {},
|
|
587
|
+
...commands.some((command) => command.id === "api") ? { api: nodeLocalRuntime("Treeseed API") } : {},
|
|
588
|
+
...commands.some((command) => command.id === "manager") ? { manager: nodeLocalRuntime("Workday Manager") } : {},
|
|
589
|
+
...commands.some((command) => command.id === "worker") ? { worker: nodeLocalRuntime("Worker Runner") } : {},
|
|
590
|
+
...commands.some((command) => command.id === "agents") ? { agents: nodeLocalRuntime("Agents Loop") } : {}
|
|
591
|
+
},
|
|
592
|
+
restartPolicy: {
|
|
593
|
+
initialBackoffMs: INITIAL_RESTART_BACKOFF_MS,
|
|
594
|
+
maxBackoffMs: MAX_RESTART_BACKOFF_MS,
|
|
595
|
+
setupRetryBackoffMs: SETUP_RETRY_BACKOFF_MS,
|
|
596
|
+
commandImplementationChangesRequireRestart: true,
|
|
597
|
+
agentChanges: "defer"
|
|
461
598
|
},
|
|
462
599
|
reset
|
|
463
600
|
};
|
|
@@ -558,6 +695,23 @@ function defaultWrite(line, stream) {
|
|
|
558
695
|
const target = stream === "stderr" ? process.stderr : process.stdout;
|
|
559
696
|
target.write(line);
|
|
560
697
|
}
|
|
698
|
+
function shouldRedactEnvValue(key) {
|
|
699
|
+
return /(TOKEN|SECRET|PASSWORD|PASSPHRASE|PRIVATE|CREDENTIAL|AUTH)/iu.test(key);
|
|
700
|
+
}
|
|
701
|
+
function redactEnvironment(env) {
|
|
702
|
+
return Object.fromEntries(
|
|
703
|
+
Object.entries(env).map(([key, value]) => [key, value == null || !shouldRedactEnvValue(key) ? value : "[redacted]"])
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
function serializeDevPlanForOutput(plan) {
|
|
707
|
+
return {
|
|
708
|
+
...plan,
|
|
709
|
+
commands: plan.commands.map((command) => ({
|
|
710
|
+
...command,
|
|
711
|
+
env: redactEnvironment(command.env)
|
|
712
|
+
}))
|
|
713
|
+
};
|
|
714
|
+
}
|
|
561
715
|
function resolveLocalMachineEnv(tenantRoot) {
|
|
562
716
|
try {
|
|
563
717
|
return resolveTreeseedMachineEnvironmentValues(tenantRoot, "local");
|
|
@@ -737,7 +891,7 @@ function runTreeseedIntegratedDevReset(reset, options, deps) {
|
|
|
737
891
|
}
|
|
738
892
|
function writePlan(plan, options, write) {
|
|
739
893
|
if (options.json) {
|
|
740
|
-
write(`${JSON.stringify({ schemaVersion: 1, kind: "treeseed.dev.plan", ok: true, payload: plan }, null, 2)}
|
|
894
|
+
write(`${JSON.stringify({ schemaVersion: 1, kind: "treeseed.dev.plan", ok: true, payload: serializeDevPlanForOutput(plan) }, null, 2)}
|
|
741
895
|
`, "stdout");
|
|
742
896
|
return;
|
|
743
897
|
}
|
|
@@ -825,7 +979,8 @@ function runSetupStep(step, plan, deps) {
|
|
|
825
979
|
TREESEED_LOCAL_DEV_MODE: "cloudflare",
|
|
826
980
|
TREESEED_PUBLIC_DEV_WATCH_RELOAD: plan.feedbackMode === "live" ? "true" : process.env.TREESEED_PUBLIC_DEV_WATCH_RELOAD
|
|
827
981
|
},
|
|
828
|
-
encoding: "utf8"
|
|
982
|
+
encoding: "utf8",
|
|
983
|
+
timeout: DEFAULT_SETUP_STEP_TIMEOUT_MS
|
|
829
984
|
});
|
|
830
985
|
if ((result.status ?? 1) === 0) {
|
|
831
986
|
return {
|
|
@@ -834,10 +989,12 @@ function runSetupStep(step, plan, deps) {
|
|
|
834
989
|
detail: [result.stdout, result.stderr].filter(Boolean).join("\n").trim() || step.detail
|
|
835
990
|
};
|
|
836
991
|
}
|
|
992
|
+
const timedOut = result.error && "code" in result.error && result.error.code === "ETIMEDOUT";
|
|
993
|
+
const timeoutDetail = timedOut ? `${step.label} timed out after ${Math.round(DEFAULT_SETUP_STEP_TIMEOUT_MS / 1e3)} seconds.` : null;
|
|
837
994
|
return {
|
|
838
995
|
...step,
|
|
839
996
|
status: step.required ? "failed" : "degraded",
|
|
840
|
-
detail: [result.stdout, result.stderr].filter(Boolean).join("\n").trim() || `Exited with ${result.status ?? 1}.`
|
|
997
|
+
detail: [timeoutDetail, result.stdout, result.stderr].filter(Boolean).join("\n").trim() || `Exited with ${result.status ?? 1}.`
|
|
841
998
|
};
|
|
842
999
|
}
|
|
843
1000
|
function runLocalSetup(plan, options, deps) {
|
|
@@ -851,6 +1008,14 @@ function runLocalSetup(plan, options, deps) {
|
|
|
851
1008
|
}
|
|
852
1009
|
for (const step of plan.setupSteps) {
|
|
853
1010
|
let result = step;
|
|
1011
|
+
if (step.status === "planned") {
|
|
1012
|
+
emitEvent(options, deps.write, {
|
|
1013
|
+
type: "setup",
|
|
1014
|
+
status: "running",
|
|
1015
|
+
message: `${step.label}: running`,
|
|
1016
|
+
detail: step.detail
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
854
1019
|
if (step.id === "workspace-links") {
|
|
855
1020
|
if (plan.setupMode === "check") {
|
|
856
1021
|
result = { ...step, status: "skipped", detail: "Workspace links were checked in non-mutating mode." };
|
|
@@ -1004,17 +1169,15 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
1004
1169
|
});
|
|
1005
1170
|
return 1;
|
|
1006
1171
|
}
|
|
1007
|
-
const setupResults = runLocalSetup(plan, options, { spawnSync: spawnSyncProcess, write });
|
|
1008
|
-
const failedSetup = setupResults.find((step) => step.status === "failed" && step.required);
|
|
1009
|
-
if (failedSetup) {
|
|
1010
|
-
emitEvent(options, write, { type: "error", message: failedSetupMessage(failedSetup), detail: failedSetup });
|
|
1011
|
-
return 1;
|
|
1012
|
-
}
|
|
1013
1172
|
writeCurrentDevRuntimeState(tenantRoot);
|
|
1014
1173
|
const children = /* @__PURE__ */ new Map();
|
|
1015
1174
|
const commandsById = new Map(plan.commands.map((command) => [command.id, command]));
|
|
1016
1175
|
const requiredSurfaceIds = new Set(plan.readyChecks.filter((check) => check.required).map((check) => check.id));
|
|
1017
1176
|
const exited = /* @__PURE__ */ new Map();
|
|
1177
|
+
const restartAttempts = /* @__PURE__ */ new Map();
|
|
1178
|
+
const restartTimers = /* @__PURE__ */ new Map();
|
|
1179
|
+
let setupRetryTimer = null;
|
|
1180
|
+
let readinessInProgress = false;
|
|
1018
1181
|
let watchController = null;
|
|
1019
1182
|
let settled = false;
|
|
1020
1183
|
let readinessComplete = false;
|
|
@@ -1032,6 +1195,16 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
1032
1195
|
watchController.stop();
|
|
1033
1196
|
watchController = null;
|
|
1034
1197
|
}
|
|
1198
|
+
function clearTimers() {
|
|
1199
|
+
if (setupRetryTimer) {
|
|
1200
|
+
clearTimeout(setupRetryTimer);
|
|
1201
|
+
setupRetryTimer = null;
|
|
1202
|
+
}
|
|
1203
|
+
for (const timer of restartTimers.values()) {
|
|
1204
|
+
clearTimeout(timer);
|
|
1205
|
+
}
|
|
1206
|
+
restartTimers.clear();
|
|
1207
|
+
}
|
|
1035
1208
|
function finalize(exitCode) {
|
|
1036
1209
|
if (settled) {
|
|
1037
1210
|
return;
|
|
@@ -1041,6 +1214,7 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
1041
1214
|
}
|
|
1042
1215
|
async function finalizeAsync(exitCode) {
|
|
1043
1216
|
stopWatching();
|
|
1217
|
+
clearTimers();
|
|
1044
1218
|
await Promise.all(
|
|
1045
1219
|
[...children.values()].map((managed) => stopManagedProcess(managed, "SIGTERM", killProcess, shutdownGraceMs))
|
|
1046
1220
|
);
|
|
@@ -1057,6 +1231,47 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
1057
1231
|
);
|
|
1058
1232
|
resolveExitCode(exitCode);
|
|
1059
1233
|
}
|
|
1234
|
+
function restartDelayFor(id) {
|
|
1235
|
+
const attempts = restartAttempts.get(id) ?? 0;
|
|
1236
|
+
return Math.min(MAX_RESTART_BACKOFF_MS, INITIAL_RESTART_BACKOFF_MS * 2 ** attempts);
|
|
1237
|
+
}
|
|
1238
|
+
function markRestartAttempt(id) {
|
|
1239
|
+
const attempts = restartAttempts.get(id) ?? 0;
|
|
1240
|
+
restartAttempts.set(id, attempts + 1);
|
|
1241
|
+
}
|
|
1242
|
+
function runSetupOnce() {
|
|
1243
|
+
const setupResults = runLocalSetup(plan, options, { spawnSync: spawnSyncProcess, write });
|
|
1244
|
+
const failedSetup = setupResults.find((step) => step.status === "failed" && step.required);
|
|
1245
|
+
if (failedSetup) {
|
|
1246
|
+
emitEvent(options, write, { type: "error", message: failedSetupMessage(failedSetup), detail: failedSetup });
|
|
1247
|
+
return false;
|
|
1248
|
+
}
|
|
1249
|
+
return true;
|
|
1250
|
+
}
|
|
1251
|
+
function scheduleSetupRetry(reason) {
|
|
1252
|
+
if (setupRetryTimer || settled) {
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
emitEvent(options, write, {
|
|
1256
|
+
type: "restart",
|
|
1257
|
+
status: "retrying",
|
|
1258
|
+
message: `${reason} Retrying local setup in ${Math.round(SETUP_RETRY_BACKOFF_MS / 1e3)}s.`
|
|
1259
|
+
}, "stderr");
|
|
1260
|
+
setupRetryTimer = setTimeout(() => {
|
|
1261
|
+
setupRetryTimer = null;
|
|
1262
|
+
if (settled) return;
|
|
1263
|
+
if (!runSetupOnce()) {
|
|
1264
|
+
scheduleSetupRetry("Local setup is still failing.");
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
for (const command of plan.commands) {
|
|
1268
|
+
if (!children.has(command.id)) {
|
|
1269
|
+
spawnCommand(command);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
void waitForReadiness();
|
|
1273
|
+
}, SETUP_RETRY_BACKOFF_MS);
|
|
1274
|
+
}
|
|
1060
1275
|
function spawnCommand(command) {
|
|
1061
1276
|
emitEvent(options, write, {
|
|
1062
1277
|
type: "spawn",
|
|
@@ -1091,9 +1306,10 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
1091
1306
|
surface: command.id,
|
|
1092
1307
|
exitCode,
|
|
1093
1308
|
signal,
|
|
1094
|
-
message: `${command.label} exited unexpectedly during ${readinessComplete ? "supervision" : "startup"} with ${signal ?? exitCode}.`
|
|
1309
|
+
message: `${command.label} exited unexpectedly during ${readinessComplete ? "supervision" : "startup"} with ${signal ?? exitCode}; restarting.`
|
|
1095
1310
|
});
|
|
1096
|
-
|
|
1311
|
+
children.delete(command.id);
|
|
1312
|
+
scheduleCommandRestart(command.id);
|
|
1097
1313
|
return;
|
|
1098
1314
|
}
|
|
1099
1315
|
const status = exitCode === 0 ? "idle" : "degraded";
|
|
@@ -1111,6 +1327,25 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
1111
1327
|
});
|
|
1112
1328
|
return child;
|
|
1113
1329
|
}
|
|
1330
|
+
function scheduleCommandRestart(id) {
|
|
1331
|
+
const command = commandsById.get(id);
|
|
1332
|
+
if (!command || settled || restartTimers.has(id)) {
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
const delayMs = restartDelayFor(id);
|
|
1336
|
+
markRestartAttempt(id);
|
|
1337
|
+
emitEvent(options, write, {
|
|
1338
|
+
type: "restart",
|
|
1339
|
+
surface: id,
|
|
1340
|
+
status: "scheduled",
|
|
1341
|
+
message: `Restarting ${command.label} in ${Math.round(delayMs / 1e3)}s.`
|
|
1342
|
+
}, "stderr");
|
|
1343
|
+
const timer = setTimeout(() => {
|
|
1344
|
+
restartTimers.delete(id);
|
|
1345
|
+
void restartCommand(id);
|
|
1346
|
+
}, delayMs);
|
|
1347
|
+
restartTimers.set(id, timer);
|
|
1348
|
+
}
|
|
1114
1349
|
async function restartCommand(id) {
|
|
1115
1350
|
const command = commandsById.get(id);
|
|
1116
1351
|
if (!command || settled) {
|
|
@@ -1127,6 +1362,7 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
1127
1362
|
}
|
|
1128
1363
|
spawnCommand(command);
|
|
1129
1364
|
emitEvent(options, write, { type: "restart", surface: id, message: `Restarted ${command.label}.` });
|
|
1365
|
+
void waitForReadiness();
|
|
1130
1366
|
}
|
|
1131
1367
|
function startLiveWatch() {
|
|
1132
1368
|
if (watchController || plan.watchEntries.length === 0 || plan.feedbackMode === "off" || settled) {
|
|
@@ -1150,28 +1386,50 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
1150
1386
|
detail: {
|
|
1151
1387
|
tenantChanged: change.tenantChanged,
|
|
1152
1388
|
tenantApiChanged: change.tenantApiChanged,
|
|
1153
|
-
|
|
1154
|
-
sdkChanged: change.sdkChanged
|
|
1389
|
+
coreChanged: change.coreChanged,
|
|
1390
|
+
sdkChanged: change.sdkChanged,
|
|
1391
|
+
agentChanged: change.agentChanged,
|
|
1392
|
+
cliChanged: change.cliChanged,
|
|
1393
|
+
commandImplementationChanged: change.commandImplementationChanged
|
|
1155
1394
|
}
|
|
1156
1395
|
});
|
|
1157
|
-
if (
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1396
|
+
if (change.commandImplementationChanged) {
|
|
1397
|
+
emitEvent(options, write, {
|
|
1398
|
+
type: "replace",
|
|
1399
|
+
status: "restart-required",
|
|
1400
|
+
message: "The dev command implementation changed. Stop and rerun `npx trsd dev` to load the new supervisor.",
|
|
1401
|
+
detail: change.changedPaths
|
|
1402
|
+
}, "stderr");
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1405
|
+
if (change.agentChanged) {
|
|
1406
|
+
emitEvent(options, write, {
|
|
1407
|
+
type: "restart",
|
|
1408
|
+
status: "deferred",
|
|
1409
|
+
message: "Agent service changes detected; running agent services will keep their current code until the next workday or a manual restart.",
|
|
1410
|
+
detail: change.changedPaths
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
if (change.tenantChanged || change.tenantApiChanged || change.coreChanged || change.sdkChanged) {
|
|
1414
|
+
if (!runSetupOnce()) {
|
|
1415
|
+
scheduleSetupRetry("Local setup failed after a development change.");
|
|
1169
1416
|
return;
|
|
1170
1417
|
}
|
|
1171
|
-
await restartCommand("web");
|
|
1172
1418
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1419
|
+
const restartIds = /* @__PURE__ */ new Set();
|
|
1420
|
+
if ((change.tenantChanged || change.coreChanged || change.sdkChanged) && commandsById.has("web")) {
|
|
1421
|
+
restartIds.add("web");
|
|
1422
|
+
}
|
|
1423
|
+
if ((change.tenantApiChanged || change.sdkChanged) && commandsById.has("api")) {
|
|
1424
|
+
restartIds.add("api");
|
|
1425
|
+
}
|
|
1426
|
+
if (change.sdkChanged) {
|
|
1427
|
+
for (const id of commandsById.keys()) {
|
|
1428
|
+
restartIds.add(id);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
for (const id of restartIds) {
|
|
1432
|
+
await restartCommand(id);
|
|
1175
1433
|
}
|
|
1176
1434
|
if (plan.feedbackMode === "live") {
|
|
1177
1435
|
writeDevReloadStamp(plan.tenantRoot);
|
|
@@ -1186,10 +1444,16 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
1186
1444
|
watchController.rebaseline();
|
|
1187
1445
|
}
|
|
1188
1446
|
async function waitForReadiness() {
|
|
1447
|
+
if (readinessInProgress) {
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
readinessInProgress = true;
|
|
1189
1451
|
const readinessTimeoutMs = options.readinessTimeoutMs ?? DEFAULT_READINESS_TIMEOUT_MS;
|
|
1190
1452
|
const processReadyGraceMs = options.processReadyGraceMs ?? DEFAULT_PROCESS_READY_GRACE_MS;
|
|
1453
|
+
let allRequiredReady = true;
|
|
1191
1454
|
for (const check of plan.readyChecks) {
|
|
1192
1455
|
if (settled) {
|
|
1456
|
+
readinessInProgress = false;
|
|
1193
1457
|
return;
|
|
1194
1458
|
}
|
|
1195
1459
|
let ready = false;
|
|
@@ -1201,17 +1465,26 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
1201
1465
|
ready = !exited.has(commandId) && children.has(commandId);
|
|
1202
1466
|
}
|
|
1203
1467
|
if (settled) {
|
|
1468
|
+
readinessInProgress = false;
|
|
1204
1469
|
return;
|
|
1205
1470
|
}
|
|
1206
1471
|
if (!ready && check.required) {
|
|
1472
|
+
allRequiredReady = false;
|
|
1207
1473
|
emitEvent(options, write, {
|
|
1208
1474
|
type: "error",
|
|
1209
1475
|
surface: check.id,
|
|
1210
1476
|
url: check.url,
|
|
1211
|
-
message: `${check.label} did not become ready${check.url ? ` at ${check.url}` : ""}.`
|
|
1477
|
+
message: `${check.label} did not become ready${check.url ? ` at ${check.url}` : ""}; keeping dev alive and retrying.`
|
|
1212
1478
|
});
|
|
1213
|
-
|
|
1214
|
-
|
|
1479
|
+
if (check.id !== "mailpit") {
|
|
1480
|
+
scheduleCommandRestart(check.id);
|
|
1481
|
+
} else {
|
|
1482
|
+
scheduleSetupRetry("Mailpit readiness failed.");
|
|
1483
|
+
}
|
|
1484
|
+
continue;
|
|
1485
|
+
}
|
|
1486
|
+
if (ready && check.id !== "mailpit") {
|
|
1487
|
+
restartAttempts.set(check.id, 0);
|
|
1215
1488
|
}
|
|
1216
1489
|
emitEvent(options, write, {
|
|
1217
1490
|
type: "ready",
|
|
@@ -1221,6 +1494,11 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
1221
1494
|
message: `${check.label} is ${ready ? "ready" : "degraded"}.`
|
|
1222
1495
|
});
|
|
1223
1496
|
}
|
|
1497
|
+
readinessInProgress = false;
|
|
1498
|
+
if (!allRequiredReady) {
|
|
1499
|
+
startLiveWatch();
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1224
1502
|
readinessComplete = true;
|
|
1225
1503
|
if (plan.webUrl) {
|
|
1226
1504
|
emitEvent(options, write, { type: "ready", url: plan.webUrl, message: `Treeseed dev ready at ${plan.webUrl}.` });
|
|
@@ -1228,12 +1506,12 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
1228
1506
|
if (shouldOpenBrowser(plan)) {
|
|
1229
1507
|
try {
|
|
1230
1508
|
await openBrowser(plan.webUrl);
|
|
1231
|
-
emitEvent(options, write, { type: "open", url: plan.webUrl, message: `Opened ${plan.webUrl}.` });
|
|
1509
|
+
emitEvent(options, write, { type: "open", url: plan.webUrl ?? void 0, message: `Opened ${plan.webUrl}.` });
|
|
1232
1510
|
} catch (error) {
|
|
1233
1511
|
emitEvent(options, write, {
|
|
1234
1512
|
type: "open",
|
|
1235
1513
|
status: "degraded",
|
|
1236
|
-
url: plan.webUrl,
|
|
1514
|
+
url: plan.webUrl ?? void 0,
|
|
1237
1515
|
message: `Could not open ${plan.webUrl}.`,
|
|
1238
1516
|
detail: error instanceof Error ? error.message : String(error)
|
|
1239
1517
|
});
|
|
@@ -1241,17 +1519,23 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
1241
1519
|
}
|
|
1242
1520
|
startLiveWatch();
|
|
1243
1521
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1522
|
+
startLiveWatch();
|
|
1523
|
+
if (runSetupOnce()) {
|
|
1524
|
+
for (const command of plan.commands) {
|
|
1525
|
+
spawnCommand(command);
|
|
1526
|
+
}
|
|
1527
|
+
void waitForReadiness().catch((error) => {
|
|
1528
|
+
readinessInProgress = false;
|
|
1529
|
+
emitEvent(options, write, {
|
|
1530
|
+
type: "error",
|
|
1531
|
+
message: "Dev readiness failed; keeping supervisor alive.",
|
|
1532
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
1533
|
+
});
|
|
1534
|
+
scheduleSetupRetry("Readiness failed unexpectedly.");
|
|
1252
1535
|
});
|
|
1253
|
-
|
|
1254
|
-
|
|
1536
|
+
} else {
|
|
1537
|
+
scheduleSetupRetry("Initial local setup failed.");
|
|
1538
|
+
}
|
|
1255
1539
|
});
|
|
1256
1540
|
}
|
|
1257
1541
|
export {
|