@agenti-os/cli 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +164 -0
- package/dashboard/404/index.html +1 -0
- package/dashboard/404.html +1 -0
- package/dashboard/_next/static/chunks/111-83d9bd0855b2c216.js +1 -0
- package/dashboard/_next/static/chunks/458.6c27f5996829de5b.js +1 -0
- package/dashboard/_next/static/chunks/583.6051aee384433b11.js +1 -0
- package/dashboard/_next/static/chunks/74ce447c-ca924ebf577cd4a4.js +1 -0
- package/dashboard/_next/static/chunks/915-98c767e3b9eb8028.js +1 -0
- package/dashboard/_next/static/chunks/app/_not-found/page-c4e9dc1c64d4da80.js +1 -0
- package/dashboard/_next/static/chunks/app/health/page-1c057112a2d6fae4.js +1 -0
- package/dashboard/_next/static/chunks/app/inbox/page-b7271d9a24defeff.js +1 -0
- package/dashboard/_next/static/chunks/app/layout-a3e5ee55560bf39f.js +1 -0
- package/dashboard/_next/static/chunks/app/page-10b9e09a1a14a650.js +1 -0
- package/dashboard/_next/static/chunks/app/timeline/page-7fe0c8e8c7d028b8.js +1 -0
- package/dashboard/_next/static/chunks/framework-1c02a2e60068b586.js +1 -0
- package/dashboard/_next/static/chunks/main-0e2c8558bbf18f15.js +1 -0
- package/dashboard/_next/static/chunks/main-app-48902e05be997ee5.js +1 -0
- package/dashboard/_next/static/chunks/pages/_app-bd106308c06033e9.js +1 -0
- package/dashboard/_next/static/chunks/pages/_error-034fd19974bd12e6.js +1 -0
- package/dashboard/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dashboard/_next/static/chunks/webpack-3aabda247bd1875f.js +1 -0
- package/dashboard/_next/static/css/53ea3bb274b2b444.css +3 -0
- package/dashboard/_next/static/wIIqV_03htqo96M3P8FEG/_buildManifest.js +1 -0
- package/dashboard/_next/static/wIIqV_03htqo96M3P8FEG/_ssgManifest.js +1 -0
- package/dashboard/health/index.html +1 -0
- package/dashboard/health/index.txt +20 -0
- package/dashboard/inbox/index.html +1 -0
- package/dashboard/inbox/index.txt +20 -0
- package/dashboard/index.html +1 -0
- package/dashboard/index.txt +20 -0
- package/dashboard/timeline/index.html +1 -0
- package/dashboard/timeline/index.txt +20 -0
- package/dist/chunk-4J3VBGVR.js +168 -0
- package/dist/chunk-CPCT2FTQ.js +48 -0
- package/dist/chunk-I442WICJ.js +155 -0
- package/dist/display-ZLPTSJQM.js +8 -0
- package/dist/dist-SD33RKHO.js +48 -0
- package/dist/gateway-client-RSQFHBFE.js +11 -0
- package/dist/index.js +1018 -0
- package/package.json +51 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1018 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
display
|
|
4
|
+
} from "./chunk-4J3VBGVR.js";
|
|
5
|
+
import {
|
|
6
|
+
GatewayNotRunningError,
|
|
7
|
+
gateway
|
|
8
|
+
} from "./chunk-I442WICJ.js";
|
|
9
|
+
import {
|
|
10
|
+
GATEWAY_BASE_URL
|
|
11
|
+
} from "./chunk-CPCT2FTQ.js";
|
|
12
|
+
|
|
13
|
+
// src/index.ts
|
|
14
|
+
import { Command } from "commander";
|
|
15
|
+
import chalk5 from "chalk";
|
|
16
|
+
|
|
17
|
+
// src/commands/init.ts
|
|
18
|
+
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
19
|
+
import { join } from "path";
|
|
20
|
+
import chalk from "chalk";
|
|
21
|
+
var AGENTOS_DIR = ".agentos";
|
|
22
|
+
function registerInitCommand(program2) {
|
|
23
|
+
program2.command("init").description("Initialize Agent OS in the current repository").option("--force", "Overwrite existing configuration files").action((opts) => {
|
|
24
|
+
runInit(opts.force);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function runInit(force = false) {
|
|
28
|
+
const cwd = process.cwd();
|
|
29
|
+
if (!existsSync(join(cwd, ".git"))) {
|
|
30
|
+
display.warning("No .git directory found. Agent OS works best inside a Git repository.");
|
|
31
|
+
console.log(chalk.grey(" Continuing anyway...\n"));
|
|
32
|
+
}
|
|
33
|
+
console.log(chalk.bold("\n Initializing Agent OS\n"));
|
|
34
|
+
const dirs = [
|
|
35
|
+
AGENTOS_DIR,
|
|
36
|
+
join(AGENTOS_DIR, "context"),
|
|
37
|
+
join(AGENTOS_DIR, "context", "api-contracts"),
|
|
38
|
+
join(AGENTOS_DIR, "context", "decisions"),
|
|
39
|
+
join(AGENTOS_DIR, "missions"),
|
|
40
|
+
join(AGENTOS_DIR, "runs"),
|
|
41
|
+
join(AGENTOS_DIR, "artifacts"),
|
|
42
|
+
join(AGENTOS_DIR, "approvals"),
|
|
43
|
+
join(AGENTOS_DIR, "workspaces"),
|
|
44
|
+
join(AGENTOS_DIR, ".secrets")
|
|
45
|
+
];
|
|
46
|
+
for (const dir of dirs) {
|
|
47
|
+
mkdirSync(join(cwd, dir), { recursive: true });
|
|
48
|
+
console.log(chalk.grey(` created `) + dir + "/");
|
|
49
|
+
}
|
|
50
|
+
const files = [
|
|
51
|
+
{
|
|
52
|
+
path: join(AGENTOS_DIR, "config.yml"),
|
|
53
|
+
content: CONFIG_YML
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
path: join(AGENTOS_DIR, "agents.yml"),
|
|
57
|
+
content: AGENTS_YML
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
path: join(AGENTOS_DIR, "policies.yml"),
|
|
61
|
+
content: POLICIES_YML
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
path: join(AGENTOS_DIR, "context", "product.md"),
|
|
65
|
+
content: PRODUCT_MD
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
path: join(AGENTOS_DIR, "context", "architecture.md"),
|
|
69
|
+
content: ARCHITECTURE_MD
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
path: join(AGENTOS_DIR, "context", "constraints.md"),
|
|
73
|
+
content: CONSTRAINTS_MD
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
path: join(AGENTOS_DIR, "context", "glossary.md"),
|
|
77
|
+
content: GLOSSARY_MD
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
path: join(AGENTOS_DIR, ".gitignore"),
|
|
81
|
+
content: GITIGNORE
|
|
82
|
+
}
|
|
83
|
+
];
|
|
84
|
+
for (const file of files) {
|
|
85
|
+
const fullPath = join(cwd, file.path);
|
|
86
|
+
if (existsSync(fullPath) && !force) {
|
|
87
|
+
console.log(chalk.grey(` exists `) + file.path);
|
|
88
|
+
} else {
|
|
89
|
+
writeFileSync(fullPath, file.content, "utf-8");
|
|
90
|
+
console.log(chalk.green(` created `) + file.path);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
console.log("\n" + chalk.bold.green(" \u2713 Agent OS initialized\n"));
|
|
94
|
+
console.log(chalk.grey(" Next steps:"));
|
|
95
|
+
console.log(chalk.grey(" 1. Edit .agentos/context/product.md with your project description"));
|
|
96
|
+
console.log(chalk.grey(" 2. Run: ") + chalk.white("agentos daemon start"));
|
|
97
|
+
console.log(chalk.grey(" 3. Run: ") + chalk.white("agentos agent add claude --role frontend"));
|
|
98
|
+
console.log();
|
|
99
|
+
}
|
|
100
|
+
var CONFIG_YML = `# Agent OS configuration
|
|
101
|
+
# This file is committed to your repository.
|
|
102
|
+
version: "1"
|
|
103
|
+
|
|
104
|
+
gateway:
|
|
105
|
+
port: 47821
|
|
106
|
+
log_level: info
|
|
107
|
+
|
|
108
|
+
dashboard:
|
|
109
|
+
port: 47822
|
|
110
|
+
|
|
111
|
+
context:
|
|
112
|
+
token_budget_default: 8000
|
|
113
|
+
token_budget_max: 16000
|
|
114
|
+
|
|
115
|
+
workspace:
|
|
116
|
+
base_path: .agentos/workspaces
|
|
117
|
+
strategy: git_worktree
|
|
118
|
+
`;
|
|
119
|
+
var AGENTS_YML = `# Registered agents
|
|
120
|
+
# This file is committed to your repository.
|
|
121
|
+
# Credentials are stored in the OS keychain \u2014 never here.
|
|
122
|
+
version: "1"
|
|
123
|
+
agents: []
|
|
124
|
+
`;
|
|
125
|
+
var POLICIES_YML = `# Risk policies for Agent OS
|
|
126
|
+
# This file is committed to your repository.
|
|
127
|
+
version: "1"
|
|
128
|
+
|
|
129
|
+
risk_thresholds:
|
|
130
|
+
auto_approve: 30
|
|
131
|
+
ask_user: 70
|
|
132
|
+
block: 100
|
|
133
|
+
|
|
134
|
+
sensitive_paths:
|
|
135
|
+
- ".env*"
|
|
136
|
+
- "**/secrets/**"
|
|
137
|
+
- "infra/**"
|
|
138
|
+
- "db/migrations/**"
|
|
139
|
+
- "auth/**"
|
|
140
|
+
- "payments/**"
|
|
141
|
+
|
|
142
|
+
dangerous_commands:
|
|
143
|
+
- "rm -rf"
|
|
144
|
+
- "drop database"
|
|
145
|
+
- "git push --force"
|
|
146
|
+
- "curl * | sh"
|
|
147
|
+
- "chmod -R 777"
|
|
148
|
+
- "npm publish"
|
|
149
|
+
- "vercel --prod"
|
|
150
|
+
- "supabase db push --linked"
|
|
151
|
+
|
|
152
|
+
auto_approve_patterns:
|
|
153
|
+
- "npm test"
|
|
154
|
+
- "pytest"
|
|
155
|
+
- "git status"
|
|
156
|
+
- "git diff"
|
|
157
|
+
`;
|
|
158
|
+
var PRODUCT_MD = `# Product Context
|
|
159
|
+
|
|
160
|
+
<!--
|
|
161
|
+
Fill this in with your project's product context.
|
|
162
|
+
This document is injected into every agent's context pack.
|
|
163
|
+
-->
|
|
164
|
+
|
|
165
|
+
## What this product is
|
|
166
|
+
[Describe what the app does and who it serves]
|
|
167
|
+
|
|
168
|
+
## Target users
|
|
169
|
+
[Who uses this app and what are their main goals]
|
|
170
|
+
|
|
171
|
+
## Key features
|
|
172
|
+
[List the main features]
|
|
173
|
+
|
|
174
|
+
## Current status
|
|
175
|
+
[What phase of development are you in]
|
|
176
|
+
`;
|
|
177
|
+
var ARCHITECTURE_MD = `# Architecture Context
|
|
178
|
+
|
|
179
|
+
<!--
|
|
180
|
+
Fill this in with your project's technical architecture.
|
|
181
|
+
Agents use this to understand the codebase structure and conventions.
|
|
182
|
+
-->
|
|
183
|
+
|
|
184
|
+
## Stack
|
|
185
|
+
[List your tech stack]
|
|
186
|
+
|
|
187
|
+
## Folder structure
|
|
188
|
+
[Describe your key directories]
|
|
189
|
+
|
|
190
|
+
## Conventions
|
|
191
|
+
[List coding conventions, naming patterns, etc.]
|
|
192
|
+
|
|
193
|
+
## Key dependencies
|
|
194
|
+
[List important libraries and why they're used]
|
|
195
|
+
`;
|
|
196
|
+
var CONSTRAINTS_MD = `# Agent Constraints
|
|
197
|
+
|
|
198
|
+
<!--
|
|
199
|
+
Hard rules that ALL agents must follow, regardless of task.
|
|
200
|
+
These are injected into every context pack.
|
|
201
|
+
-->
|
|
202
|
+
|
|
203
|
+
## Always
|
|
204
|
+
- Follow existing code style and conventions
|
|
205
|
+
- Write tests for new functionality
|
|
206
|
+
- Keep changes within your assigned scope
|
|
207
|
+
- Commit working code only \u2014 no broken states
|
|
208
|
+
|
|
209
|
+
## Never
|
|
210
|
+
- Modify files outside your assigned scope
|
|
211
|
+
- Remove existing tests
|
|
212
|
+
- Change configuration that affects other agents' work
|
|
213
|
+
- Deploy to production without explicit approval
|
|
214
|
+
`;
|
|
215
|
+
var GLOSSARY_MD = `# Project Glossary
|
|
216
|
+
|
|
217
|
+
<!--
|
|
218
|
+
Define project-specific terms agents need to understand.
|
|
219
|
+
Add entries as your project develops domain-specific vocabulary.
|
|
220
|
+
-->
|
|
221
|
+
|
|
222
|
+
| Term | Definition |
|
|
223
|
+
|------|-----------|
|
|
224
|
+
| [term] | [definition] |
|
|
225
|
+
`;
|
|
226
|
+
var GITIGNORE = `# Agent OS \u2014 never commit these
|
|
227
|
+
.secrets/
|
|
228
|
+
runs/
|
|
229
|
+
workspaces/
|
|
230
|
+
approvals/
|
|
231
|
+
*.db
|
|
232
|
+
*.db-wal
|
|
233
|
+
*.db-shm
|
|
234
|
+
`;
|
|
235
|
+
|
|
236
|
+
// src/commands/daemon.ts
|
|
237
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync as writeFileSync2, unlinkSync } from "fs";
|
|
238
|
+
import { join as join2 } from "path";
|
|
239
|
+
import { spawn } from "child_process";
|
|
240
|
+
import ora from "ora";
|
|
241
|
+
var PID_FILE = ".agentos/gateway.pid";
|
|
242
|
+
var LOG_FILE = ".agentos/gateway.log";
|
|
243
|
+
function registerDaemonCommand(program2) {
|
|
244
|
+
const daemon = program2.command("daemon").description("Manage the Agent OS gateway daemon");
|
|
245
|
+
daemon.command("start").description("Start the gateway daemon in the background").option("--port <port>", "Port to run on (default: 47821)").action(async (opts) => {
|
|
246
|
+
await startDaemon(opts.port ? parseInt(opts.port) : void 0);
|
|
247
|
+
});
|
|
248
|
+
daemon.command("stop").description("Stop the running gateway daemon").action(stopDaemon);
|
|
249
|
+
daemon.command("status").description("Check if the gateway daemon is running").action(daemonStatus);
|
|
250
|
+
}
|
|
251
|
+
async function startDaemon(port) {
|
|
252
|
+
const spinner = ora("Starting Agent OS gateway...").start();
|
|
253
|
+
try {
|
|
254
|
+
const health = await gateway.health();
|
|
255
|
+
spinner.succeed(
|
|
256
|
+
`Gateway already running (v${health.version}, uptime: ${String(Math.floor(health.uptime_seconds))}s)`
|
|
257
|
+
);
|
|
258
|
+
return;
|
|
259
|
+
} catch (err) {
|
|
260
|
+
if (!(err instanceof GatewayNotRunningError)) {
|
|
261
|
+
spinner.fail("Unexpected error checking gateway status");
|
|
262
|
+
display.error(String(err));
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const userProjectDir = process.cwd();
|
|
267
|
+
const env = {
|
|
268
|
+
...process.env,
|
|
269
|
+
AGENTOS_PORT: String(port ?? 47821),
|
|
270
|
+
AGENTOS_LOG_LEVEL: "info"
|
|
271
|
+
};
|
|
272
|
+
const logPath = join2(userProjectDir, LOG_FILE);
|
|
273
|
+
const pidPath = join2(userProjectDir, PID_FILE);
|
|
274
|
+
const proc = spawn(
|
|
275
|
+
"python",
|
|
276
|
+
["-m", "agentos_gateway"],
|
|
277
|
+
{
|
|
278
|
+
cwd: userProjectDir,
|
|
279
|
+
env,
|
|
280
|
+
detached: true,
|
|
281
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
mkdirSync2(join2(userProjectDir, ".agentos"), { recursive: true });
|
|
285
|
+
writeFileSync2(pidPath, String(proc.pid), "utf-8");
|
|
286
|
+
const logStream = (await import("fs")).createWriteStream(logPath, { flags: "a" });
|
|
287
|
+
proc.stdout.pipe(logStream);
|
|
288
|
+
proc.stderr.pipe(logStream);
|
|
289
|
+
proc.unref();
|
|
290
|
+
const maxWait = 1e4;
|
|
291
|
+
const interval = 200;
|
|
292
|
+
let elapsed = 0;
|
|
293
|
+
while (elapsed < maxWait) {
|
|
294
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
295
|
+
elapsed += interval;
|
|
296
|
+
try {
|
|
297
|
+
const health = await gateway.health();
|
|
298
|
+
spinner.succeed(`Gateway started (v${health.version}, port: ${String(port ?? 47821)})`);
|
|
299
|
+
display.info(`Logs: ${logPath}`);
|
|
300
|
+
display.info(`PID: ${String(proc.pid)}`);
|
|
301
|
+
return;
|
|
302
|
+
} catch {
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
spinner.fail("Gateway did not start within 10 seconds");
|
|
306
|
+
display.info(`Check logs: ${logPath}`);
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
function stopDaemon() {
|
|
310
|
+
const pidPath = join2(process.cwd(), PID_FILE);
|
|
311
|
+
if (!existsSync2(pidPath)) {
|
|
312
|
+
display.warning("No gateway PID file found. Is the daemon running?");
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
const pid = parseInt(readFileSync(pidPath, "utf-8").trim());
|
|
316
|
+
try {
|
|
317
|
+
process.kill(pid, "SIGTERM");
|
|
318
|
+
unlinkSync(pidPath);
|
|
319
|
+
display.success(`Gateway stopped (PID: ${String(pid)})`);
|
|
320
|
+
} catch (err) {
|
|
321
|
+
if (err instanceof Error && "code" in err && err.code === "ESRCH") {
|
|
322
|
+
display.warning(`Process ${String(pid)} not found \u2014 removing stale PID file`);
|
|
323
|
+
unlinkSync(pidPath);
|
|
324
|
+
} else {
|
|
325
|
+
display.error(`Failed to stop gateway: ${String(err)}`);
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async function daemonStatus() {
|
|
331
|
+
const pidPath = join2(process.cwd(), PID_FILE);
|
|
332
|
+
const pidExists = existsSync2(pidPath);
|
|
333
|
+
try {
|
|
334
|
+
const health = await gateway.health();
|
|
335
|
+
display.success("Gateway is running");
|
|
336
|
+
display.info(`Version: ${health.version}`);
|
|
337
|
+
display.info(`Uptime: ${String(Math.floor(health.uptime_seconds))}s`);
|
|
338
|
+
display.info(`Active missions: ${String(health.active_missions)}`);
|
|
339
|
+
display.info(`Active agents: ${String(health.active_agents)}`);
|
|
340
|
+
if (pidExists) {
|
|
341
|
+
const pid = readFileSync(pidPath, "utf-8").trim();
|
|
342
|
+
display.info(`PID: ${pid}`);
|
|
343
|
+
}
|
|
344
|
+
} catch (err) {
|
|
345
|
+
if (err instanceof GatewayNotRunningError) {
|
|
346
|
+
display.error("Gateway is not running");
|
|
347
|
+
if (pidExists) {
|
|
348
|
+
display.warning("Stale PID file found. Run: agentos daemon start");
|
|
349
|
+
}
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
throw err;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// src/commands/agent.ts
|
|
357
|
+
function collect(value, previous) {
|
|
358
|
+
return [...previous, value];
|
|
359
|
+
}
|
|
360
|
+
function registerAgentCommand(program2) {
|
|
361
|
+
const agent = program2.command("agent").description("Manage registered agents");
|
|
362
|
+
agent.command("add <name>").description("Register a new agent").option("--role <role>", "Role this agent plays (e.g. frontend, backend)", "general").option("--cmd <command>", "CLI command to invoke the agent (e.g. claude, codex)").option("--fallback <agent_id>", "Agent to route to on credential failure").option("--adapter <adapter>", "Adapter type: claude-code | codex | mock", "claude-code").option("--capability <cap>", "A capability this agent has (repeatable)", collect, []).action(async (name, opts) => {
|
|
363
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
364
|
+
if (!opts.cmd) {
|
|
365
|
+
display.error("--cmd is required (the CLI command to invoke the agent)");
|
|
366
|
+
process.exit(1);
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
const created = await gateway2.registerAgent({
|
|
370
|
+
display_name: name,
|
|
371
|
+
adapter: opts.adapter,
|
|
372
|
+
command: opts.cmd,
|
|
373
|
+
role: opts.role ?? "general",
|
|
374
|
+
capabilities: opts.capability,
|
|
375
|
+
...opts.fallback ? { fallback_agent_id: opts.fallback } : {}
|
|
376
|
+
});
|
|
377
|
+
display.success(`Registered agent "${created.display_name}" (id: ${created.id})`);
|
|
378
|
+
} catch (err) {
|
|
379
|
+
display.gatewayError(err);
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
agent.command("remove <id>").description("Remove a registered agent").action(async (id) => {
|
|
383
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
384
|
+
try {
|
|
385
|
+
await gateway2.removeAgent(id);
|
|
386
|
+
display.success(`Removed agent ${id}`);
|
|
387
|
+
} catch (err) {
|
|
388
|
+
display.gatewayError(err);
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
const agents = program2.command("agents").description("List all agents").action(async () => {
|
|
392
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
393
|
+
try {
|
|
394
|
+
const list = await gateway2.listAgents();
|
|
395
|
+
if (list.length === 0) {
|
|
396
|
+
display.info("No agents registered. Run: agentos agent add");
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
display.agentTable(list);
|
|
400
|
+
} catch (err) {
|
|
401
|
+
display.gatewayError(err);
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
agents.command("health").description("Show credential health for every registered agent").action(async () => {
|
|
405
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
406
|
+
try {
|
|
407
|
+
const list = await gateway2.listAgents();
|
|
408
|
+
if (list.length === 0) {
|
|
409
|
+
display.info("No agents registered. Run: agentos agent add");
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const rows = await Promise.all(
|
|
413
|
+
list.map(async (a) => {
|
|
414
|
+
let health = null;
|
|
415
|
+
try {
|
|
416
|
+
health = await gateway2.getAgentHealth(a.id);
|
|
417
|
+
} catch {
|
|
418
|
+
health = null;
|
|
419
|
+
}
|
|
420
|
+
return { agent: a, health };
|
|
421
|
+
})
|
|
422
|
+
);
|
|
423
|
+
display.agentHealthTable(rows);
|
|
424
|
+
} catch (err) {
|
|
425
|
+
display.gatewayError(err);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/commands/mission.ts
|
|
431
|
+
function deriveTitle(objective) {
|
|
432
|
+
return objective.length > 60 ? `${objective.slice(0, 57)}...` : objective;
|
|
433
|
+
}
|
|
434
|
+
function registerMissionCommand(program2) {
|
|
435
|
+
const mission = program2.command("mission").description("Manage missions");
|
|
436
|
+
mission.command("create <objective>").description("Create a new mission from a natural-language objective").option("--title <title>", "Short title for the mission (derived from objective if omitted)").action(async (objective, opts) => {
|
|
437
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
438
|
+
try {
|
|
439
|
+
const created = await gateway2.createMission(opts.title ?? deriveTitle(objective), objective);
|
|
440
|
+
display.success(`Created mission "${created.title}" (id: ${created.id})`);
|
|
441
|
+
} catch (err) {
|
|
442
|
+
display.gatewayError(err);
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
mission.command("plan <mission_id>").description("Generate the task breakdown for a mission").action(async (missionId) => {
|
|
446
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
447
|
+
try {
|
|
448
|
+
const [plan, agents] = await Promise.all([
|
|
449
|
+
gateway2.planMission(missionId),
|
|
450
|
+
gateway2.listAgents()
|
|
451
|
+
]);
|
|
452
|
+
const nameById = new Map(agents.map((a) => [a.id, a.display_name]));
|
|
453
|
+
display.success(`Planned ${String(plan.tasks.length)} tasks`);
|
|
454
|
+
for (const task of plan.tasks) {
|
|
455
|
+
const agentName = task.assigned_agent_id ? nameById.get(task.assigned_agent_id) ?? task.assigned_agent_id : "unassigned";
|
|
456
|
+
console.log(
|
|
457
|
+
` ${display.riskBadge(task.risk_level)} ${task.title} \u2014 ${agentName}` + (task.depends_on.length > 0 ? ` (depends on: ${task.depends_on.join(", ")})` : "")
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
} catch (err) {
|
|
461
|
+
display.gatewayError(err);
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
mission.command("run <mission_id>").description("Start mission execution and stream live events until it stops running").option("--parallel", "Run ready tasks in parallel", true).action(async (missionId, opts) => {
|
|
465
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
466
|
+
try {
|
|
467
|
+
const result = await gateway2.runMission(missionId, opts.parallel);
|
|
468
|
+
display.success(`Started ${String(result.started_tasks.length)} task(s)`);
|
|
469
|
+
} catch (err) {
|
|
470
|
+
display.gatewayError(err);
|
|
471
|
+
}
|
|
472
|
+
const { GATEWAY_BASE_URL: GATEWAY_BASE_URL2 } = await import("./dist-SD33RKHO.js");
|
|
473
|
+
const { EventSource } = await import("eventsource");
|
|
474
|
+
display.info("Streaming live events \u2014 press Ctrl+C to stop watching");
|
|
475
|
+
const es = new EventSource(`${GATEWAY_BASE_URL2}/events/stream?mission_id=${missionId}`);
|
|
476
|
+
let stopped = false;
|
|
477
|
+
const stop = () => {
|
|
478
|
+
if (stopped) return;
|
|
479
|
+
stopped = true;
|
|
480
|
+
es.close();
|
|
481
|
+
clearInterval(poll);
|
|
482
|
+
};
|
|
483
|
+
es.onmessage = (msg) => {
|
|
484
|
+
try {
|
|
485
|
+
const event = JSON.parse(String(msg.data));
|
|
486
|
+
console.log(` ${chalkTime(event.timestamp)} ${event.source}: ${event.type}`);
|
|
487
|
+
} catch {
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
es.onerror = () => {
|
|
491
|
+
};
|
|
492
|
+
const poll = setInterval(() => {
|
|
493
|
+
void gateway2.getMissionStatus(missionId).then((status) => {
|
|
494
|
+
if (status.status !== "running") {
|
|
495
|
+
display.success(`Mission ${status.status}`);
|
|
496
|
+
stop();
|
|
497
|
+
}
|
|
498
|
+
}).catch(() => {
|
|
499
|
+
stop();
|
|
500
|
+
});
|
|
501
|
+
}, 2e3);
|
|
502
|
+
await new Promise((resolve) => {
|
|
503
|
+
const check = setInterval(() => {
|
|
504
|
+
if (stopped) {
|
|
505
|
+
clearInterval(check);
|
|
506
|
+
resolve();
|
|
507
|
+
}
|
|
508
|
+
}, 250);
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
mission.command("pause <mission_id>").description("Pause a running mission \u2014 stops live agent runs and freezes their workspaces").action(async (missionId) => {
|
|
512
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
513
|
+
try {
|
|
514
|
+
const result = await gateway2.pauseMission(missionId);
|
|
515
|
+
display.success(
|
|
516
|
+
`Paused mission ${missionId} \u2014 stopped ${String(result.paused_tasks.length)} task(s)`
|
|
517
|
+
);
|
|
518
|
+
for (const skipped of result.skipped_tasks) {
|
|
519
|
+
display.warning(`Task ${skipped.task_id} could not be stopped: ${skipped.reason}`);
|
|
520
|
+
}
|
|
521
|
+
} catch (err) {
|
|
522
|
+
display.gatewayError(err);
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
mission.command("resume <mission_id>").description("Resume a paused mission \u2014 relaunches paused tasks with their preserved work").action(async (missionId) => {
|
|
526
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
527
|
+
try {
|
|
528
|
+
const result = await gateway2.resumeMission(missionId);
|
|
529
|
+
const started = result.started_tasks.length > 0 ? `, started ${String(result.started_tasks.length)} newly-ready task(s)` : "";
|
|
530
|
+
display.success(
|
|
531
|
+
`Resumed mission ${missionId} \u2014 relaunched ${String(result.resumed_tasks.length)} task(s)${started}`
|
|
532
|
+
);
|
|
533
|
+
for (const skipped of result.skipped_tasks) {
|
|
534
|
+
display.warning(`Task ${skipped.task_id} not resumed: ${skipped.reason}`);
|
|
535
|
+
}
|
|
536
|
+
} catch (err) {
|
|
537
|
+
display.gatewayError(err);
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
function chalkTime(iso) {
|
|
542
|
+
return new Date(iso).toLocaleTimeString();
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// src/commands/inbox.ts
|
|
546
|
+
function scopeFromOptions(opts) {
|
|
547
|
+
if (opts.always) return "always";
|
|
548
|
+
if (opts.mission) return "mission";
|
|
549
|
+
return "once";
|
|
550
|
+
}
|
|
551
|
+
function registerInboxCommand(program2) {
|
|
552
|
+
program2.command("inbox").description("Show pending approvals, credential failures, and conflicts").action(async () => {
|
|
553
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
554
|
+
try {
|
|
555
|
+
const inbox = await gateway2.getInbox();
|
|
556
|
+
if (inbox.action_requests.length === 0 && inbox.credential_events.length === 0 && inbox.conflicts.length === 0) {
|
|
557
|
+
display.success("Inbox is empty \u2014 no pending items");
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
display.inboxActionRequests(inbox.action_requests);
|
|
561
|
+
display.inboxCredentialEvents(inbox.credential_events);
|
|
562
|
+
display.inboxConflicts(inbox.conflicts);
|
|
563
|
+
} catch (err) {
|
|
564
|
+
display.gatewayError(err);
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
program2.command("approve <id>").description("Approve a pending action request").option("--once", "Approve this single occurrence only (default)").option("--mission", "Approve for the rest of the current mission").option("--always", "Always auto-approve this pattern going forward").action(async (id, opts) => {
|
|
568
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
569
|
+
const scope = scopeFromOptions(opts);
|
|
570
|
+
try {
|
|
571
|
+
await gateway2.approve(id, { scope });
|
|
572
|
+
display.success(`Approved ${id} (scope: ${scope})`);
|
|
573
|
+
} catch (err) {
|
|
574
|
+
display.gatewayError(err);
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
program2.command("deny <id>").description("Deny a pending action request").action(async (id) => {
|
|
578
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
579
|
+
try {
|
|
580
|
+
await gateway2.deny(id);
|
|
581
|
+
display.success(`Denied ${id}`);
|
|
582
|
+
} catch (err) {
|
|
583
|
+
display.gatewayError(err);
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// src/commands/workspace.ts
|
|
589
|
+
import chalk2 from "chalk";
|
|
590
|
+
function printDiff(diffText) {
|
|
591
|
+
for (const line of diffText.split("\n")) {
|
|
592
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
593
|
+
console.log(chalk2.green(line));
|
|
594
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
595
|
+
console.log(chalk2.red(line));
|
|
596
|
+
} else {
|
|
597
|
+
console.log(chalk2.grey(line));
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
function registerWorkspaceCommands(program2) {
|
|
602
|
+
program2.command("diff <task_id>").description("View agent changes for a task").action(async (taskId) => {
|
|
603
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
604
|
+
try {
|
|
605
|
+
const diff = await gateway2.getDiff(taskId);
|
|
606
|
+
if (!diff.diff_text) {
|
|
607
|
+
display.info("No changes yet for this task");
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
printDiff(diff.diff_text);
|
|
611
|
+
} catch (err) {
|
|
612
|
+
display.gatewayError(err);
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
program2.command("merge <task_id>").description("Merge approved task work into the base branch").action(async (taskId) => {
|
|
616
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
617
|
+
try {
|
|
618
|
+
const result = await gateway2.mergeTask(taskId);
|
|
619
|
+
display.success(`Merged task ${taskId} (sha: ${result.merged_sha.slice(0, 8)})`);
|
|
620
|
+
} catch (err) {
|
|
621
|
+
display.gatewayError(err);
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
program2.command("rollback <task_id>").description("Discard a task's latest run and restore the last clean state").action(async (taskId) => {
|
|
625
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
626
|
+
try {
|
|
627
|
+
const result = await gateway2.rollbackTask(taskId);
|
|
628
|
+
display.success(`Rolled back task ${taskId} to ${result.rolled_back_to_sha.slice(0, 8)}`);
|
|
629
|
+
} catch (err) {
|
|
630
|
+
display.gatewayError(err);
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
program2.command("route <task_id>").description("Reroute a task to a different agent").requiredOption("--to <agent_id>", "Target agent ID").action(async (taskId, opts) => {
|
|
634
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
635
|
+
try {
|
|
636
|
+
const result = await gateway2.routeTask(taskId, opts.to);
|
|
637
|
+
display.success(`Routed task ${taskId} to ${opts.to} (new run: ${result.new_run_id})`);
|
|
638
|
+
} catch (err) {
|
|
639
|
+
display.gatewayError(err);
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// src/commands/context.ts
|
|
645
|
+
import { copyFileSync, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
646
|
+
import { basename, extname, join as join3 } from "path";
|
|
647
|
+
var CONTEXT_DIR = ".agentos/context";
|
|
648
|
+
function registerContextCommand(program2) {
|
|
649
|
+
const context = program2.command("context").description("Manage the context vault");
|
|
650
|
+
context.command("add <file>").description("Copy a document into the local context vault (.agentos/context/)").action((file) => {
|
|
651
|
+
const cwd = process.cwd();
|
|
652
|
+
if (!existsSync3(file)) {
|
|
653
|
+
display.error(`File not found: ${file}`);
|
|
654
|
+
process.exit(1);
|
|
655
|
+
}
|
|
656
|
+
if (extname(file).toLowerCase() !== ".md") {
|
|
657
|
+
display.error(`Not a Markdown file: ${file}`);
|
|
658
|
+
process.exit(1);
|
|
659
|
+
}
|
|
660
|
+
const destDir = join3(cwd, CONTEXT_DIR);
|
|
661
|
+
mkdirSync3(destDir, { recursive: true });
|
|
662
|
+
const dest = join3(destDir, basename(file));
|
|
663
|
+
copyFileSync(file, dest);
|
|
664
|
+
display.success(`Added ${basename(file)} to context vault`);
|
|
665
|
+
});
|
|
666
|
+
context.command("show <task_id>").description("Show the most recent context pack built for a task (documents, token usage, preview)").action(async (taskId) => {
|
|
667
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
668
|
+
try {
|
|
669
|
+
const pack = await gateway2.getLatestContextPackForTask(taskId);
|
|
670
|
+
display.info(`Context pack ${pack.id} for task ${pack.task_id}`);
|
|
671
|
+
console.log(` documents: ${pack.documents.length > 0 ? pack.documents.join(", ") : "(none)"}`);
|
|
672
|
+
console.log(` tokens used: ${String(pack.tokens_used)} / ${String(pack.token_budget)}`);
|
|
673
|
+
if (pack.content) {
|
|
674
|
+
const preview = pack.content.length > 500 ? `${pack.content.slice(0, 500)}...` : pack.content;
|
|
675
|
+
console.log(" preview:");
|
|
676
|
+
console.log(preview);
|
|
677
|
+
}
|
|
678
|
+
} catch (err) {
|
|
679
|
+
display.gatewayError(err);
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// src/commands/credentials.ts
|
|
685
|
+
function registerCredentialsCommand(program2) {
|
|
686
|
+
const creds = program2.command("credentials").description("Manage agent credentials");
|
|
687
|
+
creds.command("rotate <agent_id>").description("Rotate the stored credential for an agent (prompts for the new value)").action(async (agentId) => {
|
|
688
|
+
const { default: inquirer } = await import("inquirer");
|
|
689
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
690
|
+
const { credentialValue } = await inquirer.prompt([
|
|
691
|
+
{
|
|
692
|
+
type: "password",
|
|
693
|
+
name: "credentialValue",
|
|
694
|
+
message: `New credential value for agent ${agentId}:`,
|
|
695
|
+
mask: "*"
|
|
696
|
+
}
|
|
697
|
+
]);
|
|
698
|
+
try {
|
|
699
|
+
const result = await gateway2.rotateCredential(agentId, credentialValue);
|
|
700
|
+
display.success(
|
|
701
|
+
`Rotated credential for ${agentId} \u2014 validated: ${result.validated ? "yes" : "no"}`
|
|
702
|
+
);
|
|
703
|
+
} catch (err) {
|
|
704
|
+
display.gatewayError(err);
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// src/commands/conflicts.ts
|
|
710
|
+
function registerConflictsCommand(program2) {
|
|
711
|
+
program2.command("conflicts").description("List all active conflicts in the current mission").action(async () => {
|
|
712
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
713
|
+
try {
|
|
714
|
+
const inbox = await gateway2.getInbox();
|
|
715
|
+
display.inboxConflicts(inbox.conflicts);
|
|
716
|
+
} catch (err) {
|
|
717
|
+
display.gatewayError(err);
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// src/commands/logs.ts
|
|
723
|
+
import chalk3 from "chalk";
|
|
724
|
+
function severityColor(severity) {
|
|
725
|
+
switch (severity) {
|
|
726
|
+
case "warning":
|
|
727
|
+
return chalk3.yellow;
|
|
728
|
+
case "error":
|
|
729
|
+
return chalk3.red;
|
|
730
|
+
case "critical":
|
|
731
|
+
return chalk3.red.bold;
|
|
732
|
+
default:
|
|
733
|
+
return chalk3.grey;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
function sourceLabel(source) {
|
|
737
|
+
if (source === "gateway") return chalk3.grey.italic(source);
|
|
738
|
+
if (source === "user") return chalk3.white(source);
|
|
739
|
+
return chalk3.cyan(source);
|
|
740
|
+
}
|
|
741
|
+
function formatEvent(event) {
|
|
742
|
+
const time = new Date(event.timestamp).toLocaleTimeString();
|
|
743
|
+
const severity = severityColor(event.severity)(`[${event.severity}]`);
|
|
744
|
+
const source = sourceLabel(event.source);
|
|
745
|
+
const prefix = `[${time}] ${severity} ${source}: ${chalk3.bold(event.type)}`;
|
|
746
|
+
if (event.type === "agent.output") {
|
|
747
|
+
const preview = event.payload.content_preview.slice(0, 80);
|
|
748
|
+
return `${prefix} \u2014 "${preview}"`;
|
|
749
|
+
}
|
|
750
|
+
const payload = JSON.stringify(event.payload);
|
|
751
|
+
const truncated = payload.length > 120 ? `${payload.slice(0, 117)}...` : payload;
|
|
752
|
+
return `${prefix} \u2014 ${chalk3.grey(truncated)}`;
|
|
753
|
+
}
|
|
754
|
+
function registerLogsCommand(program2) {
|
|
755
|
+
program2.command("logs").description("Stream or view recent event logs").option("--follow", "Keep streaming live events instead of exiting after the last 50").option("--run <id>", "Filter by run ID").option("--mission <id>", "Filter by mission ID").action(async (opts) => {
|
|
756
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
757
|
+
if (!opts.follow) {
|
|
758
|
+
try {
|
|
759
|
+
const result = await gateway2.listEvents({
|
|
760
|
+
...opts.run ? { run_id: opts.run } : {},
|
|
761
|
+
...opts.mission ? { mission_id: opts.mission } : {},
|
|
762
|
+
limit: 50
|
|
763
|
+
});
|
|
764
|
+
if (result.events.length === 0) {
|
|
765
|
+
display.info("No events recorded yet");
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
for (const event of result.events) {
|
|
769
|
+
console.log(formatEvent(event));
|
|
770
|
+
}
|
|
771
|
+
} catch (err) {
|
|
772
|
+
display.gatewayError(err);
|
|
773
|
+
}
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
const { GATEWAY_BASE_URL: GATEWAY_BASE_URL2 } = await import("./dist-SD33RKHO.js");
|
|
777
|
+
const { EventSource } = await import("eventsource");
|
|
778
|
+
const params = new URLSearchParams();
|
|
779
|
+
if (opts.run) params.set("run_id", opts.run);
|
|
780
|
+
if (opts.mission) params.set("mission_id", opts.mission);
|
|
781
|
+
const query = params.toString();
|
|
782
|
+
display.info("Following live events \u2014 press Ctrl+C to stop");
|
|
783
|
+
const es = new EventSource(`${GATEWAY_BASE_URL2}/events/stream${query ? `?${query}` : ""}`);
|
|
784
|
+
es.onmessage = (msg) => {
|
|
785
|
+
try {
|
|
786
|
+
const event = JSON.parse(String(msg.data));
|
|
787
|
+
console.log(formatEvent(event));
|
|
788
|
+
} catch {
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
await new Promise(() => {
|
|
792
|
+
});
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// src/commands/start.ts
|
|
797
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
798
|
+
import { dirname, join as join4 } from "path";
|
|
799
|
+
import { exec } from "child_process";
|
|
800
|
+
import { fileURLToPath } from "url";
|
|
801
|
+
import chalk4 from "chalk";
|
|
802
|
+
var DASHBOARD_URL = `${GATEWAY_BASE_URL}/ui`;
|
|
803
|
+
function registerStartCommand(program2) {
|
|
804
|
+
program2.command("start").description("Start the gateway, open the dashboard, and stream live events").action(runStart);
|
|
805
|
+
}
|
|
806
|
+
async function runStart() {
|
|
807
|
+
const cwd = process.cwd();
|
|
808
|
+
const bundledDashboardPath = join4(dirname(fileURLToPath(import.meta.url)), "../dashboard");
|
|
809
|
+
if (existsSync4(bundledDashboardPath)) {
|
|
810
|
+
process.env.AGENTOS_DASHBOARD_PATH = bundledDashboardPath;
|
|
811
|
+
}
|
|
812
|
+
let gatewayRunning = true;
|
|
813
|
+
try {
|
|
814
|
+
await gateway.health();
|
|
815
|
+
} catch (err) {
|
|
816
|
+
if (!(err instanceof GatewayNotRunningError)) {
|
|
817
|
+
display.gatewayError(err);
|
|
818
|
+
}
|
|
819
|
+
gatewayRunning = false;
|
|
820
|
+
}
|
|
821
|
+
if (!gatewayRunning) {
|
|
822
|
+
await startDaemon();
|
|
823
|
+
}
|
|
824
|
+
const repoRoot = findRepoRoot(cwd);
|
|
825
|
+
const dashboardBuiltInRepo = repoRoot !== null && existsSync4(join4(repoRoot, "apps", "dashboard", "out"));
|
|
826
|
+
const dashboardAvailable = dashboardBuiltInRepo || existsSync4(bundledDashboardPath);
|
|
827
|
+
if (dashboardAvailable) {
|
|
828
|
+
display.success(`Dashboard at ${DASHBOARD_URL}`);
|
|
829
|
+
} else {
|
|
830
|
+
display.warning(
|
|
831
|
+
"Dashboard not available. To enable it:\n pnpm build:dashboard && pnpm build"
|
|
832
|
+
);
|
|
833
|
+
display.info(`Gateway API available at ${GATEWAY_BASE_URL}`);
|
|
834
|
+
display.info("Use the CLI for all operations.");
|
|
835
|
+
}
|
|
836
|
+
console.log(`
|
|
837
|
+
Agent OS v0.2.0
|
|
838
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
839
|
+
Gateway ${GATEWAY_BASE_URL} \u2713
|
|
840
|
+
Dashboard ${DASHBOARD_URL} ${dashboardAvailable ? "\u2713" : "\u2717"}
|
|
841
|
+
Project ${cwd}
|
|
842
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
843
|
+
Streaming live events (Ctrl+C to stop, gateway keeps running)
|
|
844
|
+
`);
|
|
845
|
+
if (dashboardAvailable) {
|
|
846
|
+
openBrowser(DASHBOARD_URL);
|
|
847
|
+
}
|
|
848
|
+
await streamEvents();
|
|
849
|
+
}
|
|
850
|
+
function findRepoRoot(startDir) {
|
|
851
|
+
let dir = startDir;
|
|
852
|
+
for (; ; ) {
|
|
853
|
+
const pkgPath = join4(dir, "package.json");
|
|
854
|
+
if (existsSync4(pkgPath)) {
|
|
855
|
+
try {
|
|
856
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
857
|
+
if (pkg.name === "agent-os") return dir;
|
|
858
|
+
} catch {
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
const parent = dirname(dir);
|
|
862
|
+
if (parent === dir) return null;
|
|
863
|
+
dir = parent;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
function openBrowser(url) {
|
|
867
|
+
try {
|
|
868
|
+
const command = process.platform === "win32" ? `start ${url}` : process.platform === "darwin" ? `open ${url}` : `xdg-open ${url}`;
|
|
869
|
+
exec(command, () => {
|
|
870
|
+
});
|
|
871
|
+
} catch {
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
function severityColor2(severity) {
|
|
875
|
+
switch (severity) {
|
|
876
|
+
case "warning":
|
|
877
|
+
return chalk4.yellow;
|
|
878
|
+
case "error":
|
|
879
|
+
return chalk4.red;
|
|
880
|
+
case "critical":
|
|
881
|
+
return chalk4.red.bold;
|
|
882
|
+
default:
|
|
883
|
+
return (text) => text;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
function formatEvent2(event) {
|
|
887
|
+
const time = new Date(event.timestamp).toLocaleTimeString("en-GB", { hour12: false });
|
|
888
|
+
const color = severityColor2(event.severity);
|
|
889
|
+
const base = `[${time}] [${color(event.severity)}] ${event.source}: ${event.type}`;
|
|
890
|
+
if (event.type === "agent.output") {
|
|
891
|
+
return `${base} \u2014 ${event.payload.content_preview}`;
|
|
892
|
+
}
|
|
893
|
+
if (event.type === "tool.requested") {
|
|
894
|
+
const line = `\u26A0 ${base} \u2014 risk: ${event.payload.risk_level}
|
|
895
|
+
\u2192 agentos approve ${event.payload.action_request_id} --once`;
|
|
896
|
+
return line;
|
|
897
|
+
}
|
|
898
|
+
const preview = JSON.stringify(event.payload);
|
|
899
|
+
const truncated = preview.length > 120 ? `${preview.slice(0, 117)}...` : preview;
|
|
900
|
+
return `${base} \u2014 ${truncated}`;
|
|
901
|
+
}
|
|
902
|
+
async function streamEvents() {
|
|
903
|
+
const { EventSource } = await import("eventsource");
|
|
904
|
+
const es = new EventSource(`${GATEWAY_BASE_URL}/events/stream`);
|
|
905
|
+
es.onmessage = (msg) => {
|
|
906
|
+
try {
|
|
907
|
+
const event = JSON.parse(String(msg.data));
|
|
908
|
+
console.log(formatEvent2(event));
|
|
909
|
+
} catch {
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
process.on("SIGINT", () => {
|
|
913
|
+
console.log("\n Gateway is still running. Use: agentos daemon stop");
|
|
914
|
+
process.exit(0);
|
|
915
|
+
});
|
|
916
|
+
await new Promise(() => {
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// src/commands/stop.ts
|
|
921
|
+
import { createInterface } from "readline";
|
|
922
|
+
function registerStopCommand(program2) {
|
|
923
|
+
program2.command("stop").description("Stop the Agent OS gateway").action(runStop);
|
|
924
|
+
}
|
|
925
|
+
async function runStop() {
|
|
926
|
+
try {
|
|
927
|
+
await gateway.health();
|
|
928
|
+
} catch (err) {
|
|
929
|
+
if (err instanceof GatewayNotRunningError) {
|
|
930
|
+
display.info("Gateway is not running");
|
|
931
|
+
process.exit(0);
|
|
932
|
+
}
|
|
933
|
+
display.gatewayError(err);
|
|
934
|
+
}
|
|
935
|
+
const runningMissions = await gateway.listMissions("running");
|
|
936
|
+
if (runningMissions.length > 0) {
|
|
937
|
+
const proceed = await confirm("Active mission running. Stop anyway? (y/N) ");
|
|
938
|
+
if (!proceed) {
|
|
939
|
+
process.exit(0);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
stopDaemon();
|
|
943
|
+
display.success("Agent OS stopped");
|
|
944
|
+
}
|
|
945
|
+
function confirm(question) {
|
|
946
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
947
|
+
return new Promise((resolve) => {
|
|
948
|
+
rl.question(question, (answer) => {
|
|
949
|
+
rl.close();
|
|
950
|
+
resolve(answer.trim().toLowerCase() === "y");
|
|
951
|
+
});
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// src/commands/stop-task.ts
|
|
956
|
+
function registerStopTaskCommand(program2) {
|
|
957
|
+
program2.command("stop-task <task_id>").description("Stop the live agent run on a task \u2014 preserves partial work via a workspace freeze").action(stopTask);
|
|
958
|
+
}
|
|
959
|
+
async function stopTask(taskId) {
|
|
960
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
961
|
+
try {
|
|
962
|
+
const result = await gateway2.stopTask(taskId);
|
|
963
|
+
display.success(`Stopped run ${result.run_id} \u2014 task ${result.task_id} is paused`);
|
|
964
|
+
if (result.workspace_state) {
|
|
965
|
+
display.info(`Workspace frozen (${result.workspace_state}) \u2014 partial work preserved`);
|
|
966
|
+
}
|
|
967
|
+
display.info(`Resume by routing to another agent: agentos route ${taskId} --to <agent_id>`);
|
|
968
|
+
} catch (err) {
|
|
969
|
+
display.gatewayError(err);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// src/index.ts
|
|
974
|
+
var program = new Command();
|
|
975
|
+
program.name("agentos").description(
|
|
976
|
+
chalk5.bold("Agent OS") + " \u2014 Personal control plane for AI coding agents"
|
|
977
|
+
).version("0.2.0", "-v, --version", "Print version").helpOption("-h, --help", "Show help");
|
|
978
|
+
registerInitCommand(program);
|
|
979
|
+
registerDaemonCommand(program);
|
|
980
|
+
registerAgentCommand(program);
|
|
981
|
+
registerMissionCommand(program);
|
|
982
|
+
registerInboxCommand(program);
|
|
983
|
+
registerWorkspaceCommands(program);
|
|
984
|
+
registerContextCommand(program);
|
|
985
|
+
registerCredentialsCommand(program);
|
|
986
|
+
registerConflictsCommand(program);
|
|
987
|
+
registerLogsCommand(program);
|
|
988
|
+
registerStartCommand(program);
|
|
989
|
+
registerStopCommand(program);
|
|
990
|
+
registerStopTaskCommand(program);
|
|
991
|
+
program.command("status").description("Show current mission and agent status").action(async () => {
|
|
992
|
+
const { gateway: gateway2 } = await import("./gateway-client-RSQFHBFE.js");
|
|
993
|
+
const { display: display2 } = await import("./display-ZLPTSJQM.js");
|
|
994
|
+
try {
|
|
995
|
+
const health = await gateway2.health();
|
|
996
|
+
display2.success(
|
|
997
|
+
`Gateway v${health.version} \u2014 ${String(health.active_missions)} active missions, ${String(health.active_agents)} active agents`
|
|
998
|
+
);
|
|
999
|
+
const agents = await gateway2.listAgents();
|
|
1000
|
+
if (agents.length === 0) {
|
|
1001
|
+
display2.info("No agents registered. Run: agentos agent add");
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
display2.agentTable(agents);
|
|
1005
|
+
} catch (err) {
|
|
1006
|
+
display2.gatewayError(err);
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
program.exitOverride();
|
|
1010
|
+
try {
|
|
1011
|
+
await program.parseAsync(process.argv);
|
|
1012
|
+
} catch (err) {
|
|
1013
|
+
if (err instanceof Error && err.name === "CommanderError") {
|
|
1014
|
+
process.exit(err.exitCode ?? 1);
|
|
1015
|
+
}
|
|
1016
|
+
console.error(chalk5.red("Unexpected error:"), err);
|
|
1017
|
+
process.exit(1);
|
|
1018
|
+
}
|