@agentic-surfaces/cli 0.1.3 → 0.1.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 (2) hide show
  1. package/dist/index.js +54 -10
  2. package/package.json +4 -3
package/dist/index.js CHANGED
@@ -1,7 +1,31 @@
1
1
  import { readFileSync, readdirSync } from "node:fs";
2
2
  import { join, dirname, resolve } from "node:path";
3
+ import { spawn } from "node:child_process";
3
4
  import { loadWorkflow, runWorkflowOnce, Scheduler, loadProjectConfig, buildWorkflowRunner, defaultRegistry } from "@agentic-surfaces/core";
5
+ import { serve } from "@agentic-surfaces/server";
4
6
  import { buildServices } from "./services.js";
7
+ // `--ui`: start the live dashboard (HTTP server + SSE) and return its
8
+ // StreamingObserver so run lifecycle events stream to the browser.
9
+ function startUi() {
10
+ const port = parseInt(process.env["FLOW_UI_PORT"] ?? "4000", 10);
11
+ const { observer, port: p } = serve({ port });
12
+ const url = `http://127.0.0.1:${p}`;
13
+ openBrowser(url);
14
+ return { observer, url };
15
+ }
16
+ function openBrowser(url) {
17
+ if (process.env["FLOW_NO_OPEN"])
18
+ return; // headless / CI / don't hijack a browser
19
+ const cmd = process.platform === "darwin" ? "open"
20
+ : process.platform === "win32" ? "start"
21
+ : "xdg-open";
22
+ try {
23
+ spawn(cmd, [url], { stdio: "ignore", detached: true }).unref();
24
+ }
25
+ catch {
26
+ /* opening the browser is best-effort; the URL is printed regardless */
27
+ }
28
+ }
5
29
  // Walk up from a starting dir to find agentic-surfaces.config.yaml. run-once
6
30
  // is pointed at a workflow file (e.g. <proj>/workflows/foo.yaml), but the
7
31
  // project config — including the dryRun safety flag — lives at the project
@@ -27,10 +51,10 @@ export async function run(argv) {
27
51
  console.log("OK:", rest[0]);
28
52
  return 0;
29
53
  }
30
- if (cmd === "run-once") {
54
+ if (cmd === "run-once" || cmd === "run") {
31
55
  const file = rest.find(a => !a.startsWith("--"));
32
56
  if (!file) {
33
- console.error("run-once: missing workflow file");
57
+ console.error("run: missing workflow file");
34
58
  return 1;
35
59
  }
36
60
  const fake = rest.includes("--fake");
@@ -47,14 +71,30 @@ export async function run(argv) {
47
71
  catch { /* skip unparseable */ }
48
72
  }
49
73
  const registry = defaultRegistry();
50
- const runWorkflowFn = buildWorkflowRunner({ workflows: allWorkflows, registry, services });
74
+ const session = rest.includes("--ui") ? startUi() : undefined;
75
+ const observer = session?.observer;
76
+ const runWorkflowFn = buildWorkflowRunner({ workflows: allWorkflows, registry, services, observer });
51
77
  const servicesWithRunner = { ...services, runWorkflow: runWorkflowFn };
52
- const outputs = await runWorkflowOnce(wf, { registry, services: servicesWithRunner });
53
- console.log("ran", wf.name, "->", [...outputs.keys()].join(", "));
54
- return 0;
78
+ // A run error must NOT tear down the --ui server: the failure is already
79
+ // streamed to the dashboard (the failed node + run), so keep serving so it
80
+ // stays inspectable. Without --ui, an error still exits non-zero.
81
+ let failed = false;
82
+ try {
83
+ const outputs = await runWorkflowOnce(wf, { registry, services: servicesWithRunner, observer });
84
+ console.log("ran", wf.name, "->", [...outputs.keys()].join(", "));
85
+ }
86
+ catch (err) {
87
+ failed = true;
88
+ console.error("run failed:", err instanceof Error ? err.message : String(err));
89
+ }
90
+ if (session) {
91
+ console.log(`\n▶ Dashboard live at ${session.url} — Ctrl-C to exit${failed ? " (run failed — see the dashboard)" : ""}`);
92
+ await new Promise(() => { }); // keep the server up so the run stays viewable
93
+ }
94
+ return failed ? 1 : 0;
55
95
  }
56
96
  if (cmd === "start") {
57
- const dir = rest[0] ?? ".";
97
+ const dir = rest.find(a => !a.startsWith("--")) ?? ".";
58
98
  const pc = loadProjectConfig(dir);
59
99
  const workflowsDir = pc?.workflows ? join(dir, pc.workflows) : dir;
60
100
  const services = buildServices({ projectConfig: pc });
@@ -68,16 +108,20 @@ export async function run(argv) {
68
108
  catch { /* skip unparseable */ }
69
109
  }
70
110
  const registry = defaultRegistry();
71
- const runWorkflowFn = buildWorkflowRunner({ workflows: allWorkflows, registry, services });
111
+ const session = rest.includes("--ui") ? startUi() : undefined;
112
+ const observer = session?.observer;
113
+ const runWorkflowFn = buildWorkflowRunner({ workflows: allWorkflows, registry, services, observer });
72
114
  const servicesWithRunner = { ...services, runWorkflow: runWorkflowFn };
73
- const sched = new Scheduler(servicesWithRunner, registry);
115
+ const sched = new Scheduler(servicesWithRunner, registry, observer);
74
116
  for (const [, w] of allWorkflows)
75
117
  sched.add(w);
76
118
  sched.start();
77
119
  console.log("scheduler started; watching", workflowsDir);
120
+ if (session)
121
+ console.log(`▶ Dashboard live at ${session.url}`);
78
122
  return 0;
79
123
  }
80
- console.error("usage: flow <validate|run-once|start> ...");
124
+ console.error("usage: flow <validate|run|start> [workflow|projectDir] [--ui] [--fake]");
81
125
  return 1;
82
126
  }
83
127
  catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentic-surfaces/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -21,8 +21,9 @@
21
21
  "dist"
22
22
  ],
23
23
  "dependencies": {
24
- "@agentic-surfaces/agent": "0.1.3",
25
- "@agentic-surfaces/core": "0.1.3"
24
+ "@agentic-surfaces/core": "0.1.5",
25
+ "@agentic-surfaces/agent": "0.1.5",
26
+ "@agentic-surfaces/server": "0.1.5"
26
27
  },
27
28
  "scripts": {
28
29
  "build": "tsc -b",