@withone/cli 1.46.0 → 1.47.1

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 CHANGED
@@ -488,6 +488,9 @@ Press Ctrl+C during execution to pause - the run can be resumed later with `one
488
488
  | `--skip-validation` | Skip input validation against action schemas |
489
489
  | `--allow-bash` | Allow bash step execution (disabled by default for security) |
490
490
  | `-v, --verbose` | Show full request/response for each step |
491
+ | `--output-file <path>` | Stream the full result to a file instead of stdout — for large results that would otherwise be truncated or exceed the JSON string-size limit. stdout (and `--agent` output) then carries an `outputFile` pointer instead of inline `steps`. |
492
+
493
+ Step-level `if`/`unless` conditions (and `while`/`condition` steps) are null-safe: a condition that references a skipped or not-yet-run step's output (e.g. `$.steps.maybeSkipped.output.x`) evaluates to `false` rather than crashing the run.
491
494
 
492
495
  ### `one flow list`
493
496
 
@@ -566,7 +569,7 @@ Run `one update` manually whenever you want to upgrade.
566
569
 
567
570
  ### Telemetry
568
571
 
569
- The CLI collects **usage analytics** — which command was run, the CLI version, OS/arch, and whether it ran in `--agent` mode — to help us prioritize improvements. Events are linked to your One account (user id, email, name, org) so they line up with your dashboard activity. **Command arguments, inputs, connection data, and secrets are never collected** (only the command name, e.g. `actions execute`). A one-time notice is shown on first run. Events are queued locally and sent in the background, so telemetry never slows down or blocks a command.
572
+ The CLI collects **usage analytics** — which command was run, the CLI version, OS/arch, and whether it ran in `--agent` mode — to help us prioritize improvements. Events are linked to your One account (user id, email, name, org) so they line up with your dashboard activity. **Command arguments, inputs, connection data, and secrets are never collected** (only the command name, e.g. `actions execute`). A one-time notice is shown on first run. Commands are aggregated locally and sent as periodic, batched roll-ups in the background, so telemetry never slows down or blocks a command (and stays lightweight even under heavy automation).
570
573
 
571
574
  To opt out, set any of:
572
575
 
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  getMemoryConfigOrDefault
3
- } from "./chunk-BKD7A42U.js";
3
+ } from "./chunk-TXTRXV74.js";
4
4
  import {
5
5
  getOpenAiApiKey,
6
6
  readConfig
7
- } from "./chunk-OEN5DNG7.js";
7
+ } from "./chunk-K6MWE2ZH.js";
8
8
 
9
9
  // src/lib/output.ts
10
10
  import * as p from "@clack/prompts";
@@ -15,6 +15,14 @@ function setAgentMode(value) {
15
15
  function isAgentMode() {
16
16
  return _agentMode || process.env.ONE_AGENT === "1";
17
17
  }
18
+ function silenceWarningsInAgentMode() {
19
+ const agent = process.argv.includes("--agent") || process.env.ONE_AGENT === "1";
20
+ if (!agent) return;
21
+ process.env.NODE_NO_WARNINGS = "1";
22
+ process.removeAllListeners("warning");
23
+ process.emitWarning = (() => {
24
+ });
25
+ }
18
26
  function createSpinner() {
19
27
  if (isAgentMode()) {
20
28
  return { start() {
@@ -141,6 +149,7 @@ function semanticSearchUpgradeLine(opts = {}) {
141
149
  export {
142
150
  setAgentMode,
143
151
  isAgentMode,
152
+ silenceWarningsInAgentMode,
144
153
  createSpinner,
145
154
  intro2 as intro,
146
155
  outro2 as outro,
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-44CV5IMX.js";
5
5
  import {
6
6
  getCacheTtl
7
- } from "./chunk-OEN5DNG7.js";
7
+ } from "./chunk-K6MWE2ZH.js";
8
8
 
9
9
  // src/lib/flow-runner.ts
10
10
  import fs3 from "fs";
@@ -902,6 +902,14 @@ function evaluateExpression(expr, context) {
902
902
  const fn = new Function("$", `return (${expr})`);
903
903
  return fn(context);
904
904
  }
905
+ function evaluateCondition(expr, context) {
906
+ try {
907
+ return Boolean(evaluateExpression(expr, context));
908
+ } catch (err) {
909
+ if (err instanceof TypeError) return false;
910
+ throw err;
911
+ }
912
+ }
905
913
  var ALLOWED_MODULES = {
906
914
  buffer: () => import("buffer"),
907
915
  crypto: () => import("crypto"),
@@ -1106,7 +1114,7 @@ async function executeCodeModule(stepId, modulePath, context, options) {
1106
1114
  }
1107
1115
  async function executeConditionStep(step, context, api, permissions, allowedActionIds, options, flowStack) {
1108
1116
  const condition = step.condition;
1109
- const result = evaluateExpression(condition.expression, context);
1117
+ const result = evaluateCondition(condition.expression, context);
1110
1118
  const branch = result ? condition.then : condition.else || [];
1111
1119
  const branchResults = await executeSteps(branch, context, api, permissions, allowedActionIds, options, void 0, flowStack);
1112
1120
  return {
@@ -1246,7 +1254,7 @@ async function executeWhileStep(step, context, api, permissions, allowedActionId
1246
1254
  };
1247
1255
  for (let iteration = 0; iteration < maxIterations; iteration++) {
1248
1256
  if (iteration > 0) {
1249
- const conditionResult = evaluateExpression(config.condition, context);
1257
+ const conditionResult = evaluateCondition(config.condition, context);
1250
1258
  if (!conditionResult) break;
1251
1259
  }
1252
1260
  await executeSteps(config.steps, context, api, permissions, allowedActionIds, options, void 0, flowStack);
@@ -1271,7 +1279,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
1271
1279
  if (flowStack.includes(resolvedKey)) {
1272
1280
  throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
1273
1281
  }
1274
- const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-VIKPVLBL.js");
1282
+ const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-7YOPMXWO.js");
1275
1283
  const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
1276
1284
  const subContext = await executeFlow(
1277
1285
  subFlow,
@@ -1468,7 +1476,7 @@ function checkRequires(step, context) {
1468
1476
  }
1469
1477
  async function executeSingleStep(step, context, api, permissions, allowedActionIds, options, flowStack = []) {
1470
1478
  if (step.if) {
1471
- const condResult = evaluateExpression(step.if, context);
1479
+ const condResult = evaluateCondition(step.if, context);
1472
1480
  if (!condResult) {
1473
1481
  const result = { status: "skipped" };
1474
1482
  context.steps[step.id] = result;
@@ -1476,7 +1484,7 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
1476
1484
  }
1477
1485
  }
1478
1486
  if (step.unless) {
1479
- const condResult = evaluateExpression(step.unless, context);
1487
+ const condResult = evaluateCondition(step.unless, context);
1480
1488
  if (condResult) {
1481
1489
  const result = { status: "skipped" };
1482
1490
  context.steps[step.id] = result;
@@ -303,6 +303,54 @@ function writeAnalyticsQueue(lines) {
303
303
  } catch {
304
304
  }
305
305
  }
306
+ function usageLogFile() {
307
+ return path.join(configDir(), ".cli-usage-log.jsonl");
308
+ }
309
+ function usageStateFile() {
310
+ return path.join(configDir(), ".cli-usage-state.json");
311
+ }
312
+ var USAGE_LOG_MAX = 5e3;
313
+ function appendUsageLog(line) {
314
+ try {
315
+ if (!fs.existsSync(configDir())) fs.mkdirSync(configDir(), { mode: 448 });
316
+ fs.appendFileSync(usageLogFile(), `${line}
317
+ `, { mode: 384 });
318
+ } catch {
319
+ }
320
+ }
321
+ function readUsageLog() {
322
+ try {
323
+ return fs.readFileSync(usageLogFile(), "utf-8").split("\n").filter(Boolean).slice(-USAGE_LOG_MAX);
324
+ } catch {
325
+ return [];
326
+ }
327
+ }
328
+ function writeUsageLog(lines) {
329
+ try {
330
+ if (lines.length === 0) {
331
+ fs.rmSync(usageLogFile(), { force: true });
332
+ return;
333
+ }
334
+ if (!fs.existsSync(configDir())) fs.mkdirSync(configDir(), { mode: 448 });
335
+ fs.writeFileSync(usageLogFile(), `${lines.slice(-USAGE_LOG_MAX).join("\n")}
336
+ `, { mode: 384 });
337
+ } catch {
338
+ }
339
+ }
340
+ function readUsageState() {
341
+ try {
342
+ return JSON.parse(fs.readFileSync(usageStateFile(), "utf-8"));
343
+ } catch {
344
+ return {};
345
+ }
346
+ }
347
+ function writeUsageState(state) {
348
+ try {
349
+ if (!fs.existsSync(configDir())) fs.mkdirSync(configDir(), { mode: 448 });
350
+ fs.writeFileSync(usageStateFile(), JSON.stringify(state), { mode: 384 });
351
+ } catch {
352
+ }
353
+ }
306
354
 
307
355
  export {
308
356
  getProjectRoot,
@@ -334,5 +382,10 @@ export {
334
382
  markTelemetryNoticeShown,
335
383
  appendAnalyticsQueue,
336
384
  readAnalyticsQueue,
337
- writeAnalyticsQueue
385
+ writeAnalyticsQueue,
386
+ appendUsageLog,
387
+ readUsageLog,
388
+ writeUsageLog,
389
+ readUsageState,
390
+ writeUsageState
338
391
  };
@@ -5,10 +5,10 @@ import {
5
5
  getMemoryConfig,
6
6
  getMemoryConfigOrDefault,
7
7
  updateMemoryConfig
8
- } from "./chunk-BKD7A42U.js";
8
+ } from "./chunk-TXTRXV74.js";
9
9
  import {
10
10
  getOpenAiApiKey
11
- } from "./chunk-OEN5DNG7.js";
11
+ } from "./chunk-K6MWE2ZH.js";
12
12
 
13
13
  // src/lib/memory/schema.ts
14
14
  var SCHEMA_VERSION = "2.1.0";
@@ -6,11 +6,11 @@ import {
6
6
  note,
7
7
  okJson,
8
8
  requireMemoryInit
9
- } from "./chunk-56QOOA5V.js";
9
+ } from "./chunk-HS5AHQ4V.js";
10
10
  import {
11
11
  getBackend,
12
12
  upsertRecord
13
- } from "./chunk-GIMKPA2J.js";
13
+ } from "./chunk-OADHUAEU.js";
14
14
 
15
15
  // src/commands/mem/migrate.ts
16
16
  import fs4 from "fs";
@@ -3,10 +3,10 @@ import {
3
3
  isAgentMode,
4
4
  json,
5
5
  requireMemoryInit
6
- } from "./chunk-56QOOA5V.js";
6
+ } from "./chunk-HS5AHQ4V.js";
7
7
  import {
8
8
  getBackend
9
- } from "./chunk-GIMKPA2J.js";
9
+ } from "./chunk-OADHUAEU.js";
10
10
 
11
11
  // src/commands/mem/sql.ts
12
12
  async function memSqlCommand(sql) {
@@ -3,7 +3,7 @@ import {
3
3
  readConfig,
4
4
  setOpenAiApiKey,
5
5
  writeConfig
6
- } from "./chunk-OEN5DNG7.js";
6
+ } from "./chunk-K6MWE2ZH.js";
7
7
 
8
8
  // src/lib/memory/config.ts
9
9
  var DEFAULT_MEMORY_CONFIG = {
@@ -2,8 +2,8 @@ import {
2
2
  defaultSearchableText,
3
3
  embed,
4
4
  embedBatch
5
- } from "./chunk-BKD7A42U.js";
6
- import "./chunk-OEN5DNG7.js";
5
+ } from "./chunk-TXTRXV74.js";
6
+ import "./chunk-K6MWE2ZH.js";
7
7
  export {
8
8
  defaultSearchableText,
9
9
  embed,
@@ -11,9 +11,9 @@ import {
11
11
  saveFlow,
12
12
  summarizeFlowInputs,
13
13
  walkSteps
14
- } from "./chunk-GDKMOLZ5.js";
14
+ } from "./chunk-IFTHWFLL.js";
15
15
  import "./chunk-44CV5IMX.js";
16
- import "./chunk-OEN5DNG7.js";
16
+ import "./chunk-K6MWE2ZH.js";
17
17
  export {
18
18
  FlowRunner,
19
19
  collectStepTypes,
package/dist/index.js CHANGED
@@ -30,10 +30,10 @@ import {
30
30
  searchCachePath,
31
31
  validateActionInput,
32
32
  writeCache
33
- } from "./chunk-GDKMOLZ5.js";
33
+ } from "./chunk-IFTHWFLL.js";
34
34
  import {
35
35
  memSqlCommand
36
- } from "./chunk-EDVE3BQZ.js";
36
+ } from "./chunk-QV3Y5N5G.js";
37
37
  import {
38
38
  countRecords,
39
39
  deleteDatabase,
@@ -58,7 +58,7 @@ import {
58
58
  upsertRecords,
59
59
  writeDraftProfile,
60
60
  writeProfile
61
- } from "./chunk-MQU7UK5J.js";
61
+ } from "./chunk-PLMFRFTT.js";
62
62
  import {
63
63
  getByDotPath
64
64
  } from "./chunk-44CV5IMX.js";
@@ -80,8 +80,9 @@ import {
80
80
  requireMemoryInit,
81
81
  semanticSearchUpgradeHint,
82
82
  semanticSearchUpgradeLine,
83
- setAgentMode
84
- } from "./chunk-56QOOA5V.js";
83
+ setAgentMode,
84
+ silenceWarningsInAgentMode
85
+ } from "./chunk-HS5AHQ4V.js";
85
86
  import {
86
87
  SCHEMA_VERSION,
87
88
  addRecord,
@@ -91,7 +92,7 @@ import {
91
92
  listBackendPlugins,
92
93
  loadBackendFromConfig,
93
94
  upsertRecord
94
- } from "./chunk-GIMKPA2J.js";
95
+ } from "./chunk-OADHUAEU.js";
95
96
  import {
96
97
  DEFAULT_MEMORY_CONFIG,
97
98
  defaultSearchableText,
@@ -101,9 +102,10 @@ import {
101
102
  memoryConfigExists,
102
103
  setOpenAiApiKey,
103
104
  updateMemoryConfig
104
- } from "./chunk-BKD7A42U.js";
105
+ } from "./chunk-TXTRXV74.js";
105
106
  import {
106
107
  appendAnalyticsQueue,
108
+ appendUsageLog,
107
109
  configExists,
108
110
  ensureWhoAmI,
109
111
  getAccessControl,
@@ -124,14 +126,18 @@ import {
124
126
  readConfig,
125
127
  readGlobalConfig,
126
128
  readProjectConfig,
129
+ readUsageLog,
130
+ readUsageState,
127
131
  resolveConfig,
128
132
  telemetryNoticeShown,
129
133
  updateAccessControl,
130
134
  updateApiBase,
131
135
  updateWhoAmI,
132
136
  writeAnalyticsQueue,
133
- writeConfig
134
- } from "./chunk-OEN5DNG7.js";
137
+ writeConfig,
138
+ writeUsageLog,
139
+ writeUsageState
140
+ } from "./chunk-K6MWE2ZH.js";
135
141
 
136
142
  // src/cli.ts
137
143
  import { createRequire as createRequire3 } from "module";
@@ -3648,6 +3654,26 @@ function validateCodeModules(flow2, rootDir) {
3648
3654
  // src/commands/flow.ts
3649
3655
  import fs5 from "fs";
3650
3656
  import path5 from "path";
3657
+ async function writeFlowResultFile(filePath, meta, steps) {
3658
+ const abs = path5.resolve(filePath);
3659
+ const ws = fs5.createWriteStream(abs);
3660
+ const done = new Promise((resolve, reject) => {
3661
+ ws.on("finish", () => resolve());
3662
+ ws.on("error", reject);
3663
+ });
3664
+ ws.write(
3665
+ `{"event":"workflow:result","runId":${JSON.stringify(meta.runId)},"logFile":${JSON.stringify(meta.logFile)},"status":${JSON.stringify(meta.status)},"steps":{`
3666
+ );
3667
+ let first = true;
3668
+ for (const [id, result] of Object.entries(steps)) {
3669
+ ws.write(`${first ? "" : ","}${JSON.stringify(id)}:${JSON.stringify(result)}`);
3670
+ first = false;
3671
+ }
3672
+ ws.write("}}");
3673
+ ws.end();
3674
+ await done;
3675
+ return abs;
3676
+ }
3651
3677
  function getConfig2() {
3652
3678
  const apiKey = getApiKey();
3653
3679
  if (!apiKey) {
@@ -3848,16 +3874,16 @@ ${pc7.yellow("Pausing after current step completes...")} (run ID: ${runId})`);
3848
3874
  if (!options.verbose && !isAgentMode()) {
3849
3875
  execSpinner.stop("Workflow completed");
3850
3876
  }
3877
+ const resultFile = options.outputFile ? await writeFlowResultFile(options.outputFile, { runId, logFile: logPath, status: "success" }, context.steps) : void 0;
3851
3878
  if (isAgentMode()) {
3852
- json({
3853
- event: "workflow:result",
3854
- runId,
3855
- logFile: logPath,
3856
- status: "success",
3857
- steps: context.steps
3858
- });
3879
+ json(
3880
+ resultFile ? { event: "workflow:result", runId, logFile: logPath, status: "success", outputFile: resultFile } : { event: "workflow:result", runId, logFile: logPath, status: "success", steps: context.steps }
3881
+ );
3859
3882
  return;
3860
3883
  }
3884
+ if (resultFile) {
3885
+ note(`Full result written to ${resultFile}`, "Output");
3886
+ }
3861
3887
  const stepEntries = Object.entries(context.steps);
3862
3888
  const succeeded = stepEntries.filter(([, r]) => r.status === "success").length;
3863
3889
  const failed = stepEntries.filter(([, r]) => r.status === "failed").length;
@@ -5726,7 +5752,7 @@ async function syncModel(api, profile, options) {
5726
5752
  updateModelState(platform, model, { status: "failed", pagesProcessed, lastCursor }),
5727
5753
  (async () => {
5728
5754
  try {
5729
- const { getBackend: getBackend2 } = await import("./runtime-ZN7NZILR.js");
5755
+ const { getBackend: getBackend2 } = await import("./runtime-B5GCQ34P.js");
5730
5756
  const backend = await getBackend2();
5731
5757
  await Promise.race([
5732
5758
  backend.close(),
@@ -6081,7 +6107,7 @@ async function syncModel(api, profile, options) {
6081
6107
  db.exec(`DROP TABLE IF EXISTS _seen_ids`);
6082
6108
  }
6083
6109
  if (options.toMemory !== false) {
6084
- const backend = await (await import("./runtime-ZN7NZILR.js")).getBackend();
6110
+ const backend = await (await import("./runtime-B5GCQ34P.js")).getBackend();
6085
6111
  const type = `${platform}/${model}`;
6086
6112
  const existing = await backend.listKeysByType(type);
6087
6113
  const sourcePrefix = `${type}:`;
@@ -6161,7 +6187,7 @@ async function syncModel(api, profile, options) {
6161
6187
  let statusCounts;
6162
6188
  if (options.toMemory !== false) {
6163
6189
  try {
6164
- const backend = await (await import("./runtime-ZN7NZILR.js")).getBackend();
6190
+ const backend = await (await import("./runtime-B5GCQ34P.js")).getBackend();
6165
6191
  const typeName = `${platform}/${model}`;
6166
6192
  const [active, archived] = await Promise.all([
6167
6193
  backend.count(typeName, { status: "active" }),
@@ -7794,7 +7820,7 @@ ${result.total} results`);
7794
7820
  }
7795
7821
  }
7796
7822
  async function syncSqlCommand(platformModel, sql) {
7797
- const { syncSqlCommand: runSyncSql } = await import("./sql-YMHNH3DT.js");
7823
+ const { syncSqlCommand: runSyncSql } = await import("./sql-W3ZUOUNR.js");
7798
7824
  await runSyncSql(platformModel, sql);
7799
7825
  }
7800
7826
  async function syncDeleteCommand(platformModel, options) {
@@ -7872,7 +7898,7 @@ async function syncDeleteCommand(platformModel, options) {
7872
7898
  async function maybeAutoMigrateLegacy(platform, models) {
7873
7899
  const dbSize = getDatabaseSize(platform);
7874
7900
  if (!dbSize || dbSize === "0 B") return;
7875
- const { getBackend: getBackend2 } = await import("./runtime-ZN7NZILR.js");
7901
+ const { getBackend: getBackend2 } = await import("./runtime-B5GCQ34P.js");
7876
7902
  const backend = await getBackend2();
7877
7903
  let memoryHasData = false;
7878
7904
  for (const model of models) {
@@ -7888,7 +7914,7 @@ async function maybeAutoMigrateLegacy(platform, models) {
7888
7914
  ` detected legacy .one/sync/data/${platform}.db (${dbSize}) \u2014 auto-migrating into memory before sync.
7889
7915
  `
7890
7916
  );
7891
- const { memMigrateCommand: memMigrateCommand3 } = await import("./migrate-SJXHOA2G.js");
7917
+ const { memMigrateCommand: memMigrateCommand3 } = await import("./migrate-UDEANXEZ.js");
7892
7918
  await memMigrateCommand3({ platform, yes: true });
7893
7919
  return;
7894
7920
  }
@@ -7897,7 +7923,7 @@ async function maybeAutoMigrateLegacy(platform, models) {
7897
7923
  initialValue: true
7898
7924
  });
7899
7925
  if (p7.isCancel(shouldMigrate) || !shouldMigrate) return;
7900
- const { memMigrateCommand: memMigrateCommand2 } = await import("./migrate-SJXHOA2G.js");
7926
+ const { memMigrateCommand: memMigrateCommand2 } = await import("./migrate-UDEANXEZ.js");
7901
7927
  await memMigrateCommand2({ platform, yes: true });
7902
7928
  }
7903
7929
  async function syncSuggestSearchableCommand(platformModel, options = {}) {
@@ -7958,7 +7984,7 @@ async function syncSuggestSearchableCommand(platformModel, options = {}) {
7958
7984
  async function syncListCommand(platform) {
7959
7985
  const profiles = listProfiles(platform);
7960
7986
  const state = await readSyncState();
7961
- const { getBackend: getBackend2 } = await import("./runtime-ZN7NZILR.js");
7987
+ const { getBackend: getBackend2 } = await import("./runtime-B5GCQ34P.js");
7962
7988
  const backend = await getBackend2();
7963
7989
  const syncs = await Promise.all(profiles.map(async (p10) => {
7964
7990
  const modelState = state[p10.platform]?.[p10.model];
@@ -8876,7 +8902,7 @@ async function memDoctorCommand() {
8876
8902
  }
8877
8903
  if (cfg.embedding.provider === "openai") {
8878
8904
  try {
8879
- const { embed: embed2 } = await import("./embedding-4T4VYFEI.js");
8905
+ const { embed: embed2 } = await import("./embedding-NVFDR5PK.js");
8880
8906
  const result = await embed2("connectivity check");
8881
8907
  checks.push({
8882
8908
  name: "OpenAI embedding provider reachable",
@@ -9386,6 +9412,8 @@ one --agent flow list # List all workflows
9386
9412
  - AI analysis via bash steps: \`claude --print\` with \`parseJson: true\`
9387
9413
  - Use \`--allow-bash\` to enable bash steps, \`--mock\` for dry-run with realistic mock responses (uses example data from action schemas)
9388
9414
  - Use \`--skip-validation\` to bypass input validation on action steps
9415
+ - Use \`--output-file <path>\` to stream the full result to a file instead of stdout \u2014 for large results that would otherwise be truncated or hit the JSON string-size limit; stdout (and \`--agent\` output) then carries an \`outputFile\` pointer instead of inline \`steps\`
9416
+ - Step-level \`if\`/\`unless\` (and \`while\`/\`condition\` steps) are null-safe: a condition referencing a skipped or not-yet-run step's output (e.g. \`$.steps.maybeSkipped.output.x\`) evaluates to \`false\` instead of crashing the flow
9389
9417
 
9390
9418
  ### 3. Relay \u2014 Webhook event forwarding between platforms
9391
9419
  Receive webhooks from platforms (Stripe, GitHub, Airtable, Attio, Google Calendar) and forward event data to any connected platform using passthrough actions with Handlebars templates. No middleware, no code.
@@ -10822,24 +10850,100 @@ function send(item) {
10822
10850
  }
10823
10851
  })();
10824
10852
  }
10825
- function capture(event, properties = {}) {
10853
+ function capture(event, properties = {}, opts = {}) {
10826
10854
  if (isTelemetryDisabled()) {
10827
10855
  debugLog(`disabled \u2014 skipping "${event}"`);
10828
10856
  return;
10829
10857
  }
10858
+ const did = opts.distinctId ?? distinctId();
10830
10859
  const props = { ...baseProperties(), ...properties, $insert_id: randomUUID() };
10831
- const set = personSet();
10832
- if (set) props.$set = set;
10860
+ if (did === distinctId()) {
10861
+ const set = personSet();
10862
+ if (set) props.$set = set;
10863
+ }
10833
10864
  const item = {
10834
10865
  event,
10835
- distinct_id: distinctId(),
10866
+ distinct_id: did,
10836
10867
  properties: props,
10837
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
10868
+ timestamp: opts.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
10838
10869
  };
10839
10870
  appendAnalyticsQueue(JSON.stringify(item));
10840
10871
  }
10841
- function captureCommand(command) {
10842
- capture("CLI Command Run", { command: commandPath(command) });
10872
+ var ROLLUP_WINDOW_MS = 5 * 60 * 1e3;
10873
+ var ROLLUP_MAX_BATCH = 500;
10874
+ function utcDay(ts) {
10875
+ return new Date(ts).toISOString().slice(0, 10);
10876
+ }
10877
+ function recordCommand(command) {
10878
+ if (isTelemetryDisabled()) {
10879
+ writeUsageLog([]);
10880
+ return;
10881
+ }
10882
+ const did = distinctId();
10883
+ const entry = { ts: Date.now(), command: commandPath(command), agent: isAgentMode(), did };
10884
+ appendUsageLog(JSON.stringify(entry));
10885
+ const today = utcDay(entry.ts);
10886
+ const state = readUsageState();
10887
+ const firstTouch = state.lastDay !== today || state.distinctId !== did;
10888
+ flushUsageRollups({ force: firstTouch });
10889
+ if (firstTouch) writeUsageState({ lastDay: today, distinctId: did });
10890
+ }
10891
+ function flushUsageRollups(opts = {}) {
10892
+ if (isTelemetryDisabled()) {
10893
+ writeUsageLog([]);
10894
+ return;
10895
+ }
10896
+ const entries = [];
10897
+ for (const line of readUsageLog()) {
10898
+ try {
10899
+ const e = JSON.parse(line);
10900
+ if (e && typeof e.ts === "number" && typeof e.command === "string" && typeof e.did === "string") {
10901
+ entries.push(e);
10902
+ }
10903
+ } catch {
10904
+ }
10905
+ }
10906
+ if (entries.length === 0) {
10907
+ writeUsageLog([]);
10908
+ return;
10909
+ }
10910
+ const currentDid = entries[entries.length - 1].did;
10911
+ const now = Date.now();
10912
+ const groups = /* @__PURE__ */ new Map();
10913
+ for (const e of entries) {
10914
+ const g = groups.get(e.did);
10915
+ if (g) g.push(e);
10916
+ else groups.set(e.did, [e]);
10917
+ }
10918
+ const kept = [];
10919
+ for (const [did, group] of groups) {
10920
+ const due = opts.force === true || did !== currentDid || // a superseded login's batch — flush it now
10921
+ group.length >= ROLLUP_MAX_BATCH || now - group[0].ts >= ROLLUP_WINDOW_MS;
10922
+ if (due) emitRollup(did, group);
10923
+ else kept.push(...group);
10924
+ }
10925
+ writeUsageLog(kept.map((e) => JSON.stringify(e)));
10926
+ }
10927
+ function emitRollup(did, group) {
10928
+ const byCommand = {};
10929
+ let agentCount = 0;
10930
+ for (const e of group) {
10931
+ byCommand[e.command] = (byCommand[e.command] ?? 0) + 1;
10932
+ if (e.agent) agentCount += 1;
10933
+ }
10934
+ capture(
10935
+ "CLI Usage Rollup",
10936
+ {
10937
+ command_count: group.length,
10938
+ by_command: byCommand,
10939
+ agent_count: agentCount,
10940
+ human_count: group.length - agentCount,
10941
+ window_start: new Date(group[0].ts).toISOString(),
10942
+ window_end: new Date(group[group.length - 1].ts).toISOString()
10943
+ },
10944
+ { distinctId: did, timestamp: new Date(group[group.length - 1].ts).toISOString() }
10945
+ );
10946
+ debugLog(`rollup \u2014 ${group.length} command(s) for ${did}`);
10843
10947
  }
10844
10948
  function commandPath(command) {
10845
10949
  const parts = [];
@@ -10887,6 +10991,7 @@ function maybeShowTelemetryNotice() {
10887
10991
  }
10888
10992
 
10889
10993
  // src/cli.ts
10994
+ silenceWarningsInAgentMode();
10890
10995
  var require4 = createRequire3(import.meta.url);
10891
10996
  var { version } = require4("../package.json");
10892
10997
  var program = new Command();
@@ -10970,7 +11075,7 @@ program.hook("preAction", (thisCommand, actionCommand) => {
10970
11075
  setAgentMode(true);
10971
11076
  }
10972
11077
  maybeShowTelemetryNotice();
10973
- captureCommand(actionCommand);
11078
+ recordCommand(actionCommand);
10974
11079
  drainQueue();
10975
11080
  const commandName = thisCommand.args?.[0];
10976
11081
  if (commandName !== "update") {
@@ -10985,6 +11090,7 @@ program.hook("preAction", (thisCommand, actionCommand) => {
10985
11090
  });
10986
11091
  program.hook("postAction", async () => {
10987
11092
  await closeBackendIfCached();
11093
+ flushUsageRollups();
10988
11094
  flush();
10989
11095
  if (!updateCheckPromise) return;
10990
11096
  const info = await updateCheckPromise;
@@ -11201,7 +11307,7 @@ var flow = program.command("flow").alias("f").description("Create, execute, and
11201
11307
  flow.command("create [key]").description("Create a new workflow from JSON definition").option("--definition <json>", "Workflow definition as JSON string").option("-o, --output <path>", "Custom output path (default .one/flows/<key>/flow.json)").action(async (key, options) => {
11202
11308
  await flowCreateCommand(key, options);
11203
11309
  });
11204
- flow.command("execute <keyOrPath>").alias("x").description("Execute a workflow by key or file path").option("-i, --input <name=value>", "Input parameter (repeatable)", collect, []).option("--dry-run", "Validate and show execution plan without running").option("--mock", "With --dry-run: execute transforms/code with realistic mock API responses").option("--skip-validation", "Skip input validation against action schemas").option("--allow-bash", "Allow bash step execution (disabled by default for security)").option("-v, --verbose", "Show full request/response for each step").action(async (keyOrPath, options) => {
11310
+ flow.command("execute <keyOrPath>").alias("x").description("Execute a workflow by key or file path").option("-i, --input <name=value>", "Input parameter (repeatable)", collect, []).option("--dry-run", "Validate and show execution plan without running").option("--mock", "With --dry-run: execute transforms/code with realistic mock API responses").option("--skip-validation", "Skip input validation against action schemas").option("--allow-bash", "Allow bash step execution (disabled by default for security)").option("-v, --verbose", "Show full request/response for each step").option("--output-file <path>", "Write the full result to a file (streamed) instead of stdout \u2014 avoids truncation/string-limit errors for large results; stdout/agent output then carries an outputFile pointer").action(async (keyOrPath, options) => {
11205
11311
  await flowExecuteCommand(keyOrPath, options);
11206
11312
  });
11207
11313
  flow.command("list").alias("ls").description("List all workflows in .one/flows/").action(async () => {
@@ -3,12 +3,12 @@ import {
3
3
  dotPathToJsonbExpr,
4
4
  memMigrateCommand,
5
5
  reviveStringifiedJson
6
- } from "./chunk-MQU7UK5J.js";
6
+ } from "./chunk-PLMFRFTT.js";
7
7
  import "./chunk-44CV5IMX.js";
8
- import "./chunk-56QOOA5V.js";
9
- import "./chunk-GIMKPA2J.js";
10
- import "./chunk-BKD7A42U.js";
11
- import "./chunk-OEN5DNG7.js";
8
+ import "./chunk-HS5AHQ4V.js";
9
+ import "./chunk-OADHUAEU.js";
10
+ import "./chunk-TXTRXV74.js";
11
+ import "./chunk-K6MWE2ZH.js";
12
12
  export {
13
13
  buildIdentityMap,
14
14
  dotPathToJsonbExpr,
@@ -4,9 +4,9 @@ import {
4
4
  getBackend,
5
5
  resetBackendSingleton,
6
6
  upsertRecord
7
- } from "./chunk-GIMKPA2J.js";
8
- import "./chunk-BKD7A42U.js";
9
- import "./chunk-OEN5DNG7.js";
7
+ } from "./chunk-OADHUAEU.js";
8
+ import "./chunk-TXTRXV74.js";
9
+ import "./chunk-K6MWE2ZH.js";
10
10
  export {
11
11
  addRecord,
12
12
  closeBackendIfCached,
@@ -0,0 +1,12 @@
1
+ import {
2
+ memSqlCommand,
3
+ syncSqlCommand
4
+ } from "./chunk-QV3Y5N5G.js";
5
+ import "./chunk-HS5AHQ4V.js";
6
+ import "./chunk-OADHUAEU.js";
7
+ import "./chunk-TXTRXV74.js";
8
+ import "./chunk-K6MWE2ZH.js";
9
+ export {
10
+ memSqlCommand,
11
+ syncSqlCommand
12
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withone/cli",
3
- "version": "1.46.0",
3
+ "version": "1.47.1",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [
@@ -271,6 +271,10 @@ Selectors in data fields (`data`, `queryParams`, `pathVars`, `connectionKey`) ar
271
271
 
272
272
  The `if`, `unless`, `condition.expression`, `while.condition`, `transform.expression`, and `code.source` fields **do** support full JavaScript expressions (e.g., `$.input.email && $.input.email.length > 0`).
273
273
 
274
+ **Condition null-safety:** `if`/`unless`, `while.condition`, and `condition.expression` are null-safe. A condition that walks into a skipped or not-yet-run step's output — e.g. `$.steps.maybeSkipped.output.value` — evaluates to `false` instead of throwing `Cannot read properties of undefined`. (This applies to *conditions* only; `transform.expression` and `code.source` still throw on undefined access, since their output feeds downstream steps and should fail loudly.)
275
+
276
+ **Large results:** pass `--output-file <path>` to `flow execute` to stream the full result to a file instead of stdout. Use it when a flow aggregates large outputs that would otherwise be truncated on stdout or exceed the JSON string-size limit. stdout (and `--agent` output) then carries `{"event":"workflow:result", ..., "outputFile":"<path>"}` instead of inline `steps` — read the file for the full result.
277
+
274
278
  ## Step Types
275
279
 
276
280
  ### `action` — Execute a One API action
@@ -1,12 +0,0 @@
1
- import {
2
- memSqlCommand,
3
- syncSqlCommand
4
- } from "./chunk-EDVE3BQZ.js";
5
- import "./chunk-56QOOA5V.js";
6
- import "./chunk-GIMKPA2J.js";
7
- import "./chunk-BKD7A42U.js";
8
- import "./chunk-OEN5DNG7.js";
9
- export {
10
- memSqlCommand,
11
- syncSqlCommand
12
- };