@agentic-surfaces/cli 0.1.2 → 0.1.4

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 +58 -7
  2. package/package.json +4 -3
package/dist/index.js CHANGED
@@ -1,7 +1,48 @@
1
1
  import { readFileSync, readdirSync } from "node:fs";
2
- import { join, dirname } from "node:path";
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
+ }
29
+ // Walk up from a starting dir to find agentic-surfaces.config.yaml. run-once
30
+ // is pointed at a workflow file (e.g. <proj>/workflows/foo.yaml), but the
31
+ // project config — including the dryRun safety flag — lives at the project
32
+ // root (<proj>/). Without this, dryRun would default to false and a one-shot
33
+ // run could perform live writes. SAFETY-CRITICAL: do not remove.
34
+ function findProjectConfig(startDir) {
35
+ let dir = resolve(startDir);
36
+ for (;;) {
37
+ const pc = loadProjectConfig(dir);
38
+ if (pc)
39
+ return pc;
40
+ const parent = dirname(dir);
41
+ if (parent === dir)
42
+ return undefined;
43
+ dir = parent;
44
+ }
45
+ }
5
46
  export async function run(argv) {
6
47
  const [cmd, ...rest] = argv;
7
48
  try {
@@ -17,7 +58,7 @@ export async function run(argv) {
17
58
  return 1;
18
59
  }
19
60
  const fake = rest.includes("--fake");
20
- const pc = loadProjectConfig(dirname(file));
61
+ const pc = findProjectConfig(dirname(file));
21
62
  const wf = loadWorkflow(readFileSync(file, "utf8"));
22
63
  const services = buildServices(fake ? { fakeAgent: [{ text: "fake reply" }], projectConfig: pc } : { projectConfig: pc });
23
64
  const dir = dirname(file);
@@ -30,14 +71,20 @@ export async function run(argv) {
30
71
  catch { /* skip unparseable */ }
31
72
  }
32
73
  const registry = defaultRegistry();
33
- 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 });
34
77
  const servicesWithRunner = { ...services, runWorkflow: runWorkflowFn };
35
- const outputs = await runWorkflowOnce(wf, { registry, services: servicesWithRunner });
78
+ const outputs = await runWorkflowOnce(wf, { registry, services: servicesWithRunner, observer });
36
79
  console.log("ran", wf.name, "->", [...outputs.keys()].join(", "));
80
+ if (session) {
81
+ console.log(`\n▶ Dashboard live at ${session.url} — Ctrl-C to exit`);
82
+ await new Promise(() => { }); // keep the server up so the run stays viewable
83
+ }
37
84
  return 0;
38
85
  }
39
86
  if (cmd === "start") {
40
- const dir = rest[0] ?? ".";
87
+ const dir = rest.find(a => !a.startsWith("--")) ?? ".";
41
88
  const pc = loadProjectConfig(dir);
42
89
  const workflowsDir = pc?.workflows ? join(dir, pc.workflows) : dir;
43
90
  const services = buildServices({ projectConfig: pc });
@@ -51,13 +98,17 @@ export async function run(argv) {
51
98
  catch { /* skip unparseable */ }
52
99
  }
53
100
  const registry = defaultRegistry();
54
- const runWorkflowFn = buildWorkflowRunner({ workflows: allWorkflows, registry, services });
101
+ const session = rest.includes("--ui") ? startUi() : undefined;
102
+ const observer = session?.observer;
103
+ const runWorkflowFn = buildWorkflowRunner({ workflows: allWorkflows, registry, services, observer });
55
104
  const servicesWithRunner = { ...services, runWorkflow: runWorkflowFn };
56
- const sched = new Scheduler(servicesWithRunner, registry);
105
+ const sched = new Scheduler(servicesWithRunner, registry, observer);
57
106
  for (const [, w] of allWorkflows)
58
107
  sched.add(w);
59
108
  sched.start();
60
109
  console.log("scheduler started; watching", workflowsDir);
110
+ if (session)
111
+ console.log(`▶ Dashboard live at ${session.url}`);
61
112
  return 0;
62
113
  }
63
114
  console.error("usage: flow <validate|run-once|start> ...");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentic-surfaces/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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/core": "0.1.2",
25
- "@agentic-surfaces/agent": "0.1.2"
24
+ "@agentic-surfaces/core": "0.1.4",
25
+ "@agentic-surfaces/agent": "0.1.4",
26
+ "@agentic-surfaces/server": "0.1.4"
26
27
  },
27
28
  "scripts": {
28
29
  "build": "tsc -b",