@withone/cli 1.47.5 → 1.47.7
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
|
@@ -168,10 +168,15 @@ Connect a new platform via OAuth.
|
|
|
168
168
|
one add shopify
|
|
169
169
|
one add hubspot
|
|
170
170
|
one add gmail
|
|
171
|
+
one add gmail --tag work # tag the connection
|
|
171
172
|
```
|
|
172
173
|
|
|
173
174
|
Opens your browser, you authorize, done. The CLI polls until the connection is live. Platform names are lowercase (with dashes for multi-word names) - run `one platforms` to see them all.
|
|
174
175
|
|
|
176
|
+
| Flag | What it does |
|
|
177
|
+
|------|-------------|
|
|
178
|
+
| `--tag <name>` | Tag the new connection once it's created. Lets sync/flow profiles target a specific connection via `"connection": { "platform": "gmail", "tag": "work" }` when you have several connections for one platform (e.g. personal vs work Gmail). |
|
|
179
|
+
|
|
175
180
|
### `one list`
|
|
176
181
|
|
|
177
182
|
List your active connections with their status and connection keys.
|
|
@@ -106,6 +106,16 @@ var OneApi = class {
|
|
|
106
106
|
async deleteConnection(id) {
|
|
107
107
|
await this.requestFull({ path: `/vault/connections/${id}`, method: "DELETE" });
|
|
108
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Set the tag set on a connection (replaces existing tags). Backs
|
|
111
|
+
* `one add <platform> --tag <name>`, which tags a connection right after
|
|
112
|
+
* the OAuth flow creates it so sync/flow profiles can reference it via
|
|
113
|
+
* `connection: { platform, tag }` when several connections share a platform.
|
|
114
|
+
* Maps to `PATCH /v1/vault/connections/{id}` (UpdateConnection { tags }).
|
|
115
|
+
*/
|
|
116
|
+
async updateConnectionTags(id, tags) {
|
|
117
|
+
await this.requestFull({ path: `/vault/connections/${id}`, method: "PATCH", body: { tags } });
|
|
118
|
+
}
|
|
109
119
|
/**
|
|
110
120
|
* Resolve a late-bound `ConnectionRef` to a current `Connection`. Pass
|
|
111
121
|
* `cache` (a pre-fetched connection list) when resolving many refs in a
|
|
@@ -1279,7 +1289,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
|
|
|
1279
1289
|
if (flowStack.includes(resolvedKey)) {
|
|
1280
1290
|
throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
|
|
1281
1291
|
}
|
|
1282
|
-
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-
|
|
1292
|
+
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-7VRNSKWU.js");
|
|
1283
1293
|
const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
|
|
1284
1294
|
const subContext = await executeFlow(
|
|
1285
1295
|
subFlow,
|
|
@@ -1493,16 +1503,17 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
|
|
|
1493
1503
|
}
|
|
1494
1504
|
const startTime = Date.now();
|
|
1495
1505
|
let lastError;
|
|
1496
|
-
const
|
|
1506
|
+
const onError = step.onError ?? context._defaultOnError;
|
|
1507
|
+
const maxAttempts = onError?.strategy === "retry" && onError.retries ? onError.retries + 1 : 1;
|
|
1497
1508
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1498
1509
|
try {
|
|
1499
1510
|
if (attempt > 1) {
|
|
1500
|
-
const delay = computeRetryDelay(
|
|
1511
|
+
const delay = computeRetryDelay(onError, attempt);
|
|
1501
1512
|
options.onEvent?.({
|
|
1502
1513
|
event: "step:retry",
|
|
1503
1514
|
stepId: step.id,
|
|
1504
1515
|
attempt,
|
|
1505
|
-
maxRetries:
|
|
1516
|
+
maxRetries: onError.retries,
|
|
1506
1517
|
delayMs: delay
|
|
1507
1518
|
});
|
|
1508
1519
|
await sleep2(delay);
|
|
@@ -1589,8 +1600,8 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
|
|
|
1589
1600
|
if (attempt === maxAttempts) {
|
|
1590
1601
|
break;
|
|
1591
1602
|
}
|
|
1592
|
-
if (
|
|
1593
|
-
const decision = shouldRetryError(lastError,
|
|
1603
|
+
if (onError?.strategy === "retry" && (onError.retryOn || onError.failFastOn)) {
|
|
1604
|
+
const decision = shouldRetryError(lastError, onError);
|
|
1594
1605
|
if (!decision.retry) {
|
|
1595
1606
|
options.onEvent?.({
|
|
1596
1607
|
event: "step:retry-skip",
|
|
@@ -1604,7 +1615,7 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
|
|
|
1604
1615
|
}
|
|
1605
1616
|
}
|
|
1606
1617
|
const errorMessage = lastError?.message || "Unknown error";
|
|
1607
|
-
const strategy =
|
|
1618
|
+
const strategy = onError?.strategy || "fail";
|
|
1608
1619
|
const retriesUsed = Math.max(0, maxAttempts - 1);
|
|
1609
1620
|
const isTimeout = lastError instanceof StepTimeoutError;
|
|
1610
1621
|
const errorCode = lastError?.errorCode;
|
|
@@ -1619,7 +1630,7 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
|
|
|
1619
1630
|
context.steps[step.id] = result;
|
|
1620
1631
|
return result;
|
|
1621
1632
|
}
|
|
1622
|
-
if (strategy === "fallback" &&
|
|
1633
|
+
if (strategy === "fallback" && onError?.fallbackStepId) {
|
|
1623
1634
|
const result = {
|
|
1624
1635
|
status: isTimeout ? "timeout" : "failed",
|
|
1625
1636
|
error: errorMessage,
|
|
@@ -1740,6 +1751,7 @@ async function executeFlow(flow, inputs, api, permissions, allowedActionIds, opt
|
|
|
1740
1751
|
loop: {}
|
|
1741
1752
|
};
|
|
1742
1753
|
context.input = resolvedInputs;
|
|
1754
|
+
context._defaultOnError = flow.defaultOnError;
|
|
1743
1755
|
const completedStepIds = resumeState ? new Set(resumeState.completedSteps) : void 0;
|
|
1744
1756
|
if (options.dryRun && !options.mock) {
|
|
1745
1757
|
options.onEvent?.({
|
|
@@ -1797,7 +1809,8 @@ var FLOW_SCHEMA = {
|
|
|
1797
1809
|
description: { type: "string", required: false, description: "What this flow does" },
|
|
1798
1810
|
version: { type: "string", required: false, description: "Semver or arbitrary version string" },
|
|
1799
1811
|
inputs: { type: "object", required: true, description: "Input declarations (Record<string, InputDeclaration>)" },
|
|
1800
|
-
steps: { type: "array", required: true, description: "Ordered array of steps", stepsArray: true }
|
|
1812
|
+
steps: { type: "array", required: true, description: "Ordered array of steps", stepsArray: true },
|
|
1813
|
+
defaultOnError: { type: "object", required: false, description: 'Default error strategy inherited by every step without its own `onError` (e.g. { "strategy": "continue" }). A step opts out with its own `onError`.' }
|
|
1801
1814
|
},
|
|
1802
1815
|
inputFields: {
|
|
1803
1816
|
type: { type: "string", required: true, description: "Data type: string, number, boolean, object, array", enum: ["string", "number", "boolean", "object", "array"] },
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
searchCachePath,
|
|
31
31
|
validateActionInput,
|
|
32
32
|
writeCache
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-KWGN3RJR.js";
|
|
34
34
|
import {
|
|
35
35
|
memSqlCommand
|
|
36
36
|
} from "./chunk-QV3Y5N5G.js";
|
|
@@ -2012,7 +2012,7 @@ function stripAnsi(str) {
|
|
|
2012
2012
|
}
|
|
2013
2013
|
|
|
2014
2014
|
// src/commands/connection.ts
|
|
2015
|
-
async function connectionAddCommand(platformArg) {
|
|
2015
|
+
async function connectionAddCommand(platformArg, options) {
|
|
2016
2016
|
if (isAgentMode()) {
|
|
2017
2017
|
error("This command requires interactive input. Run without --agent.");
|
|
2018
2018
|
}
|
|
@@ -2107,7 +2107,22 @@ ${url}`);
|
|
|
2107
2107
|
try {
|
|
2108
2108
|
const connection2 = await api.waitForConnection(platform, 5 * 60 * 1e3, 5e3);
|
|
2109
2109
|
pollSpinner.stop(`${platform} connected!`);
|
|
2110
|
-
|
|
2110
|
+
const tag = options?.tag?.trim();
|
|
2111
|
+
if (tag) {
|
|
2112
|
+
const tagSpinner = p4.spinner();
|
|
2113
|
+
tagSpinner.start(`Tagging connection "${tag}"...`);
|
|
2114
|
+
try {
|
|
2115
|
+
await api.updateConnectionTags(connection2.id, [tag]);
|
|
2116
|
+
tagSpinner.stop(`Tagged "${tag}"`);
|
|
2117
|
+
} catch (err) {
|
|
2118
|
+
tagSpinner.stop("Could not set tag");
|
|
2119
|
+
p4.log.warn(
|
|
2120
|
+
`Connected, but tagging failed: ${err instanceof Error ? err.message : "Unknown error"}
|
|
2121
|
+
The connection is usable; set the tag later in the dashboard or retry.`
|
|
2122
|
+
);
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
p4.log.success(`${pc4.green("\u2713")} ${connection2.platform} is now available to your AI agents.${tag ? ` (tag: ${tag})` : ""}`);
|
|
2111
2126
|
p4.outro("Connection complete!");
|
|
2112
2127
|
} catch (error2) {
|
|
2113
2128
|
pollSpinner.stop("Connection timed out");
|
|
@@ -3086,6 +3101,16 @@ function validateFlowSchema(flow2) {
|
|
|
3086
3101
|
if (f.version !== void 0 && typeof f.version !== "string") {
|
|
3087
3102
|
errors.push({ path: "version", message: '"version" must be a string' });
|
|
3088
3103
|
}
|
|
3104
|
+
if (f.defaultOnError !== void 0) {
|
|
3105
|
+
if (!f.defaultOnError || typeof f.defaultOnError !== "object" || Array.isArray(f.defaultOnError)) {
|
|
3106
|
+
errors.push({ path: "defaultOnError", message: '"defaultOnError" must be an object (e.g. { "strategy": "continue" })' });
|
|
3107
|
+
} else {
|
|
3108
|
+
const oe = f.defaultOnError;
|
|
3109
|
+
if (!FLOW_SCHEMA.errorStrategies.includes(oe.strategy)) {
|
|
3110
|
+
errors.push({ path: "defaultOnError.strategy", message: `Error strategy must be one of: ${FLOW_SCHEMA.errorStrategies.join(", ")}` });
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3089
3114
|
if (!f.inputs || typeof f.inputs !== "object" || Array.isArray(f.inputs)) {
|
|
3090
3115
|
errors.push({ path: "inputs", message: 'Flow must have an "inputs" object' });
|
|
3091
3116
|
} else {
|
|
@@ -11285,8 +11310,8 @@ config.command("reset").description("Remove the project config for the current d
|
|
|
11285
11310
|
}
|
|
11286
11311
|
});
|
|
11287
11312
|
var connection = program.command("connection").description("Manage connections");
|
|
11288
|
-
connection.command("add [platform]").alias("a").description("Add a new connection").action(async (platform) => {
|
|
11289
|
-
await connectionAddCommand(platform);
|
|
11313
|
+
connection.command("add [platform]").alias("a").description("Add a new connection").option("--tag <name>", "Tag the new connection (disambiguates multiple connections per platform in sync/flow profiles)").action(async (platform, options) => {
|
|
11314
|
+
await connectionAddCommand(platform, { tag: options.tag });
|
|
11290
11315
|
});
|
|
11291
11316
|
connection.command("list").alias("ls").description("List your connections").option("-s, --search <query>", "Filter connections by platform name").option("-l, --limit <n>", "Max connections to return (agent mode default: 20)").action(async (options) => {
|
|
11292
11317
|
await connectionListCommand(options);
|
|
@@ -11450,8 +11475,8 @@ program.command("whoami").description("Show the user, organization, and project
|
|
|
11450
11475
|
console.log(` ${pc14.dim("API:")} ${apiBase}`);
|
|
11451
11476
|
console.log();
|
|
11452
11477
|
});
|
|
11453
|
-
program.command("add [platform]").description("Shortcut for: connection add").action(async (platform) => {
|
|
11454
|
-
await connectionAddCommand(platform);
|
|
11478
|
+
program.command("add [platform]").description("Shortcut for: connection add").option("--tag <name>", "Tag the new connection (disambiguates multiple connections per platform in sync/flow profiles)").action(async (platform, options) => {
|
|
11479
|
+
await connectionAddCommand(platform, { tag: options.tag });
|
|
11455
11480
|
});
|
|
11456
11481
|
program.command("list").alias("ls").description("Shortcut for: connection list").option("-s, --search <query>", "Filter connections by platform name").option("-l, --limit <n>", "Max connections to return (agent mode default: 20)").action(async (options) => {
|
|
11457
11482
|
await connectionListCommand(options);
|
package/package.json
CHANGED
package/skills/one/SKILL.md
CHANGED
|
@@ -251,7 +251,7 @@ Without declared paths, the default walker concatenates every string in the reco
|
|
|
251
251
|
|
|
252
252
|
**Sync rejects custom actions** — profiles must use passthrough. `sync init` only surfaces passthrough models; `sync run` aborts if the list or enrich action is tagged `custom`. If no passthrough exists, compose a flow instead.
|
|
253
253
|
|
|
254
|
-
**Connections are late-bound** — profiles use `"connection": { "platform": "<name>" }`, not literal `connectionKey` strings. The key is resolved at sync time, so `one add <platform>` (re-auth) doesn't break the profile. For multi-account platforms, add `"tag": "<connection-tag>"` to disambiguate
|
|
254
|
+
**Connections are late-bound** — profiles use `"connection": { "platform": "<name>" }`, not literal `connectionKey` strings. The key is resolved at sync time, so `one add <platform>` (re-auth) doesn't break the profile. For multi-account platforms, add `"tag": "<connection-tag>"` to disambiguate, and create the tagged connection with `one add <platform> --tag <name>`. Don't hardcode connection keys in profiles.
|
|
255
255
|
|
|
256
256
|
**Advanced features** (enrich, transform, exclude, identityKey, hooks, --full-refresh, alternative backends, embedding tuning): run `one guide memory` or `one guide sync` for the full reference.
|
|
257
257
|
|
|
@@ -267,8 +267,9 @@ One also supports more advanced patterns. Read the relevant reference file befor
|
|
|
267
267
|
If the user needs a platform that isn't connected yet, tell them to run:
|
|
268
268
|
```bash
|
|
269
269
|
one add <platform>
|
|
270
|
+
one add <platform> --tag <name> # tag it (for multiple connections per platform)
|
|
270
271
|
```
|
|
271
|
-
This is interactive and opens the browser for OAuth. After connecting, the platform will appear in `one --agent connection list`.
|
|
272
|
+
This is interactive and opens the browser for OAuth. After connecting, the platform will appear in `one --agent connection list`. Use `--tag` when the user has (or will have) more than one connection for the same platform so sync/flow profiles can target a specific one via `"connection": { "platform": "<name>", "tag": "<name>" }`.
|
|
272
273
|
|
|
273
274
|
## Removing Connections
|
|
274
275
|
|
|
@@ -566,6 +566,22 @@ the outputSchema declaration.
|
|
|
566
566
|
|
|
567
567
|
Strategies: `fail` (default), `continue`, `retry`, `fallback`.
|
|
568
568
|
|
|
569
|
+
**Flow-level default (cli#93).** Set `defaultOnError` at the top of the flow and every step without its own `onError` inherits it — handy when most steps should `continue` (e.g. rendering/formatting pipelines) and you don't want to repeat it N times. A step opts out by declaring its own `onError`:
|
|
570
|
+
|
|
571
|
+
```json
|
|
572
|
+
{
|
|
573
|
+
"key": "render-report",
|
|
574
|
+
"defaultOnError": { "strategy": "continue" },
|
|
575
|
+
"steps": [
|
|
576
|
+
{ "id": "critical", "onError": { "strategy": "fail" }, ... }, // stays fatal
|
|
577
|
+
{ "id": "chart", ... }, // inherits continue
|
|
578
|
+
{ "id": "thumbnail", ... } // inherits continue
|
|
579
|
+
]
|
|
580
|
+
}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
Scoped per-flow: a sub-flow uses its own `defaultOnError`, not the parent's.
|
|
584
|
+
|
|
569
585
|
**Retry backoff.** By default each retry waits exactly `retryDelayMs`. For rate-limited APIs add `"backoff": "exponential"` (or `"exponential-jitter"`) and an optional `"maxDelayMs"` cap (defaults to 30000):
|
|
570
586
|
|
|
571
587
|
```json
|