@agent-compose/cli 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -137,7 +137,8 @@ async function saveLocalSettings(projectDir, settings) {
137
137
  var [_project, _projectDir] = findProjectRoot();
138
138
  var _local = loadLocalSettings(_projectDir);
139
139
  var _global = loadGlobalSettings();
140
- var _activeEnv = _local.env ?? (_project.envs?.["prod"] ? "prod" : undefined);
140
+ var _candidateEnv = _local.env ?? _project.defaultEnv ?? "prod";
141
+ var _activeEnv = _project.envs?.[_candidateEnv] ? _candidateEnv : undefined;
141
142
  var _envUrl = _activeEnv ? _project.envs?.[_activeEnv]?.url : undefined;
142
143
  var _envDashboardUrl = _activeEnv ? _project.envs?.[_activeEnv]?.dashboardUrl : undefined;
143
144
  var _envKey = _activeEnv ? _local.keys?.[_activeEnv] : undefined;
@@ -257,8 +258,15 @@ function fmt(ms) {
257
258
  return `${Math.floor(ms / 60000)}m ${Math.floor(ms % 60000 / 1000)}s`;
258
259
  }
259
260
  function extractToolSnippet(toolInput) {
260
- const raw = toolInput ? String(toolInput) : "";
261
- return raw.match(/"(?:command|file_path|url|query|pattern)":"([^"]{1,80})/)?.[1] ?? "";
261
+ if (!toolInput || typeof toolInput !== "object")
262
+ return "";
263
+ const input = toolInput;
264
+ for (const key of ["command", "file_path", "url", "query", "pattern"]) {
265
+ const value = input[key];
266
+ if (typeof value === "string" && value.length > 0)
267
+ return value.slice(0, 80);
268
+ }
269
+ return "";
262
270
  }
263
271
  function printAgentMessage(data) {
264
272
  const message = data.message;
@@ -280,7 +288,7 @@ function printAgentMessage(data) {
280
288
  break;
281
289
  }
282
290
  case "tool_use": {
283
- const detail = extractToolSnippet(message.toolInputPreview);
291
+ const detail = extractToolSnippet(message.toolInput);
284
292
  process.stdout.write(` ${pc.cyan("→")} ${pc.bold(String(message.toolName ?? "tool"))}${detail ? ` ${pc.dim(detail)}` : ""}
285
293
  `);
286
294
  break;
@@ -291,6 +299,15 @@ function printAgentMessage(data) {
291
299
  break;
292
300
  }
293
301
  }
302
+ function printConsoleLine(line) {
303
+ const text = line.line.trimEnd();
304
+ if (!text)
305
+ return;
306
+ const label = line.stream === "stderr" ? pc.red("stderr") : pc.dim("stdout");
307
+ const color = line.stream === "stderr" ? pc.red : pc.dim;
308
+ process.stdout.write(`${label} ${color(text)}
309
+ `);
310
+ }
294
311
  function printEvent(event, data, state) {
295
312
  switch (event) {
296
313
  case "step_started":
@@ -424,45 +441,81 @@ ${pc.bold(pc.yellow("⊘ Run canceled"))}
424
441
  }
425
442
  }
426
443
  var TERMINAL_EVENTS = new Set(["run_complete", "run_failed", "run_canceled"]);
444
+ var CONSOLE_POLL_MS = 500;
427
445
  async function streamLogs(runId, opts) {
428
446
  const client = makeClient(opts);
447
+ const view = opts.view ?? "all";
429
448
  const state = {
430
449
  agentStart: new Map,
431
450
  agentLabel: new Map,
432
451
  startedAt: Date.now()
433
452
  };
434
453
  let lastSeq = 0;
454
+ let lastLogId = 0;
455
+ let stopConsole = false;
435
456
  const MAX_RETRIES = 5;
436
- for (let attempt = 0;attempt <= MAX_RETRIES; attempt++) {
437
- let settled = false;
438
- try {
439
- for await (const ev of client.streamRunLogs(runId, { lastEventId: lastSeq })) {
440
- const seq = ev.seq ?? 0;
441
- if (seq > 0)
442
- lastSeq = seq;
443
- printEvent(ev.event, ev, state);
444
- if (TERMINAL_EVENTS.has(ev.event)) {
445
- settled = true;
446
- break;
457
+ const drainConsole = async () => {
458
+ const lines = await client.listRunLogs(runId, { afterId: lastLogId, limit: 200, direction: "asc" });
459
+ for (const line of lines) {
460
+ if (line.id > lastLogId)
461
+ lastLogId = line.id;
462
+ printConsoleLine(line);
463
+ }
464
+ };
465
+ const consoleLoop = view === "agent" ? null : (async () => {
466
+ while (!stopConsole) {
467
+ await drainConsole().catch((err) => {
468
+ if (err instanceof AgentComposeError2 && err.status === 404)
469
+ return;
470
+ throw err;
471
+ });
472
+ if (!stopConsole)
473
+ await new Promise((r) => setTimeout(r, CONSOLE_POLL_MS));
474
+ }
475
+ await drainConsole().catch(() => {});
476
+ })();
477
+ try {
478
+ for (let attempt = 0;attempt <= MAX_RETRIES; attempt++) {
479
+ let settled = false;
480
+ try {
481
+ for await (const ev of client.streamRunLogs(runId, { lastEventId: lastSeq })) {
482
+ const seq = ev.seq ?? 0;
483
+ if (seq > 0)
484
+ lastSeq = seq;
485
+ if (view !== "console" || TERMINAL_EVENTS.has(ev.event)) {
486
+ printEvent(ev.event, ev, state);
487
+ }
488
+ if (TERMINAL_EVENTS.has(ev.event)) {
489
+ settled = true;
490
+ break;
491
+ }
447
492
  }
493
+ } catch (err) {
494
+ if (err instanceof AgentComposeError2) {
495
+ console.error(`Error: failed to connect to log stream (${err.status})`);
496
+ process.exit(1);
497
+ }
498
+ throw err;
448
499
  }
449
- } catch (err) {
450
- if (err instanceof AgentComposeError2) {
451
- console.error(`Error: failed to connect to log stream (${err.status})`);
452
- process.exit(1);
453
- }
454
- throw err;
500
+ if (settled)
501
+ return;
502
+ if (attempt < MAX_RETRIES)
503
+ await new Promise((r) => setTimeout(r, 200));
455
504
  }
456
- if (settled)
457
- return;
458
- if (attempt < MAX_RETRIES)
459
- await new Promise((r) => setTimeout(r, 200));
505
+ } finally {
506
+ stopConsole = true;
507
+ await consoleLoop;
460
508
  }
461
509
  console.error("Error: stream closed without terminal event after retries");
462
510
  process.exit(1);
463
511
  }
464
- var logsCommand = new Command2("logs").description("Stream logs for a run").argument("<run-id>", "Run ID").option("--url <url>", "Server URL", parseUrlFlag, defaultUrl).option("--api-key <key>", "API key", defaultApiKey).action(async (runId, opts) => {
465
- await streamLogs(runId, opts);
512
+ var logsCommand = new Command2("logs").description("Stream logs for a run").argument("<run-id>", "Run ID").option("--agent", "Show structured agent/lifecycle events only").option("--console", "Show raw runner stdout/stderr only").option("--url <url>", "Server URL", parseUrlFlag, defaultUrl).option("--api-key <key>", "API key", defaultApiKey).action(async (runId, opts) => {
513
+ if (opts.agent && opts.console) {
514
+ console.error("Error: choose only one of --agent or --console");
515
+ process.exit(1);
516
+ }
517
+ const view = opts.agent ? "agent" : opts.console ? "console" : "all";
518
+ await streamLogs(runId, { ...opts, view });
466
519
  });
467
520
 
468
521
  // src/commands/invoke.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-compose/cli",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Command-line interface for agent-compose — register, invoke, and monitor workflows from your terminal.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -44,7 +44,7 @@
44
44
  "prepublishOnly": "bun run build"
45
45
  },
46
46
  "dependencies": {
47
- "@agent-compose/sdk": "^0.5.0",
47
+ "@agent-compose/sdk": "^0.5.2",
48
48
  "commander": "^12.0.0",
49
49
  "picocolors": "^1.1.1"
50
50
  },
@@ -355,9 +355,7 @@ workflow that must be registered in the same factory. Walk them
355
355
  through it in user terms:
356
356
 
357
357
  1. "I'll add the `workflow-memory` recipe to your project."
358
- Copy from `templates/workflow-memory.ts` (or
359
- `.agentc/smoketest/workflows/workflow-memory.ts` for the smoke
360
- variant).
358
+ Copy from `.agentc/templates/workflow-memory.ts`.
361
359
  2. "Run `agentc register ./workflow-memory.ts` once to install it."
362
360
  3. "Now any workflow with `memory: true` will trigger it after each
363
361
  successful run."