@sapienx/agentos 0.5.2 → 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 (82) hide show
  1. package/README.md +4 -1
  2. package/bin/agentos.js +498 -32
  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 +11 -11
  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 +11 -11
  68. package/bundle/.next/server/chunks/4767.js +11 -11
  69. package/bundle/.next/server/chunks/5151.js +2 -2
  70. package/bundle/.next/server/chunks/6639.js +5 -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/package.json +1 -1
  78. package/bundle/server.js +1 -1
  79. package/package.json +1 -1
  80. package/bundle/.next/server/chunks/5831.js +0 -2
  81. /package/bundle/.next/static/{uAxTxVvm7tdXyEJ7SnLpD → tsDQB0DKe1D5mWm-AHkYf}/_buildManifest.js +0 -0
  82. /package/bundle/.next/static/{uAxTxVvm7tdXyEJ7SnLpD → tsDQB0DKe1D5mWm-AHkYf}/_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");
@@ -29,6 +31,16 @@ const updateCacheTtlMs = 24 * 60 * 60 * 1000;
29
31
  const updateWarningCooldownMs = 24 * 60 * 60 * 1000;
30
32
  const updateRequestTimeoutMs = 5_000;
31
33
  const cliSmokeTestMode = process.env.AGENTOS_CLI_TEST === "1";
34
+ const bundleRuntimeEnvFiles = [
35
+ ".env",
36
+ ".env.local",
37
+ ".env.development",
38
+ ".env.development.local",
39
+ ".env.production",
40
+ ".env.production.local",
41
+ ".env.test",
42
+ ".env.test.local"
43
+ ];
32
44
 
33
45
  main().catch((error) => {
34
46
  console.error(error instanceof Error ? error.message : String(error));
@@ -59,6 +71,16 @@ async function main() {
59
71
  return;
60
72
  }
61
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
+
62
84
  if (firstArg === "update") {
63
85
  if (args[1] === "--help" || args[1] === "-h" || args[1] === "help") {
64
86
  printUpdateHelp();
@@ -89,7 +111,7 @@ async function main() {
89
111
  return;
90
112
  }
91
113
 
92
- if (firstArg === "start") {
114
+ if (firstArg === "start" || firstArg === "dev") {
93
115
  await startServer(args.slice(1));
94
116
  return;
95
117
  }
@@ -99,6 +121,7 @@ async function main() {
99
121
 
100
122
  async function startServer(rawArgs) {
101
123
  ensureBundleExists();
124
+ removeBundleRuntimeEnvFiles();
102
125
 
103
126
  const options = parseStartArgs(rawArgs);
104
127
  const runtimeStatePath = resolveRuntimeStatePath(options.port);
@@ -132,18 +155,34 @@ async function startServer(rawArgs) {
132
155
  clearRuntimeState(runtimeStatePath);
133
156
  }
134
157
 
135
- 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
+ });
136
164
 
137
- if (!openClawCheck.available) {
138
- console.log("OpenClaw was not found in PATH or the default local install paths. AgentOS will start and guide onboarding in the UI.");
139
- } else if (openClawCheck.version) {
140
- console.log(`OpenClaw detected: ${openClawCheck.version}`);
165
+ if (boot.isPlain()) {
166
+ console.log(`Starting AgentOS on ${url}`);
167
+
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);
141
177
  }
142
178
 
143
179
  if (options.open && !browserOpener.available) {
144
- console.warn(
145
- `Browser auto-open is unavailable on this machine${browserOpener.detail ? ` (${browserOpener.detail})` : ""}.`
146
- );
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
+ }
147
186
  }
148
187
 
149
188
  const child = spawn(process.execPath, [bundledServerPath], {
@@ -152,7 +191,10 @@ async function startServer(rawArgs) {
152
191
  env: {
153
192
  ...process.env,
154
193
  PORT: String(options.port),
155
- HOSTNAME: options.host
194
+ HOSTNAME: options.host,
195
+ AGENTOS_PACKAGE_RUNTIME: "1",
196
+ AGENTOS_RUNTIME_DIR: runtimeInstallRoot,
197
+ AGENTOS_BUNDLE_DIR: bundleDir
156
198
  }
157
199
  });
158
200
 
@@ -173,14 +215,46 @@ async function startServer(rawArgs) {
173
215
  throw error;
174
216
  }
175
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
+ };
176
229
  const browserState = { opened: false };
177
- const relayStdout = createRelay(process.stdout, options, url, browserOpener, browserState);
178
- 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);
179
251
 
180
252
  child.stdout.on("data", relayStdout);
181
253
  child.stderr.on("data", relayStderr);
182
254
 
183
- schedulePassiveUpdateNotice();
255
+ if (boot.isPlain()) {
256
+ schedulePassiveUpdateNotice();
257
+ }
184
258
 
185
259
  let cleanedUp = false;
186
260
  const shutdownState = {
@@ -213,8 +287,12 @@ async function startServer(rawArgs) {
213
287
  shutdownState.parentSignal = signal;
214
288
 
215
289
  if (signal === "SIGINT") {
290
+ boot.stop({ clear: true });
291
+ flushBufferedStartupLogs(startupState);
216
292
  process.stdout.write("\nStopping AgentOS... Press Ctrl+C again to force quit.\n");
217
293
  } else {
294
+ boot.stop({ clear: true });
295
+ flushBufferedStartupLogs(startupState);
218
296
  console.log(`Stopping AgentOS after ${signal}...`);
219
297
  }
220
298
 
@@ -233,6 +311,8 @@ async function startServer(rawArgs) {
233
311
 
234
312
  child.on("error", (error) => {
235
313
  cleanup();
314
+ boot.stop({ clear: true });
315
+ flushBufferedStartupLogs(startupState);
236
316
  console.error(`AgentOS failed to start: ${error.message}`);
237
317
  process.exit(1);
238
318
  });
@@ -240,6 +320,11 @@ async function startServer(rawArgs) {
240
320
  child.on("exit", (code, signal) => {
241
321
  cleanup();
242
322
 
323
+ if (!startupState.completed) {
324
+ boot.stop({ clear: true });
325
+ flushBufferedStartupLogs(startupState);
326
+ }
327
+
243
328
  if (shutdownState.parentSignal) {
244
329
  process.kill(process.pid, shutdownState.parentSignal);
245
330
  return;
@@ -257,55 +342,63 @@ async function startServer(rawArgs) {
257
342
  function runDoctor() {
258
343
  const options = parseStartArgs([]);
259
344
  const openClawCheck = detectOpenClaw();
345
+ const gatewayStatus = openClawCheck.available ? inspectOpenClawGatewayStatus(openClawCheck) : null;
260
346
  const browserOpener = detectBrowserOpener();
261
347
  const targetUrl = `http://${displayHost(options.host)}:${options.port}`;
262
348
  const checks = [
263
349
  {
264
- ok: true,
350
+ state: "ok",
265
351
  label: "Package",
266
352
  detail: `${packageJson.name}@${packageJson.version}`
267
353
  },
268
354
  {
269
- ok: true,
355
+ state: "ok",
270
356
  label: "Install",
271
357
  detail: formatInstallKind(inspectInstallation())
272
358
  },
273
359
  {
274
- ok: isSupportedNodeVersion(process.versions.node),
360
+ state: isSupportedNodeVersion(process.versions.node) ? "ok" : "failed",
275
361
  label: "Node.js",
276
362
  detail: `${process.version} (required >= 20.9.0)`
277
363
  },
278
364
  {
279
- ok: true,
365
+ state: "ok",
280
366
  label: "Platform",
281
367
  detail: `${os.platform()} ${os.release()}`
282
368
  },
283
369
  {
284
- ok: existsSync(bundledServerPath),
370
+ state: existsSync(bundledServerPath) ? "ok" : "failed",
285
371
  label: "Bundle",
286
372
  detail: existsSync(bundledServerPath)
287
373
  ? `ready at ${bundledServerPath}`
288
374
  : `missing at ${bundledServerPath}`
289
375
  },
290
376
  {
291
- ok: true,
377
+ state: "ok",
292
378
  label: "Target URL",
293
379
  detail: targetUrl
294
380
  },
295
381
  {
296
- ok: true,
382
+ state: "ok",
297
383
  label: "Configured env",
298
384
  detail: formatConfiguredEnv(options)
299
385
  },
300
386
  {
301
- ok: openClawCheck.available,
387
+ state: openClawCheck.available ? "ok" : "warning",
302
388
  label: "OpenClaw",
303
389
  detail: openClawCheck.available
304
390
  ? `${openClawCheck.version || "installed"}${openClawCheck.path ? ` at ${openClawCheck.path}` : ""}`
305
391
  : "not found in PATH or default local install paths; install OpenClaw or continue with the AgentOS onboarding flow"
306
392
  },
307
393
  {
308
- 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",
309
402
  label: "Browser opener",
310
403
  detail: browserOpener.available
311
404
  ? `${browserOpener.command} is available`
@@ -313,15 +406,93 @@ function runDoctor() {
313
406
  }
314
407
  ];
315
408
 
316
- for (const check of checks) {
317
- 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;
318
421
  }
422
+ }
319
423
 
320
- 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) {
321
486
  process.exitCode = 1;
322
487
  }
323
488
  }
324
489
 
490
+ function removeBundleRuntimeEnvFiles() {
491
+ for (const fileName of bundleRuntimeEnvFiles) {
492
+ rmSync(path.join(bundleDir, fileName), { force: true });
493
+ }
494
+ }
495
+
325
496
  async function runUpdate(rawArgs) {
326
497
  const options = parseUpdateArgs(rawArgs);
327
498
  const install = inspectInstallation();
@@ -445,12 +616,219 @@ async function runStop(rawArgs) {
445
616
  console.log(`Stopped AgentOS on port ${options.port}.`);
446
617
  }
447
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
+
448
825
  function parseStartArgs(rawArgs) {
449
826
  const envPort = process.env.AGENTOS_PORT || process.env.PORT;
450
827
  const options = {
451
828
  host: process.env.AGENTOS_HOST || "127.0.0.1",
452
829
  port: envPort && /^\d+$/.test(envPort) ? Number(envPort) : 3000,
453
- open: parseBooleanEnv(process.env.AGENTOS_OPEN)
830
+ open: parseBooleanEnv(process.env.AGENTOS_OPEN),
831
+ plain: false
454
832
  };
455
833
 
456
834
  for (let index = 0; index < rawArgs.length; index += 1) {
@@ -496,6 +874,57 @@ function parseStartArgs(rawArgs) {
496
874
  continue;
497
875
  }
498
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
+
499
928
  throw new Error(`Unknown argument: ${arg}`);
500
929
  }
501
930
 
@@ -778,6 +1207,8 @@ function printHelp() {
778
1207
  Usage:
779
1208
  agentos
780
1209
  agentos start --port 3000 --host 127.0.0.1 --open
1210
+ agentos dev --plain
1211
+ agentos status
781
1212
  agentos update [--check]
782
1213
  agentos stop --port 3000 [--force]
783
1214
  agentos doctor
@@ -789,11 +1220,27 @@ Options:
789
1220
  start: --host, -H Host to bind the local server (default: 127.0.0.1)
790
1221
  start: --open, -o Open AgentOS in the default browser after startup or reuse an existing instance
791
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)
792
1226
  stop: --port, -p Port to stop (default: 3000)
793
1227
  stop: --force, -f Send SIGKILL if SIGTERM does not stop the server
794
1228
  `);
795
1229
  }
796
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
+
797
1244
  function printStopHelp() {
798
1245
  console.log(`Stop a running AgentOS server.
799
1246
 
@@ -820,22 +1267,41 @@ Options:
820
1267
  `);
821
1268
  }
822
1269
 
823
- function createRelay(target, options, url, browserOpener, browserState) {
1270
+ function createRelay(target, boot, startupState, onServerReady) {
824
1271
  return (chunk) => {
825
1272
  const text = chunk.toString();
826
- target.write(text);
1273
+ const ready = isServerReadyOutput(text);
827
1274
 
828
- if (browserState.opened || !options.open || !browserOpener.available) {
1275
+ if (ready) {
1276
+ onServerReady();
1277
+ }
1278
+
1279
+ if (boot.isPlain() || startupState.completed) {
1280
+ target.write(text);
829
1281
  return;
830
1282
  }
831
1283
 
832
- if (text.includes("Ready in") || text.includes("Local:")) {
833
- browserState.opened = true;
834
- 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);
835
1293
  }
836
1294
  };
837
1295
  }
838
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
+
839
1305
  function printUpdateStatus(status, options = {}) {
840
1306
  if (status.updateAvailable) {
841
1307
  console.log(`Update available: ${status.currentVersion} -> ${status.latestVersion}.`);