@withone/cli 1.47.6 → 1.47.8
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/dist/{chunk-SFGH6VAJ.js → chunk-KWGN3RJR.js} +12 -9
- package/dist/{flow-runner-KL2ATVBD.js → flow-runner-7VRNSKWU.js} +1 -1
- package/dist/index.js +17 -1
- package/dist/schema-DXHEU47V.js +109 -0
- package/package.json +1 -1
- package/skills/one/SKILL.md +1 -0
- package/skills/one/references/flows.md +16 -0
|
@@ -1289,7 +1289,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
|
|
|
1289
1289
|
if (flowStack.includes(resolvedKey)) {
|
|
1290
1290
|
throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
|
|
1291
1291
|
}
|
|
1292
|
-
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-
|
|
1292
|
+
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-7VRNSKWU.js");
|
|
1293
1293
|
const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
|
|
1294
1294
|
const subContext = await executeFlow(
|
|
1295
1295
|
subFlow,
|
|
@@ -1503,16 +1503,17 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
|
|
|
1503
1503
|
}
|
|
1504
1504
|
const startTime = Date.now();
|
|
1505
1505
|
let lastError;
|
|
1506
|
-
const
|
|
1506
|
+
const onError = step.onError ?? context._defaultOnError;
|
|
1507
|
+
const maxAttempts = onError?.strategy === "retry" && onError.retries ? onError.retries + 1 : 1;
|
|
1507
1508
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1508
1509
|
try {
|
|
1509
1510
|
if (attempt > 1) {
|
|
1510
|
-
const delay = computeRetryDelay(
|
|
1511
|
+
const delay = computeRetryDelay(onError, attempt);
|
|
1511
1512
|
options.onEvent?.({
|
|
1512
1513
|
event: "step:retry",
|
|
1513
1514
|
stepId: step.id,
|
|
1514
1515
|
attempt,
|
|
1515
|
-
maxRetries:
|
|
1516
|
+
maxRetries: onError.retries,
|
|
1516
1517
|
delayMs: delay
|
|
1517
1518
|
});
|
|
1518
1519
|
await sleep2(delay);
|
|
@@ -1599,8 +1600,8 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
|
|
|
1599
1600
|
if (attempt === maxAttempts) {
|
|
1600
1601
|
break;
|
|
1601
1602
|
}
|
|
1602
|
-
if (
|
|
1603
|
-
const decision = shouldRetryError(lastError,
|
|
1603
|
+
if (onError?.strategy === "retry" && (onError.retryOn || onError.failFastOn)) {
|
|
1604
|
+
const decision = shouldRetryError(lastError, onError);
|
|
1604
1605
|
if (!decision.retry) {
|
|
1605
1606
|
options.onEvent?.({
|
|
1606
1607
|
event: "step:retry-skip",
|
|
@@ -1614,7 +1615,7 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
|
|
|
1614
1615
|
}
|
|
1615
1616
|
}
|
|
1616
1617
|
const errorMessage = lastError?.message || "Unknown error";
|
|
1617
|
-
const strategy =
|
|
1618
|
+
const strategy = onError?.strategy || "fail";
|
|
1618
1619
|
const retriesUsed = Math.max(0, maxAttempts - 1);
|
|
1619
1620
|
const isTimeout = lastError instanceof StepTimeoutError;
|
|
1620
1621
|
const errorCode = lastError?.errorCode;
|
|
@@ -1629,7 +1630,7 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
|
|
|
1629
1630
|
context.steps[step.id] = result;
|
|
1630
1631
|
return result;
|
|
1631
1632
|
}
|
|
1632
|
-
if (strategy === "fallback" &&
|
|
1633
|
+
if (strategy === "fallback" && onError?.fallbackStepId) {
|
|
1633
1634
|
const result = {
|
|
1634
1635
|
status: isTimeout ? "timeout" : "failed",
|
|
1635
1636
|
error: errorMessage,
|
|
@@ -1750,6 +1751,7 @@ async function executeFlow(flow, inputs, api, permissions, allowedActionIds, opt
|
|
|
1750
1751
|
loop: {}
|
|
1751
1752
|
};
|
|
1752
1753
|
context.input = resolvedInputs;
|
|
1754
|
+
context._defaultOnError = flow.defaultOnError;
|
|
1753
1755
|
const completedStepIds = resumeState ? new Set(resumeState.completedSteps) : void 0;
|
|
1754
1756
|
if (options.dryRun && !options.mock) {
|
|
1755
1757
|
options.onEvent?.({
|
|
@@ -1807,7 +1809,8 @@ var FLOW_SCHEMA = {
|
|
|
1807
1809
|
description: { type: "string", required: false, description: "What this flow does" },
|
|
1808
1810
|
version: { type: "string", required: false, description: "Semver or arbitrary version string" },
|
|
1809
1811
|
inputs: { type: "object", required: true, description: "Input declarations (Record<string, InputDeclaration>)" },
|
|
1810
|
-
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`.' }
|
|
1811
1814
|
},
|
|
1812
1815
|
inputFields: {
|
|
1813
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";
|
|
@@ -3101,6 +3101,16 @@ function validateFlowSchema(flow2) {
|
|
|
3101
3101
|
if (f.version !== void 0 && typeof f.version !== "string") {
|
|
3102
3102
|
errors.push({ path: "version", message: '"version" must be a string' });
|
|
3103
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
|
+
}
|
|
3104
3114
|
if (!f.inputs || typeof f.inputs !== "object" || Array.isArray(f.inputs)) {
|
|
3105
3115
|
errors.push({ path: "inputs", message: 'Flow must have an "inputs" object' });
|
|
3106
3116
|
} else {
|
|
@@ -8290,6 +8300,10 @@ function registerSyncSubcommands(sync) {
|
|
|
8290
8300
|
sync.command("sql <platform/model> <sql>").description("Run a read-only SELECT / WITH / EXPLAIN against the memory store (type-scoped helper)").action(async (platformModel, sql) => {
|
|
8291
8301
|
await syncSqlCommand(platformModel, sql);
|
|
8292
8302
|
});
|
|
8303
|
+
sync.command("schema <platform/model>").description("Inspect the JSON structure of synced records (field paths, types, examples) \u2014 useful before writing `sync sql` queries").action(async (platformModel) => {
|
|
8304
|
+
const { syncSchemaCommand } = await import("./schema-DXHEU47V.js");
|
|
8305
|
+
await syncSchemaCommand(platformModel);
|
|
8306
|
+
});
|
|
8293
8307
|
sync.command("delete <platform/model>").description('Delete records from local sync data (e.g. one sync delete notion/pages --id "abc-123")').option("--id <value>", "Delete record by ID").option("--where <conditions>", 'Delete records matching conditions (e.g. "status=archived")').option("--where-sql <predicate>", `Delete using a raw SQL WHERE clause (e.g. "json_extract(data, '$.type') = 'promotion'")`).option("--yes", "Skip confirmation prompt").action(async (platformModel, options) => {
|
|
8294
8308
|
await syncDeleteCommand(platformModel, options);
|
|
8295
8309
|
});
|
|
@@ -10009,6 +10023,7 @@ one --agent sync run stripe
|
|
|
10009
10023
|
one --agent mem sync run stripe # identical (alias)
|
|
10010
10024
|
|
|
10011
10025
|
# 5. Query + search (read from memory)
|
|
10026
|
+
one --agent sync schema stripe/customers # inspect field paths/types first
|
|
10012
10027
|
one --agent sync query stripe/balanceTransactions --where "status=available" --limit 20
|
|
10013
10028
|
one --agent sync query stripe/customers --where 'address.city=SF' # dotted --where paths
|
|
10014
10029
|
one --agent sync search "refund" --platform stripe # hybrid FTS + semantic
|
|
@@ -10249,6 +10264,7 @@ Every \`sync X\` command is also exposed as \`mem sync X\` \u2014 same handlers,
|
|
|
10249
10264
|
| \`sync suggest-searchable <plat>/<model>\` | Rank candidate \`memory.searchable\` paths by signal density; emits paste-ready config |
|
|
10250
10265
|
| \`sync run <platform>\` | Sync data (\`--full-refresh\`, \`--since\`, \`--dry-run\`, \`--no-memory\`) |
|
|
10251
10266
|
| \`sync query <plat>/<model>\` | Query memory with \`--where\` (dotted paths), \`--after/before\` |
|
|
10267
|
+
| \`sync schema <plat>/<model>\` | Inspect the JSON structure of synced records (field paths, types, examples) \u2014 run before writing \`--where\` / query paths |
|
|
10252
10268
|
| \`sync search "<query>"\` | Hybrid FTS + semantic across all synced data |
|
|
10253
10269
|
| \`sync list [platform]\` | Show profiles, record counts, freshness |
|
|
10254
10270
|
| \`sync schedule add/list/status/remove/repair\` | Manage cron schedules |
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isAgentMode,
|
|
3
|
+
note,
|
|
4
|
+
okJson,
|
|
5
|
+
requireMemoryInit
|
|
6
|
+
} from "./chunk-HS5AHQ4V.js";
|
|
7
|
+
import {
|
|
8
|
+
getBackend
|
|
9
|
+
} from "./chunk-OADHUAEU.js";
|
|
10
|
+
import "./chunk-TXTRXV74.js";
|
|
11
|
+
import "./chunk-K6MWE2ZH.js";
|
|
12
|
+
|
|
13
|
+
// src/lib/memory/sync/schema.ts
|
|
14
|
+
import pc from "picocolors";
|
|
15
|
+
var SAMPLE_SIZE = 100;
|
|
16
|
+
function typeOf(v) {
|
|
17
|
+
if (v === null) return "null";
|
|
18
|
+
if (Array.isArray(v)) return "array";
|
|
19
|
+
return typeof v;
|
|
20
|
+
}
|
|
21
|
+
function exampleOf(v) {
|
|
22
|
+
if (typeof v === "string") return v.length > 60 ? `${v.slice(0, 57)}\u2026` : v;
|
|
23
|
+
return v;
|
|
24
|
+
}
|
|
25
|
+
function addPath(path, value, acc) {
|
|
26
|
+
const t = typeOf(value);
|
|
27
|
+
let typeLabel;
|
|
28
|
+
if (t === "array") {
|
|
29
|
+
const arr = value;
|
|
30
|
+
const elemType = arr.length ? typeOf(arr[0]) : "unknown";
|
|
31
|
+
typeLabel = `array[${elemType}]`;
|
|
32
|
+
} else {
|
|
33
|
+
typeLabel = t;
|
|
34
|
+
}
|
|
35
|
+
const entry = acc.get(path) ?? { types: /* @__PURE__ */ new Set(), hasExample: false, presence: 0 };
|
|
36
|
+
entry.types.add(typeLabel);
|
|
37
|
+
entry.presence += 1;
|
|
38
|
+
if (!entry.hasExample && t !== "object" && t !== "array" && value !== null && value !== void 0) {
|
|
39
|
+
entry.example = exampleOf(value);
|
|
40
|
+
entry.hasExample = true;
|
|
41
|
+
}
|
|
42
|
+
acc.set(path, entry);
|
|
43
|
+
if (t === "object") {
|
|
44
|
+
for (const [k, v] of Object.entries(value)) {
|
|
45
|
+
addPath(path ? `${path}.${k}` : k, v, acc);
|
|
46
|
+
}
|
|
47
|
+
} else if (t === "array") {
|
|
48
|
+
const arr = value;
|
|
49
|
+
if (arr.length && typeOf(arr[0]) === "object") {
|
|
50
|
+
for (const [k, v] of Object.entries(arr[0])) {
|
|
51
|
+
addPath(`${path}[].${k}`, v, acc);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function inferSyncSchema(dataRecords) {
|
|
57
|
+
const acc = /* @__PURE__ */ new Map();
|
|
58
|
+
for (const data of dataRecords) {
|
|
59
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) continue;
|
|
60
|
+
for (const [k, v] of Object.entries(data)) addPath(k, v, acc);
|
|
61
|
+
}
|
|
62
|
+
return [...acc.entries()].map(([path, e]) => ({
|
|
63
|
+
path,
|
|
64
|
+
types: [...e.types].sort(),
|
|
65
|
+
...e.hasExample ? { example: e.example } : {},
|
|
66
|
+
presence: e.presence
|
|
67
|
+
})).sort((a, b) => a.path.localeCompare(b.path));
|
|
68
|
+
}
|
|
69
|
+
async function syncSchemaCommand(platformModel, _options = {}) {
|
|
70
|
+
requireMemoryInit();
|
|
71
|
+
const type = platformModel;
|
|
72
|
+
const backend = await getBackend();
|
|
73
|
+
let recordCount = 0;
|
|
74
|
+
try {
|
|
75
|
+
recordCount = await backend.count(type, { status: "active" });
|
|
76
|
+
} catch {
|
|
77
|
+
}
|
|
78
|
+
const records = await backend.list(type, { limit: SAMPLE_SIZE, status: "active" });
|
|
79
|
+
if (records.length === 0) {
|
|
80
|
+
if (isAgentMode()) {
|
|
81
|
+
okJson({ type, recordCount: 0, sampled: 0, fields: [] });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
note(`No records found for "${type}". Run \`one sync run <platform>\` first, or check \`one --agent sync status\`.`, "Schema");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const fields = inferSyncSchema(records.map((r) => r.data));
|
|
88
|
+
if (isAgentMode()) {
|
|
89
|
+
okJson({ type, recordCount, sampled: records.length, fields });
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
console.log();
|
|
93
|
+
console.log(`${pc.bold(type)} ${pc.dim(`(${recordCount.toLocaleString()} record${recordCount === 1 ? "" : "s"}, sampled ${records.length})`)}`);
|
|
94
|
+
console.log();
|
|
95
|
+
const pathWidth = Math.min(Math.max(...fields.map((f) => f.path.length), 4) + 2, 50);
|
|
96
|
+
const typeWidth = Math.max(...fields.map((f) => f.types.join("|").length), 4) + 2;
|
|
97
|
+
for (const f of fields) {
|
|
98
|
+
const optional = f.presence < records.length ? pc.yellow(" ?") : "";
|
|
99
|
+
const ex = f.example !== void 0 ? pc.dim(JSON.stringify(f.example)) : "";
|
|
100
|
+
console.log(` ${f.path.padEnd(pathWidth)}${pc.cyan(f.types.join("|").padEnd(typeWidth))}${ex}${optional}`);
|
|
101
|
+
}
|
|
102
|
+
console.log();
|
|
103
|
+
console.log(pc.dim(` ? = present in only some sampled records (optional/sparse field)`));
|
|
104
|
+
console.log();
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
inferSyncSchema,
|
|
108
|
+
syncSchemaCommand
|
|
109
|
+
};
|
package/package.json
CHANGED
package/skills/one/SKILL.md
CHANGED
|
@@ -229,6 +229,7 @@ one --agent sync test attio/attioPeople --show-searchable
|
|
|
229
229
|
|
|
230
230
|
# Run — memory is always written; pass --no-memory to skip (rare)
|
|
231
231
|
one --agent sync run stripe
|
|
232
|
+
one --agent sync schema stripe/customers # inspect field paths/types before querying
|
|
232
233
|
one --agent sync query stripe/balanceTransactions --where "status=available" --limit 20
|
|
233
234
|
one --agent sync search "refund" # hybrid across all synced platforms
|
|
234
235
|
one --agent sync list stripe # progress + freshness
|
|
@@ -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
|