@withone/cli 1.21.0 → 1.23.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
|
@@ -380,6 +380,17 @@ one config
|
|
|
380
380
|
|
|
381
381
|
Settings propagate automatically to all installed agent configs.
|
|
382
382
|
|
|
383
|
+
#### `one config skills status` / `one config skills sync`
|
|
384
|
+
|
|
385
|
+
`one init` copies the packaged skill files (`SKILL.md`, `references/`) into `~/.agents/skills/one/` and symlinks per-agent paths to that canonical directory. When the CLI self-updates, the skill files in the canonical dir would normally stay frozen at the version that was installed. To prevent stale docs, every CLI command checks a `.one-cli-version` marker in the canonical dir and silently refreshes the skill files if they don't match the running CLI version. No user action required.
|
|
386
|
+
|
|
387
|
+
| Command | What it does |
|
|
388
|
+
|---------|--------------|
|
|
389
|
+
| `one config skills status` | Show installed skill version, current CLI version, and path |
|
|
390
|
+
| `one config skills sync` | Force a re-copy of packaged skill files (for troubleshooting) |
|
|
391
|
+
|
|
392
|
+
Auto-sync refuses to resurrect skills if you opted out of skill installation during `one init` — the canonical dir has to already exist.
|
|
393
|
+
|
|
383
394
|
## The workflow
|
|
384
395
|
|
|
385
396
|
The power of One is in the workflow. Every interaction follows the same pattern:
|
|
@@ -447,6 +447,22 @@ var execAsync = promisify(exec);
|
|
|
447
447
|
function sleep2(ms) {
|
|
448
448
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
449
449
|
}
|
|
450
|
+
function computeRetryDelay(onError, attempt) {
|
|
451
|
+
const base = onError.retryDelayMs ?? 1e3;
|
|
452
|
+
const max = onError.maxDelayMs ?? 3e4;
|
|
453
|
+
const backoff = onError.backoff ?? "fixed";
|
|
454
|
+
const retryIndex = attempt - 2;
|
|
455
|
+
let delay;
|
|
456
|
+
if (backoff === "exponential" || backoff === "exponential-jitter") {
|
|
457
|
+
delay = Math.min(base * Math.pow(2, retryIndex), max);
|
|
458
|
+
if (backoff === "exponential-jitter") {
|
|
459
|
+
delay = delay * (0.5 + Math.random() * 0.5);
|
|
460
|
+
}
|
|
461
|
+
} else {
|
|
462
|
+
delay = base;
|
|
463
|
+
}
|
|
464
|
+
return Math.round(delay);
|
|
465
|
+
}
|
|
450
466
|
function resolveSelector(selectorPath, context) {
|
|
451
467
|
if (!selectorPath.startsWith("$.")) return selectorPath;
|
|
452
468
|
const parts = selectorPath.slice(2).split(/\.|\[/).map((p) => p.replace(/\]$/, ""));
|
|
@@ -474,12 +490,22 @@ function resolveSelector(selectorPath, context) {
|
|
|
474
490
|
}
|
|
475
491
|
return current;
|
|
476
492
|
}
|
|
493
|
+
function shellQuote(value) {
|
|
494
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
495
|
+
}
|
|
477
496
|
function interpolateString(str, context) {
|
|
478
|
-
return str.replace(/\{\{(\$\.[^}]+)\}\}/g, (_match, selector) => {
|
|
497
|
+
return str.replace(/\{\{\s*(q\s+)?(\$\.[^}\s]+)\s*\}\}/g, (_match, qFlag, selector) => {
|
|
479
498
|
const value = resolveSelector(selector, context);
|
|
480
|
-
if (value === void 0 || value === null) return "";
|
|
481
|
-
if (typeof value === "object")
|
|
482
|
-
|
|
499
|
+
if (value === void 0 || value === null) return qFlag ? `''` : "";
|
|
500
|
+
if (typeof value === "object") {
|
|
501
|
+
console.warn(
|
|
502
|
+
`[flow] WARNING: Handlebars expression "{{${qFlag ? "q " : ""}${selector}}}" resolved to ${Array.isArray(value) ? "an array" : "an object"} and was stringified as JSON. To pass objects/arrays as native values, use a direct selector without {{ }}: "${selector}"`
|
|
503
|
+
);
|
|
504
|
+
const json = JSON.stringify(value);
|
|
505
|
+
return qFlag ? shellQuote(json) : json;
|
|
506
|
+
}
|
|
507
|
+
const str2 = String(value);
|
|
508
|
+
return qFlag ? shellQuote(str2) : str2;
|
|
483
509
|
});
|
|
484
510
|
}
|
|
485
511
|
function resolveValue(value, context) {
|
|
@@ -487,7 +513,7 @@ function resolveValue(value, context) {
|
|
|
487
513
|
if (value.startsWith("$.") && !value.includes("{{")) {
|
|
488
514
|
return resolveSelector(value, context);
|
|
489
515
|
}
|
|
490
|
-
if (value.includes("{{$.")) {
|
|
516
|
+
if (value.includes("{{$.") || /\{\{\s*q\s+\$\./.test(value)) {
|
|
491
517
|
return interpolateString(value, context);
|
|
492
518
|
}
|
|
493
519
|
return value;
|
|
@@ -609,8 +635,8 @@ function executeTransformStep(step, context) {
|
|
|
609
635
|
async function executeCodeStep(step, context, options) {
|
|
610
636
|
const config = step.code;
|
|
611
637
|
if (config.module) {
|
|
612
|
-
const
|
|
613
|
-
return { status: "success", output
|
|
638
|
+
const output = await executeCodeModule(step.id, config.module, context, options);
|
|
639
|
+
return { status: "success", output, response: output };
|
|
614
640
|
}
|
|
615
641
|
if (typeof config.source !== "string") {
|
|
616
642
|
throw new Error(`Code step "${step.id}" must define either "source" or "module"`);
|
|
@@ -618,9 +644,41 @@ async function executeCodeStep(step, context, options) {
|
|
|
618
644
|
const AsyncFunction = Object.getPrototypeOf(async function() {
|
|
619
645
|
}).constructor;
|
|
620
646
|
const sandboxedRequire = createSandboxedRequire();
|
|
621
|
-
const
|
|
622
|
-
const
|
|
623
|
-
|
|
647
|
+
const sourceURL = `code:${step.id}`;
|
|
648
|
+
const taggedSource = `${config.source}
|
|
649
|
+
//# sourceURL=${sourceURL}`;
|
|
650
|
+
const fn = new AsyncFunction("$", "require", taggedSource);
|
|
651
|
+
try {
|
|
652
|
+
const output = await fn(context, sandboxedRequire);
|
|
653
|
+
return { status: "success", output, response: output };
|
|
654
|
+
} catch (err) {
|
|
655
|
+
throw rewriteCodeStepError(err, step.id, config.source, sourceURL);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
function rewriteCodeStepError(err, stepId, source, sourceURL) {
|
|
659
|
+
if (!(err instanceof Error)) return new Error(String(err));
|
|
660
|
+
const WRAPPER_LINE_OFFSET = 2;
|
|
661
|
+
const sourceLines = source.split("\n");
|
|
662
|
+
const stack = err.stack || "";
|
|
663
|
+
const re = new RegExp(`${sourceURL.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}:(\\d+):(\\d+)`);
|
|
664
|
+
const match = stack.match(re);
|
|
665
|
+
if (!match) {
|
|
666
|
+
err.message = `Code step "${stepId}" failed: ${err.message}`;
|
|
667
|
+
return err;
|
|
668
|
+
}
|
|
669
|
+
const wrappedLine = parseInt(match[1], 10);
|
|
670
|
+
const col = parseInt(match[2], 10);
|
|
671
|
+
const userLine = wrappedLine - WRAPPER_LINE_OFFSET;
|
|
672
|
+
const lineContent = sourceLines[userLine - 1] ?? "";
|
|
673
|
+
const trimmed = lineContent.trim();
|
|
674
|
+
err.message = `Code step "${stepId}" failed at line ${userLine}:${col}
|
|
675
|
+
${trimmed}
|
|
676
|
+
${err.message}`;
|
|
677
|
+
err.stack = stack.replace(
|
|
678
|
+
new RegExp(`(${sourceURL.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}:)(\\d+)`, "g"),
|
|
679
|
+
(_m, prefix, l) => `${prefix}${parseInt(l, 10) - WRAPPER_LINE_OFFSET}`
|
|
680
|
+
);
|
|
681
|
+
return err;
|
|
624
682
|
}
|
|
625
683
|
async function executeCodeModule(stepId, modulePath, context, options) {
|
|
626
684
|
const rootDir = options.rootDir;
|
|
@@ -840,7 +898,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
|
|
|
840
898
|
if (flowStack.includes(resolvedKey)) {
|
|
841
899
|
throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
|
|
842
900
|
}
|
|
843
|
-
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-
|
|
901
|
+
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-DFUFU2LM.js");
|
|
844
902
|
const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
|
|
845
903
|
const subContext = await executeFlow(
|
|
846
904
|
subFlow,
|
|
@@ -855,7 +913,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
|
|
|
855
913
|
return {
|
|
856
914
|
status: "success",
|
|
857
915
|
output: subContext.steps,
|
|
858
|
-
response: subContext
|
|
916
|
+
response: subContext.steps
|
|
859
917
|
};
|
|
860
918
|
}
|
|
861
919
|
async function executePaginateStep(step, context, api, permissions, allowedActionIds, options) {
|
|
@@ -951,13 +1009,14 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
|
|
|
951
1009
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
952
1010
|
try {
|
|
953
1011
|
if (attempt > 1) {
|
|
1012
|
+
const delay = computeRetryDelay(step.onError, attempt);
|
|
954
1013
|
options.onEvent?.({
|
|
955
1014
|
event: "step:retry",
|
|
956
1015
|
stepId: step.id,
|
|
957
1016
|
attempt,
|
|
958
|
-
maxRetries: step.onError.retries
|
|
1017
|
+
maxRetries: step.onError.retries,
|
|
1018
|
+
delayMs: delay
|
|
959
1019
|
});
|
|
960
|
-
const delay = step.onError?.retryDelayMs || 1e3;
|
|
961
1020
|
await sleep2(delay);
|
|
962
1021
|
}
|
|
963
1022
|
if (options.mock && (step.type === "action" || step.type === "paginate" || step.type === "bash")) {
|
|
@@ -1014,7 +1073,14 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
|
|
|
1014
1073
|
throw new Error(`Unknown step type: ${step.type}`);
|
|
1015
1074
|
}
|
|
1016
1075
|
result.durationMs = Date.now() - startTime;
|
|
1017
|
-
if (attempt > 1)
|
|
1076
|
+
if (attempt > 1) {
|
|
1077
|
+
result.retries = attempt - 1;
|
|
1078
|
+
options.onEvent?.({
|
|
1079
|
+
event: "step:retry-success",
|
|
1080
|
+
stepId: step.id,
|
|
1081
|
+
retries: attempt - 1
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1018
1084
|
context.steps[step.id] = result;
|
|
1019
1085
|
return result;
|
|
1020
1086
|
} catch (err) {
|
|
@@ -1026,11 +1092,13 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
|
|
|
1026
1092
|
}
|
|
1027
1093
|
const errorMessage = lastError?.message || "Unknown error";
|
|
1028
1094
|
const strategy = step.onError?.strategy || "fail";
|
|
1095
|
+
const retriesUsed = Math.max(0, maxAttempts - 1);
|
|
1029
1096
|
if (strategy === "continue") {
|
|
1030
1097
|
const result = {
|
|
1031
1098
|
status: "failed",
|
|
1032
1099
|
error: errorMessage,
|
|
1033
|
-
durationMs: Date.now() - startTime
|
|
1100
|
+
durationMs: Date.now() - startTime,
|
|
1101
|
+
retries: retriesUsed
|
|
1034
1102
|
};
|
|
1035
1103
|
context.steps[step.id] = result;
|
|
1036
1104
|
return result;
|
|
@@ -1039,7 +1107,8 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
|
|
|
1039
1107
|
const result = {
|
|
1040
1108
|
status: "failed",
|
|
1041
1109
|
error: errorMessage,
|
|
1042
|
-
durationMs: Date.now() - startTime
|
|
1110
|
+
durationMs: Date.now() - startTime,
|
|
1111
|
+
retries: retriesUsed
|
|
1043
1112
|
};
|
|
1044
1113
|
context.steps[step.id] = result;
|
|
1045
1114
|
return result;
|
|
@@ -1614,7 +1683,7 @@ Every step MUST have \`id\`, \`name\`, and \`type\`. The \`type\` determines whi
|
|
|
1614
1683
|
for (const [name, fd] of Object.entries(FLOW_SCHEMA.stepCommonFields)) {
|
|
1615
1684
|
sections.push(`| \`${name}\` | ${fd.type} | ${fd.required ? "yes" : "no"} | ${fd.description} |`);
|
|
1616
1685
|
}
|
|
1617
|
-
sections.push(`| \`onError\` | object | no | Error handling: \`{ "strategy": "${FLOW_SCHEMA.errorStrategies.join(" | ")}", "retries": 3, "retryDelayMs": 1000 }\` |`);
|
|
1686
|
+
sections.push(`| \`onError\` | object | no | Error handling: \`{ "strategy": "${FLOW_SCHEMA.errorStrategies.join(" | ")}", "retries": 3, "retryDelayMs": 1000, "backoff": "fixed \\| exponential \\| exponential-jitter", "maxDelayMs": 30000 }\` |`);
|
|
1618
1687
|
sections.push(`
|
|
1619
1688
|
## Step Types
|
|
1620
1689
|
|