@withone/cli 1.35.0 → 1.37.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 CHANGED
@@ -255,6 +255,7 @@ one actions execute stripe <actionId> <connectionKey> \
255
255
  | `--dry-run` | Show the request without executing it |
256
256
  | `--mock` | Return example response without making an API call |
257
257
  | `--skip-validation` | Skip input validation against the action schema |
258
+ | `--output <path>` | Save response to a file (for binary downloads) |
258
259
 
259
260
  The CLI validates required parameters (path variables, query params, body fields) against the action schema before executing. Missing params return a clear error with the flag name and description. Pass `--skip-validation` to bypass.
260
261
 
@@ -11,6 +11,10 @@ import { exec, spawn } from "child_process";
11
11
  import { promisify } from "util";
12
12
 
13
13
  // src/lib/api.ts
14
+ import { createWriteStream } from "fs";
15
+ import { resolve } from "path";
16
+ import { pipeline } from "stream/promises";
17
+ import { Readable } from "stream";
14
18
  var ApiError = class extends Error {
15
19
  constructor(status, message, retryAfterSeconds) {
16
20
  super(message);
@@ -328,12 +332,45 @@ var OneApi = class {
328
332
  const text = await response.text();
329
333
  throw new ApiError(response.status, text || `HTTP ${response.status}`);
330
334
  }
335
+ const responseContentType = response.headers.get("content-type") || "";
336
+ const isExplicitJson = responseContentType.includes("application/json") || responseContentType.includes("text/");
337
+ if (isExplicitJson || !responseContentType) {
338
+ const responseText2 = await response.text();
339
+ const responseData = responseText2 ? JSON.parse(responseText2) : {};
340
+ return { requestConfig: sanitizedConfig, responseData };
341
+ }
342
+ if (args.output) {
343
+ const outputPath = resolve(args.output);
344
+ const body = response.body;
345
+ if (!body) {
346
+ throw new ApiError(0, "Response body is null \u2014 cannot save to file");
347
+ }
348
+ const nodeReadable = Readable.fromWeb(body);
349
+ await pipeline(nodeReadable, createWriteStream(outputPath));
350
+ const contentLength = response.headers.get("content-length");
351
+ const size = contentLength ? parseInt(contentLength, 10) : void 0;
352
+ return {
353
+ requestConfig: sanitizedConfig,
354
+ responseData: { saved: true, path: outputPath, size, contentType: responseContentType }
355
+ };
356
+ }
331
357
  const responseText = await response.text();
332
- const responseData = responseText ? JSON.parse(responseText) : {};
333
- return {
334
- requestConfig: sanitizedConfig,
335
- responseData
336
- };
358
+ try {
359
+ const responseData = responseText ? JSON.parse(responseText) : {};
360
+ return { requestConfig: sanitizedConfig, responseData };
361
+ } catch {
362
+ const contentLength = response.headers.get("content-length");
363
+ const size = contentLength ? parseInt(contentLength, 10) : responseText.length;
364
+ return {
365
+ requestConfig: sanitizedConfig,
366
+ responseData: {
367
+ binary: true,
368
+ size,
369
+ contentType: responseContentType,
370
+ message: "Binary response received. Use --output <path> to save to a file."
371
+ }
372
+ };
373
+ }
337
374
  }
338
375
  // Webhook Relay methods
339
376
  async createRelayEndpoint(body) {
@@ -394,7 +431,7 @@ var TimeoutError = class extends Error {
394
431
  }
395
432
  };
396
433
  function sleep(ms) {
397
- return new Promise((resolve) => setTimeout(resolve, ms));
434
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
398
435
  }
399
436
  function replacePathVariables(path3, variables) {
400
437
  if (!path3) return path3;
@@ -532,7 +569,7 @@ function setByDotPath(obj, dotPath, value) {
532
569
  // src/lib/flow-engine.ts
533
570
  var execAsync = promisify(exec);
534
571
  function sleep2(ms) {
535
- return new Promise((resolve) => setTimeout(resolve, ms));
572
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
536
573
  }
537
574
  var StepTimeoutError = class extends Error {
538
575
  errorCode = "TIMEOUT";
@@ -843,7 +880,7 @@ async function executeCodeModule(stepId, modulePath, context, options) {
843
880
  const { env: _omitEnv, ...safeContext } = context;
844
881
  void _omitEnv;
845
882
  const stdinPayload = JSON.stringify(safeContext);
846
- return await new Promise((resolve, reject) => {
883
+ return await new Promise((resolve2, reject) => {
847
884
  const child = spawn(process.execPath, [absPath], {
848
885
  cwd: rootDir,
849
886
  stdio: ["pipe", "pipe", "pipe"]
@@ -862,11 +899,11 @@ async function executeCodeModule(stepId, modulePath, context, options) {
862
899
  }
863
900
  const trimmed = stdout.trim();
864
901
  if (trimmed === "") {
865
- resolve(void 0);
902
+ resolve2(void 0);
866
903
  return;
867
904
  }
868
905
  try {
869
- resolve(JSON.parse(stripCodeFences(trimmed)));
906
+ resolve2(JSON.parse(stripCodeFences(trimmed)));
870
907
  } catch (err) {
871
908
  reject(new Error(`Code module "${modulePath}" did not print valid JSON to stdout: ${err.message}`));
872
909
  }
@@ -1042,7 +1079,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
1042
1079
  if (flowStack.includes(resolvedKey)) {
1043
1080
  throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
1044
1081
  }
1045
- const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-TNI7YWVY.js");
1082
+ const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-OVOGDXNR.js");
1046
1083
  const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
1047
1084
  const subContext = await executeFlow(
1048
1085
  subFlow,
@@ -11,7 +11,7 @@ import {
11
11
  saveFlow,
12
12
  summarizeFlowInputs,
13
13
  walkSteps
14
- } from "./chunk-HZP7NT4K.js";
14
+ } from "./chunk-MNJJE4PJ.js";
15
15
  export {
16
16
  FlowRunner,
17
17
  collectStepTypes,
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  resolveFlowPath,
20
20
  saveFlow,
21
21
  validateActionInput
22
- } from "./chunk-HZP7NT4K.js";
22
+ } from "./chunk-MNJJE4PJ.js";
23
23
 
24
24
  // src/index.ts
25
25
  import { createRequire as createRequire2 } from "module";
@@ -2771,7 +2771,8 @@ async function actionsExecuteCommand(platform, actionId, connectionKey, options)
2771
2771
  headers,
2772
2772
  isFormData: options.formData,
2773
2773
  isFormUrlEncoded: options.formUrlEncoded,
2774
- dryRun: options.dryRun
2774
+ dryRun: options.dryRun,
2775
+ output: options.output
2775
2776
  },
2776
2777
  actionDetails
2777
2778
  );
@@ -4333,6 +4334,7 @@ async function relayCreateCommand(options) {
4333
4334
  if (options.description) body.description = options.description;
4334
4335
  if (options.eventFilters) body.eventFilters = parseJsonArg2(options.eventFilters, "--event-filters");
4335
4336
  if (options.tags) body.tags = parseJsonArg2(options.tags, "--tags");
4337
+ if (options.metadata) body.metadata = parseJsonArg2(options.metadata, "--metadata");
4336
4338
  if (options.createWebhook) body.createWebhook = true;
4337
4339
  const result = await api.createRelayEndpoint(body);
4338
4340
  if (isAgentMode()) {
@@ -4347,6 +4349,10 @@ async function relayCreateCommand(options) {
4347
4349
  if (result.description) console.log(` ${pc8.dim("Description:")} ${result.description}`);
4348
4350
  if (result.eventFilters?.length) console.log(` ${pc8.dim("Events:")} ${result.eventFilters.join(", ")}`);
4349
4351
  if (result.webhookPayload?.id) console.log(` ${pc8.dim("Webhook ID:")} ${result.webhookPayload.id}`);
4352
+ if (result.warning) {
4353
+ console.log();
4354
+ console.log(` ${pc8.yellow("\u26A0 Warning:")} ${result.warning}`);
4355
+ }
4350
4356
  console.log();
4351
4357
  } catch (error2) {
4352
4358
  spinner6.stop("Failed to create relay endpoint");
@@ -8006,6 +8012,7 @@ one --agent actions execute <platform> <actionId> <key> -d '{}' # Execute it
8006
8012
  - \`--dry-run\` \u2014 Preview request without executing
8007
8013
  - \`--mock\` \u2014 Return example response without making an API call (useful for building UI against a response shape)
8008
8014
  - \`--skip-validation\` \u2014 Skip input validation against the action schema
8015
+ - \`--output <path>\` \u2014 Save response to a file (for binary downloads like PDFs, images, documents)
8009
8016
 
8010
8017
  The CLI validates required parameters against the action schema before executing. If you're missing a required path variable, query param, or body field, you'll get a clear error listing what's missing and which flag to use. Pass \`--skip-validation\` to bypass.
8011
8018
 
@@ -8129,6 +8136,7 @@ one --agent actions execute <platform> <actionId> <connectionKey> [options]
8129
8136
  - \`--dry-run\` \u2014 Preview without executing
8130
8137
  - \`--mock\` \u2014 Return example response without making an API call
8131
8138
  - \`--skip-validation\` \u2014 Skip input validation against the action schema
8139
+ - \`--output <path>\` \u2014 Save response to a file (for binary downloads like PDFs, images, documents)
8132
8140
 
8133
8141
  **Do NOT** pass path or query parameters in \`-d\`. Use the correct flags.
8134
8142
 
@@ -8198,8 +8206,32 @@ one --agent relay deliveries --endpoint-id <id> # Check delivery status
8198
8206
  2. **Get event types** \u2014 \`one --agent relay event-types <platform>\`
8199
8207
  3. **Get source knowledge** \u2014 understand the incoming webhook payload shape (\`{{payload.*}}\` paths)
8200
8208
  4. **Get destination knowledge** \u2014 understand the outgoing API body shape
8201
- 5. **Create endpoint** \u2014 with \`--create-webhook\` and \`--event-filters\`
8202
- 6. **Activate** \u2014 with passthrough action mapping source fields to destination fields
8209
+ 5. **Create endpoint** \u2014 with \`--create-webhook\`, \`--event-filters\`, and \`--metadata\` if the source platform requires it
8210
+ 6. **Activate** \u2014 with passthrough action mapping source fields to destination fields. **Do NOT pass \`--webhook-secret\`** when the endpoint was created with \`--create-webhook\` \u2014 the correct secret is auto-stored, and supplying a wrong one silently drops every delivery (events arrive, 0 deliveries).
8211
+
8212
+ ## Platform-Specific Metadata (\`--metadata\`)
8213
+
8214
+ Some source platforms need extra identifiers to register a webhook. Pass these via \`--metadata '<json>'\` on \`relay create\`. Without them, \`--create-webhook\` silently fails:
8215
+
8216
+ | Platform | Required metadata keys |
8217
+ |---|---|
8218
+ | \`github\` | \`GITHUB_OWNER\`, \`GITHUB_REPOSITORY\` |
8219
+ | \`typeform\` | \`TYPEFORM_FORM_ID\` |
8220
+ | \`stripe\` | (none) |
8221
+ | \`airtable\` | (none) |
8222
+ | \`attio\` | (none) |
8223
+ | \`google-calendar\` | (none) |
8224
+
8225
+ Example (GitHub):
8226
+
8227
+ \`\`\`bash
8228
+ one --agent relay create \\
8229
+ --connection-key "live::github::default::<key>" \\
8230
+ --event-filters '["issues","pull_request"]' \\
8231
+ --metadata '{"GITHUB_OWNER":"my-org","GITHUB_REPOSITORY":"my-repo"}' \\
8232
+ --description "GitHub relay" \\
8233
+ --create-webhook
8234
+ \`\`\`
8203
8235
 
8204
8236
  ## Template Context
8205
8237
 
@@ -8250,6 +8282,8 @@ Any connected platform can be a destination via passthrough actions.
8250
8282
  2. \`relay events --platform <p>\` \u2014 check events are arriving
8251
8283
  3. \`relay deliveries --event-id <id>\` \u2014 check delivery status and errors
8252
8284
  4. \`relay event <id>\` \u2014 inspect full payload to verify template paths
8285
+
8286
+ **If events arrive but 0 deliveries succeed**: you likely passed a wrong \`--webhook-secret\` on \`relay activate\`. When you created the endpoint with \`--create-webhook\`, the secret was registered with the source platform and stored automatically \u2014 do not pass it again on activate. Signature verification will fail silently and every event will be dropped.
8253
8287
  `;
8254
8288
  var GUIDE_CACHE = `# One Cache \u2014 Reference
8255
8289
 
@@ -9369,7 +9403,7 @@ actions.command("search <platform> <query>").description('Search for actions on
9369
9403
  actions.command("knowledge <platform> <actionId>").alias("k").description("Get full docs for an action \u2014 MUST call before execute to know required params").option("--no-cache", "Skip cache, fetch fresh from API").option("--cache-status", "Print cache metadata without fetching").action(async (platform, actionId, options) => {
9370
9404
  await actionsKnowledgeCommand(platform, actionId, options);
9371
9405
  });
9372
- actions.command("execute [platform] [actionId] [connectionKey]").alias("x").allowUnknownOption(true).allowExcessArguments(true).description("Execute an action (or multiple with --parallel, separated by --)").option("-d, --data <json>", "Request body as JSON").option("--path-vars <json>", "Path variables as JSON").option("--query-params <json>", "Query parameters as JSON").option("--headers <json>", "Additional headers as JSON").option("--form-data", "Send as multipart/form-data").option("--form-url-encoded", "Send as application/x-www-form-urlencoded").option("--dry-run", "Show request that would be sent without executing").option("--mock", "Return example response without making an API call").option("--skip-validation", "Skip input validation against the action schema").option("--parallel", "Execute multiple actions concurrently (separate actions with --)").option("--max-concurrency <n>", "Max concurrent actions when using --parallel (default: 5)", "5").action(async (platform, actionId, connectionKey, options) => {
9406
+ actions.command("execute [platform] [actionId] [connectionKey]").alias("x").allowUnknownOption(true).allowExcessArguments(true).description("Execute an action (or multiple with --parallel, separated by --)").option("-d, --data <json>", "Request body as JSON").option("--path-vars <json>", "Path variables as JSON").option("--query-params <json>", "Query parameters as JSON").option("--headers <json>", "Additional headers as JSON").option("--form-data", "Send as multipart/form-data").option("--form-url-encoded", "Send as application/x-www-form-urlencoded").option("--dry-run", "Show request that would be sent without executing").option("--mock", "Return example response without making an API call").option("--skip-validation", "Skip input validation against the action schema").option("--output <path>", "Save binary response to a file (for non-JSON responses like file downloads)").option("--parallel", "Execute multiple actions concurrently (separate actions with --)").option("--max-concurrency <n>", "Max concurrent actions when using --parallel (default: 5)", "5").action(async (platform, actionId, connectionKey, options) => {
9373
9407
  if (options.parallel) {
9374
9408
  await actionsExecuteParallelCommand();
9375
9409
  return;
@@ -9386,7 +9420,8 @@ actions.command("execute [platform] [actionId] [connectionKey]").alias("x").allo
9386
9420
  formUrlEncoded: options.formUrlEncoded,
9387
9421
  dryRun: options.dryRun,
9388
9422
  mock: options.mock,
9389
- skipValidation: options.skipValidation
9423
+ skipValidation: options.skipValidation,
9424
+ output: options.output
9390
9425
  });
9391
9426
  });
9392
9427
  var flow = program.command("flow").alias("f").description("Create, execute, and manage multi-step workflows");
@@ -9412,7 +9447,7 @@ flow.command("scaffold [template]").description("Generate a workflow scaffold (t
9412
9447
  await flowScaffoldCommand(template);
9413
9448
  });
9414
9449
  var relay = program.command("relay").alias("r").description("Receive webhooks from platforms and relay them via passthrough actions");
9415
- relay.command("create").description("Create a new relay endpoint for a connection").requiredOption("--connection-key <key>", "Connection key for the source platform").option("--description <desc>", "Description of the relay endpoint").option("--event-filters <json>", `JSON array of event types to filter (e.g. '["customer.created"]')`).option("--tags <json>", "JSON array of tags").option("--create-webhook", "Automatically register the webhook with the source platform").action(async (options) => {
9450
+ relay.command("create").description("Create a new relay endpoint for a connection").requiredOption("--connection-key <key>", "Connection key for the source platform").option("--description <desc>", "Description of the relay endpoint").option("--event-filters <json>", `JSON array of event types to filter (e.g. '["customer.created"]')`).option("--tags <json>", "JSON array of tags").option("--metadata <json>", `JSON object of platform-specific metadata required to register the webhook (e.g. GitHub: '{"GITHUB_OWNER":"org","GITHUB_REPOSITORY":"repo"}', Typeform: '{"TYPEFORM_FORM_ID":"abc"}')`).option("--create-webhook", "Automatically register the webhook with the source platform").action(async (options) => {
9416
9451
  await relayCreateCommand(options);
9417
9452
  });
9418
9453
  relay.command("list").alias("ls").description("List all relay endpoints").option("--limit <n>", "Max results per page").option("--page <n>", "Page number").action(async (options) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withone/cli",
3
- "version": "1.35.0",
3
+ "version": "1.37.0",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [
@@ -87,6 +87,7 @@ Options:
87
87
  - `--dry-run` — Preview the request without executing
88
88
  - `--mock` — Return example response without making an API call (useful for building UI)
89
89
  - `--skip-validation` — Skip input validation against the action schema
90
+ - `--output <path>` — Save response to a file (for binary downloads like PDFs, images, documents)
90
91
 
91
92
  The CLI validates required parameters before executing. Missing params return a structured error with the flag name, parameter name, and description. Pass `--skip-validation` to bypass.
92
93
 
@@ -52,6 +52,25 @@ one --agent relay create \
52
52
 
53
53
  Always use `--create-webhook` — it registers the webhook URL with the source platform automatically.
54
54
 
55
+ **Some source platforms require extra identifiers via `--metadata`:**
56
+
57
+ | Platform | Required metadata keys |
58
+ |---|---|
59
+ | `github` | `GITHUB_OWNER`, `GITHUB_REPOSITORY` |
60
+ | `typeform` | `TYPEFORM_FORM_ID` |
61
+ | `stripe`, `airtable`, `attio`, `google-calendar` | (none) |
62
+
63
+ Without metadata, `--create-webhook` silently fails for these platforms. Example for GitHub:
64
+
65
+ ```bash
66
+ one --agent relay create \
67
+ --connection-key "live::github::default::<key>" \
68
+ --event-filters '["issues","pull_request"]' \
69
+ --metadata '{"GITHUB_OWNER":"my-org","GITHUB_REPOSITORY":"my-repo"}' \
70
+ --description "GitHub relay" \
71
+ --create-webhook
72
+ ```
73
+
55
74
  ### Step 6: Activate with a passthrough action
56
75
 
57
76
  ```bash
@@ -66,6 +85,8 @@ one --agent relay activate <relay-id> --actions '[{
66
85
  }]'
67
86
  ```
68
87
 
88
+ **Do NOT pass `--webhook-secret` on activate** when you created the endpoint with `--create-webhook`. The correct secret is registered with the source platform and stored automatically. Supplying a wrong one does not error — events arrive but every delivery is dropped during signature verification (0 deliveries). If you don't have a reason to override the secret, omit the flag.
89
+
69
90
  ## Template Context
70
91
 
71
92
  | Variable | Description |
@@ -177,3 +198,5 @@ one --agent relay deliveries --event-id <id>
177
198
  - Event filters on both the endpoint and individual actions must match
178
199
  - Multiple actions can be attached to a single relay endpoint
179
200
  - Missing template variables resolve to empty strings — verify `{{payload.*}}` paths against the actual payload
201
+ - GitHub and Typeform relays require `--metadata` on create; without it `--create-webhook` silently fails
202
+ - Never pass `--webhook-secret` on activate when the endpoint was created with `--create-webhook` — the auto-stored secret is correct, and a wrong one causes signature verification to drop every event silently