@withone/cli 2.4.0 → 2.6.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.
@@ -3,21 +3,674 @@ import {
3
3
  setByDotPath
4
4
  } from "./chunk-44CV5IMX.js";
5
5
  import {
6
+ isAgentMode
7
+ } from "./chunk-Q6PWTATS.js";
8
+ import {
9
+ appendAnalyticsQueue,
10
+ appendUsageLog,
11
+ claimUsageLog,
12
+ getApiKey,
6
13
  getCacheTtl,
7
- homeDir
14
+ getDeviceId,
15
+ getEnvFromApiKey,
16
+ getProjectRoot,
17
+ getWhoAmI,
18
+ homeDir,
19
+ markTelemetryNoticeShown,
20
+ readAnalyticsQueue,
21
+ readConfig,
22
+ readUsageState,
23
+ telemetryNoticeShown,
24
+ writeAnalyticsQueue,
25
+ writeUsageLog,
26
+ writeUsageState
8
27
  } from "./chunk-HTXZZQ6U.js";
9
28
 
10
29
  // src/lib/flow-runner.ts
11
- import fs3 from "fs";
12
- import path3 from "path";
30
+ import fs5 from "fs";
31
+ import path6 from "path";
13
32
  import crypto from "crypto";
14
33
 
15
34
  // src/lib/flow-engine.ts
35
+ import fs4 from "fs";
36
+ import os2 from "os";
37
+ import path5 from "path";
38
+ import { exec, spawn } from "child_process";
39
+ import { promisify } from "util";
40
+
41
+ // src/lib/install-context.ts
16
42
  import fs2 from "fs";
17
43
  import os from "os";
44
+ import path3 from "path";
45
+
46
+ // src/lib/agents.ts
47
+ import fs from "fs";
48
+ import path from "path";
49
+ import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
50
+ function expandPath(p) {
51
+ if (p.startsWith("~/")) {
52
+ return path.join(homeDir(), p.slice(2));
53
+ }
54
+ return p;
55
+ }
56
+ function getClaudeDesktopConfigPath() {
57
+ switch (process.platform) {
58
+ case "darwin":
59
+ return "~/Library/Application Support/Claude/claude_desktop_config.json";
60
+ case "win32":
61
+ return path.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
62
+ default:
63
+ return "~/.config/Claude/claude_desktop_config.json";
64
+ }
65
+ }
66
+ function getClaudeDesktopDetectDir() {
67
+ switch (process.platform) {
68
+ case "darwin":
69
+ return "~/Library/Application Support/Claude";
70
+ case "win32":
71
+ return path.join(process.env.APPDATA || "", "Claude");
72
+ default:
73
+ return "~/.config/Claude";
74
+ }
75
+ }
76
+ function getWindsurfConfigPath() {
77
+ if (process.platform === "win32") {
78
+ return path.join(homeDir(), ".codeium", "windsurf", "mcp_config.json");
79
+ }
80
+ return "~/.codeium/windsurf/mcp_config.json";
81
+ }
82
+ function getWindsurfDetectDir() {
83
+ if (process.platform === "win32") {
84
+ return path.join(homeDir(), ".codeium", "windsurf");
85
+ }
86
+ return "~/.codeium/windsurf";
87
+ }
88
+ function getCursorConfigPath() {
89
+ if (process.platform === "win32") {
90
+ return path.join(homeDir(), ".cursor", "mcp.json");
91
+ }
92
+ return "~/.cursor/mcp.json";
93
+ }
94
+ function getAgents() {
95
+ return [
96
+ {
97
+ id: "claude-code",
98
+ name: "Claude Code",
99
+ configPath: "~/.claude.json",
100
+ configKey: "mcpServers",
101
+ detectDir: "~/.claude",
102
+ projectConfigPath: ".mcp.json"
103
+ },
104
+ {
105
+ id: "claude-desktop",
106
+ name: "Claude Desktop",
107
+ configPath: getClaudeDesktopConfigPath(),
108
+ configKey: "mcpServers",
109
+ detectDir: getClaudeDesktopDetectDir()
110
+ },
111
+ {
112
+ id: "cursor",
113
+ name: "Cursor",
114
+ configPath: getCursorConfigPath(),
115
+ configKey: "mcpServers",
116
+ detectDir: "~/.cursor",
117
+ projectConfigPath: ".cursor/mcp.json"
118
+ },
119
+ {
120
+ id: "windsurf",
121
+ name: "Windsurf",
122
+ configPath: getWindsurfConfigPath(),
123
+ configKey: "mcpServers",
124
+ detectDir: getWindsurfDetectDir()
125
+ },
126
+ {
127
+ id: "codex",
128
+ name: "Codex",
129
+ configPath: "~/.codex/config.toml",
130
+ configKey: "mcp_servers",
131
+ detectDir: "~/.codex",
132
+ projectConfigPath: ".codex/config.toml",
133
+ configFormat: "toml"
134
+ },
135
+ {
136
+ id: "kiro",
137
+ name: "Kiro",
138
+ configPath: "~/.kiro/settings/mcp.json",
139
+ configKey: "mcpServers",
140
+ detectDir: "~/.kiro",
141
+ projectConfigPath: ".kiro/settings/mcp.json"
142
+ }
143
+ ];
144
+ }
145
+ function detectInstalledAgents() {
146
+ return getAgents().filter((agent) => {
147
+ const detectDir = expandPath(agent.detectDir);
148
+ return fs.existsSync(detectDir);
149
+ });
150
+ }
151
+ function getAgentConfigPath(agent, scope = "global") {
152
+ if (scope === "project" && agent.projectConfigPath) {
153
+ return path.join(process.cwd(), agent.projectConfigPath);
154
+ }
155
+ return expandPath(agent.configPath);
156
+ }
157
+ function readAgentConfig(agent, scope = "global") {
158
+ const configPath = getAgentConfigPath(agent, scope);
159
+ if (!fs.existsSync(configPath)) {
160
+ return {};
161
+ }
162
+ try {
163
+ const content = fs.readFileSync(configPath, "utf-8");
164
+ if (agent.configFormat === "toml") {
165
+ return parseToml(content);
166
+ }
167
+ return JSON.parse(content);
168
+ } catch {
169
+ return {};
170
+ }
171
+ }
172
+ function writeAgentConfig(agent, config, scope = "global") {
173
+ const configPath = getAgentConfigPath(agent, scope);
174
+ const configDir = path.dirname(configPath);
175
+ if (!fs.existsSync(configDir)) {
176
+ fs.mkdirSync(configDir, { recursive: true });
177
+ }
178
+ if (agent.configFormat === "toml") {
179
+ fs.writeFileSync(configPath, stringifyToml(config));
180
+ } else {
181
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
182
+ }
183
+ }
184
+ function getMcpServerConfig(apiKey, accessControl) {
185
+ const env = {
186
+ ONE_SECRET: apiKey
187
+ };
188
+ if (accessControl) {
189
+ if (accessControl.permissions && accessControl.permissions !== "admin") {
190
+ env.ONE_PERMISSIONS = accessControl.permissions;
191
+ }
192
+ if (accessControl.connectionKeys && !(accessControl.connectionKeys.length === 1 && accessControl.connectionKeys[0] === "*")) {
193
+ env.ONE_CONNECTION_KEYS = accessControl.connectionKeys.join(",");
194
+ }
195
+ if (accessControl.actionIds && !(accessControl.actionIds.length === 1 && accessControl.actionIds[0] === "*")) {
196
+ env.ONE_ACTION_IDS = accessControl.actionIds.join(",");
197
+ }
198
+ if (accessControl.knowledgeAgent) {
199
+ env.ONE_KNOWLEDGE_AGENT = "true";
200
+ }
201
+ }
202
+ return {
203
+ command: "npx",
204
+ args: ["-y", "@withone/mcp"],
205
+ env
206
+ };
207
+ }
208
+ function installMcpConfig(agent, apiKey, scope = "global", accessControl) {
209
+ const config = readAgentConfig(agent, scope);
210
+ const configKey = agent.configKey;
211
+ const mcpServers = config[configKey] || {};
212
+ mcpServers["one"] = getMcpServerConfig(apiKey, accessControl);
213
+ config[configKey] = mcpServers;
214
+ writeAgentConfig(agent, config, scope);
215
+ }
216
+ function isMcpInstalled(agent, scope = "global") {
217
+ const config = readAgentConfig(agent, scope);
218
+ const configKey = agent.configKey;
219
+ const mcpServers = config[configKey];
220
+ return mcpServers?.["one"] !== void 0;
221
+ }
222
+ function getAgentStatuses() {
223
+ return getAgents().map((agent) => {
224
+ const detected = fs.existsSync(expandPath(agent.detectDir));
225
+ const globalMcp = detected && isMcpInstalled(agent, "global");
226
+ const projectMcp = agent.projectConfigPath ? isMcpInstalled(agent, "project") : null;
227
+ return { agent, detected, globalMcp, projectMcp };
228
+ });
229
+ }
230
+
231
+ // src/lib/analytics.ts
232
+ import { createRequire as createRequire2 } from "module";
233
+ import { randomUUID, createHash } from "crypto";
234
+ import pc from "picocolors";
235
+
236
+ // src/lib/version.ts
237
+ import { createRequire } from "module";
238
+ import { existsSync } from "fs";
18
239
  import path2 from "path";
19
- import { exec, spawn } from "child_process";
20
- import { promisify } from "util";
240
+ import { fileURLToPath } from "url";
241
+ var require2 = createRequire(import.meta.url);
242
+ var cached = null;
243
+ function cliVersion() {
244
+ if (cached !== null) return cached;
245
+ const dir = path2.dirname(fileURLToPath(import.meta.url));
246
+ for (let i = 1; i <= 4; i++) {
247
+ const candidate = path2.resolve(dir, ...Array(i).fill(".."), "package.json");
248
+ if (!existsSync(candidate)) continue;
249
+ try {
250
+ const version = require2(candidate).version;
251
+ if (version) {
252
+ cached = version;
253
+ return cached;
254
+ }
255
+ } catch {
256
+ }
257
+ }
258
+ cached = "unknown";
259
+ return cached;
260
+ }
261
+
262
+ // src/lib/analytics.ts
263
+ var require3 = createRequire2(import.meta.url);
264
+ var DEFAULT_POSTHOG_HOST = "https://us.i.posthog.com";
265
+ var DEFAULT_POSTHOG_KEY = "phc_a9ok4w0uxiZcVoSWOISIlin85lHMXQD3vWPaYnuRlRV";
266
+ var SEND_MAX_ATTEMPTS = 3;
267
+ var QUEUE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
268
+ var EXIT_GRACE_MS = 300;
269
+ var inFlight = /* @__PURE__ */ new Set();
270
+ var pending = /* @__PURE__ */ new Set();
271
+ var dispatched = /* @__PURE__ */ new Set();
272
+ var delivered = /* @__PURE__ */ new Set();
273
+ var UUID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
274
+ function uuidFromInsertId(insertId) {
275
+ if (UUID_SHAPE.test(insertId)) return insertId.toLowerCase();
276
+ const h = createHash("sha1").update(`one-cli-event:${insertId}`).digest("hex");
277
+ const variant = (parseInt(h[16], 16) & 3 | 8).toString(16);
278
+ return `${h.slice(0, 8)}-${h.slice(8, 12)}-5${h.slice(13, 16)}-${variant}${h.slice(17, 20)}-${h.slice(20, 32)}`;
279
+ }
280
+ function posthogHost() {
281
+ return process.env.ONE_POSTHOG_HOST || DEFAULT_POSTHOG_HOST;
282
+ }
283
+ function posthogKey() {
284
+ return process.env.ONE_POSTHOG_KEY || DEFAULT_POSTHOG_KEY;
285
+ }
286
+ function envName() {
287
+ const key = getApiKey();
288
+ return key ? getEnvFromApiKey(key) : "live";
289
+ }
290
+ function isOn(value) {
291
+ return value === "1" || value === "true";
292
+ }
293
+ function debugLog(message) {
294
+ if (isOn(process.env.ONE_ANALYTICS_DEBUG)) {
295
+ process.stderr.write(`[analytics] ${message}
296
+ `);
297
+ }
298
+ }
299
+ function isTelemetryDisabled() {
300
+ if (isOn(process.env.ONE_NO_TELEMETRY) || isOn(process.env.ONE_DISABLE_TELEMETRY)) return true;
301
+ if (isOn(process.env.DO_NOT_TRACK)) return true;
302
+ if (isOn(process.env.CI)) return true;
303
+ if (readConfig()?.telemetry === "off") return true;
304
+ return false;
305
+ }
306
+ function distinctId() {
307
+ return getWhoAmI()?.user?.id ?? getDeviceId();
308
+ }
309
+ function isAuthenticated() {
310
+ return !!getWhoAmI()?.user || !!getApiKey();
311
+ }
312
+ function baseProperties() {
313
+ return {
314
+ $lib: "one-cli",
315
+ cli_version: cliVersion(),
316
+ agent_mode: isAgentMode(),
317
+ env: envName(),
318
+ os: process.platform,
319
+ arch: process.arch,
320
+ node_version: process.versions.node,
321
+ authenticated: isAuthenticated()
322
+ };
323
+ }
324
+ function personSet() {
325
+ const whoami = getWhoAmI();
326
+ if (!whoami?.user) return void 0;
327
+ const set = {};
328
+ if (whoami.user.email) set.email = whoami.user.email;
329
+ if (whoami.user.name) set.name = whoami.user.name;
330
+ if (whoami.organization?.id) set.organization_id = whoami.organization.id;
331
+ return Object.keys(set).length ? set : void 0;
332
+ }
333
+ function send(item) {
334
+ const insertId = item.properties.$insert_id;
335
+ if (insertId) dispatched.add(insertId);
336
+ const controller = new AbortController();
337
+ inFlight.add(controller);
338
+ const run = (async () => {
339
+ try {
340
+ const res = await fetch(`${posthogHost()}/i/v0/e/`, {
341
+ method: "POST",
342
+ headers: { "Content-Type": "application/json" },
343
+ body: JSON.stringify({
344
+ api_key: posthogKey(),
345
+ event: item.event,
346
+ distinct_id: item.distinct_id,
347
+ // PostHog's dedupe key — a re-sent copy is dropped on ingest.
348
+ uuid: item.uuid ?? (insertId ? uuidFromInsertId(insertId) : void 0),
349
+ properties: item.properties,
350
+ timestamp: item.timestamp
351
+ }),
352
+ signal: controller.signal
353
+ });
354
+ if (res.ok && insertId) delivered.add(insertId);
355
+ debugLog(`"${item.event}" -> HTTP ${res.status}${res.ok ? "" : " (retry next run)"}`);
356
+ } catch (err) {
357
+ debugLog(`"${item.event}" not sent: ${err instanceof Error ? err.message : String(err)} (retry next run)`);
358
+ } finally {
359
+ inFlight.delete(controller);
360
+ }
361
+ })();
362
+ pending.add(run);
363
+ void run.finally(() => pending.delete(run));
364
+ }
365
+ function capture(event, properties = {}, opts = {}) {
366
+ if (isTelemetryDisabled()) {
367
+ debugLog(`disabled \u2014 skipping "${event}"`);
368
+ return;
369
+ }
370
+ const did = opts.distinctId ?? distinctId();
371
+ const props = { ...baseProperties(), ...properties };
372
+ if (props.$insert_id === void 0) props.$insert_id = randomUUID();
373
+ const insertId = props.$insert_id;
374
+ if (opts.personProfile === false) {
375
+ props.$process_person_profile = false;
376
+ } else if (did === distinctId()) {
377
+ const set = personSet();
378
+ if (set) props.$set = set;
379
+ }
380
+ const item = {
381
+ event,
382
+ distinct_id: did,
383
+ properties: props,
384
+ timestamp: opts.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
385
+ uuid: uuidFromInsertId(insertId)
386
+ };
387
+ appendAnalyticsQueue(JSON.stringify(item));
388
+ }
389
+ var ROLLUP_WINDOW_MS = 5 * 60 * 1e3;
390
+ var ROLLUP_MAX_BATCH = 500;
391
+ function utcDay(ts) {
392
+ return new Date(ts).toISOString().slice(0, 10);
393
+ }
394
+ var PRE_AUTH_COMMANDS = /* @__PURE__ */ new Set([
395
+ "init",
396
+ "login",
397
+ "logout",
398
+ "guide",
399
+ "platforms",
400
+ "onboard",
401
+ "config",
402
+ "update",
403
+ "help"
404
+ ]);
405
+ function shouldRecord(commandPath2) {
406
+ if (isAuthenticated()) return true;
407
+ return PRE_AUTH_COMMANDS.has(commandPath2.split(" ")[0]);
408
+ }
409
+ function recordCommand(command) {
410
+ if (isTelemetryDisabled()) {
411
+ writeUsageLog([]);
412
+ return;
413
+ }
414
+ const cmdPath = commandPath(command);
415
+ if (!shouldRecord(cmdPath)) return;
416
+ const did = distinctId();
417
+ const entry = { ts: Date.now(), command: cmdPath, agent: isAgentMode(), did };
418
+ appendUsageLog(JSON.stringify(entry));
419
+ const today = utcDay(entry.ts);
420
+ const state = readUsageState();
421
+ const firstTouch = state.lastDay !== today || state.distinctId !== did;
422
+ flushUsageRollups({ force: firstTouch });
423
+ if (firstTouch) writeUsageState({ lastDay: today, distinctId: did });
424
+ }
425
+ function flushUsageRollups(opts = {}) {
426
+ if (isTelemetryDisabled()) {
427
+ writeUsageLog([]);
428
+ return;
429
+ }
430
+ const lines = claimUsageLog();
431
+ if (!lines || lines.length === 0) return;
432
+ const entries = [];
433
+ for (const line of lines) {
434
+ try {
435
+ const e = JSON.parse(line);
436
+ if (e && typeof e.ts === "number" && typeof e.command === "string" && typeof e.did === "string") {
437
+ entries.push(e);
438
+ }
439
+ } catch {
440
+ }
441
+ }
442
+ if (entries.length === 0) return;
443
+ const currentDid = entries[entries.length - 1].did;
444
+ const now = Date.now();
445
+ const groups = /* @__PURE__ */ new Map();
446
+ for (const e of entries) {
447
+ const g = groups.get(e.did);
448
+ if (g) g.push(e);
449
+ else groups.set(e.did, [e]);
450
+ }
451
+ const kept = [];
452
+ for (const [did, group] of groups) {
453
+ const due = opts.force === true || did !== currentDid || // a superseded login's batch — flush it now
454
+ group.length >= ROLLUP_MAX_BATCH || now - group[0].ts >= ROLLUP_WINDOW_MS;
455
+ if (due) emitRollup(did, group);
456
+ else kept.push(...group);
457
+ }
458
+ for (const e of kept) appendUsageLog(JSON.stringify(e));
459
+ }
460
+ function emitRollup(did, group) {
461
+ const byCommand = {};
462
+ let agentCount = 0;
463
+ for (const e of group) {
464
+ byCommand[e.command] = (byCommand[e.command] ?? 0) + 1;
465
+ if (e.agent) agentCount += 1;
466
+ }
467
+ const insertId = createHash("sha1").update(`${did}|${group.map((e) => `${e.ts}:${e.command}:${e.agent ? 1 : 0}`).join("|")}`).digest("hex");
468
+ capture(
469
+ "CLI Usage Rollup",
470
+ {
471
+ command_count: group.length,
472
+ by_command: byCommand,
473
+ agent_count: agentCount,
474
+ human_count: group.length - agentCount,
475
+ window_start: new Date(group[0].ts).toISOString(),
476
+ window_end: new Date(group[group.length - 1].ts).toISOString(),
477
+ $insert_id: insertId
478
+ },
479
+ {
480
+ distinctId: did,
481
+ timestamp: new Date(group[group.length - 1].ts).toISOString(),
482
+ // Rollups bill at the anonymous rate; the person already exists.
483
+ personProfile: false
484
+ }
485
+ );
486
+ debugLog(`rollup \u2014 ${group.length} command(s) for ${did}`);
487
+ }
488
+ function commandPath(command) {
489
+ const parts = [];
490
+ let current = command;
491
+ while (current && current.name() && current.name() !== "one") {
492
+ parts.unshift(current.name());
493
+ current = current.parent;
494
+ }
495
+ return parts.join(" ") || command.name();
496
+ }
497
+ function drainQueue() {
498
+ if (isTelemetryDisabled()) {
499
+ writeAnalyticsQueue([]);
500
+ return;
501
+ }
502
+ const now = Date.now();
503
+ const kept = [];
504
+ let changed = false;
505
+ for (const line of readAnalyticsQueue()) {
506
+ let item;
507
+ try {
508
+ item = JSON.parse(line);
509
+ } catch {
510
+ changed = true;
511
+ continue;
512
+ }
513
+ const insertId = item?.properties?.$insert_id;
514
+ if (!insertId) {
515
+ changed = true;
516
+ continue;
517
+ }
518
+ const age = now - Date.parse(item.timestamp);
519
+ const attempts = item.attempts ?? 0;
520
+ if (!(age < QUEUE_MAX_AGE_MS) || attempts >= SEND_MAX_ATTEMPTS) {
521
+ debugLog(`"${item.event}" dropped (${attempts} attempts, ${Math.round(age / 6e4)} min old)`);
522
+ changed = true;
523
+ continue;
524
+ }
525
+ if (dispatched.has(insertId)) {
526
+ kept.push(line);
527
+ continue;
528
+ }
529
+ item.attempts = attempts + 1;
530
+ if (!item.uuid) item.uuid = uuidFromInsertId(insertId);
531
+ kept.push(JSON.stringify(item));
532
+ changed = true;
533
+ send(item);
534
+ }
535
+ if (changed) writeAnalyticsQueue(kept);
536
+ }
537
+ async function flush() {
538
+ if (pending.size > 0) {
539
+ await Promise.race([
540
+ Promise.allSettled([...pending]),
541
+ new Promise((resolve2) => setTimeout(resolve2, EXIT_GRACE_MS))
542
+ ]);
543
+ }
544
+ for (const controller of inFlight) controller.abort();
545
+ const remaining = readAnalyticsQueue().filter((line) => {
546
+ try {
547
+ const item = JSON.parse(line);
548
+ const id = item.properties?.$insert_id;
549
+ if (!id || delivered.has(id)) return false;
550
+ return (item.attempts ?? 0) < SEND_MAX_ATTEMPTS;
551
+ } catch {
552
+ return false;
553
+ }
554
+ });
555
+ writeAnalyticsQueue(remaining);
556
+ dispatched.clear();
557
+ delivered.clear();
558
+ }
559
+ function maybeShowTelemetryNotice() {
560
+ if (isTelemetryDisabled() || isAgentMode()) return;
561
+ if (telemetryNoticeShown()) return;
562
+ markTelemetryNoticeShown();
563
+ process.stderr.write(
564
+ pc.dim(
565
+ "One CLI collects usage analytics (which commands run, linked to your One account) to improve the product.\nNo arguments, inputs, or secrets are ever collected. Opt out anytime with ONE_NO_TELEMETRY=1.\n"
566
+ )
567
+ );
568
+ }
569
+
570
+ // src/lib/install-context.ts
571
+ var EXTRA_HARNESS_DIRS = [
572
+ { id: "gemini-cli", dir: ".gemini" },
573
+ { id: "openclaw", dir: ".openclaw" },
574
+ { id: "hermes", dir: ".hermes" },
575
+ { id: "devin", dir: ".devin" }
576
+ ];
577
+ var LAUNCHER_ENV = [
578
+ { id: "claude-code", vars: ["CLAUDECODE", "CLAUDE_CODE_ENTRYPOINT"] },
579
+ { id: "codex", vars: ["CODEX_SANDBOX", "CODEX_CI", "CODEX_THREAD_ID"] },
580
+ { id: "gemini-cli", vars: ["GEMINI_CLI"] },
581
+ // CURSOR_TRACE_ID is deliberately absent: Cursor exports it into every
582
+ // integrated-terminal shell, so it marks the editor, not an agent.
583
+ { id: "cursor", vars: ["CURSOR_AGENT"] },
584
+ { id: "windsurf", vars: ["WINDSURF_AGENT"] },
585
+ { id: "kiro", vars: ["KIRO_AGENT"] },
586
+ { id: "openclaw", vars: ["OPENCLAW_AGENT", "OPENCLAW_SESSION"] },
587
+ { id: "hermes", vars: ["HERMES_AGENT", "HERMES_SESSION"] },
588
+ { id: "devin", vars: ["DEVIN_SESSION_ID"] }
589
+ ];
590
+ function detectLauncher(env = process.env) {
591
+ for (const entry of LAUNCHER_ENV) {
592
+ if (entry.vars.some((v) => env[v] !== void 0 && env[v] !== "")) return entry.id;
593
+ }
594
+ return void 0;
595
+ }
596
+ function detectInstalledHarnesses() {
597
+ const ids = new Set(detectInstalledAgents().map((a) => a.id));
598
+ for (const { id, dir } of EXTRA_HARNESS_DIRS) {
599
+ if (fs2.existsSync(path3.join(homeDir(), dir))) ids.add(id);
600
+ }
601
+ return [...ids].sort();
602
+ }
603
+ function tryRead(read) {
604
+ try {
605
+ const value = read();
606
+ return value === null || value === "" ? void 0 : value;
607
+ } catch {
608
+ return void 0;
609
+ }
610
+ }
611
+ function collectInstallContext(opts) {
612
+ const env = opts.env ?? process.env;
613
+ const ctx = {
614
+ scope: opts.scope,
615
+ harnesses: tryRead(() => detectInstalledHarnesses()) ?? []
616
+ };
617
+ if (opts.scope === "project") {
618
+ ctx.path = opts.projectRoot ?? tryRead(() => getProjectRoot());
619
+ }
620
+ ctx.host = tryRead(() => os.hostname());
621
+ ctx.os = process.platform;
622
+ ctx.osVersion = tryRead(() => os.release());
623
+ ctx.arch = process.arch;
624
+ ctx.user = tryRead(() => os.userInfo().username);
625
+ if (!isTelemetryDisabled()) ctx.device = tryRead(() => getDeviceId());
626
+ const cli = tryRead(() => cliVersion());
627
+ ctx.cli = cli === "unknown" ? void 0 : cli;
628
+ ctx.launcher = detectLauncher(env);
629
+ return ctx;
630
+ }
631
+ function installContextToParams(ctx) {
632
+ const params = new URLSearchParams();
633
+ const set = (key, value) => {
634
+ if (value !== void 0 && value !== "") params.set(key, value);
635
+ };
636
+ set("scope", ctx.scope);
637
+ set("path", ctx.path);
638
+ set("host", ctx.host);
639
+ set("os", ctx.os);
640
+ set("osv", ctx.osVersion);
641
+ set("arch", ctx.arch);
642
+ set("user", ctx.user);
643
+ set("device", ctx.device);
644
+ set("cli", ctx.cli);
645
+ if (ctx.harnesses.length > 0) params.set("harnesses", ctx.harnesses.join(","));
646
+ set("launcher", ctx.launcher);
647
+ return params;
648
+ }
649
+ function describeInstallContext(ctx) {
650
+ const lines = [`scope: ${ctx.scope}`];
651
+ if (ctx.path) lines.push(`path: ${ctx.path}`);
652
+ if (ctx.host || ctx.os) {
653
+ const osPart = [ctx.os, ctx.osVersion].filter(Boolean).join(" ");
654
+ const detail = [osPart, ctx.arch].filter(Boolean).join(", ");
655
+ lines.push(`machine: ${ctx.host ?? "unknown"}${detail ? ` (${detail})` : ""}`);
656
+ }
657
+ if (ctx.user) lines.push(`user: ${ctx.user}`);
658
+ if (ctx.device) lines.push(`device: ${ctx.device}`);
659
+ if (ctx.harnesses.length > 0) lines.push(`harnesses: ${ctx.harnesses.join(", ")}`);
660
+ if (ctx.launcher) lines.push(`launched by: ${ctx.launcher}`);
661
+ if (ctx.cli) lines.push(`cli: ${ctx.cli}`);
662
+ return lines.join("\n");
663
+ }
664
+
665
+ // src/lib/caller.ts
666
+ var CALLER_SOURCE_HEADER = "x-one-source";
667
+ var CALLER_AGENT_HEADER = "x-one-agent";
668
+ function callerHeaders(env = process.env) {
669
+ const headers = { [CALLER_SOURCE_HEADER]: "cli" };
670
+ const launcher = detectLauncher(env);
671
+ if (launcher) headers[CALLER_AGENT_HEADER] = launcher;
672
+ return headers;
673
+ }
21
674
 
22
675
  // src/lib/api.ts
23
676
  import { createWriteStream } from "fs";
@@ -74,8 +727,8 @@ var OneApi = class {
74
727
  this.apiBase = apiBase ?? "https://api.withone.ai/v1";
75
728
  }
76
729
  apiBase;
77
- async request(path4) {
78
- return this.requestFull({ path: path4 });
730
+ async request(path7) {
731
+ return this.requestFull({ path: path7 });
79
732
  }
80
733
  async requestFull(opts) {
81
734
  let url = `${this.apiBase}${opts.path}`;
@@ -104,6 +757,20 @@ var OneApi = class {
104
757
  if (!text) return {};
105
758
  return JSON.parse(text);
106
759
  }
760
+ /**
761
+ * The calling key's own access, as One enforces it: `GET /v1/access/me`.
762
+ * Resolves `null` for a key with no restrictions. Throws `ApiError` when the
763
+ * API does not answer it (for example an older backend without the route).
764
+ */
765
+ async getOwnAccess() {
766
+ const body = await this.request("/access/me");
767
+ if (body === null) return null;
768
+ if (typeof body !== "object" || !("rules" in body || "methods" in body)) {
769
+ throw new ApiError(502, "Unexpected answer from /access/me");
770
+ }
771
+ const doc = body;
772
+ return { methods: doc.methods ?? null, rules: doc.rules ?? null };
773
+ }
107
774
  async whoami() {
108
775
  return this.request("/users/whoami");
109
776
  }
@@ -300,6 +967,9 @@ var OneApi = class {
300
967
  "x-one-action-id": action._id,
301
968
  "Content-Type": contentType,
302
969
  Accept: "application/json, text/event-stream, */*",
970
+ // Who is calling, for the Logs page. Before the caller's own headers so
971
+ // an explicit --headers value still wins.
972
+ ...callerHeaders(),
303
973
  ...args.headers
304
974
  };
305
975
  const finalActionPath = args.pathVariables ? replacePathVariables(action.path, args.pathVariables) : action.path;
@@ -508,9 +1178,9 @@ var TimeoutError = class extends Error {
508
1178
  function sleep(ms) {
509
1179
  return new Promise((resolve2) => setTimeout(resolve2, ms));
510
1180
  }
511
- function replacePathVariables(path4, variables) {
512
- if (!path4) return path4;
513
- let result = path4;
1181
+ function replacePathVariables(path7, variables) {
1182
+ if (!path7) return path7;
1183
+ let result = path7;
514
1184
  result = result.replace(/\{\{([^}]+)\}\}/g, (_match, variable) => {
515
1185
  const trimmedVariable = variable.trim();
516
1186
  const value = variables[trimmedVariable];
@@ -544,23 +1214,23 @@ function isActionAllowed(actionId, allowedActionIds) {
544
1214
  }
545
1215
 
546
1216
  // src/lib/cache.ts
547
- import fs from "fs";
548
- import path from "path";
1217
+ import fs3 from "fs";
1218
+ import path4 from "path";
549
1219
  function knowledgeDir() {
550
- return path.join(homeDir(), ".one", "cache", "knowledge");
1220
+ return path4.join(homeDir(), ".one", "cache", "knowledge");
551
1221
  }
552
1222
  function legacySearchDir() {
553
- return path.join(homeDir(), ".one", "cache", "search");
1223
+ return path4.join(homeDir(), ".one", "cache", "search");
554
1224
  }
555
1225
  function sanitizeFilename(input) {
556
1226
  return input.replace(/[^a-zA-Z0-9_\-\.]/g, "_");
557
1227
  }
558
1228
  function knowledgeCachePath(actionId) {
559
- return path.join(knowledgeDir(), `${sanitizeFilename(actionId)}.json`);
1229
+ return path4.join(knowledgeDir(), `${sanitizeFilename(actionId)}.json`);
560
1230
  }
561
1231
  function readCache(filePath) {
562
1232
  try {
563
- const content = fs.readFileSync(filePath, "utf-8");
1233
+ const content = fs3.readFileSync(filePath, "utf-8");
564
1234
  return JSON.parse(content);
565
1235
  } catch {
566
1236
  return null;
@@ -568,9 +1238,9 @@ function readCache(filePath) {
568
1238
  }
569
1239
  function writeCache(filePath, entry) {
570
1240
  try {
571
- const dir = path.dirname(filePath);
572
- fs.mkdirSync(dir, { recursive: true });
573
- fs.writeFileSync(filePath, JSON.stringify(entry, null, 2));
1241
+ const dir = path4.dirname(filePath);
1242
+ fs3.mkdirSync(dir, { recursive: true });
1243
+ fs3.writeFileSync(filePath, JSON.stringify(entry, null, 2));
574
1244
  } catch {
575
1245
  }
576
1246
  }
@@ -597,9 +1267,9 @@ function formatAge(seconds) {
597
1267
  function listCacheEntries() {
598
1268
  const entries = [];
599
1269
  try {
600
- for (const file of fs.readdirSync(knowledgeDir())) {
1270
+ for (const file of fs3.readdirSync(knowledgeDir())) {
601
1271
  if (!file.endsWith(".json")) continue;
602
- const filePath = path.join(knowledgeDir(), file);
1272
+ const filePath = path4.join(knowledgeDir(), file);
603
1273
  const entry = readCache(filePath);
604
1274
  if (entry) {
605
1275
  entries.push({ type: "knowledge", filePath, entry });
@@ -613,12 +1283,12 @@ function clearAll() {
613
1283
  let count = 0;
614
1284
  for (const dir of [knowledgeDir(), legacySearchDir()]) {
615
1285
  try {
616
- const files = fs.readdirSync(dir);
1286
+ const files = fs3.readdirSync(dir);
617
1287
  for (const file of files) {
618
- fs.unlinkSync(path.join(dir, file));
1288
+ fs3.unlinkSync(path4.join(dir, file));
619
1289
  count++;
620
1290
  }
621
- fs.rmdirSync(dir);
1291
+ fs3.rmdirSync(dir);
622
1292
  } catch {
623
1293
  }
624
1294
  }
@@ -627,7 +1297,7 @@ function clearAll() {
627
1297
  function clearEntry(actionId) {
628
1298
  const filePath = knowledgeCachePath(actionId);
629
1299
  try {
630
- fs.unlinkSync(filePath);
1300
+ fs3.unlinkSync(filePath);
631
1301
  return true;
632
1302
  } catch {
633
1303
  return false;
@@ -655,27 +1325,27 @@ async function resolveActionDetails(api, actionId, opts = {}) {
655
1325
  });
656
1326
  const cachePath = knowledgeCachePath(actionId);
657
1327
  const raw = useCache ? readCache(cachePath) : null;
658
- const cached = isActionDetailsEntry(raw) ? raw : null;
659
- if (cached && isFresh(cached)) {
660
- return { details: cached.data, cacheHit: true, stale: false, entry: cached };
1328
+ const cached2 = isActionDetailsEntry(raw) ? raw : null;
1329
+ if (cached2 && isFresh(cached2)) {
1330
+ return { details: cached2.data, cacheHit: true, stale: false, entry: cached2 };
661
1331
  }
662
1332
  try {
663
- const result = await api.getActionDetailsWithMeta(actionId, cached?.etag ?? void 0);
664
- if (result.status === 304 && cached) {
665
- cached.cachedAt = (/* @__PURE__ */ new Date()).toISOString();
666
- writeCache(cachePath, cached);
667
- return { details: cached.data, cacheHit: true, stale: false, entry: cached };
1333
+ const result = await api.getActionDetailsWithMeta(actionId, cached2?.etag ?? void 0);
1334
+ if (result.status === 304 && cached2) {
1335
+ cached2.cachedAt = (/* @__PURE__ */ new Date()).toISOString();
1336
+ writeCache(cachePath, cached2);
1337
+ return { details: cached2.data, cacheHit: true, stale: false, entry: cached2 };
668
1338
  }
669
1339
  const entry = makeCacheEntry(actionId, result.data, result.etag);
670
1340
  writeCache(cachePath, entry);
671
1341
  return { details: result.data, cacheHit: false, stale: false, entry };
672
1342
  } catch (fetchError) {
673
- if (cached) {
1343
+ if (cached2) {
674
1344
  warn(
675
- `Warning: serving cached action details (network unavailable, cached ${formatAge(getAge(cached))} ago)
1345
+ `Warning: serving cached action details (network unavailable, cached ${formatAge(getAge(cached2))} ago)
676
1346
  `
677
1347
  );
678
- return { details: cached.data, cacheHit: true, stale: true, entry: cached };
1348
+ return { details: cached2.data, cacheHit: true, stale: true, entry: cached2 };
679
1349
  }
680
1350
  throw fetchError;
681
1351
  }
@@ -1550,48 +2220,48 @@ function collectFileReadViolations(value, schema, basePath, out) {
1550
2220
  const obj = value;
1551
2221
  for (const [field, raw] of Object.entries(schema)) {
1552
2222
  const rule = asFieldSchema(raw);
1553
- const path4 = `${basePath}${field}`;
2223
+ const path7 = `${basePath}${field}`;
1554
2224
  const present = Object.prototype.hasOwnProperty.call(obj, field) && obj[field] !== void 0;
1555
2225
  if (!present) {
1556
- if (rule.required) out.push(`field "${path4}" is required but missing`);
2226
+ if (rule.required) out.push(`field "${path7}" is required but missing`);
1557
2227
  continue;
1558
2228
  }
1559
- checkFileReadRule(obj[field], rule, path4, out);
2229
+ checkFileReadRule(obj[field], rule, path7, out);
1560
2230
  }
1561
2231
  }
1562
- function checkFileReadRule(value, rule, path4, out) {
2232
+ function checkFileReadRule(value, rule, path7, out) {
1563
2233
  if (rule.type && !matchesLeafType(value, rule.type)) {
1564
- out.push(`field "${path4}" expected ${rule.type} but got ${actualType(value)}`);
2234
+ out.push(`field "${path7}" expected ${rule.type} but got ${actualType(value)}`);
1565
2235
  return;
1566
2236
  }
1567
2237
  if (Array.isArray(rule.enum) && !rule.enum.some((e) => e === value)) {
1568
- out.push(`field "${path4}" value ${JSON.stringify(value)} is not one of [${rule.enum.map((e) => JSON.stringify(e)).join(", ")}]`);
2238
+ out.push(`field "${path7}" value ${JSON.stringify(value)} is not one of [${rule.enum.map((e) => JSON.stringify(e)).join(", ")}]`);
1569
2239
  }
1570
2240
  const hasArrayConstraint = rule.items !== void 0 || rule.minItems !== void 0 || rule.maxItems !== void 0 || rule.length !== void 0;
1571
2241
  if (hasArrayConstraint) {
1572
2242
  if (!Array.isArray(value)) {
1573
- if (!rule.type) out.push(`field "${path4}" expected array but got ${actualType(value)}`);
2243
+ if (!rule.type) out.push(`field "${path7}" expected array but got ${actualType(value)}`);
1574
2244
  } else {
1575
2245
  if (rule.length !== void 0 && value.length !== rule.length) {
1576
- out.push(`field "${path4}" expected array of length ${rule.length} but got length ${value.length}`);
2246
+ out.push(`field "${path7}" expected array of length ${rule.length} but got length ${value.length}`);
1577
2247
  }
1578
2248
  if (rule.minItems !== void 0 && value.length < rule.minItems) {
1579
- out.push(`field "${path4}" expected at least ${rule.minItems} item${rule.minItems === 1 ? "" : "s"} but got ${value.length}`);
2249
+ out.push(`field "${path7}" expected at least ${rule.minItems} item${rule.minItems === 1 ? "" : "s"} but got ${value.length}`);
1580
2250
  }
1581
2251
  if (rule.maxItems !== void 0 && value.length > rule.maxItems) {
1582
- out.push(`field "${path4}" expected at most ${rule.maxItems} item${rule.maxItems === 1 ? "" : "s"} but got ${value.length}`);
2252
+ out.push(`field "${path7}" expected at most ${rule.maxItems} item${rule.maxItems === 1 ? "" : "s"} but got ${value.length}`);
1583
2253
  }
1584
2254
  if (rule.items !== void 0) {
1585
2255
  const itemRule = asFieldSchema(rule.items);
1586
- value.forEach((el, i) => checkFileReadRule(el, itemRule, `${path4}[${i}]`, out));
2256
+ value.forEach((el, i) => checkFileReadRule(el, itemRule, `${path7}[${i}]`, out));
1587
2257
  }
1588
2258
  }
1589
2259
  }
1590
2260
  if (rule.properties) {
1591
2261
  if (isPlainObjectValue(value)) {
1592
- collectFileReadViolations(value, rule.properties, `${path4}.`, out);
2262
+ collectFileReadViolations(value, rule.properties, `${path7}.`, out);
1593
2263
  } else if (!rule.type) {
1594
- out.push(`field "${path4}" expected object but got ${actualType(value)}`);
2264
+ out.push(`field "${path7}" expected object but got ${actualType(value)}`);
1595
2265
  }
1596
2266
  }
1597
2267
  }
@@ -2069,15 +2739,15 @@ async function executeCodeModule(stepId, modulePath, context, options) {
2069
2739
  if (!rootDir) {
2070
2740
  throw new Error(`Code step "${stepId}" uses module "${modulePath}" but no flow rootDir is available. Flows that use code modules must be loaded via loadFlowWithMeta.`);
2071
2741
  }
2072
- if (path2.isAbsolute(modulePath)) {
2742
+ if (path5.isAbsolute(modulePath)) {
2073
2743
  throw new Error(`Code module path must be relative to the flow root, got absolute: "${modulePath}"`);
2074
2744
  }
2075
- const absPath = path2.resolve(rootDir, modulePath);
2076
- const relFromRoot = path2.relative(rootDir, absPath);
2077
- if (relFromRoot.startsWith("..") || path2.isAbsolute(relFromRoot)) {
2745
+ const absPath = path5.resolve(rootDir, modulePath);
2746
+ const relFromRoot = path5.relative(rootDir, absPath);
2747
+ if (relFromRoot.startsWith("..") || path5.isAbsolute(relFromRoot)) {
2078
2748
  throw new Error(`Code module "${modulePath}" resolves outside the flow directory`);
2079
2749
  }
2080
- if (!fs2.existsSync(absPath)) {
2750
+ if (!fs4.existsSync(absPath)) {
2081
2751
  throw new Error(`Code module not found: ${absPath}`);
2082
2752
  }
2083
2753
  const { env: _omitEnv, ...safeContext } = context;
@@ -2225,8 +2895,8 @@ async function executeParallelStep(step, context, api, permissions, allowedActio
2225
2895
  function executeFileReadStep(step, context) {
2226
2896
  const config = step.fileRead;
2227
2897
  const filePath = resolveValue(config.path, context);
2228
- const resolvedPath = path2.resolve(filePath);
2229
- const content = fs2.readFileSync(resolvedPath, "utf-8");
2898
+ const resolvedPath = path5.resolve(filePath);
2899
+ const content = fs4.readFileSync(resolvedPath, "utf-8");
2230
2900
  const output = config.parseJson ? JSON.parse(stripCodeFences(content)) : content;
2231
2901
  if (config.parseJson && config.schema) {
2232
2902
  assertMatchesFileReadSchema(output, config.schema, step.id);
@@ -2237,16 +2907,16 @@ function executeFileWriteStep(step, context) {
2237
2907
  const config = step.fileWrite;
2238
2908
  const filePath = resolveValue(config.path, context);
2239
2909
  const content = resolveValue(config.content, context);
2240
- const resolvedPath = path2.resolve(filePath);
2241
- const dir = path2.dirname(resolvedPath);
2242
- if (!fs2.existsSync(dir)) {
2243
- fs2.mkdirSync(dir, { recursive: true });
2910
+ const resolvedPath = path5.resolve(filePath);
2911
+ const dir = path5.dirname(resolvedPath);
2912
+ if (!fs4.existsSync(dir)) {
2913
+ fs4.mkdirSync(dir, { recursive: true });
2244
2914
  }
2245
2915
  const stringContent = typeof content === "string" ? content : JSON.stringify(content, null, 2);
2246
2916
  if (config.append) {
2247
- fs2.appendFileSync(resolvedPath, stringContent);
2917
+ fs4.appendFileSync(resolvedPath, stringContent);
2248
2918
  } else {
2249
- fs2.writeFileSync(resolvedPath, stringContent);
2919
+ fs4.writeFileSync(resolvedPath, stringContent);
2250
2920
  }
2251
2921
  return { status: "success", output: { path: resolvedPath, bytesWritten: stringContent.length }, response: { path: resolvedPath } };
2252
2922
  }
@@ -2285,7 +2955,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
2285
2955
  if (flowStack.includes(resolvedKey)) {
2286
2956
  throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
2287
2957
  }
2288
- const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-SFECNAO2.js");
2958
+ const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-FME4AVVN.js");
2289
2959
  const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
2290
2960
  const subContext = await executeFlow(
2291
2961
  subFlow,
@@ -2388,11 +3058,11 @@ function resolveBashEnv(envConfig, context, stepId) {
2388
3058
  if ("json" in obj) {
2389
3059
  const resolved2 = resolveValue(obj.json, context);
2390
3060
  const json = JSON.stringify(resolved2 ?? null);
2391
- const tmp = path2.join(
2392
- os.tmpdir(),
3061
+ const tmp = path5.join(
3062
+ os2.tmpdir(),
2393
3063
  `one-flow-${stepId}-${key}-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`
2394
3064
  );
2395
- fs2.writeFileSync(tmp, json, { encoding: "utf-8" });
3065
+ fs4.writeFileSync(tmp, json, { encoding: "utf-8" });
2396
3066
  tempFiles.push(tmp);
2397
3067
  out[key] = tmp;
2398
3068
  continue;
@@ -2439,7 +3109,7 @@ async function executeBashStep(step, context, options) {
2439
3109
  } finally {
2440
3110
  for (const tmp of tempFiles) {
2441
3111
  try {
2442
- fs2.unlinkSync(tmp);
3112
+ fs4.unlinkSync(tmp);
2443
3113
  } catch {
2444
3114
  }
2445
3115
  }
@@ -2829,8 +3499,8 @@ var FLOWS_DIR = ".one/flows";
2829
3499
  var RUNS_DIR = ".one/flows/.runs";
2830
3500
  var LOGS_DIR = ".one/flows/.logs";
2831
3501
  function ensureDir(dir) {
2832
- if (!fs3.existsSync(dir)) {
2833
- fs3.mkdirSync(dir, { recursive: true });
3502
+ if (!fs5.existsSync(dir)) {
3503
+ fs5.mkdirSync(dir, { recursive: true });
2834
3504
  }
2835
3505
  }
2836
3506
  function generateRunId() {
@@ -2851,8 +3521,8 @@ var FlowRunner = class _FlowRunner {
2851
3521
  this.flowKey = flow.key;
2852
3522
  ensureDir(RUNS_DIR);
2853
3523
  ensureDir(LOGS_DIR);
2854
- this.statePath = path3.join(RUNS_DIR, `${flow.key}-${this.runId}.state.json`);
2855
- this.logPath = path3.join(LOGS_DIR, `${flow.key}-${this.runId}.log`);
3524
+ this.statePath = path6.join(RUNS_DIR, `${flow.key}-${this.runId}.state.json`);
3525
+ this.logPath = path6.join(LOGS_DIR, `${flow.key}-${this.runId}.log`);
2856
3526
  const resolvedInputs = { ...inputs };
2857
3527
  for (const [name, decl] of Object.entries(flow.inputs)) {
2858
3528
  if (resolvedInputs[name] === void 0 && decl.default !== void 0) {
@@ -2893,10 +3563,10 @@ var FlowRunner = class _FlowRunner {
2893
3563
  msg,
2894
3564
  ...data
2895
3565
  };
2896
- fs3.appendFileSync(this.logPath, JSON.stringify(entry) + "\n");
3566
+ fs5.appendFileSync(this.logPath, JSON.stringify(entry) + "\n");
2897
3567
  }
2898
3568
  saveState() {
2899
- fs3.writeFileSync(this.statePath, JSON.stringify(this.state, stripStepsAlias, 2));
3569
+ fs5.writeFileSync(this.statePath, JSON.stringify(this.state, stripStepsAlias, 2));
2900
3570
  }
2901
3571
  createEventHandler(externalHandler) {
2902
3572
  return (event) => {
@@ -2994,14 +3664,14 @@ var FlowRunner = class _FlowRunner {
2994
3664
  }
2995
3665
  /** Path to a run's persisted state file. The post-mortem artifact surfaced by `one flow inspect`. */
2996
3666
  static statePathFor(flowKey, runId) {
2997
- return path3.join(RUNS_DIR, `${flowKey}-${runId}.state.json`);
3667
+ return path6.join(RUNS_DIR, `${flowKey}-${runId}.state.json`);
2998
3668
  }
2999
3669
  static loadRunState(runId) {
3000
3670
  ensureDir(RUNS_DIR);
3001
- const files = fs3.readdirSync(RUNS_DIR).filter((f) => f.includes(runId) && f.endsWith(".state.json"));
3671
+ const files = fs5.readdirSync(RUNS_DIR).filter((f) => f.includes(runId) && f.endsWith(".state.json"));
3002
3672
  if (files.length === 0) return null;
3003
3673
  try {
3004
- const content = fs3.readFileSync(path3.join(RUNS_DIR, files[0]), "utf-8");
3674
+ const content = fs5.readFileSync(path6.join(RUNS_DIR, files[0]), "utf-8");
3005
3675
  return JSON.parse(content);
3006
3676
  } catch {
3007
3677
  return null;
@@ -3013,17 +3683,17 @@ var FlowRunner = class _FlowRunner {
3013
3683
  runner.flowKey = state.flowKey;
3014
3684
  runner.state = state;
3015
3685
  runner.paused = false;
3016
- runner.statePath = path3.join(RUNS_DIR, `${state.flowKey}-${state.runId}.state.json`);
3017
- runner.logPath = path3.join(LOGS_DIR, `${state.flowKey}-${state.runId}.log`);
3686
+ runner.statePath = path6.join(RUNS_DIR, `${state.flowKey}-${state.runId}.state.json`);
3687
+ runner.logPath = path6.join(LOGS_DIR, `${state.flowKey}-${state.runId}.log`);
3018
3688
  return runner;
3019
3689
  }
3020
3690
  static listRuns(flowKey) {
3021
3691
  ensureDir(RUNS_DIR);
3022
- const files = fs3.readdirSync(RUNS_DIR).filter((f) => f.endsWith(".state.json"));
3692
+ const files = fs5.readdirSync(RUNS_DIR).filter((f) => f.endsWith(".state.json"));
3023
3693
  const runs = [];
3024
3694
  for (const file of files) {
3025
3695
  try {
3026
- const content = fs3.readFileSync(path3.join(RUNS_DIR, file), "utf-8");
3696
+ const content = fs5.readFileSync(path6.join(RUNS_DIR, file), "utf-8");
3027
3697
  const state = JSON.parse(content);
3028
3698
  if (!flowKey || state.flowKey === flowKey) {
3029
3699
  runs.push(state);
@@ -3036,44 +3706,44 @@ var FlowRunner = class _FlowRunner {
3036
3706
  };
3037
3707
  function resolveFlowPath(keyOrPath) {
3038
3708
  if (keyOrPath.endsWith(".json")) {
3039
- return path3.resolve(keyOrPath);
3709
+ return path6.resolve(keyOrPath);
3040
3710
  }
3041
3711
  if (keyOrPath.includes("\\")) {
3042
- return path3.resolve(keyOrPath);
3712
+ return path6.resolve(keyOrPath);
3043
3713
  }
3044
3714
  if (keyOrPath.includes("/")) {
3045
- const nestedFolder = path3.resolve(FLOWS_DIR, keyOrPath, "flow.json");
3046
- if (fs3.existsSync(nestedFolder)) return nestedFolder;
3047
- const literal = path3.resolve(keyOrPath);
3048
- if (fs3.existsSync(literal)) return literal;
3715
+ const nestedFolder = path6.resolve(FLOWS_DIR, keyOrPath, "flow.json");
3716
+ if (fs5.existsSync(nestedFolder)) return nestedFolder;
3717
+ const literal = path6.resolve(keyOrPath);
3718
+ if (fs5.existsSync(literal)) return literal;
3049
3719
  return nestedFolder;
3050
3720
  }
3051
- const folderPath = path3.resolve(FLOWS_DIR, keyOrPath, "flow.json");
3052
- if (fs3.existsSync(folderPath)) return folderPath;
3053
- const legacyPath = path3.resolve(FLOWS_DIR, `${keyOrPath}.flow.json`);
3054
- if (fs3.existsSync(legacyPath)) return legacyPath;
3055
- const flowsDir = path3.resolve(FLOWS_DIR);
3056
- if (fs3.existsSync(flowsDir)) {
3057
- for (const entry of fs3.readdirSync(flowsDir, { withFileTypes: true })) {
3721
+ const folderPath = path6.resolve(FLOWS_DIR, keyOrPath, "flow.json");
3722
+ if (fs5.existsSync(folderPath)) return folderPath;
3723
+ const legacyPath = path6.resolve(FLOWS_DIR, `${keyOrPath}.flow.json`);
3724
+ if (fs5.existsSync(legacyPath)) return legacyPath;
3725
+ const flowsDir = path6.resolve(FLOWS_DIR);
3726
+ if (fs5.existsSync(flowsDir)) {
3727
+ for (const entry of fs5.readdirSync(flowsDir, { withFileTypes: true })) {
3058
3728
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
3059
- const nested = path3.join(flowsDir, entry.name, keyOrPath, "flow.json");
3060
- if (fs3.existsSync(nested)) return nested;
3729
+ const nested = path6.join(flowsDir, entry.name, keyOrPath, "flow.json");
3730
+ if (fs5.existsSync(nested)) return nested;
3061
3731
  }
3062
3732
  }
3063
3733
  return folderPath;
3064
3734
  }
3065
3735
  function getFlowRootDir(flowFilePath) {
3066
- const dir = path3.dirname(flowFilePath);
3067
- const base = path3.basename(flowFilePath);
3736
+ const dir = path6.dirname(flowFilePath);
3737
+ const base = path6.basename(flowFilePath);
3068
3738
  if (base === "flow.json") return dir;
3069
3739
  return dir;
3070
3740
  }
3071
3741
  function loadFlowWithMeta(keyOrPath) {
3072
3742
  const filePath = resolveFlowPath(keyOrPath);
3073
- if (!fs3.existsSync(filePath)) {
3743
+ if (!fs5.existsSync(filePath)) {
3074
3744
  throw new Error(`Flow not found: ${filePath}`);
3075
3745
  }
3076
- const content = fs3.readFileSync(filePath, "utf-8");
3746
+ const content = fs5.readFileSync(filePath, "utf-8");
3077
3747
  const flow = JSON.parse(content);
3078
3748
  return { flow, filePath, rootDir: getFlowRootDir(filePath) };
3079
3749
  }
@@ -3120,13 +3790,13 @@ function summarizeFlowInputs(flow) {
3120
3790
  }));
3121
3791
  }
3122
3792
  function listFlows() {
3123
- const flowsDir = path3.resolve(FLOWS_DIR);
3124
- if (!fs3.existsSync(flowsDir)) return [];
3793
+ const flowsDir = path6.resolve(FLOWS_DIR);
3794
+ if (!fs5.existsSync(flowsDir)) return [];
3125
3795
  const flows = [];
3126
3796
  const seenKeys = /* @__PURE__ */ new Set();
3127
3797
  const readFlowFile = (filePath, group) => {
3128
3798
  try {
3129
- const content = fs3.readFileSync(filePath, "utf-8");
3799
+ const content = fs5.readFileSync(filePath, "utf-8");
3130
3800
  const flow = JSON.parse(content);
3131
3801
  const nsKey = group ? `${group}/${flow.key}` : flow.key;
3132
3802
  if (seenKeys.has(nsKey)) return;
@@ -3138,7 +3808,7 @@ function listFlows() {
3138
3808
  inputCount: Object.keys(flow.inputs).length,
3139
3809
  stepCount: flow.steps.length,
3140
3810
  path: filePath,
3141
- layout: path3.basename(filePath) === "flow.json" ? "folder" : "legacy",
3811
+ layout: path6.basename(filePath) === "flow.json" ? "folder" : "legacy",
3142
3812
  group,
3143
3813
  stepTypes: collectStepTypes(flow),
3144
3814
  requiresBash: flowRequiresBash(flow),
@@ -3149,12 +3819,12 @@ function listFlows() {
3149
3819
  }
3150
3820
  };
3151
3821
  const scanDir = (dir, group) => {
3152
- for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
3822
+ for (const entry of fs5.readdirSync(dir, { withFileTypes: true })) {
3153
3823
  if (entry.name.startsWith(".")) continue;
3154
- const full = path3.join(dir, entry.name);
3824
+ const full = path6.join(dir, entry.name);
3155
3825
  if (entry.isDirectory()) {
3156
- const flowJson = path3.join(full, "flow.json");
3157
- if (fs3.existsSync(flowJson)) {
3826
+ const flowJson = path6.join(full, "flow.json");
3827
+ if (fs5.existsSync(flowJson)) {
3158
3828
  readFlowFile(flowJson, group);
3159
3829
  } else {
3160
3830
  if (!group) {
@@ -3172,7 +3842,7 @@ function listFlows() {
3172
3842
  function saveFlow(flow, outputPath, group) {
3173
3843
  let flowPath;
3174
3844
  if (outputPath) {
3175
- flowPath = path3.resolve(outputPath);
3845
+ flowPath = path6.resolve(outputPath);
3176
3846
  } else {
3177
3847
  let bareKey = flow.key;
3178
3848
  let resolvedGroup = group;
@@ -3182,25 +3852,36 @@ function saveFlow(flow, outputPath, group) {
3182
3852
  resolvedGroup = resolvedGroup || parts.join("/");
3183
3853
  flow.key = bareKey;
3184
3854
  }
3185
- const basePath = resolvedGroup ? path3.resolve(FLOWS_DIR, resolvedGroup, bareKey) : path3.resolve(FLOWS_DIR, bareKey);
3186
- const legacyPath = path3.resolve(FLOWS_DIR, `${bareKey}.flow.json`);
3187
- const folderPath = path3.join(basePath, "flow.json");
3188
- if (!resolvedGroup && fs3.existsSync(legacyPath) && !fs3.existsSync(folderPath)) {
3855
+ const basePath = resolvedGroup ? path6.resolve(FLOWS_DIR, resolvedGroup, bareKey) : path6.resolve(FLOWS_DIR, bareKey);
3856
+ const legacyPath = path6.resolve(FLOWS_DIR, `${bareKey}.flow.json`);
3857
+ const folderPath = path6.join(basePath, "flow.json");
3858
+ if (!resolvedGroup && fs5.existsSync(legacyPath) && !fs5.existsSync(folderPath)) {
3189
3859
  flowPath = legacyPath;
3190
3860
  } else {
3191
3861
  flowPath = folderPath;
3192
3862
  }
3193
3863
  }
3194
- const dir = path3.dirname(flowPath);
3864
+ const dir = path6.dirname(flowPath);
3195
3865
  ensureDir(dir);
3196
- if (path3.basename(flowPath) === "flow.json") {
3197
- ensureDir(path3.join(dir, "lib"));
3866
+ if (path6.basename(flowPath) === "flow.json") {
3867
+ ensureDir(path6.join(dir, "lib"));
3198
3868
  }
3199
- fs3.writeFileSync(flowPath, JSON.stringify(flow, null, 2) + "\n");
3869
+ fs5.writeFileSync(flowPath, JSON.stringify(flow, null, 2) + "\n");
3200
3870
  return flowPath;
3201
3871
  }
3202
3872
 
3203
3873
  export {
3874
+ installMcpConfig,
3875
+ getAgentStatuses,
3876
+ cliVersion,
3877
+ recordCommand,
3878
+ flushUsageRollups,
3879
+ drainQueue,
3880
+ flush,
3881
+ maybeShowTelemetryNotice,
3882
+ collectInstallContext,
3883
+ installContextToParams,
3884
+ describeInstallContext,
3204
3885
  ApiError,
3205
3886
  OneApi,
3206
3887
  TimeoutError,