@withone/cli 2.5.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.
- package/README.md +2 -0
- package/dist/{chunk-MRQ47KSZ.js → chunk-4KX5ZERS.js} +783 -116
- package/dist/{chunk-7FPQOAQ4.js → chunk-O3HDWHHS.js} +5 -3
- package/dist/{chunk-XKUTG7JD.js → chunk-PUQKTRYC.js} +5 -3
- package/dist/chunk-Q6PWTATS.js +79 -0
- package/dist/{chunk-NOFJ4NYD.js → chunk-VMFWA43Q.js} +5 -77
- package/dist/{flow-runner-4A5ANCTT.js → flow-runner-FME4AVVN.js} +2 -1
- package/dist/index.js +692 -1316
- package/dist/{migrate-3LSEIPRF.js → migrate-KWEEEQ3E.js} +3 -2
- package/dist/{schema-FICQRMZ7.js → schema-YJ7JTGFL.js} +5 -3
- package/dist/{sql-AKQSXRV2.js → sql-NDMMFQP6.js} +3 -2
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
|
12
|
-
import
|
|
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 {
|
|
20
|
-
|
|
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(
|
|
78
|
-
return this.requestFull({ path:
|
|
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}`;
|
|
@@ -314,6 +967,9 @@ var OneApi = class {
|
|
|
314
967
|
"x-one-action-id": action._id,
|
|
315
968
|
"Content-Type": contentType,
|
|
316
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(),
|
|
317
973
|
...args.headers
|
|
318
974
|
};
|
|
319
975
|
const finalActionPath = args.pathVariables ? replacePathVariables(action.path, args.pathVariables) : action.path;
|
|
@@ -522,9 +1178,9 @@ var TimeoutError = class extends Error {
|
|
|
522
1178
|
function sleep(ms) {
|
|
523
1179
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
524
1180
|
}
|
|
525
|
-
function replacePathVariables(
|
|
526
|
-
if (!
|
|
527
|
-
let result =
|
|
1181
|
+
function replacePathVariables(path7, variables) {
|
|
1182
|
+
if (!path7) return path7;
|
|
1183
|
+
let result = path7;
|
|
528
1184
|
result = result.replace(/\{\{([^}]+)\}\}/g, (_match, variable) => {
|
|
529
1185
|
const trimmedVariable = variable.trim();
|
|
530
1186
|
const value = variables[trimmedVariable];
|
|
@@ -558,23 +1214,23 @@ function isActionAllowed(actionId, allowedActionIds) {
|
|
|
558
1214
|
}
|
|
559
1215
|
|
|
560
1216
|
// src/lib/cache.ts
|
|
561
|
-
import
|
|
562
|
-
import
|
|
1217
|
+
import fs3 from "fs";
|
|
1218
|
+
import path4 from "path";
|
|
563
1219
|
function knowledgeDir() {
|
|
564
|
-
return
|
|
1220
|
+
return path4.join(homeDir(), ".one", "cache", "knowledge");
|
|
565
1221
|
}
|
|
566
1222
|
function legacySearchDir() {
|
|
567
|
-
return
|
|
1223
|
+
return path4.join(homeDir(), ".one", "cache", "search");
|
|
568
1224
|
}
|
|
569
1225
|
function sanitizeFilename(input) {
|
|
570
1226
|
return input.replace(/[^a-zA-Z0-9_\-\.]/g, "_");
|
|
571
1227
|
}
|
|
572
1228
|
function knowledgeCachePath(actionId) {
|
|
573
|
-
return
|
|
1229
|
+
return path4.join(knowledgeDir(), `${sanitizeFilename(actionId)}.json`);
|
|
574
1230
|
}
|
|
575
1231
|
function readCache(filePath) {
|
|
576
1232
|
try {
|
|
577
|
-
const content =
|
|
1233
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
578
1234
|
return JSON.parse(content);
|
|
579
1235
|
} catch {
|
|
580
1236
|
return null;
|
|
@@ -582,9 +1238,9 @@ function readCache(filePath) {
|
|
|
582
1238
|
}
|
|
583
1239
|
function writeCache(filePath, entry) {
|
|
584
1240
|
try {
|
|
585
|
-
const dir =
|
|
586
|
-
|
|
587
|
-
|
|
1241
|
+
const dir = path4.dirname(filePath);
|
|
1242
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
1243
|
+
fs3.writeFileSync(filePath, JSON.stringify(entry, null, 2));
|
|
588
1244
|
} catch {
|
|
589
1245
|
}
|
|
590
1246
|
}
|
|
@@ -611,9 +1267,9 @@ function formatAge(seconds) {
|
|
|
611
1267
|
function listCacheEntries() {
|
|
612
1268
|
const entries = [];
|
|
613
1269
|
try {
|
|
614
|
-
for (const file of
|
|
1270
|
+
for (const file of fs3.readdirSync(knowledgeDir())) {
|
|
615
1271
|
if (!file.endsWith(".json")) continue;
|
|
616
|
-
const filePath =
|
|
1272
|
+
const filePath = path4.join(knowledgeDir(), file);
|
|
617
1273
|
const entry = readCache(filePath);
|
|
618
1274
|
if (entry) {
|
|
619
1275
|
entries.push({ type: "knowledge", filePath, entry });
|
|
@@ -627,12 +1283,12 @@ function clearAll() {
|
|
|
627
1283
|
let count = 0;
|
|
628
1284
|
for (const dir of [knowledgeDir(), legacySearchDir()]) {
|
|
629
1285
|
try {
|
|
630
|
-
const files =
|
|
1286
|
+
const files = fs3.readdirSync(dir);
|
|
631
1287
|
for (const file of files) {
|
|
632
|
-
|
|
1288
|
+
fs3.unlinkSync(path4.join(dir, file));
|
|
633
1289
|
count++;
|
|
634
1290
|
}
|
|
635
|
-
|
|
1291
|
+
fs3.rmdirSync(dir);
|
|
636
1292
|
} catch {
|
|
637
1293
|
}
|
|
638
1294
|
}
|
|
@@ -641,7 +1297,7 @@ function clearAll() {
|
|
|
641
1297
|
function clearEntry(actionId) {
|
|
642
1298
|
const filePath = knowledgeCachePath(actionId);
|
|
643
1299
|
try {
|
|
644
|
-
|
|
1300
|
+
fs3.unlinkSync(filePath);
|
|
645
1301
|
return true;
|
|
646
1302
|
} catch {
|
|
647
1303
|
return false;
|
|
@@ -669,27 +1325,27 @@ async function resolveActionDetails(api, actionId, opts = {}) {
|
|
|
669
1325
|
});
|
|
670
1326
|
const cachePath = knowledgeCachePath(actionId);
|
|
671
1327
|
const raw = useCache ? readCache(cachePath) : null;
|
|
672
|
-
const
|
|
673
|
-
if (
|
|
674
|
-
return { details:
|
|
1328
|
+
const cached2 = isActionDetailsEntry(raw) ? raw : null;
|
|
1329
|
+
if (cached2 && isFresh(cached2)) {
|
|
1330
|
+
return { details: cached2.data, cacheHit: true, stale: false, entry: cached2 };
|
|
675
1331
|
}
|
|
676
1332
|
try {
|
|
677
|
-
const result = await api.getActionDetailsWithMeta(actionId,
|
|
678
|
-
if (result.status === 304 &&
|
|
679
|
-
|
|
680
|
-
writeCache(cachePath,
|
|
681
|
-
return { details:
|
|
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 };
|
|
682
1338
|
}
|
|
683
1339
|
const entry = makeCacheEntry(actionId, result.data, result.etag);
|
|
684
1340
|
writeCache(cachePath, entry);
|
|
685
1341
|
return { details: result.data, cacheHit: false, stale: false, entry };
|
|
686
1342
|
} catch (fetchError) {
|
|
687
|
-
if (
|
|
1343
|
+
if (cached2) {
|
|
688
1344
|
warn(
|
|
689
|
-
`Warning: serving cached action details (network unavailable, cached ${formatAge(getAge(
|
|
1345
|
+
`Warning: serving cached action details (network unavailable, cached ${formatAge(getAge(cached2))} ago)
|
|
690
1346
|
`
|
|
691
1347
|
);
|
|
692
|
-
return { details:
|
|
1348
|
+
return { details: cached2.data, cacheHit: true, stale: true, entry: cached2 };
|
|
693
1349
|
}
|
|
694
1350
|
throw fetchError;
|
|
695
1351
|
}
|
|
@@ -1564,48 +2220,48 @@ function collectFileReadViolations(value, schema, basePath, out) {
|
|
|
1564
2220
|
const obj = value;
|
|
1565
2221
|
for (const [field, raw] of Object.entries(schema)) {
|
|
1566
2222
|
const rule = asFieldSchema(raw);
|
|
1567
|
-
const
|
|
2223
|
+
const path7 = `${basePath}${field}`;
|
|
1568
2224
|
const present = Object.prototype.hasOwnProperty.call(obj, field) && obj[field] !== void 0;
|
|
1569
2225
|
if (!present) {
|
|
1570
|
-
if (rule.required) out.push(`field "${
|
|
2226
|
+
if (rule.required) out.push(`field "${path7}" is required but missing`);
|
|
1571
2227
|
continue;
|
|
1572
2228
|
}
|
|
1573
|
-
checkFileReadRule(obj[field], rule,
|
|
2229
|
+
checkFileReadRule(obj[field], rule, path7, out);
|
|
1574
2230
|
}
|
|
1575
2231
|
}
|
|
1576
|
-
function checkFileReadRule(value, rule,
|
|
2232
|
+
function checkFileReadRule(value, rule, path7, out) {
|
|
1577
2233
|
if (rule.type && !matchesLeafType(value, rule.type)) {
|
|
1578
|
-
out.push(`field "${
|
|
2234
|
+
out.push(`field "${path7}" expected ${rule.type} but got ${actualType(value)}`);
|
|
1579
2235
|
return;
|
|
1580
2236
|
}
|
|
1581
2237
|
if (Array.isArray(rule.enum) && !rule.enum.some((e) => e === value)) {
|
|
1582
|
-
out.push(`field "${
|
|
2238
|
+
out.push(`field "${path7}" value ${JSON.stringify(value)} is not one of [${rule.enum.map((e) => JSON.stringify(e)).join(", ")}]`);
|
|
1583
2239
|
}
|
|
1584
2240
|
const hasArrayConstraint = rule.items !== void 0 || rule.minItems !== void 0 || rule.maxItems !== void 0 || rule.length !== void 0;
|
|
1585
2241
|
if (hasArrayConstraint) {
|
|
1586
2242
|
if (!Array.isArray(value)) {
|
|
1587
|
-
if (!rule.type) out.push(`field "${
|
|
2243
|
+
if (!rule.type) out.push(`field "${path7}" expected array but got ${actualType(value)}`);
|
|
1588
2244
|
} else {
|
|
1589
2245
|
if (rule.length !== void 0 && value.length !== rule.length) {
|
|
1590
|
-
out.push(`field "${
|
|
2246
|
+
out.push(`field "${path7}" expected array of length ${rule.length} but got length ${value.length}`);
|
|
1591
2247
|
}
|
|
1592
2248
|
if (rule.minItems !== void 0 && value.length < rule.minItems) {
|
|
1593
|
-
out.push(`field "${
|
|
2249
|
+
out.push(`field "${path7}" expected at least ${rule.minItems} item${rule.minItems === 1 ? "" : "s"} but got ${value.length}`);
|
|
1594
2250
|
}
|
|
1595
2251
|
if (rule.maxItems !== void 0 && value.length > rule.maxItems) {
|
|
1596
|
-
out.push(`field "${
|
|
2252
|
+
out.push(`field "${path7}" expected at most ${rule.maxItems} item${rule.maxItems === 1 ? "" : "s"} but got ${value.length}`);
|
|
1597
2253
|
}
|
|
1598
2254
|
if (rule.items !== void 0) {
|
|
1599
2255
|
const itemRule = asFieldSchema(rule.items);
|
|
1600
|
-
value.forEach((el, i) => checkFileReadRule(el, itemRule, `${
|
|
2256
|
+
value.forEach((el, i) => checkFileReadRule(el, itemRule, `${path7}[${i}]`, out));
|
|
1601
2257
|
}
|
|
1602
2258
|
}
|
|
1603
2259
|
}
|
|
1604
2260
|
if (rule.properties) {
|
|
1605
2261
|
if (isPlainObjectValue(value)) {
|
|
1606
|
-
collectFileReadViolations(value, rule.properties, `${
|
|
2262
|
+
collectFileReadViolations(value, rule.properties, `${path7}.`, out);
|
|
1607
2263
|
} else if (!rule.type) {
|
|
1608
|
-
out.push(`field "${
|
|
2264
|
+
out.push(`field "${path7}" expected object but got ${actualType(value)}`);
|
|
1609
2265
|
}
|
|
1610
2266
|
}
|
|
1611
2267
|
}
|
|
@@ -2083,15 +2739,15 @@ async function executeCodeModule(stepId, modulePath, context, options) {
|
|
|
2083
2739
|
if (!rootDir) {
|
|
2084
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.`);
|
|
2085
2741
|
}
|
|
2086
|
-
if (
|
|
2742
|
+
if (path5.isAbsolute(modulePath)) {
|
|
2087
2743
|
throw new Error(`Code module path must be relative to the flow root, got absolute: "${modulePath}"`);
|
|
2088
2744
|
}
|
|
2089
|
-
const absPath =
|
|
2090
|
-
const relFromRoot =
|
|
2091
|
-
if (relFromRoot.startsWith("..") ||
|
|
2745
|
+
const absPath = path5.resolve(rootDir, modulePath);
|
|
2746
|
+
const relFromRoot = path5.relative(rootDir, absPath);
|
|
2747
|
+
if (relFromRoot.startsWith("..") || path5.isAbsolute(relFromRoot)) {
|
|
2092
2748
|
throw new Error(`Code module "${modulePath}" resolves outside the flow directory`);
|
|
2093
2749
|
}
|
|
2094
|
-
if (!
|
|
2750
|
+
if (!fs4.existsSync(absPath)) {
|
|
2095
2751
|
throw new Error(`Code module not found: ${absPath}`);
|
|
2096
2752
|
}
|
|
2097
2753
|
const { env: _omitEnv, ...safeContext } = context;
|
|
@@ -2239,8 +2895,8 @@ async function executeParallelStep(step, context, api, permissions, allowedActio
|
|
|
2239
2895
|
function executeFileReadStep(step, context) {
|
|
2240
2896
|
const config = step.fileRead;
|
|
2241
2897
|
const filePath = resolveValue(config.path, context);
|
|
2242
|
-
const resolvedPath =
|
|
2243
|
-
const content =
|
|
2898
|
+
const resolvedPath = path5.resolve(filePath);
|
|
2899
|
+
const content = fs4.readFileSync(resolvedPath, "utf-8");
|
|
2244
2900
|
const output = config.parseJson ? JSON.parse(stripCodeFences(content)) : content;
|
|
2245
2901
|
if (config.parseJson && config.schema) {
|
|
2246
2902
|
assertMatchesFileReadSchema(output, config.schema, step.id);
|
|
@@ -2251,16 +2907,16 @@ function executeFileWriteStep(step, context) {
|
|
|
2251
2907
|
const config = step.fileWrite;
|
|
2252
2908
|
const filePath = resolveValue(config.path, context);
|
|
2253
2909
|
const content = resolveValue(config.content, context);
|
|
2254
|
-
const resolvedPath =
|
|
2255
|
-
const dir =
|
|
2256
|
-
if (!
|
|
2257
|
-
|
|
2910
|
+
const resolvedPath = path5.resolve(filePath);
|
|
2911
|
+
const dir = path5.dirname(resolvedPath);
|
|
2912
|
+
if (!fs4.existsSync(dir)) {
|
|
2913
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
2258
2914
|
}
|
|
2259
2915
|
const stringContent = typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
2260
2916
|
if (config.append) {
|
|
2261
|
-
|
|
2917
|
+
fs4.appendFileSync(resolvedPath, stringContent);
|
|
2262
2918
|
} else {
|
|
2263
|
-
|
|
2919
|
+
fs4.writeFileSync(resolvedPath, stringContent);
|
|
2264
2920
|
}
|
|
2265
2921
|
return { status: "success", output: { path: resolvedPath, bytesWritten: stringContent.length }, response: { path: resolvedPath } };
|
|
2266
2922
|
}
|
|
@@ -2299,7 +2955,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
|
|
|
2299
2955
|
if (flowStack.includes(resolvedKey)) {
|
|
2300
2956
|
throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
|
|
2301
2957
|
}
|
|
2302
|
-
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-
|
|
2958
|
+
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-FME4AVVN.js");
|
|
2303
2959
|
const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
|
|
2304
2960
|
const subContext = await executeFlow(
|
|
2305
2961
|
subFlow,
|
|
@@ -2402,11 +3058,11 @@ function resolveBashEnv(envConfig, context, stepId) {
|
|
|
2402
3058
|
if ("json" in obj) {
|
|
2403
3059
|
const resolved2 = resolveValue(obj.json, context);
|
|
2404
3060
|
const json = JSON.stringify(resolved2 ?? null);
|
|
2405
|
-
const tmp =
|
|
2406
|
-
|
|
3061
|
+
const tmp = path5.join(
|
|
3062
|
+
os2.tmpdir(),
|
|
2407
3063
|
`one-flow-${stepId}-${key}-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`
|
|
2408
3064
|
);
|
|
2409
|
-
|
|
3065
|
+
fs4.writeFileSync(tmp, json, { encoding: "utf-8" });
|
|
2410
3066
|
tempFiles.push(tmp);
|
|
2411
3067
|
out[key] = tmp;
|
|
2412
3068
|
continue;
|
|
@@ -2453,7 +3109,7 @@ async function executeBashStep(step, context, options) {
|
|
|
2453
3109
|
} finally {
|
|
2454
3110
|
for (const tmp of tempFiles) {
|
|
2455
3111
|
try {
|
|
2456
|
-
|
|
3112
|
+
fs4.unlinkSync(tmp);
|
|
2457
3113
|
} catch {
|
|
2458
3114
|
}
|
|
2459
3115
|
}
|
|
@@ -2843,8 +3499,8 @@ var FLOWS_DIR = ".one/flows";
|
|
|
2843
3499
|
var RUNS_DIR = ".one/flows/.runs";
|
|
2844
3500
|
var LOGS_DIR = ".one/flows/.logs";
|
|
2845
3501
|
function ensureDir(dir) {
|
|
2846
|
-
if (!
|
|
2847
|
-
|
|
3502
|
+
if (!fs5.existsSync(dir)) {
|
|
3503
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
2848
3504
|
}
|
|
2849
3505
|
}
|
|
2850
3506
|
function generateRunId() {
|
|
@@ -2865,8 +3521,8 @@ var FlowRunner = class _FlowRunner {
|
|
|
2865
3521
|
this.flowKey = flow.key;
|
|
2866
3522
|
ensureDir(RUNS_DIR);
|
|
2867
3523
|
ensureDir(LOGS_DIR);
|
|
2868
|
-
this.statePath =
|
|
2869
|
-
this.logPath =
|
|
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`);
|
|
2870
3526
|
const resolvedInputs = { ...inputs };
|
|
2871
3527
|
for (const [name, decl] of Object.entries(flow.inputs)) {
|
|
2872
3528
|
if (resolvedInputs[name] === void 0 && decl.default !== void 0) {
|
|
@@ -2907,10 +3563,10 @@ var FlowRunner = class _FlowRunner {
|
|
|
2907
3563
|
msg,
|
|
2908
3564
|
...data
|
|
2909
3565
|
};
|
|
2910
|
-
|
|
3566
|
+
fs5.appendFileSync(this.logPath, JSON.stringify(entry) + "\n");
|
|
2911
3567
|
}
|
|
2912
3568
|
saveState() {
|
|
2913
|
-
|
|
3569
|
+
fs5.writeFileSync(this.statePath, JSON.stringify(this.state, stripStepsAlias, 2));
|
|
2914
3570
|
}
|
|
2915
3571
|
createEventHandler(externalHandler) {
|
|
2916
3572
|
return (event) => {
|
|
@@ -3008,14 +3664,14 @@ var FlowRunner = class _FlowRunner {
|
|
|
3008
3664
|
}
|
|
3009
3665
|
/** Path to a run's persisted state file. The post-mortem artifact surfaced by `one flow inspect`. */
|
|
3010
3666
|
static statePathFor(flowKey, runId) {
|
|
3011
|
-
return
|
|
3667
|
+
return path6.join(RUNS_DIR, `${flowKey}-${runId}.state.json`);
|
|
3012
3668
|
}
|
|
3013
3669
|
static loadRunState(runId) {
|
|
3014
3670
|
ensureDir(RUNS_DIR);
|
|
3015
|
-
const files =
|
|
3671
|
+
const files = fs5.readdirSync(RUNS_DIR).filter((f) => f.includes(runId) && f.endsWith(".state.json"));
|
|
3016
3672
|
if (files.length === 0) return null;
|
|
3017
3673
|
try {
|
|
3018
|
-
const content =
|
|
3674
|
+
const content = fs5.readFileSync(path6.join(RUNS_DIR, files[0]), "utf-8");
|
|
3019
3675
|
return JSON.parse(content);
|
|
3020
3676
|
} catch {
|
|
3021
3677
|
return null;
|
|
@@ -3027,17 +3683,17 @@ var FlowRunner = class _FlowRunner {
|
|
|
3027
3683
|
runner.flowKey = state.flowKey;
|
|
3028
3684
|
runner.state = state;
|
|
3029
3685
|
runner.paused = false;
|
|
3030
|
-
runner.statePath =
|
|
3031
|
-
runner.logPath =
|
|
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`);
|
|
3032
3688
|
return runner;
|
|
3033
3689
|
}
|
|
3034
3690
|
static listRuns(flowKey) {
|
|
3035
3691
|
ensureDir(RUNS_DIR);
|
|
3036
|
-
const files =
|
|
3692
|
+
const files = fs5.readdirSync(RUNS_DIR).filter((f) => f.endsWith(".state.json"));
|
|
3037
3693
|
const runs = [];
|
|
3038
3694
|
for (const file of files) {
|
|
3039
3695
|
try {
|
|
3040
|
-
const content =
|
|
3696
|
+
const content = fs5.readFileSync(path6.join(RUNS_DIR, file), "utf-8");
|
|
3041
3697
|
const state = JSON.parse(content);
|
|
3042
3698
|
if (!flowKey || state.flowKey === flowKey) {
|
|
3043
3699
|
runs.push(state);
|
|
@@ -3050,44 +3706,44 @@ var FlowRunner = class _FlowRunner {
|
|
|
3050
3706
|
};
|
|
3051
3707
|
function resolveFlowPath(keyOrPath) {
|
|
3052
3708
|
if (keyOrPath.endsWith(".json")) {
|
|
3053
|
-
return
|
|
3709
|
+
return path6.resolve(keyOrPath);
|
|
3054
3710
|
}
|
|
3055
3711
|
if (keyOrPath.includes("\\")) {
|
|
3056
|
-
return
|
|
3712
|
+
return path6.resolve(keyOrPath);
|
|
3057
3713
|
}
|
|
3058
3714
|
if (keyOrPath.includes("/")) {
|
|
3059
|
-
const nestedFolder =
|
|
3060
|
-
if (
|
|
3061
|
-
const literal =
|
|
3062
|
-
if (
|
|
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;
|
|
3063
3719
|
return nestedFolder;
|
|
3064
3720
|
}
|
|
3065
|
-
const folderPath =
|
|
3066
|
-
if (
|
|
3067
|
-
const legacyPath =
|
|
3068
|
-
if (
|
|
3069
|
-
const flowsDir =
|
|
3070
|
-
if (
|
|
3071
|
-
for (const entry of
|
|
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 })) {
|
|
3072
3728
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
3073
|
-
const nested =
|
|
3074
|
-
if (
|
|
3729
|
+
const nested = path6.join(flowsDir, entry.name, keyOrPath, "flow.json");
|
|
3730
|
+
if (fs5.existsSync(nested)) return nested;
|
|
3075
3731
|
}
|
|
3076
3732
|
}
|
|
3077
3733
|
return folderPath;
|
|
3078
3734
|
}
|
|
3079
3735
|
function getFlowRootDir(flowFilePath) {
|
|
3080
|
-
const dir =
|
|
3081
|
-
const base =
|
|
3736
|
+
const dir = path6.dirname(flowFilePath);
|
|
3737
|
+
const base = path6.basename(flowFilePath);
|
|
3082
3738
|
if (base === "flow.json") return dir;
|
|
3083
3739
|
return dir;
|
|
3084
3740
|
}
|
|
3085
3741
|
function loadFlowWithMeta(keyOrPath) {
|
|
3086
3742
|
const filePath = resolveFlowPath(keyOrPath);
|
|
3087
|
-
if (!
|
|
3743
|
+
if (!fs5.existsSync(filePath)) {
|
|
3088
3744
|
throw new Error(`Flow not found: ${filePath}`);
|
|
3089
3745
|
}
|
|
3090
|
-
const content =
|
|
3746
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
3091
3747
|
const flow = JSON.parse(content);
|
|
3092
3748
|
return { flow, filePath, rootDir: getFlowRootDir(filePath) };
|
|
3093
3749
|
}
|
|
@@ -3134,13 +3790,13 @@ function summarizeFlowInputs(flow) {
|
|
|
3134
3790
|
}));
|
|
3135
3791
|
}
|
|
3136
3792
|
function listFlows() {
|
|
3137
|
-
const flowsDir =
|
|
3138
|
-
if (!
|
|
3793
|
+
const flowsDir = path6.resolve(FLOWS_DIR);
|
|
3794
|
+
if (!fs5.existsSync(flowsDir)) return [];
|
|
3139
3795
|
const flows = [];
|
|
3140
3796
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
3141
3797
|
const readFlowFile = (filePath, group) => {
|
|
3142
3798
|
try {
|
|
3143
|
-
const content =
|
|
3799
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
3144
3800
|
const flow = JSON.parse(content);
|
|
3145
3801
|
const nsKey = group ? `${group}/${flow.key}` : flow.key;
|
|
3146
3802
|
if (seenKeys.has(nsKey)) return;
|
|
@@ -3152,7 +3808,7 @@ function listFlows() {
|
|
|
3152
3808
|
inputCount: Object.keys(flow.inputs).length,
|
|
3153
3809
|
stepCount: flow.steps.length,
|
|
3154
3810
|
path: filePath,
|
|
3155
|
-
layout:
|
|
3811
|
+
layout: path6.basename(filePath) === "flow.json" ? "folder" : "legacy",
|
|
3156
3812
|
group,
|
|
3157
3813
|
stepTypes: collectStepTypes(flow),
|
|
3158
3814
|
requiresBash: flowRequiresBash(flow),
|
|
@@ -3163,12 +3819,12 @@ function listFlows() {
|
|
|
3163
3819
|
}
|
|
3164
3820
|
};
|
|
3165
3821
|
const scanDir = (dir, group) => {
|
|
3166
|
-
for (const entry of
|
|
3822
|
+
for (const entry of fs5.readdirSync(dir, { withFileTypes: true })) {
|
|
3167
3823
|
if (entry.name.startsWith(".")) continue;
|
|
3168
|
-
const full =
|
|
3824
|
+
const full = path6.join(dir, entry.name);
|
|
3169
3825
|
if (entry.isDirectory()) {
|
|
3170
|
-
const flowJson =
|
|
3171
|
-
if (
|
|
3826
|
+
const flowJson = path6.join(full, "flow.json");
|
|
3827
|
+
if (fs5.existsSync(flowJson)) {
|
|
3172
3828
|
readFlowFile(flowJson, group);
|
|
3173
3829
|
} else {
|
|
3174
3830
|
if (!group) {
|
|
@@ -3186,7 +3842,7 @@ function listFlows() {
|
|
|
3186
3842
|
function saveFlow(flow, outputPath, group) {
|
|
3187
3843
|
let flowPath;
|
|
3188
3844
|
if (outputPath) {
|
|
3189
|
-
flowPath =
|
|
3845
|
+
flowPath = path6.resolve(outputPath);
|
|
3190
3846
|
} else {
|
|
3191
3847
|
let bareKey = flow.key;
|
|
3192
3848
|
let resolvedGroup = group;
|
|
@@ -3196,25 +3852,36 @@ function saveFlow(flow, outputPath, group) {
|
|
|
3196
3852
|
resolvedGroup = resolvedGroup || parts.join("/");
|
|
3197
3853
|
flow.key = bareKey;
|
|
3198
3854
|
}
|
|
3199
|
-
const basePath = resolvedGroup ?
|
|
3200
|
-
const legacyPath =
|
|
3201
|
-
const folderPath =
|
|
3202
|
-
if (!resolvedGroup &&
|
|
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)) {
|
|
3203
3859
|
flowPath = legacyPath;
|
|
3204
3860
|
} else {
|
|
3205
3861
|
flowPath = folderPath;
|
|
3206
3862
|
}
|
|
3207
3863
|
}
|
|
3208
|
-
const dir =
|
|
3864
|
+
const dir = path6.dirname(flowPath);
|
|
3209
3865
|
ensureDir(dir);
|
|
3210
|
-
if (
|
|
3211
|
-
ensureDir(
|
|
3866
|
+
if (path6.basename(flowPath) === "flow.json") {
|
|
3867
|
+
ensureDir(path6.join(dir, "lib"));
|
|
3212
3868
|
}
|
|
3213
|
-
|
|
3869
|
+
fs5.writeFileSync(flowPath, JSON.stringify(flow, null, 2) + "\n");
|
|
3214
3870
|
return flowPath;
|
|
3215
3871
|
}
|
|
3216
3872
|
|
|
3217
3873
|
export {
|
|
3874
|
+
installMcpConfig,
|
|
3875
|
+
getAgentStatuses,
|
|
3876
|
+
cliVersion,
|
|
3877
|
+
recordCommand,
|
|
3878
|
+
flushUsageRollups,
|
|
3879
|
+
drainQueue,
|
|
3880
|
+
flush,
|
|
3881
|
+
maybeShowTelemetryNotice,
|
|
3882
|
+
collectInstallContext,
|
|
3883
|
+
installContextToParams,
|
|
3884
|
+
describeInstallContext,
|
|
3218
3885
|
ApiError,
|
|
3219
3886
|
OneApi,
|
|
3220
3887
|
TimeoutError,
|