@sapienx/agentos 0.5.3 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +4 -1
  2. package/bin/agentos.js +580 -31
  3. package/bin/terminal-boot.js +620 -0
  4. package/bundle/.next/BUILD_ID +1 -1
  5. package/bundle/.next/app-path-routes-manifest.json +9 -9
  6. package/bundle/.next/build-manifest.json +2 -2
  7. package/bundle/.next/prerender-manifest.json +3 -3
  8. package/bundle/.next/required-server-files.json +1 -1
  9. package/bundle/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  10. package/bundle/.next/server/app/_global-error.html +1 -1
  11. package/bundle/.next/server/app/_global-error.rsc +1 -1
  12. package/bundle/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  13. package/bundle/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  14. package/bundle/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  15. package/bundle/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  16. package/bundle/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  17. package/bundle/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  18. package/bundle/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  19. package/bundle/.next/server/app/_not-found.html +1 -1
  20. package/bundle/.next/server/app/_not-found.rsc +1 -1
  21. package/bundle/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  22. package/bundle/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  23. package/bundle/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  24. package/bundle/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  25. package/bundle/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  26. package/bundle/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  27. package/bundle/.next/server/app/api/agents/[agentId]/chat/route.js.nft.json +1 -1
  28. package/bundle/.next/server/app/api/agents/route.js.nft.json +1 -1
  29. package/bundle/.next/server/app/api/diagnostics/route.js.nft.json +1 -1
  30. package/bundle/.next/server/app/api/gateway/control/route.js.nft.json +1 -1
  31. package/bundle/.next/server/app/api/mission/route.js.nft.json +1 -1
  32. package/bundle/.next/server/app/api/models/catalog/route.js +2 -1
  33. package/bundle/.next/server/app/api/models/catalog/route.js.nft.json +1 -1
  34. package/bundle/.next/server/app/api/models/providers/route.js +2 -1
  35. package/bundle/.next/server/app/api/models/providers/route.js.nft.json +1 -1
  36. package/bundle/.next/server/app/api/onboarding/models/route.js +8 -7
  37. package/bundle/.next/server/app/api/onboarding/models/route.js.nft.json +1 -1
  38. package/bundle/.next/server/app/api/onboarding/route.js +12 -6
  39. package/bundle/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  40. package/bundle/.next/server/app/api/planner/[planId]/deploy/route.js.nft.json +1 -1
  41. package/bundle/.next/server/app/api/planner/[planId]/document-rewrite/route.js.nft.json +1 -1
  42. package/bundle/.next/server/app/api/planner/[planId]/route.js.nft.json +1 -1
  43. package/bundle/.next/server/app/api/planner/[planId]/simulate/route.js.nft.json +1 -1
  44. package/bundle/.next/server/app/api/planner/[planId]/turn/route.js.nft.json +1 -1
  45. package/bundle/.next/server/app/api/planner/route.js.nft.json +1 -1
  46. package/bundle/.next/server/app/api/reset/route.js.nft.json +1 -1
  47. package/bundle/.next/server/app/api/runtimes/[runtimeId]/route.js.nft.json +1 -1
  48. package/bundle/.next/server/app/api/settings/gateway/route.js.nft.json +1 -1
  49. package/bundle/.next/server/app/api/settings/openclaw-binary/route.js.nft.json +1 -1
  50. package/bundle/.next/server/app/api/settings/workspace-root/route.js.nft.json +1 -1
  51. package/bundle/.next/server/app/api/snapshot/route.js.nft.json +1 -1
  52. package/bundle/.next/server/app/api/stream/route.js.nft.json +1 -1
  53. package/bundle/.next/server/app/api/tasks/[taskId]/abort/route.js.nft.json +1 -1
  54. package/bundle/.next/server/app/api/tasks/[taskId]/control/route.js.nft.json +1 -1
  55. package/bundle/.next/server/app/api/tasks/[taskId]/stream/route.js.nft.json +1 -1
  56. package/bundle/.next/server/app/api/update/route.js.nft.json +1 -1
  57. package/bundle/.next/server/app/api/workspaces/[workspaceId]/channels/discovered-groups/route.js.nft.json +1 -1
  58. package/bundle/.next/server/app/api/workspaces/[workspaceId]/channels/route.js.nft.json +1 -1
  59. package/bundle/.next/server/app/api/workspaces/[workspaceId]/edit-draft/route.js.nft.json +1 -1
  60. package/bundle/.next/server/app/api/workspaces/[workspaceId]/files/route.js.nft.json +1 -1
  61. package/bundle/.next/server/app/api/workspaces/[workspaceId]/surfaces/discovery/route.js.nft.json +1 -1
  62. package/bundle/.next/server/app/api/workspaces/route.js.nft.json +1 -1
  63. package/bundle/.next/server/app/page.js.nft.json +1 -1
  64. package/bundle/.next/server/app/page_client-reference-manifest.js +1 -1
  65. package/bundle/.next/server/app/settings/page.js.nft.json +1 -1
  66. package/bundle/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  67. package/bundle/.next/server/app-paths-manifest.json +9 -9
  68. package/bundle/.next/server/chunks/4767.js +9 -9
  69. package/bundle/.next/server/chunks/5151.js +2 -2
  70. package/bundle/.next/server/chunks/6639.js +6 -5
  71. package/bundle/.next/server/chunks/7125.js +4 -4
  72. package/bundle/.next/server/middleware-build-manifest.js +1 -1
  73. package/bundle/.next/server/pages/404.html +1 -1
  74. package/bundle/.next/server/pages/500.html +1 -1
  75. package/bundle/.next/server/server-reference-manifest.json +1 -1
  76. package/bundle/.next/static/chunks/{7442-f5f805e43da2c5b6.js → 7442-e148a49efcfa2d32.js} +4 -4
  77. package/bundle/server.js +1 -1
  78. package/package.json +1 -1
  79. package/bundle/.next/server/chunks/5831.js +0 -2
  80. /package/bundle/.next/static/{easVJgXM2ae2U4xGhNmSj → ujFbMZ9arxb7I3_g4GzfY}/_buildManifest.js +0 -0
  81. /package/bundle/.next/static/{easVJgXM2ae2U4xGhNmSj → ujFbMZ9arxb7I3_g4GzfY}/_ssgManifest.js +0 -0
package/bin/agentos.js CHANGED
@@ -9,6 +9,8 @@ import path from "node:path";
9
9
  import process from "node:process";
10
10
  import { fileURLToPath } from "node:url";
11
11
 
12
+ import { createTerminalBoot, renderDoctorReport, renderStatusDashboard } from "./terminal-boot.js";
13
+
12
14
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
15
  const packageRoot = path.resolve(__dirname, "..");
14
16
  const packageJsonPath = path.join(packageRoot, "package.json");
@@ -69,6 +71,16 @@ async function main() {
69
71
  return;
70
72
  }
71
73
 
74
+ if (firstArg === "status") {
75
+ if (args[1] === "--help" || args[1] === "-h" || args[1] === "help") {
76
+ printStatusHelp();
77
+ return;
78
+ }
79
+
80
+ runStatus(args.slice(1));
81
+ return;
82
+ }
83
+
72
84
  if (firstArg === "update") {
73
85
  if (args[1] === "--help" || args[1] === "-h" || args[1] === "help") {
74
86
  printUpdateHelp();
@@ -99,7 +111,7 @@ async function main() {
99
111
  return;
100
112
  }
101
113
 
102
- if (firstArg === "start") {
114
+ if (firstArg === "start" || firstArg === "dev") {
103
115
  await startServer(args.slice(1));
104
116
  return;
105
117
  }
@@ -143,18 +155,35 @@ async function startServer(rawArgs) {
143
155
  clearRuntimeState(runtimeStatePath);
144
156
  }
145
157
 
146
- console.log(`Starting AgentOS on ${url}`);
158
+ const boot = createTerminalBoot({
159
+ plain: options.plain,
160
+ stdout: process.stdout,
161
+ stderr: process.stderr,
162
+ env: process.env
163
+ });
164
+
165
+ if (boot.isPlain()) {
166
+ console.log(`Starting AgentOS ${packageJson.version} on ${url}`);
147
167
 
148
- if (!openClawCheck.available) {
149
- console.log("OpenClaw was not found in PATH or the default local install paths. AgentOS will start and guide onboarding in the UI.");
150
- } else if (openClawCheck.version) {
151
- console.log(`OpenClaw detected: ${openClawCheck.version}`);
168
+ if (!openClawCheck.available) {
169
+ console.log("OpenClaw was not found in PATH or the default local install paths. AgentOS will start and guide onboarding in the UI.");
170
+ } else if (openClawCheck.version) {
171
+ console.log(`OpenClaw detected: ${openClawCheck.version}`);
172
+ }
173
+ } else {
174
+ initializeBootVersionStatus(boot);
175
+ boot.start();
176
+ boot.updateStatus("workspaceEngine", "loading", "bundle ready");
177
+ applyOpenClawBootStatus(boot, openClawCheck);
152
178
  }
153
179
 
154
180
  if (options.open && !browserOpener.available) {
155
- console.warn(
156
- `Browser auto-open is unavailable on this machine${browserOpener.detail ? ` (${browserOpener.detail})` : ""}.`
157
- );
181
+ const message = `Browser auto-open is unavailable on this machine${browserOpener.detail ? ` (${browserOpener.detail})` : ""}.`;
182
+ if (boot.isPlain()) {
183
+ console.warn(message);
184
+ } else {
185
+ boot.warn(message);
186
+ }
158
187
  }
159
188
 
160
189
  const child = spawn(process.execPath, [bundledServerPath], {
@@ -187,14 +216,46 @@ async function startServer(rawArgs) {
187
216
  throw error;
188
217
  }
189
218
 
219
+ if (!boot.isPlain()) {
220
+ boot.updateStatus("workspaceEngine", "ready", "runtime state written");
221
+ boot.updateStatus("agentRuntime", "starting", `pid ${child.pid}`);
222
+ }
223
+
224
+ const startupState = {
225
+ ready: false,
226
+ completing: false,
227
+ completed: boot.isPlain(),
228
+ bufferedLogs: []
229
+ };
190
230
  const browserState = { opened: false };
191
- const relayStdout = createRelay(process.stdout, options, url, browserOpener, browserState);
192
- const relayStderr = createRelay(process.stderr, options, url, browserOpener, browserState);
231
+ const onServerReady = () => {
232
+ if (startupState.ready) {
233
+ return;
234
+ }
235
+
236
+ startupState.ready = true;
237
+
238
+ if (options.open && browserOpener.available && !browserState.opened) {
239
+ browserState.opened = true;
240
+ openBrowser(url, browserOpener);
241
+ }
242
+
243
+ if (boot.isPlain()) {
244
+ return;
245
+ }
246
+
247
+ startupState.completing = true;
248
+ void completeBootAfterServerReady(boot, startupState, url);
249
+ };
250
+ const relayStdout = createRelay(process.stdout, boot, startupState, onServerReady);
251
+ const relayStderr = createRelay(process.stderr, boot, startupState, onServerReady);
193
252
 
194
253
  child.stdout.on("data", relayStdout);
195
254
  child.stderr.on("data", relayStderr);
196
255
 
197
- schedulePassiveUpdateNotice();
256
+ if (boot.isPlain()) {
257
+ schedulePassiveUpdateNotice();
258
+ }
198
259
 
199
260
  let cleanedUp = false;
200
261
  const shutdownState = {
@@ -227,8 +288,12 @@ async function startServer(rawArgs) {
227
288
  shutdownState.parentSignal = signal;
228
289
 
229
290
  if (signal === "SIGINT") {
291
+ boot.stop({ clear: true });
292
+ flushBufferedStartupLogs(startupState);
230
293
  process.stdout.write("\nStopping AgentOS... Press Ctrl+C again to force quit.\n");
231
294
  } else {
295
+ boot.stop({ clear: true });
296
+ flushBufferedStartupLogs(startupState);
232
297
  console.log(`Stopping AgentOS after ${signal}...`);
233
298
  }
234
299
 
@@ -247,6 +312,8 @@ async function startServer(rawArgs) {
247
312
 
248
313
  child.on("error", (error) => {
249
314
  cleanup();
315
+ boot.stop({ clear: true });
316
+ flushBufferedStartupLogs(startupState);
250
317
  console.error(`AgentOS failed to start: ${error.message}`);
251
318
  process.exit(1);
252
319
  });
@@ -254,6 +321,11 @@ async function startServer(rawArgs) {
254
321
  child.on("exit", (code, signal) => {
255
322
  cleanup();
256
323
 
324
+ if (!startupState.completed) {
325
+ boot.stop({ clear: true });
326
+ flushBufferedStartupLogs(startupState);
327
+ }
328
+
257
329
  if (shutdownState.parentSignal) {
258
330
  process.kill(process.pid, shutdownState.parentSignal);
259
331
  return;
@@ -271,55 +343,63 @@ async function startServer(rawArgs) {
271
343
  function runDoctor() {
272
344
  const options = parseStartArgs([]);
273
345
  const openClawCheck = detectOpenClaw();
346
+ const gatewayStatus = openClawCheck.available ? inspectOpenClawGatewayStatus(openClawCheck) : null;
274
347
  const browserOpener = detectBrowserOpener();
275
348
  const targetUrl = `http://${displayHost(options.host)}:${options.port}`;
276
349
  const checks = [
277
350
  {
278
- ok: true,
351
+ state: "ok",
279
352
  label: "Package",
280
353
  detail: `${packageJson.name}@${packageJson.version}`
281
354
  },
282
355
  {
283
- ok: true,
356
+ state: "ok",
284
357
  label: "Install",
285
358
  detail: formatInstallKind(inspectInstallation())
286
359
  },
287
360
  {
288
- ok: isSupportedNodeVersion(process.versions.node),
361
+ state: isSupportedNodeVersion(process.versions.node) ? "ok" : "failed",
289
362
  label: "Node.js",
290
363
  detail: `${process.version} (required >= 20.9.0)`
291
364
  },
292
365
  {
293
- ok: true,
366
+ state: "ok",
294
367
  label: "Platform",
295
368
  detail: `${os.platform()} ${os.release()}`
296
369
  },
297
370
  {
298
- ok: existsSync(bundledServerPath),
371
+ state: existsSync(bundledServerPath) ? "ok" : "failed",
299
372
  label: "Bundle",
300
373
  detail: existsSync(bundledServerPath)
301
374
  ? `ready at ${bundledServerPath}`
302
375
  : `missing at ${bundledServerPath}`
303
376
  },
304
377
  {
305
- ok: true,
378
+ state: "ok",
306
379
  label: "Target URL",
307
380
  detail: targetUrl
308
381
  },
309
382
  {
310
- ok: true,
383
+ state: "ok",
311
384
  label: "Configured env",
312
385
  detail: formatConfiguredEnv(options)
313
386
  },
314
387
  {
315
- ok: openClawCheck.available,
388
+ state: openClawCheck.available ? "ok" : "warning",
316
389
  label: "OpenClaw",
317
390
  detail: openClawCheck.available
318
391
  ? `${openClawCheck.version || "installed"}${openClawCheck.path ? ` at ${openClawCheck.path}` : ""}`
319
392
  : "not found in PATH or default local install paths; install OpenClaw or continue with the AgentOS onboarding flow"
320
393
  },
321
394
  {
322
- ok: browserOpener.available,
395
+ state: !openClawCheck.available ? "disabled" : gatewayStatus?.ok ? "ok" : "warning",
396
+ label: "Gateway",
397
+ detail: !openClawCheck.available
398
+ ? "OpenClaw is required before Gateway RPC can be checked"
399
+ : gatewayStatus?.gatewayMessage || "Gateway status unavailable"
400
+ },
401
+ {
402
+ state: browserOpener.available ? "ok" : "warning",
323
403
  label: "Browser opener",
324
404
  detail: browserOpener.available
325
405
  ? `${browserOpener.command} is available`
@@ -327,11 +407,90 @@ function runDoctor() {
327
407
  }
328
408
  ];
329
409
 
330
- for (const check of checks) {
331
- console.log(`${check.ok ? "OK" : "WARN"} ${check.label}: ${check.detail}`);
410
+ console.log(renderDoctorReport({
411
+ title: "AGENTOS DOCTOR",
412
+ rows: checks.map((check) => ({
413
+ label: check.label,
414
+ state: check.state,
415
+ message: check.detail
416
+ })),
417
+ footer: "Doctor shows install and runtime diagnostics. The in-app diagnostics panel has live Gateway/model detail."
418
+ }));
419
+
420
+ if (checks.some((check) => check.state === "failed")) {
421
+ process.exitCode = 1;
332
422
  }
423
+ }
333
424
 
334
- if (!checks.every((check) => check.ok || check.label === "OpenClaw" || check.label === "Browser opener")) {
425
+ function runStatus(rawArgs) {
426
+ const options = parseStatusArgs(rawArgs);
427
+ const runtimeStatePath = resolveRuntimeStatePath(options.port);
428
+ const trackedState = readRuntimeState(runtimeStatePath);
429
+ const listeningPid = findListeningPidForPort(options.port);
430
+ const trackedPid = trackedState?.pid && isProcessRunning(trackedState.pid) ? trackedState.pid : null;
431
+ const runningPid = listeningPid ?? trackedPid;
432
+ const host = trackedState?.host || options.host;
433
+ const url = createAgentOsUrl(host, options.port);
434
+ const openClawCheck = detectOpenClaw();
435
+ const gatewayStatus = openClawCheck.available ? inspectOpenClawGatewayStatus(openClawCheck) : null;
436
+ const bundleReady = existsSync(bundledServerPath);
437
+ const install = inspectInstallation();
438
+ const rows = [
439
+ {
440
+ label: "AgentOS",
441
+ state: "ready",
442
+ message: packageJson.version
443
+ },
444
+ buildUpdateDashboardRow(install),
445
+ {
446
+ label: "OpenClaw Gateway",
447
+ state: !openClawCheck.available ? "warning" : gatewayStatus?.ok ? "connected" : "degraded",
448
+ message: !openClawCheck.available
449
+ ? "OpenClaw not found; setup will guide installation"
450
+ : gatewayStatus?.gatewayMessage || "needs attention"
451
+ },
452
+ {
453
+ label: "Native Gateway",
454
+ state: !openClawCheck.available ? "disabled" : gatewayStatus?.nativeState || "warning",
455
+ message: !openClawCheck.available
456
+ ? "waiting for OpenClaw"
457
+ : gatewayStatus?.nativeMessage || "check Gateway diagnostics"
458
+ },
459
+ {
460
+ label: "Workspace Engine",
461
+ state: bundleReady ? "ready" : "failed",
462
+ message: bundleReady ? "bundle ready" : "bundle missing"
463
+ },
464
+ {
465
+ label: "Agent Runtime",
466
+ state: runningPid ? "ready" : "inactive",
467
+ message: runningPid ? `server running (PID ${runningPid})` : "server not running"
468
+ },
469
+ {
470
+ label: "Models",
471
+ state: runningPid ? "warning" : "pending",
472
+ message: runningPid ? "verify in setup or diagnostics" : "available after server start"
473
+ },
474
+ {
475
+ label: "Channels",
476
+ state: "disabled",
477
+ message: "no channel status loaded from CLI"
478
+ },
479
+ {
480
+ label: "Local Server",
481
+ state: runningPid ? "ready" : "pending",
482
+ message: url
483
+ }
484
+ ];
485
+
486
+ console.log(renderStatusDashboard({
487
+ title: "SYSTEM CHECK",
488
+ rows,
489
+ finalInfo: runningPid ? url : "",
490
+ footer: runningPid ? "" : "AgentOS is not running. Start it with agentos start --open."
491
+ }));
492
+
493
+ if (!bundleReady) {
335
494
  process.exitCode = 1;
336
495
  }
337
496
  }
@@ -465,12 +624,314 @@ async function runStop(rawArgs) {
465
624
  console.log(`Stopped AgentOS on port ${options.port}.`);
466
625
  }
467
626
 
627
+ function applyOpenClawBootStatus(boot, openClawCheck) {
628
+ if (!openClawCheck.available) {
629
+ boot.updateStatus("openclawGateway", "warning", "OpenClaw not found; onboarding will guide setup");
630
+ boot.updateStatus("nativeGateway", "disabled", "waiting for OpenClaw");
631
+ return;
632
+ }
633
+
634
+ boot.updateStatus("openclawGateway", "checking", openClawCheck.version || "OpenClaw detected");
635
+ boot.updateStatus("nativeGateway", "waiting", "probing gateway");
636
+
637
+ const gatewayStatus = inspectOpenClawGatewayStatus(openClawCheck);
638
+
639
+ if (gatewayStatus.ok) {
640
+ boot.updateStatus("openclawGateway", "connected", gatewayStatus.gatewayMessage);
641
+ boot.updateStatus("nativeGateway", gatewayStatus.nativeState, gatewayStatus.nativeMessage);
642
+ return;
643
+ }
644
+
645
+ boot.updateStatus("openclawGateway", "warning", gatewayStatus.gatewayMessage);
646
+ boot.updateStatus("nativeGateway", "disabled", gatewayStatus.nativeMessage);
647
+ }
648
+
649
+ function initializeBootVersionStatus(boot) {
650
+ boot.updateStatus("agentosVersion", "ready", packageJson.version);
651
+
652
+ const install = inspectInstallation();
653
+ const cached = readCachedUpdateStatus(install);
654
+
655
+ if (install.kind === "source") {
656
+ boot.updateStatus("update", "disabled", "source checkout");
657
+ return;
658
+ }
659
+
660
+ if (cached) {
661
+ updateBootUpdateStatus(boot, buildUpdateStatusFromCache(cached, install));
662
+ } else {
663
+ boot.updateStatus("update", "checking", "checking latest version");
664
+ }
665
+
666
+ if (cached && isUpdateCacheFresh(cached)) {
667
+ return;
668
+ }
669
+
670
+ void getUpdateStatus({
671
+ install,
672
+ forceRefresh: false,
673
+ timeoutMs: 1_500,
674
+ fallbackToCache: true
675
+ })
676
+ .then((status) => {
677
+ if (!status.ok) {
678
+ boot.updateStatus("update", "disabled", "check unavailable");
679
+ return;
680
+ }
681
+
682
+ updateBootUpdateStatus(boot, status);
683
+ })
684
+ .catch(() => {
685
+ boot.updateStatus("update", "disabled", "check unavailable");
686
+ });
687
+ }
688
+
689
+ function updateBootUpdateStatus(boot, status) {
690
+ const row = buildUpdateRowFromStatus(status);
691
+ boot.updateStatus("update", row.state, row.message);
692
+ }
693
+
694
+ function buildUpdateDashboardRow(install) {
695
+ if (install.kind === "source") {
696
+ return {
697
+ label: "Update",
698
+ state: "disabled",
699
+ message: "source checkout"
700
+ };
701
+ }
702
+
703
+ const cached = readCachedUpdateStatus(install);
704
+
705
+ if (!cached) {
706
+ return {
707
+ label: "Update",
708
+ state: "pending",
709
+ message: 'run "agentos update --check"'
710
+ };
711
+ }
712
+
713
+ return {
714
+ label: "Update",
715
+ ...buildUpdateRowFromStatus(buildUpdateStatusFromCache(cached, install))
716
+ };
717
+ }
718
+
719
+ function buildUpdateRowFromStatus(status) {
720
+ if (status.updateAvailable) {
721
+ return {
722
+ state: "warning",
723
+ message: `${status.latestVersion} available · ${formatBootUpdateAction(status.install)}`
724
+ };
725
+ }
726
+
727
+ return {
728
+ state: "ready",
729
+ message: "up to date"
730
+ };
731
+ }
732
+
733
+ function formatBootUpdateAction(install) {
734
+ if (install.kind === "release") {
735
+ return "run agentos update";
736
+ }
737
+
738
+ if (install.kind === "package-manager") {
739
+ return "update package";
740
+ }
741
+
742
+ return 'run "agentos update --check"';
743
+ }
744
+
745
+ function inspectOpenClawGatewayStatus(openClawCheck) {
746
+ const command = openClawCheck.path || "openclaw";
747
+ const result = spawnSync(command, ["gateway", "status", "--json"], {
748
+ encoding: "utf8",
749
+ timeout: 1_500
750
+ });
751
+ const output = `${result.stdout || ""}\n${result.stderr || ""}`.trim();
752
+
753
+ if (result.error) {
754
+ return {
755
+ ok: false,
756
+ gatewayMessage: result.error.code === "ETIMEDOUT" ? "gateway probe timed out" : "gateway status unavailable",
757
+ nativeMessage: "Gateway will be checked in the UI",
758
+ nativeState: "disabled"
759
+ };
760
+ }
761
+
762
+ if (result.status !== 0) {
763
+ return {
764
+ ok: false,
765
+ gatewayMessage: summarizeBootText(output) || "gateway not reachable yet",
766
+ nativeMessage: "start or repair OpenClaw Gateway in setup",
767
+ nativeState: "disabled"
768
+ };
769
+ }
770
+
771
+ const parsed = parseFirstJsonObject(output);
772
+ const capability = String(parsed?.capability || parsed?.Capability || "");
773
+ const connected = /connected|ok|reachable/i.test(output);
774
+ const missingOperatorScope = /no-operator-scope|operator scope/i.test(capability || output);
775
+
776
+ return {
777
+ ok: true,
778
+ gatewayMessage: connected ? "reachable" : "status available",
779
+ nativeState: missingOperatorScope ? "warning" : "active",
780
+ nativeMessage: missingOperatorScope ? "operator scope needs repair" : "native RPC available"
781
+ };
782
+ }
783
+
784
+ async function completeBootAfterServerReady(boot, startupState, url) {
785
+ boot.updateStatus("agentRuntime", "ready", "server ready");
786
+ boot.updateStatus("localServerUrl", "ready", url);
787
+ boot.updateStatus("models", "loading", "checking readiness");
788
+ boot.updateStatus("channels", "loading", "checking registry");
789
+
790
+ const readiness = await readStartupReadiness(url);
791
+
792
+ if (readiness.openClawGateway) {
793
+ boot.updateStatus("openclawGateway", readiness.openClawGateway.state, readiness.openClawGateway.message);
794
+ }
795
+
796
+ if (readiness.nativeGateway) {
797
+ boot.updateStatus("nativeGateway", readiness.nativeGateway.state, readiness.nativeGateway.message);
798
+ }
799
+
800
+ boot.updateStatus("workspaceEngine", readiness.workspaceEngine.state, readiness.workspaceEngine.message);
801
+ boot.updateStatus("models", readiness.models.state, readiness.models.message);
802
+ boot.updateStatus("channels", readiness.channels.state, readiness.channels.message);
803
+ boot.complete(url);
804
+ startupState.bufferedLogs = [];
805
+ startupState.completed = true;
806
+ }
807
+
808
+ async function readStartupReadiness(url) {
809
+ const fallback = {
810
+ workspaceEngine: { state: "ready", message: "available" },
811
+ models: { state: "warning", message: "finish setup in the UI" },
812
+ channels: { state: "disabled", message: "none confirmed yet" }
813
+ };
814
+ const controller = new AbortController();
815
+ const timeout = setTimeout(() => controller.abort(), 2_500);
816
+
817
+ try {
818
+ const response = await fetch(`${url}/api/snapshot`, {
819
+ signal: controller.signal
820
+ });
821
+
822
+ if (!response.ok) {
823
+ return fallback;
824
+ }
825
+
826
+ const snapshot = await response.json();
827
+ return summarizeStartupSnapshot(snapshot);
828
+ } catch {
829
+ return fallback;
830
+ } finally {
831
+ clearTimeout(timeout);
832
+ }
833
+ }
834
+
835
+ function summarizeStartupSnapshot(snapshot) {
836
+ const diagnostics = isRecord(snapshot?.diagnostics) ? snapshot.diagnostics : {};
837
+ const modelReadiness = isRecord(diagnostics.modelReadiness) ? diagnostics.modelReadiness : {};
838
+ const transport = isRecord(diagnostics.transport) ? diagnostics.transport : {};
839
+ const workspaces = Array.isArray(snapshot?.workspaces) ? snapshot.workspaces : [];
840
+ const channelAccounts = Array.isArray(snapshot?.channelAccounts) ? snapshot.channelAccounts : [];
841
+ const channelRegistry = isRecord(snapshot?.channelRegistry) && Array.isArray(snapshot.channelRegistry.channels)
842
+ ? snapshot.channelRegistry.channels
843
+ : [];
844
+ const rpcOk = diagnostics.rpcOk === true;
845
+ const modelReady = modelReadiness.ready === true || modelReadiness.defaultModelReady === true;
846
+ const availableModelCount = numberOrZero(modelReadiness.availableModelCount);
847
+ const modelIssues = Array.isArray(modelReadiness.issues) ? modelReadiness.issues.filter(Boolean) : [];
848
+ const enabledChannels = channelAccounts.filter((channel) => channel && channel.enabled !== false).length + channelRegistry.length;
849
+ const transportStatus = typeof transport.gatewayMode === "string"
850
+ ? transport.gatewayMode
851
+ : typeof transport.statusLabel === "string"
852
+ ? transport.statusLabel
853
+ : "";
854
+
855
+ return {
856
+ openClawGateway: {
857
+ state: rpcOk ? "connected" : "warning",
858
+ message: rpcOk ? "authenticated RPC ready" : summarizeBootText(diagnostics.health) || "setup may be required"
859
+ },
860
+ nativeGateway: {
861
+ state: rpcOk ? "active" : "warning",
862
+ message: transportStatus || (rpcOk ? "native Gateway active" : "check diagnostics")
863
+ },
864
+ workspaceEngine: {
865
+ state: "ready",
866
+ message: workspaces.length > 0 ? `${workspaces.length} workspace${workspaces.length === 1 ? "" : "s"}` : "ready for first workspace"
867
+ },
868
+ models: {
869
+ state: modelReady || availableModelCount > 0 ? "ready" : "warning",
870
+ message: modelReady
871
+ ? modelReadiness.resolvedDefaultModel || modelReadiness.defaultModel || "default model ready"
872
+ : modelIssues[0] || "model setup needed"
873
+ },
874
+ channels: {
875
+ state: enabledChannels > 0 ? "ready" : "disabled",
876
+ message: enabledChannels > 0 ? `${enabledChannels} configured` : "no channels configured"
877
+ }
878
+ };
879
+ }
880
+
881
+ function flushBufferedStartupLogs(startupState) {
882
+ if (!startupState?.bufferedLogs?.length) {
883
+ return;
884
+ }
885
+
886
+ for (const entry of startupState.bufferedLogs) {
887
+ entry.target.write(entry.text);
888
+ }
889
+
890
+ startupState.bufferedLogs = [];
891
+ }
892
+
893
+ function parseFirstJsonObject(value) {
894
+ if (!value) {
895
+ return null;
896
+ }
897
+
898
+ const start = value.indexOf("{");
899
+ const end = value.lastIndexOf("}");
900
+
901
+ if (start < 0 || end <= start) {
902
+ return null;
903
+ }
904
+
905
+ try {
906
+ return JSON.parse(value.slice(start, end + 1));
907
+ } catch {
908
+ return null;
909
+ }
910
+ }
911
+
912
+ function summarizeBootText(value) {
913
+ if (typeof value !== "string") {
914
+ return "";
915
+ }
916
+
917
+ return value.replace(/\s+/g, " ").trim().slice(0, 96);
918
+ }
919
+
920
+ function numberOrZero(value) {
921
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
922
+ }
923
+
924
+ function isRecord(value) {
925
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
926
+ }
927
+
468
928
  function parseStartArgs(rawArgs) {
469
929
  const envPort = process.env.AGENTOS_PORT || process.env.PORT;
470
930
  const options = {
471
931
  host: process.env.AGENTOS_HOST || "127.0.0.1",
472
932
  port: envPort && /^\d+$/.test(envPort) ? Number(envPort) : 3000,
473
- open: parseBooleanEnv(process.env.AGENTOS_OPEN)
933
+ open: parseBooleanEnv(process.env.AGENTOS_OPEN),
934
+ plain: false
474
935
  };
475
936
 
476
937
  for (let index = 0; index < rawArgs.length; index += 1) {
@@ -516,6 +977,57 @@ function parseStartArgs(rawArgs) {
516
977
  continue;
517
978
  }
518
979
 
980
+ if (arg === "--plain") {
981
+ options.plain = true;
982
+ continue;
983
+ }
984
+
985
+ throw new Error(`Unknown argument: ${arg}`);
986
+ }
987
+
988
+ return options;
989
+ }
990
+
991
+ function parseStatusArgs(rawArgs) {
992
+ const envPort = process.env.AGENTOS_PORT || process.env.PORT;
993
+ const options = {
994
+ host: process.env.AGENTOS_HOST || "127.0.0.1",
995
+ port: envPort && /^\d+$/.test(envPort) ? Number(envPort) : 3000
996
+ };
997
+
998
+ for (let index = 0; index < rawArgs.length; index += 1) {
999
+ const arg = rawArgs[index];
1000
+
1001
+ if (arg === "--port" || arg === "-p") {
1002
+ const value = rawArgs[index + 1];
1003
+ index += 1;
1004
+ assertPort(value);
1005
+ options.port = Number(value);
1006
+ continue;
1007
+ }
1008
+
1009
+ if (arg.startsWith("--port=")) {
1010
+ const value = arg.slice("--port=".length);
1011
+ assertPort(value);
1012
+ options.port = Number(value);
1013
+ continue;
1014
+ }
1015
+
1016
+ if (arg === "--host" || arg === "-H") {
1017
+ const value = rawArgs[index + 1];
1018
+ index += 1;
1019
+ assertHost(value);
1020
+ options.host = value;
1021
+ continue;
1022
+ }
1023
+
1024
+ if (arg.startsWith("--host=")) {
1025
+ const value = arg.slice("--host=".length);
1026
+ assertHost(value);
1027
+ options.host = value;
1028
+ continue;
1029
+ }
1030
+
519
1031
  throw new Error(`Unknown argument: ${arg}`);
520
1032
  }
521
1033
 
@@ -798,6 +1310,8 @@ function printHelp() {
798
1310
  Usage:
799
1311
  agentos
800
1312
  agentos start --port 3000 --host 127.0.0.1 --open
1313
+ agentos dev --plain
1314
+ agentos status
801
1315
  agentos update [--check]
802
1316
  agentos stop --port 3000 [--force]
803
1317
  agentos doctor
@@ -809,11 +1323,27 @@ Options:
809
1323
  start: --host, -H Host to bind the local server (default: 127.0.0.1)
810
1324
  start: --open, -o Open AgentOS in the default browser after startup or reuse an existing instance
811
1325
  start: --no-open Disable browser auto-open even if AGENTOS_OPEN is set
1326
+ start: --plain Disable the AgentOS boot splash and live startup UI
1327
+ status: --port, -p Port to inspect (default: 3000)
1328
+ status: --host, -H Host to display when no runtime state exists (default: 127.0.0.1)
812
1329
  stop: --port, -p Port to stop (default: 3000)
813
1330
  stop: --force, -f Send SIGKILL if SIGTERM does not stop the server
814
1331
  `);
815
1332
  }
816
1333
 
1334
+ function printStatusHelp() {
1335
+ console.log(`Show the local AgentOS runtime dashboard.
1336
+
1337
+ Usage:
1338
+ agentos status
1339
+ agentos status --port 3000 --host 127.0.0.1
1340
+
1341
+ Options:
1342
+ --port, -p Port to inspect (default: 3000)
1343
+ --host, -H Host to display when no runtime state exists (default: 127.0.0.1)
1344
+ `);
1345
+ }
1346
+
817
1347
  function printStopHelp() {
818
1348
  console.log(`Stop a running AgentOS server.
819
1349
 
@@ -840,22 +1370,41 @@ Options:
840
1370
  `);
841
1371
  }
842
1372
 
843
- function createRelay(target, options, url, browserOpener, browserState) {
1373
+ function createRelay(target, boot, startupState, onServerReady) {
844
1374
  return (chunk) => {
845
1375
  const text = chunk.toString();
846
- target.write(text);
1376
+ const ready = isServerReadyOutput(text);
1377
+
1378
+ if (ready) {
1379
+ onServerReady();
1380
+ }
847
1381
 
848
- if (browserState.opened || !options.open || !browserOpener.available) {
1382
+ if (boot.isPlain() || startupState.completed) {
1383
+ target.write(text);
849
1384
  return;
850
1385
  }
851
1386
 
852
- if (text.includes("Ready in") || text.includes("Local:")) {
853
- browserState.opened = true;
854
- openBrowser(url, browserOpener);
1387
+ startupState.bufferedLogs.push({
1388
+ target,
1389
+ text
1390
+ });
1391
+
1392
+ if (target === process.stderr) {
1393
+ boot.warn(text);
1394
+ } else if (isImportantStartupLog(text)) {
1395
+ boot.log(text);
855
1396
  }
856
1397
  };
857
1398
  }
858
1399
 
1400
+ function isServerReadyOutput(text) {
1401
+ return text.includes("Ready in") || text.includes("Local:");
1402
+ }
1403
+
1404
+ function isImportantStartupLog(text) {
1405
+ return /\b(warn|warning|error|failed|exception|EADDRINUSE|EACCES)\b/i.test(text);
1406
+ }
1407
+
859
1408
  function printUpdateStatus(status, options = {}) {
860
1409
  if (status.updateAvailable) {
861
1410
  console.log(`Update available: ${status.currentVersion} -> ${status.latestVersion}.`);