@withone/cli 1.32.0 → 1.33.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/dist/index.js +140 -41
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
|
|
24
24
|
// src/index.ts
|
|
25
25
|
import { createRequire as createRequire2 } from "module";
|
|
26
|
+
import path16 from "path";
|
|
26
27
|
import { Command } from "commander";
|
|
27
28
|
|
|
28
29
|
// src/commands/init.ts
|
|
@@ -73,6 +74,19 @@ function resolveConfig() {
|
|
|
73
74
|
return { config: config2, scope: "project", path: projectPath, projectRoot, projectSlug };
|
|
74
75
|
}
|
|
75
76
|
}
|
|
77
|
+
const root = path.parse(process.cwd()).root;
|
|
78
|
+
let dir = path.resolve(process.cwd());
|
|
79
|
+
while (dir !== root) {
|
|
80
|
+
dir = path.dirname(dir);
|
|
81
|
+
const slug = getProjectSlug(dir);
|
|
82
|
+
const configPath = path.join(PROJECTS_DIR, slug, "config.json");
|
|
83
|
+
if (fs.existsSync(configPath)) {
|
|
84
|
+
const config2 = readConfigFile(configPath);
|
|
85
|
+
if (config2) {
|
|
86
|
+
return { config: config2, scope: "project", path: configPath, projectRoot: dir, projectSlug: slug };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
76
90
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
77
91
|
const config2 = readConfigFile(CONFIG_FILE);
|
|
78
92
|
if (config2) {
|
|
@@ -847,8 +861,16 @@ async function updateCommand() {
|
|
|
847
861
|
}
|
|
848
862
|
s.stop(`Update available: v${currentVersion} \u2192 v${latestVersion}`);
|
|
849
863
|
console.log(`Updating @withone/cli: v${currentVersion} \u2192 v${latestVersion}...`);
|
|
864
|
+
await new Promise((resolve) => {
|
|
865
|
+
const child = spawn("npm", ["cache", "clean", "--force"], {
|
|
866
|
+
stdio: "ignore",
|
|
867
|
+
shell: true
|
|
868
|
+
});
|
|
869
|
+
child.on("close", () => resolve());
|
|
870
|
+
child.on("error", () => resolve());
|
|
871
|
+
});
|
|
850
872
|
const code = await new Promise((resolve) => {
|
|
851
|
-
const child = spawn("npm", ["install", "-g",
|
|
873
|
+
const child = spawn("npm", ["install", "-g", `@withone/cli@${latestVersion}`, "--force"], {
|
|
852
874
|
stdio: isAgentMode() ? "pipe" : "inherit",
|
|
853
875
|
shell: true
|
|
854
876
|
});
|
|
@@ -2874,32 +2896,32 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
2874
2896
|
const validTypes = FLOW_SCHEMA.stepTypes.map((st) => st.type);
|
|
2875
2897
|
for (let i = 0; i < steps.length; i++) {
|
|
2876
2898
|
const step = steps[i];
|
|
2877
|
-
const
|
|
2899
|
+
const path17 = `${pathPrefix}[${i}]`;
|
|
2878
2900
|
if (!step || typeof step !== "object" || Array.isArray(step)) {
|
|
2879
|
-
errors.push({ path:
|
|
2901
|
+
errors.push({ path: path17, message: "Step must be an object" });
|
|
2880
2902
|
continue;
|
|
2881
2903
|
}
|
|
2882
2904
|
const s = step;
|
|
2883
2905
|
if (!s.id || typeof s.id !== "string") {
|
|
2884
|
-
errors.push({ path: `${
|
|
2906
|
+
errors.push({ path: `${path17}.id`, message: 'Step must have a string "id"' });
|
|
2885
2907
|
}
|
|
2886
2908
|
if (!s.name || typeof s.name !== "string") {
|
|
2887
|
-
errors.push({ path: `${
|
|
2909
|
+
errors.push({ path: `${path17}.name`, message: 'Step must have a string "name"' });
|
|
2888
2910
|
}
|
|
2889
2911
|
if (!s.type || !validTypes.includes(s.type)) {
|
|
2890
|
-
errors.push({ path: `${
|
|
2912
|
+
errors.push({ path: `${path17}.type`, message: `Step type must be one of: ${validTypes.join(", ")}` });
|
|
2891
2913
|
continue;
|
|
2892
2914
|
}
|
|
2893
2915
|
if (s.requires !== void 0) {
|
|
2894
2916
|
if (!Array.isArray(s.requires)) {
|
|
2895
|
-
errors.push({ path: `${
|
|
2917
|
+
errors.push({ path: `${path17}.requires`, message: '"requires" must be an array of selector strings (e.g. ["$.steps.foo.output.bar"])' });
|
|
2896
2918
|
} else {
|
|
2897
2919
|
for (let r = 0; r < s.requires.length; r++) {
|
|
2898
2920
|
const sel = s.requires[r];
|
|
2899
2921
|
if (typeof sel !== "string") {
|
|
2900
|
-
errors.push({ path: `${
|
|
2922
|
+
errors.push({ path: `${path17}.requires[${r}]`, message: '"requires" entry must be a selector string' });
|
|
2901
2923
|
} else if (!sel.startsWith("$.")) {
|
|
2902
|
-
errors.push({ path: `${
|
|
2924
|
+
errors.push({ path: `${path17}.requires[${r}]`, message: `"requires" entry "${sel}" must be a selector starting with "$." (e.g. "$.steps.foo.output.bar")` });
|
|
2903
2925
|
}
|
|
2904
2926
|
}
|
|
2905
2927
|
}
|
|
@@ -2907,7 +2929,7 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
2907
2929
|
if (s.onError && typeof s.onError === "object") {
|
|
2908
2930
|
const oe = s.onError;
|
|
2909
2931
|
if (!FLOW_SCHEMA.errorStrategies.includes(oe.strategy)) {
|
|
2910
|
-
errors.push({ path: `${
|
|
2932
|
+
errors.push({ path: `${path17}.onError.strategy`, message: `Error strategy must be one of: ${FLOW_SCHEMA.errorStrategies.join(", ")}` });
|
|
2911
2933
|
}
|
|
2912
2934
|
}
|
|
2913
2935
|
const descriptor = getStepTypeDescriptor(s.type);
|
|
@@ -2917,14 +2939,14 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
2917
2939
|
if (!configObj || typeof configObj !== "object") {
|
|
2918
2940
|
const hint = detectFlatConfigHint(s, descriptor);
|
|
2919
2941
|
errors.push({
|
|
2920
|
-
path: `${
|
|
2942
|
+
path: `${path17}.${configKey}`,
|
|
2921
2943
|
message: `${capitalize(descriptor.type)} step must have a "${configKey}" config object${hint}`
|
|
2922
2944
|
});
|
|
2923
2945
|
continue;
|
|
2924
2946
|
}
|
|
2925
2947
|
const config2 = configObj;
|
|
2926
2948
|
for (const [fieldName, fd] of Object.entries(descriptor.fields)) {
|
|
2927
|
-
const fieldPath = `${
|
|
2949
|
+
const fieldPath = `${path17}.${configKey}.${fieldName}`;
|
|
2928
2950
|
const value = config2[fieldName];
|
|
2929
2951
|
if (fd.required && (value === void 0 || value === null || value === "")) {
|
|
2930
2952
|
errors.push({ path: fieldPath, message: `${capitalize(descriptor.type)} must have ${fd.type === "string" ? "a string" : fd.type === "array" ? "a" : "a"} "${fieldName}"` });
|
|
@@ -2960,24 +2982,24 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
2960
2982
|
const hasSource = typeof config2.source === "string" && config2.source.length > 0;
|
|
2961
2983
|
const hasModule = typeof config2.module === "string" && config2.module.length > 0;
|
|
2962
2984
|
if (!hasSource && !hasModule) {
|
|
2963
|
-
errors.push({ path: `${
|
|
2985
|
+
errors.push({ path: `${path17}.${configKey}`, message: 'Code step must define either "source" (inline JS) or "module" (path to .mjs file)' });
|
|
2964
2986
|
} else if (hasSource && hasModule) {
|
|
2965
|
-
errors.push({ path: `${
|
|
2987
|
+
errors.push({ path: `${path17}.${configKey}`, message: 'Code step cannot define both "source" and "module" \u2014 pick one' });
|
|
2966
2988
|
}
|
|
2967
2989
|
if (hasModule) {
|
|
2968
2990
|
const m = config2.module;
|
|
2969
2991
|
if (m.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(m)) {
|
|
2970
|
-
errors.push({ path: `${
|
|
2992
|
+
errors.push({ path: `${path17}.${configKey}.module`, message: "Code module path must be relative to the flow folder (no absolute paths)" });
|
|
2971
2993
|
} else if (m.split(/[\\/]/).includes("..")) {
|
|
2972
|
-
errors.push({ path: `${
|
|
2994
|
+
errors.push({ path: `${path17}.${configKey}.module`, message: 'Code module path must not escape the flow folder ("..")' });
|
|
2973
2995
|
} else if (!m.endsWith(".mjs")) {
|
|
2974
|
-
errors.push({ path: `${
|
|
2996
|
+
errors.push({ path: `${path17}.${configKey}.module`, message: "Code module must be a .mjs file" });
|
|
2975
2997
|
}
|
|
2976
2998
|
}
|
|
2977
2999
|
if (hasSource) {
|
|
2978
3000
|
const syntaxError = checkCodeSourceSyntax(config2.source);
|
|
2979
3001
|
if (syntaxError) {
|
|
2980
|
-
errors.push({ path: `${
|
|
3002
|
+
errors.push({ path: `${path17}.${configKey}.source`, message: `Syntax error in code step: ${syntaxError}` });
|
|
2981
3003
|
}
|
|
2982
3004
|
}
|
|
2983
3005
|
}
|
|
@@ -3012,16 +3034,16 @@ function validateStepIds(flow2) {
|
|
|
3012
3034
|
function collectIds(steps, pathPrefix) {
|
|
3013
3035
|
for (let i = 0; i < steps.length; i++) {
|
|
3014
3036
|
const step = steps[i];
|
|
3015
|
-
const
|
|
3037
|
+
const path17 = `${pathPrefix}[${i}]`;
|
|
3016
3038
|
if (seen.has(step.id)) {
|
|
3017
|
-
errors.push({ path: `${
|
|
3039
|
+
errors.push({ path: `${path17}.id`, message: `Duplicate step ID: "${step.id}"` });
|
|
3018
3040
|
} else {
|
|
3019
3041
|
seen.add(step.id);
|
|
3020
3042
|
}
|
|
3021
3043
|
for (const { configKey, fieldName } of nestedKeys) {
|
|
3022
3044
|
const config2 = step[configKey];
|
|
3023
3045
|
if (config2 && Array.isArray(config2[fieldName])) {
|
|
3024
|
-
collectIds(config2[fieldName], `${
|
|
3046
|
+
collectIds(config2[fieldName], `${path17}.${configKey}.${fieldName}`);
|
|
3025
3047
|
}
|
|
3026
3048
|
}
|
|
3027
3049
|
}
|
|
@@ -3069,7 +3091,7 @@ function validateSelectorReferences(flow2) {
|
|
|
3069
3091
|
}
|
|
3070
3092
|
return selectors;
|
|
3071
3093
|
}
|
|
3072
|
-
function checkSelectors(selectors,
|
|
3094
|
+
function checkSelectors(selectors, path17, precedingStepIds) {
|
|
3073
3095
|
for (const selector of selectors) {
|
|
3074
3096
|
const parts = selector.split(".");
|
|
3075
3097
|
if (parts.length < 3) continue;
|
|
@@ -3077,15 +3099,15 @@ function validateSelectorReferences(flow2) {
|
|
|
3077
3099
|
if (root === "input") {
|
|
3078
3100
|
const inputName = parts[2];
|
|
3079
3101
|
if (!inputNames.has(inputName)) {
|
|
3080
|
-
errors.push({ path:
|
|
3102
|
+
errors.push({ path: path17, message: `Selector "${selector}" references undefined input "${inputName}"` });
|
|
3081
3103
|
}
|
|
3082
3104
|
} else if (root === "steps") {
|
|
3083
3105
|
const stepId = parts[2].replace(/[\[\]]/g, "").split(/[\[\]]/)[0];
|
|
3084
3106
|
if (!allStepIds.has(stepId)) {
|
|
3085
|
-
errors.push({ path:
|
|
3107
|
+
errors.push({ path: path17, message: `Selector "${selector}" references undefined step "${stepId}"` });
|
|
3086
3108
|
} else if (precedingStepIds && !precedingStepIds.has(stepId)) {
|
|
3087
3109
|
errors.push({
|
|
3088
|
-
path:
|
|
3110
|
+
path: path17,
|
|
3089
3111
|
message: `Selector "${selector}" references step "${stepId}" which is declared after the current step. Steps execute in declaration order, so this will always resolve to undefined at runtime \u2014 move the dependency earlier in the steps array.`
|
|
3090
3112
|
});
|
|
3091
3113
|
}
|
|
@@ -3093,20 +3115,20 @@ function validateSelectorReferences(flow2) {
|
|
|
3093
3115
|
}
|
|
3094
3116
|
}
|
|
3095
3117
|
const EXPRESSION_FIELDS = /* @__PURE__ */ new Set(["condition.expression", "while.condition"]);
|
|
3096
|
-
function checkOperatorsInSelectorField(value,
|
|
3118
|
+
function checkOperatorsInSelectorField(value, path17) {
|
|
3097
3119
|
if (typeof value === "string" && value.startsWith("$.")) {
|
|
3098
3120
|
if (value.includes("||")) {
|
|
3099
|
-
errors.push({ path:
|
|
3121
|
+
errors.push({ path: path17, message: `Selector "${value}" contains unsupported operator "||". Selectors in data fields use dot-path resolution, not JS evaluation. Use the "default" field on the input definition instead, or use a "code" step for complex expressions.` });
|
|
3100
3122
|
} else if (value.includes("&&")) {
|
|
3101
|
-
errors.push({ path:
|
|
3123
|
+
errors.push({ path: path17, message: `Selector "${value}" contains unsupported operator "&&". Selectors in data fields use dot-path resolution, not JS evaluation. Use a "condition" step or "code" step for complex expressions.` });
|
|
3102
3124
|
}
|
|
3103
3125
|
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
3104
3126
|
for (const [k, v] of Object.entries(value)) {
|
|
3105
|
-
checkOperatorsInSelectorField(v, `${
|
|
3127
|
+
checkOperatorsInSelectorField(v, `${path17}.${k}`);
|
|
3106
3128
|
}
|
|
3107
3129
|
} else if (Array.isArray(value)) {
|
|
3108
3130
|
for (let i = 0; i < value.length; i++) {
|
|
3109
|
-
checkOperatorsInSelectorField(value[i], `${
|
|
3131
|
+
checkOperatorsInSelectorField(value[i], `${path17}[${i}]`);
|
|
3110
3132
|
}
|
|
3111
3133
|
}
|
|
3112
3134
|
}
|
|
@@ -3174,10 +3196,10 @@ var VALID_OUTPUT_SCHEMA_TYPES = /* @__PURE__ */ new Set(["string", "number", "bo
|
|
|
3174
3196
|
function isOutputSchemaObject(v) {
|
|
3175
3197
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
3176
3198
|
}
|
|
3177
|
-
function walkOutputSchema(schema,
|
|
3199
|
+
function walkOutputSchema(schema, path17) {
|
|
3178
3200
|
let current = schema;
|
|
3179
|
-
for (let i = 0; i <
|
|
3180
|
-
const seg =
|
|
3201
|
+
for (let i = 0; i < path17.length; i++) {
|
|
3202
|
+
const seg = path17[i];
|
|
3181
3203
|
if (typeof current === "string") {
|
|
3182
3204
|
return current === "unknown" || current === "object" || current === "array" ? "opaque" : "opaque";
|
|
3183
3205
|
}
|
|
@@ -4946,8 +4968,8 @@ function sleep(ms) {
|
|
|
4946
4968
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4947
4969
|
}
|
|
4948
4970
|
function interpolate(template, record) {
|
|
4949
|
-
return template.replace(/\{?\{(\w+(?:\.\w+)*)\}\}?/g, (_,
|
|
4950
|
-
const parts =
|
|
4971
|
+
return template.replace(/\{?\{(\w+(?:\.\w+)*)\}\}?/g, (_, path17) => {
|
|
4972
|
+
const parts = path17.split(".");
|
|
4951
4973
|
let value = record;
|
|
4952
4974
|
for (const part of parts) {
|
|
4953
4975
|
if (typeof value !== "object" || value === null) return "";
|
|
@@ -4975,8 +4997,8 @@ function deepMerge(target, source) {
|
|
|
4975
4997
|
}
|
|
4976
4998
|
return result;
|
|
4977
4999
|
}
|
|
4978
|
-
function getByDotPath2(obj,
|
|
4979
|
-
const parts =
|
|
5000
|
+
function getByDotPath2(obj, path17) {
|
|
5001
|
+
const parts = path17.split(".");
|
|
4980
5002
|
let current = obj;
|
|
4981
5003
|
for (const part of parts) {
|
|
4982
5004
|
if (current === null || current === void 0 || typeof current !== "object") return void 0;
|
|
@@ -4985,8 +5007,8 @@ function getByDotPath2(obj, path16) {
|
|
|
4985
5007
|
return current;
|
|
4986
5008
|
}
|
|
4987
5009
|
function stripExcludedFields(obj, paths) {
|
|
4988
|
-
for (const
|
|
4989
|
-
stripOnePath(obj,
|
|
5010
|
+
for (const path17 of paths) {
|
|
5011
|
+
stripOnePath(obj, path17.replace(/\[\]/g, ".*").split("."));
|
|
4990
5012
|
}
|
|
4991
5013
|
}
|
|
4992
5014
|
function stripOnePath(obj, parts) {
|
|
@@ -5292,8 +5314,8 @@ function sleep2(ms) {
|
|
|
5292
5314
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5293
5315
|
}
|
|
5294
5316
|
function stripFields(record, paths) {
|
|
5295
|
-
for (const
|
|
5296
|
-
stripOnePath2(record,
|
|
5317
|
+
for (const path17 of paths) {
|
|
5318
|
+
stripOnePath2(record, path17.split("."));
|
|
5297
5319
|
}
|
|
5298
5320
|
}
|
|
5299
5321
|
function stripOnePath2(obj, parts) {
|
|
@@ -8820,6 +8842,83 @@ configSkills.command("status").description("Show installed skill version and whe
|
|
|
8820
8842
|
console.log(` current: ${status.currentVersion}`);
|
|
8821
8843
|
console.log(` path: ${status.canonicalPath}`);
|
|
8822
8844
|
});
|
|
8845
|
+
config.command("reset").description("Remove the project config for the current directory (falls back to global)").option("--global", "Remove the global config instead").action(async (opts) => {
|
|
8846
|
+
const resolved = resolveConfig();
|
|
8847
|
+
if (opts.global) {
|
|
8848
|
+
const globalPath = getGlobalConfigPath();
|
|
8849
|
+
if (!globalConfigExists()) {
|
|
8850
|
+
if (isAgentMode()) {
|
|
8851
|
+
json({ deleted: false, reason: "No global config found" });
|
|
8852
|
+
} else {
|
|
8853
|
+
console.log("No global config found.");
|
|
8854
|
+
}
|
|
8855
|
+
return;
|
|
8856
|
+
}
|
|
8857
|
+
if (!isAgentMode()) {
|
|
8858
|
+
const p8 = await import("@clack/prompts");
|
|
8859
|
+
const confirmed = await p8.confirm({
|
|
8860
|
+
message: "Delete the global config? This removes your API key for all projects without a project config.",
|
|
8861
|
+
initialValue: false
|
|
8862
|
+
});
|
|
8863
|
+
if (p8.isCancel(confirmed) || !confirmed) {
|
|
8864
|
+
console.log("Cancelled.");
|
|
8865
|
+
return;
|
|
8866
|
+
}
|
|
8867
|
+
}
|
|
8868
|
+
const fs17 = await import("fs");
|
|
8869
|
+
fs17.unlinkSync(globalPath);
|
|
8870
|
+
if (isAgentMode()) {
|
|
8871
|
+
json({ deleted: true, scope: "global" });
|
|
8872
|
+
} else {
|
|
8873
|
+
console.log("Global config removed.");
|
|
8874
|
+
}
|
|
8875
|
+
return;
|
|
8876
|
+
}
|
|
8877
|
+
if (resolved.scope !== "project") {
|
|
8878
|
+
if (isAgentMode()) {
|
|
8879
|
+
json({ deleted: false, reason: "No project config found for this directory" });
|
|
8880
|
+
} else {
|
|
8881
|
+
console.log("No project config found for this directory.");
|
|
8882
|
+
console.log(`Currently using: ${resolved.scope ?? "none"}`);
|
|
8883
|
+
}
|
|
8884
|
+
return;
|
|
8885
|
+
}
|
|
8886
|
+
const fs16 = await import("fs");
|
|
8887
|
+
const configContent = fs16.readFileSync(resolved.path, "utf-8");
|
|
8888
|
+
fs16.unlinkSync(resolved.path);
|
|
8889
|
+
const next = resolveConfig();
|
|
8890
|
+
fs16.mkdirSync(path16.dirname(resolved.path), { recursive: true });
|
|
8891
|
+
fs16.writeFileSync(resolved.path, configContent);
|
|
8892
|
+
let fallbackLabel;
|
|
8893
|
+
if (next.scope === "project") {
|
|
8894
|
+
fallbackLabel = `parent project config (${path16.basename(next.projectRoot)})`;
|
|
8895
|
+
} else if (next.scope === "global") {
|
|
8896
|
+
fallbackLabel = "global config";
|
|
8897
|
+
} else {
|
|
8898
|
+
fallbackLabel = "no config";
|
|
8899
|
+
}
|
|
8900
|
+
if (!isAgentMode()) {
|
|
8901
|
+
const p8 = await import("@clack/prompts");
|
|
8902
|
+
const confirmed = await p8.confirm({
|
|
8903
|
+
message: `Delete project config for ${path16.basename(resolved.projectRoot)}? Will fall back to ${fallbackLabel}.`,
|
|
8904
|
+
initialValue: false
|
|
8905
|
+
});
|
|
8906
|
+
if (p8.isCancel(confirmed) || !confirmed) {
|
|
8907
|
+
console.log("Cancelled.");
|
|
8908
|
+
return;
|
|
8909
|
+
}
|
|
8910
|
+
}
|
|
8911
|
+
fs16.unlinkSync(resolved.path);
|
|
8912
|
+
try {
|
|
8913
|
+
fs16.rmdirSync(path16.dirname(resolved.path));
|
|
8914
|
+
} catch {
|
|
8915
|
+
}
|
|
8916
|
+
if (isAgentMode()) {
|
|
8917
|
+
json({ deleted: true, scope: "project", projectRoot: resolved.projectRoot, fallback: next.scope });
|
|
8918
|
+
} else {
|
|
8919
|
+
console.log(`Project config removed. Now using ${fallbackLabel}.`);
|
|
8920
|
+
}
|
|
8921
|
+
});
|
|
8823
8922
|
var connection = program.command("connection").description("Manage connections");
|
|
8824
8923
|
connection.command("add [platform]").alias("a").description("Add a new connection").action(async (platform) => {
|
|
8825
8924
|
await connectionAddCommand(platform);
|