@sapienx/agentos 0.5.3 → 0.5.5

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 +477 -31
  3. package/bin/terminal-boot.js +618 -0
  4. package/bundle/.next/BUILD_ID +1 -1
  5. package/bundle/.next/app-path-routes-manifest.json +8 -8
  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 +6 -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 +8 -8
  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 +4 -4
  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 → tsDQB0DKe1D5mWm-AHkYf}/_buildManifest.js +0 -0
  81. /package/bundle/.next/static/{easVJgXM2ae2U4xGhNmSj → tsDQB0DKe1D5mWm-AHkYf}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -29,6 +29,7 @@ agentos update
29
29
  agentos update --check
30
30
  agentos stop
31
31
  agentos stop --port 3000 --force
32
+ agentos status
32
33
  agentos doctor
33
34
  agentos uninstall
34
35
  ```
@@ -41,7 +42,9 @@ AGENTOS_PORT=3000
41
42
  AGENTOS_OPEN=1
42
43
  ```
43
44
 
44
- `agentos doctor` prints the effective URL, bundle status, Node.js compatibility, OpenClaw detection, and browser auto-open support.
45
+ `agentos status` prints a concise local dashboard for Gateway, runtime, model, channel, and server readiness.
46
+
47
+ `agentos doctor` prints deeper install diagnostics: effective URL, bundle status, Node.js compatibility, OpenClaw detection, Gateway reachability, and browser auto-open support.
45
48
 
46
49
  `agentos stop` sends `SIGTERM` to the AgentOS server listening on the selected port. If the runtime state is stale and no process is listening there, the CLI clears that stale state automatically.
47
50
 
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,34 @@ 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 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
+ boot.start();
175
+ boot.updateStatus("workspaceEngine", "loading", "bundle ready");
176
+ applyOpenClawBootStatus(boot, openClawCheck);
152
177
  }
153
178
 
154
179
  if (options.open && !browserOpener.available) {
155
- console.warn(
156
- `Browser auto-open is unavailable on this machine${browserOpener.detail ? ` (${browserOpener.detail})` : ""}.`
157
- );
180
+ const message = `Browser auto-open is unavailable on this machine${browserOpener.detail ? ` (${browserOpener.detail})` : ""}.`;
181
+ if (boot.isPlain()) {
182
+ console.warn(message);
183
+ } else {
184
+ boot.warn(message);
185
+ }
158
186
  }
159
187
 
160
188
  const child = spawn(process.execPath, [bundledServerPath], {
@@ -187,14 +215,46 @@ async function startServer(rawArgs) {
187
215
  throw error;
188
216
  }
189
217
 
218
+ if (!boot.isPlain()) {
219
+ boot.updateStatus("workspaceEngine", "ready", "runtime state written");
220
+ boot.updateStatus("agentRuntime", "starting", `pid ${child.pid}`);
221
+ }
222
+
223
+ const startupState = {
224
+ ready: false,
225
+ completing: false,
226
+ completed: boot.isPlain(),
227
+ bufferedLogs: []
228
+ };
190
229
  const browserState = { opened: false };
191
- const relayStdout = createRelay(process.stdout, options, url, browserOpener, browserState);
192
- const relayStderr = createRelay(process.stderr, options, url, browserOpener, browserState);
230
+ const onServerReady = () => {
231
+ if (startupState.ready) {
232
+ return;
233
+ }
234
+
235
+ startupState.ready = true;
236
+
237
+ if (options.open && browserOpener.available && !browserState.opened) {
238
+ browserState.opened = true;
239
+ openBrowser(url, browserOpener);
240
+ }
241
+
242
+ if (boot.isPlain()) {
243
+ return;
244
+ }
245
+
246
+ startupState.completing = true;
247
+ void completeBootAfterServerReady(boot, startupState, url);
248
+ };
249
+ const relayStdout = createRelay(process.stdout, boot, startupState, onServerReady);
250
+ const relayStderr = createRelay(process.stderr, boot, startupState, onServerReady);
193
251
 
194
252
  child.stdout.on("data", relayStdout);
195
253
  child.stderr.on("data", relayStderr);
196
254
 
197
- schedulePassiveUpdateNotice();
255
+ if (boot.isPlain()) {
256
+ schedulePassiveUpdateNotice();
257
+ }
198
258
 
199
259
  let cleanedUp = false;
200
260
  const shutdownState = {
@@ -227,8 +287,12 @@ async function startServer(rawArgs) {
227
287
  shutdownState.parentSignal = signal;
228
288
 
229
289
  if (signal === "SIGINT") {
290
+ boot.stop({ clear: true });
291
+ flushBufferedStartupLogs(startupState);
230
292
  process.stdout.write("\nStopping AgentOS... Press Ctrl+C again to force quit.\n");
231
293
  } else {
294
+ boot.stop({ clear: true });
295
+ flushBufferedStartupLogs(startupState);
232
296
  console.log(`Stopping AgentOS after ${signal}...`);
233
297
  }
234
298
 
@@ -247,6 +311,8 @@ async function startServer(rawArgs) {
247
311
 
248
312
  child.on("error", (error) => {
249
313
  cleanup();
314
+ boot.stop({ clear: true });
315
+ flushBufferedStartupLogs(startupState);
250
316
  console.error(`AgentOS failed to start: ${error.message}`);
251
317
  process.exit(1);
252
318
  });
@@ -254,6 +320,11 @@ async function startServer(rawArgs) {
254
320
  child.on("exit", (code, signal) => {
255
321
  cleanup();
256
322
 
323
+ if (!startupState.completed) {
324
+ boot.stop({ clear: true });
325
+ flushBufferedStartupLogs(startupState);
326
+ }
327
+
257
328
  if (shutdownState.parentSignal) {
258
329
  process.kill(process.pid, shutdownState.parentSignal);
259
330
  return;
@@ -271,55 +342,63 @@ async function startServer(rawArgs) {
271
342
  function runDoctor() {
272
343
  const options = parseStartArgs([]);
273
344
  const openClawCheck = detectOpenClaw();
345
+ const gatewayStatus = openClawCheck.available ? inspectOpenClawGatewayStatus(openClawCheck) : null;
274
346
  const browserOpener = detectBrowserOpener();
275
347
  const targetUrl = `http://${displayHost(options.host)}:${options.port}`;
276
348
  const checks = [
277
349
  {
278
- ok: true,
350
+ state: "ok",
279
351
  label: "Package",
280
352
  detail: `${packageJson.name}@${packageJson.version}`
281
353
  },
282
354
  {
283
- ok: true,
355
+ state: "ok",
284
356
  label: "Install",
285
357
  detail: formatInstallKind(inspectInstallation())
286
358
  },
287
359
  {
288
- ok: isSupportedNodeVersion(process.versions.node),
360
+ state: isSupportedNodeVersion(process.versions.node) ? "ok" : "failed",
289
361
  label: "Node.js",
290
362
  detail: `${process.version} (required >= 20.9.0)`
291
363
  },
292
364
  {
293
- ok: true,
365
+ state: "ok",
294
366
  label: "Platform",
295
367
  detail: `${os.platform()} ${os.release()}`
296
368
  },
297
369
  {
298
- ok: existsSync(bundledServerPath),
370
+ state: existsSync(bundledServerPath) ? "ok" : "failed",
299
371
  label: "Bundle",
300
372
  detail: existsSync(bundledServerPath)
301
373
  ? `ready at ${bundledServerPath}`
302
374
  : `missing at ${bundledServerPath}`
303
375
  },
304
376
  {
305
- ok: true,
377
+ state: "ok",
306
378
  label: "Target URL",
307
379
  detail: targetUrl
308
380
  },
309
381
  {
310
- ok: true,
382
+ state: "ok",
311
383
  label: "Configured env",
312
384
  detail: formatConfiguredEnv(options)
313
385
  },
314
386
  {
315
- ok: openClawCheck.available,
387
+ state: openClawCheck.available ? "ok" : "warning",
316
388
  label: "OpenClaw",
317
389
  detail: openClawCheck.available
318
390
  ? `${openClawCheck.version || "installed"}${openClawCheck.path ? ` at ${openClawCheck.path}` : ""}`
319
391
  : "not found in PATH or default local install paths; install OpenClaw or continue with the AgentOS onboarding flow"
320
392
  },
321
393
  {
322
- ok: browserOpener.available,
394
+ state: !openClawCheck.available ? "disabled" : gatewayStatus?.ok ? "ok" : "warning",
395
+ label: "Gateway",
396
+ detail: !openClawCheck.available
397
+ ? "OpenClaw is required before Gateway RPC can be checked"
398
+ : gatewayStatus?.gatewayMessage || "Gateway status unavailable"
399
+ },
400
+ {
401
+ state: browserOpener.available ? "ok" : "warning",
323
402
  label: "Browser opener",
324
403
  detail: browserOpener.available
325
404
  ? `${browserOpener.command} is available`
@@ -327,11 +406,83 @@ function runDoctor() {
327
406
  }
328
407
  ];
329
408
 
330
- for (const check of checks) {
331
- console.log(`${check.ok ? "OK" : "WARN"} ${check.label}: ${check.detail}`);
409
+ console.log(renderDoctorReport({
410
+ title: "AGENTOS DOCTOR",
411
+ rows: checks.map((check) => ({
412
+ label: check.label,
413
+ state: check.state,
414
+ message: check.detail
415
+ })),
416
+ footer: "Doctor shows install and runtime diagnostics. The in-app diagnostics panel has live Gateway/model detail."
417
+ }));
418
+
419
+ if (checks.some((check) => check.state === "failed")) {
420
+ process.exitCode = 1;
332
421
  }
422
+ }
333
423
 
334
- if (!checks.every((check) => check.ok || check.label === "OpenClaw" || check.label === "Browser opener")) {
424
+ function runStatus(rawArgs) {
425
+ const options = parseStatusArgs(rawArgs);
426
+ const runtimeStatePath = resolveRuntimeStatePath(options.port);
427
+ const trackedState = readRuntimeState(runtimeStatePath);
428
+ const listeningPid = findListeningPidForPort(options.port);
429
+ const trackedPid = trackedState?.pid && isProcessRunning(trackedState.pid) ? trackedState.pid : null;
430
+ const runningPid = listeningPid ?? trackedPid;
431
+ const host = trackedState?.host || options.host;
432
+ const url = createAgentOsUrl(host, options.port);
433
+ const openClawCheck = detectOpenClaw();
434
+ const gatewayStatus = openClawCheck.available ? inspectOpenClawGatewayStatus(openClawCheck) : null;
435
+ const bundleReady = existsSync(bundledServerPath);
436
+ const rows = [
437
+ {
438
+ label: "OpenClaw Gateway",
439
+ state: !openClawCheck.available ? "warning" : gatewayStatus?.ok ? "connected" : "degraded",
440
+ message: !openClawCheck.available
441
+ ? "OpenClaw not found; setup will guide installation"
442
+ : gatewayStatus?.gatewayMessage || "needs attention"
443
+ },
444
+ {
445
+ label: "Native Gateway",
446
+ state: !openClawCheck.available ? "disabled" : gatewayStatus?.nativeState || "warning",
447
+ message: !openClawCheck.available
448
+ ? "waiting for OpenClaw"
449
+ : gatewayStatus?.nativeMessage || "check Gateway diagnostics"
450
+ },
451
+ {
452
+ label: "Workspace Engine",
453
+ state: bundleReady ? "ready" : "failed",
454
+ message: bundleReady ? "bundle ready" : "bundle missing"
455
+ },
456
+ {
457
+ label: "Agent Runtime",
458
+ state: runningPid ? "ready" : "inactive",
459
+ message: runningPid ? `server running (PID ${runningPid})` : "server not running"
460
+ },
461
+ {
462
+ label: "Models",
463
+ state: runningPid ? "warning" : "pending",
464
+ message: runningPid ? "verify in setup or diagnostics" : "available after server start"
465
+ },
466
+ {
467
+ label: "Channels",
468
+ state: "disabled",
469
+ message: "no channel status loaded from CLI"
470
+ },
471
+ {
472
+ label: "Local Server",
473
+ state: runningPid ? "ready" : "pending",
474
+ message: url
475
+ }
476
+ ];
477
+
478
+ console.log(renderStatusDashboard({
479
+ title: "SYSTEM CHECK",
480
+ rows,
481
+ finalInfo: runningPid ? url : "",
482
+ footer: runningPid ? "" : "AgentOS is not running. Start it with agentos start --open."
483
+ }));
484
+
485
+ if (!bundleReady) {
335
486
  process.exitCode = 1;
336
487
  }
337
488
  }
@@ -465,12 +616,219 @@ async function runStop(rawArgs) {
465
616
  console.log(`Stopped AgentOS on port ${options.port}.`);
466
617
  }
467
618
 
619
+ function applyOpenClawBootStatus(boot, openClawCheck) {
620
+ if (!openClawCheck.available) {
621
+ boot.updateStatus("openclawGateway", "warning", "OpenClaw not found; onboarding will guide setup");
622
+ boot.updateStatus("nativeGateway", "disabled", "waiting for OpenClaw");
623
+ return;
624
+ }
625
+
626
+ boot.updateStatus("openclawGateway", "checking", openClawCheck.version || "OpenClaw detected");
627
+ boot.updateStatus("nativeGateway", "waiting", "probing gateway");
628
+
629
+ const gatewayStatus = inspectOpenClawGatewayStatus(openClawCheck);
630
+
631
+ if (gatewayStatus.ok) {
632
+ boot.updateStatus("openclawGateway", "connected", gatewayStatus.gatewayMessage);
633
+ boot.updateStatus("nativeGateway", gatewayStatus.nativeState, gatewayStatus.nativeMessage);
634
+ return;
635
+ }
636
+
637
+ boot.updateStatus("openclawGateway", "warning", gatewayStatus.gatewayMessage);
638
+ boot.updateStatus("nativeGateway", "disabled", gatewayStatus.nativeMessage);
639
+ }
640
+
641
+ function inspectOpenClawGatewayStatus(openClawCheck) {
642
+ const command = openClawCheck.path || "openclaw";
643
+ const result = spawnSync(command, ["gateway", "status", "--json"], {
644
+ encoding: "utf8",
645
+ timeout: 1_500
646
+ });
647
+ const output = `${result.stdout || ""}\n${result.stderr || ""}`.trim();
648
+
649
+ if (result.error) {
650
+ return {
651
+ ok: false,
652
+ gatewayMessage: result.error.code === "ETIMEDOUT" ? "gateway probe timed out" : "gateway status unavailable",
653
+ nativeMessage: "Gateway will be checked in the UI",
654
+ nativeState: "disabled"
655
+ };
656
+ }
657
+
658
+ if (result.status !== 0) {
659
+ return {
660
+ ok: false,
661
+ gatewayMessage: summarizeBootText(output) || "gateway not reachable yet",
662
+ nativeMessage: "start or repair OpenClaw Gateway in setup",
663
+ nativeState: "disabled"
664
+ };
665
+ }
666
+
667
+ const parsed = parseFirstJsonObject(output);
668
+ const capability = String(parsed?.capability || parsed?.Capability || "");
669
+ const connected = /connected|ok|reachable/i.test(output);
670
+ const missingOperatorScope = /no-operator-scope|operator scope/i.test(capability || output);
671
+
672
+ return {
673
+ ok: true,
674
+ gatewayMessage: connected ? "reachable" : "status available",
675
+ nativeState: missingOperatorScope ? "warning" : "active",
676
+ nativeMessage: missingOperatorScope ? "operator scope needs repair" : "native RPC available"
677
+ };
678
+ }
679
+
680
+ async function completeBootAfterServerReady(boot, startupState, url) {
681
+ boot.updateStatus("agentRuntime", "ready", "server ready");
682
+ boot.updateStatus("localServerUrl", "ready", url);
683
+ boot.updateStatus("models", "loading", "checking readiness");
684
+ boot.updateStatus("channels", "loading", "checking registry");
685
+
686
+ const readiness = await readStartupReadiness(url);
687
+
688
+ if (readiness.openClawGateway) {
689
+ boot.updateStatus("openclawGateway", readiness.openClawGateway.state, readiness.openClawGateway.message);
690
+ }
691
+
692
+ if (readiness.nativeGateway) {
693
+ boot.updateStatus("nativeGateway", readiness.nativeGateway.state, readiness.nativeGateway.message);
694
+ }
695
+
696
+ boot.updateStatus("workspaceEngine", readiness.workspaceEngine.state, readiness.workspaceEngine.message);
697
+ boot.updateStatus("models", readiness.models.state, readiness.models.message);
698
+ boot.updateStatus("channels", readiness.channels.state, readiness.channels.message);
699
+ boot.complete(url);
700
+ startupState.bufferedLogs = [];
701
+ startupState.completed = true;
702
+ schedulePassiveUpdateNotice();
703
+ }
704
+
705
+ async function readStartupReadiness(url) {
706
+ const fallback = {
707
+ workspaceEngine: { state: "ready", message: "available" },
708
+ models: { state: "warning", message: "finish setup in the UI" },
709
+ channels: { state: "disabled", message: "none confirmed yet" }
710
+ };
711
+ const controller = new AbortController();
712
+ const timeout = setTimeout(() => controller.abort(), 2_500);
713
+
714
+ try {
715
+ const response = await fetch(`${url}/api/snapshot`, {
716
+ signal: controller.signal
717
+ });
718
+
719
+ if (!response.ok) {
720
+ return fallback;
721
+ }
722
+
723
+ const snapshot = await response.json();
724
+ return summarizeStartupSnapshot(snapshot);
725
+ } catch {
726
+ return fallback;
727
+ } finally {
728
+ clearTimeout(timeout);
729
+ }
730
+ }
731
+
732
+ function summarizeStartupSnapshot(snapshot) {
733
+ const diagnostics = isRecord(snapshot?.diagnostics) ? snapshot.diagnostics : {};
734
+ const modelReadiness = isRecord(diagnostics.modelReadiness) ? diagnostics.modelReadiness : {};
735
+ const transport = isRecord(diagnostics.transport) ? diagnostics.transport : {};
736
+ const workspaces = Array.isArray(snapshot?.workspaces) ? snapshot.workspaces : [];
737
+ const channelAccounts = Array.isArray(snapshot?.channelAccounts) ? snapshot.channelAccounts : [];
738
+ const channelRegistry = isRecord(snapshot?.channelRegistry) && Array.isArray(snapshot.channelRegistry.channels)
739
+ ? snapshot.channelRegistry.channels
740
+ : [];
741
+ const rpcOk = diagnostics.rpcOk === true;
742
+ const modelReady = modelReadiness.ready === true || modelReadiness.defaultModelReady === true;
743
+ const availableModelCount = numberOrZero(modelReadiness.availableModelCount);
744
+ const modelIssues = Array.isArray(modelReadiness.issues) ? modelReadiness.issues.filter(Boolean) : [];
745
+ const enabledChannels = channelAccounts.filter((channel) => channel && channel.enabled !== false).length + channelRegistry.length;
746
+ const transportStatus = typeof transport.gatewayMode === "string"
747
+ ? transport.gatewayMode
748
+ : typeof transport.statusLabel === "string"
749
+ ? transport.statusLabel
750
+ : "";
751
+
752
+ return {
753
+ openClawGateway: {
754
+ state: rpcOk ? "connected" : "warning",
755
+ message: rpcOk ? "authenticated RPC ready" : summarizeBootText(diagnostics.health) || "setup may be required"
756
+ },
757
+ nativeGateway: {
758
+ state: rpcOk ? "active" : "warning",
759
+ message: transportStatus || (rpcOk ? "native Gateway active" : "check diagnostics")
760
+ },
761
+ workspaceEngine: {
762
+ state: "ready",
763
+ message: workspaces.length > 0 ? `${workspaces.length} workspace${workspaces.length === 1 ? "" : "s"}` : "ready for first workspace"
764
+ },
765
+ models: {
766
+ state: modelReady || availableModelCount > 0 ? "ready" : "warning",
767
+ message: modelReady
768
+ ? modelReadiness.resolvedDefaultModel || modelReadiness.defaultModel || "default model ready"
769
+ : modelIssues[0] || "model setup needed"
770
+ },
771
+ channels: {
772
+ state: enabledChannels > 0 ? "ready" : "disabled",
773
+ message: enabledChannels > 0 ? `${enabledChannels} configured` : "no channels configured"
774
+ }
775
+ };
776
+ }
777
+
778
+ function flushBufferedStartupLogs(startupState) {
779
+ if (!startupState?.bufferedLogs?.length) {
780
+ return;
781
+ }
782
+
783
+ for (const entry of startupState.bufferedLogs) {
784
+ entry.target.write(entry.text);
785
+ }
786
+
787
+ startupState.bufferedLogs = [];
788
+ }
789
+
790
+ function parseFirstJsonObject(value) {
791
+ if (!value) {
792
+ return null;
793
+ }
794
+
795
+ const start = value.indexOf("{");
796
+ const end = value.lastIndexOf("}");
797
+
798
+ if (start < 0 || end <= start) {
799
+ return null;
800
+ }
801
+
802
+ try {
803
+ return JSON.parse(value.slice(start, end + 1));
804
+ } catch {
805
+ return null;
806
+ }
807
+ }
808
+
809
+ function summarizeBootText(value) {
810
+ if (typeof value !== "string") {
811
+ return "";
812
+ }
813
+
814
+ return value.replace(/\s+/g, " ").trim().slice(0, 96);
815
+ }
816
+
817
+ function numberOrZero(value) {
818
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
819
+ }
820
+
821
+ function isRecord(value) {
822
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
823
+ }
824
+
468
825
  function parseStartArgs(rawArgs) {
469
826
  const envPort = process.env.AGENTOS_PORT || process.env.PORT;
470
827
  const options = {
471
828
  host: process.env.AGENTOS_HOST || "127.0.0.1",
472
829
  port: envPort && /^\d+$/.test(envPort) ? Number(envPort) : 3000,
473
- open: parseBooleanEnv(process.env.AGENTOS_OPEN)
830
+ open: parseBooleanEnv(process.env.AGENTOS_OPEN),
831
+ plain: false
474
832
  };
475
833
 
476
834
  for (let index = 0; index < rawArgs.length; index += 1) {
@@ -516,6 +874,57 @@ function parseStartArgs(rawArgs) {
516
874
  continue;
517
875
  }
518
876
 
877
+ if (arg === "--plain") {
878
+ options.plain = true;
879
+ continue;
880
+ }
881
+
882
+ throw new Error(`Unknown argument: ${arg}`);
883
+ }
884
+
885
+ return options;
886
+ }
887
+
888
+ function parseStatusArgs(rawArgs) {
889
+ const envPort = process.env.AGENTOS_PORT || process.env.PORT;
890
+ const options = {
891
+ host: process.env.AGENTOS_HOST || "127.0.0.1",
892
+ port: envPort && /^\d+$/.test(envPort) ? Number(envPort) : 3000
893
+ };
894
+
895
+ for (let index = 0; index < rawArgs.length; index += 1) {
896
+ const arg = rawArgs[index];
897
+
898
+ if (arg === "--port" || arg === "-p") {
899
+ const value = rawArgs[index + 1];
900
+ index += 1;
901
+ assertPort(value);
902
+ options.port = Number(value);
903
+ continue;
904
+ }
905
+
906
+ if (arg.startsWith("--port=")) {
907
+ const value = arg.slice("--port=".length);
908
+ assertPort(value);
909
+ options.port = Number(value);
910
+ continue;
911
+ }
912
+
913
+ if (arg === "--host" || arg === "-H") {
914
+ const value = rawArgs[index + 1];
915
+ index += 1;
916
+ assertHost(value);
917
+ options.host = value;
918
+ continue;
919
+ }
920
+
921
+ if (arg.startsWith("--host=")) {
922
+ const value = arg.slice("--host=".length);
923
+ assertHost(value);
924
+ options.host = value;
925
+ continue;
926
+ }
927
+
519
928
  throw new Error(`Unknown argument: ${arg}`);
520
929
  }
521
930
 
@@ -798,6 +1207,8 @@ function printHelp() {
798
1207
  Usage:
799
1208
  agentos
800
1209
  agentos start --port 3000 --host 127.0.0.1 --open
1210
+ agentos dev --plain
1211
+ agentos status
801
1212
  agentos update [--check]
802
1213
  agentos stop --port 3000 [--force]
803
1214
  agentos doctor
@@ -809,11 +1220,27 @@ Options:
809
1220
  start: --host, -H Host to bind the local server (default: 127.0.0.1)
810
1221
  start: --open, -o Open AgentOS in the default browser after startup or reuse an existing instance
811
1222
  start: --no-open Disable browser auto-open even if AGENTOS_OPEN is set
1223
+ start: --plain Disable the AgentOS boot splash and live startup UI
1224
+ status: --port, -p Port to inspect (default: 3000)
1225
+ status: --host, -H Host to display when no runtime state exists (default: 127.0.0.1)
812
1226
  stop: --port, -p Port to stop (default: 3000)
813
1227
  stop: --force, -f Send SIGKILL if SIGTERM does not stop the server
814
1228
  `);
815
1229
  }
816
1230
 
1231
+ function printStatusHelp() {
1232
+ console.log(`Show the local AgentOS runtime dashboard.
1233
+
1234
+ Usage:
1235
+ agentos status
1236
+ agentos status --port 3000 --host 127.0.0.1
1237
+
1238
+ Options:
1239
+ --port, -p Port to inspect (default: 3000)
1240
+ --host, -H Host to display when no runtime state exists (default: 127.0.0.1)
1241
+ `);
1242
+ }
1243
+
817
1244
  function printStopHelp() {
818
1245
  console.log(`Stop a running AgentOS server.
819
1246
 
@@ -840,22 +1267,41 @@ Options:
840
1267
  `);
841
1268
  }
842
1269
 
843
- function createRelay(target, options, url, browserOpener, browserState) {
1270
+ function createRelay(target, boot, startupState, onServerReady) {
844
1271
  return (chunk) => {
845
1272
  const text = chunk.toString();
846
- target.write(text);
1273
+ const ready = isServerReadyOutput(text);
847
1274
 
848
- if (browserState.opened || !options.open || !browserOpener.available) {
1275
+ if (ready) {
1276
+ onServerReady();
1277
+ }
1278
+
1279
+ if (boot.isPlain() || startupState.completed) {
1280
+ target.write(text);
849
1281
  return;
850
1282
  }
851
1283
 
852
- if (text.includes("Ready in") || text.includes("Local:")) {
853
- browserState.opened = true;
854
- openBrowser(url, browserOpener);
1284
+ startupState.bufferedLogs.push({
1285
+ target,
1286
+ text
1287
+ });
1288
+
1289
+ if (target === process.stderr) {
1290
+ boot.warn(text);
1291
+ } else if (isImportantStartupLog(text)) {
1292
+ boot.log(text);
855
1293
  }
856
1294
  };
857
1295
  }
858
1296
 
1297
+ function isServerReadyOutput(text) {
1298
+ return text.includes("Ready in") || text.includes("Local:");
1299
+ }
1300
+
1301
+ function isImportantStartupLog(text) {
1302
+ return /\b(warn|warning|error|failed|exception|EADDRINUSE|EACCES)\b/i.test(text);
1303
+ }
1304
+
859
1305
  function printUpdateStatus(status, options = {}) {
860
1306
  if (status.updateAvailable) {
861
1307
  console.log(`Update available: ${status.currentVersion} -> ${status.latestVersion}.`);