surf-cli 2.9.0 → 2.11.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 +61 -4
- package/dist/content/accessibility-tree.js +11 -0
- package/dist/content/accessibility-tree.js.map +1 -0
- package/dist/content/visual-indicator.js +111 -0
- package/dist/content/visual-indicator.js.map +1 -0
- package/dist/manifest.json +11 -2
- package/dist/options/options.js +3 -3
- package/dist/options/options.js.map +1 -1
- package/dist/service-worker/index.js +61 -261
- package/dist/service-worker/index.js.map +1 -1
- package/native/activity-journal.cjs +55 -0
- package/native/chatgpt-client-response.cjs +336 -0
- package/native/chatgpt-client-selection.cjs +119 -0
- package/native/chatgpt-client-ui.cjs +481 -0
- package/native/chatgpt-client.cjs +254 -664
- package/native/cli.cjs +100 -273
- package/native/do-executor.cjs +52 -475
- package/native/do-parser.cjs +8 -249
- package/native/host-helpers.cjs +32 -15
- package/native/host-sessions.cjs +6 -1
- package/native/host.cjs +228 -6
- package/native/network-export.cjs +20 -17
- package/native/network-store.cjs +38 -58
- package/native/oracle-cli.cjs +434 -0
- package/native/oracle-context.cjs +311 -0
- package/native/oracle-host.cjs +301 -0
- package/native/oracle-jobs.cjs +253 -0
- package/native/playbook-authoring.cjs +44 -0
- package/native/playbook-cli.cjs +157 -0
- package/native/playbook-client.cjs +259 -0
- package/native/playbook-receipts.cjs +109 -0
- package/native/playbook-records.cjs +208 -0
- package/native/playbook-runtime.cjs +177 -0
- package/native/playbooks.cjs +235 -0
- package/native/private-state.cjs +156 -0
- package/native/redaction.cjs +104 -0
- package/native/workflow-definition.cjs +369 -0
- package/native/workflow-runtime.cjs +225 -0
- package/package.json +2 -1
- package/playbooks/page/ops/read.json +22 -0
- package/playbooks/page/playbook.json +7 -0
- package/skills/surf/SKILL.md +72 -1
- package/dist/content/index.js +0 -116
- package/dist/content/index.js.map +0 -1
package/native/cli.cjs
CHANGED
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const fs = require("fs");
|
|
3
3
|
const path = require("path");
|
|
4
|
-
const os = require("os");
|
|
5
4
|
const { execFileSync, execSync } = require("child_process");
|
|
6
5
|
const { loadConfig, getConfigPath, createStarterConfig } = require("./config.cjs");
|
|
7
6
|
const networkFormatters = require("./formatters/network.cjs");
|
|
8
|
-
const
|
|
9
|
-
|
|
7
|
+
const {
|
|
8
|
+
applyArgDefaults,
|
|
9
|
+
formatStep,
|
|
10
|
+
getWorkflowDirs,
|
|
11
|
+
getWorkflowInfo,
|
|
12
|
+
listWorkflows,
|
|
13
|
+
normalizeWorkflow,
|
|
14
|
+
parseDoCommands,
|
|
15
|
+
resolveWorkflow,
|
|
16
|
+
validateWorkflowArgs,
|
|
17
|
+
validateWorkflowFile,
|
|
18
|
+
} = require("./workflow-definition.cjs");
|
|
10
19
|
const { executeDoSteps } = require("./do-executor.cjs");
|
|
11
20
|
const { openClientTransport } = require("./client-transport.cjs");
|
|
12
21
|
const { version: VERSION } = require("../package.json");
|
|
22
|
+
const {
|
|
23
|
+
formatOracleError,
|
|
24
|
+
formatOracleOutput,
|
|
25
|
+
handleOracleCli,
|
|
26
|
+
} = require("./oracle-cli.cjs");
|
|
27
|
+
const { formatPlaybookOutput, handlePlaybookCli, playbookCommandNeedsBrowser } = require("./playbook-cli.cjs");
|
|
13
28
|
|
|
14
29
|
const IS_WIN = process.platform === "win32";
|
|
15
30
|
const { SURF_TMP, formatSocketError } = require("./socket-path.cjs");
|
|
@@ -63,247 +78,26 @@ function installBrowserLock({ noLock, timeoutMs }, endpoint) {
|
|
|
63
78
|
});
|
|
64
79
|
}
|
|
65
80
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
*/
|
|
74
|
-
function getWorkflowDirs() {
|
|
75
|
-
return [
|
|
76
|
-
{ path: path.join(process.cwd(), '.surf', 'workflows'), scope: 'project' },
|
|
77
|
-
{ path: path.join(os.homedir(), '.surf', 'workflows'), scope: 'user' },
|
|
78
|
-
];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Resolve a workflow by name or path
|
|
83
|
-
* @param {string} nameOrPath - Workflow name or file path
|
|
84
|
-
* @returns {{ type: 'inline'|'file'|'not_found', content?: string, path?: string, name?: string }}
|
|
85
|
-
*/
|
|
86
|
-
function resolveWorkflow(nameOrPath) {
|
|
87
|
-
// Check if it's an inline workflow (contains pipe)
|
|
88
|
-
if (nameOrPath.includes('|')) {
|
|
89
|
-
return { type: 'inline', content: nameOrPath };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Check if it's a direct file path (with extension or path separator)
|
|
93
|
-
if (nameOrPath.includes('/') || nameOrPath.includes('\\') || nameOrPath.endsWith('.json')) {
|
|
94
|
-
if (fs.existsSync(nameOrPath)) {
|
|
95
|
-
return { type: 'file', path: nameOrPath };
|
|
96
|
-
}
|
|
97
|
-
return { type: 'not_found', name: nameOrPath };
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Look up by name in workflow directories
|
|
101
|
-
const searchDirs = getWorkflowDirs();
|
|
102
|
-
|
|
103
|
-
for (const { path: dir } of searchDirs) {
|
|
104
|
-
const filePath = path.join(dir, `${nameOrPath}.json`);
|
|
105
|
-
if (fs.existsSync(filePath)) {
|
|
106
|
-
return { type: 'file', path: filePath };
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return { type: 'not_found', name: nameOrPath };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* List all available workflows
|
|
115
|
-
* @returns {Array<{name: string, description: string, scope: string, path: string, args?: object}>}
|
|
116
|
-
*/
|
|
117
|
-
function listWorkflows() {
|
|
118
|
-
const workflows = [];
|
|
119
|
-
const searchDirs = getWorkflowDirs();
|
|
120
|
-
|
|
121
|
-
for (const { path: dir, scope } of searchDirs) {
|
|
122
|
-
if (fs.existsSync(dir)) {
|
|
123
|
-
try {
|
|
124
|
-
const files = fs.readdirSync(dir).filter(f => f.endsWith('.json'));
|
|
125
|
-
for (const file of files) {
|
|
126
|
-
const filePath = path.join(dir, file);
|
|
127
|
-
try {
|
|
128
|
-
const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
129
|
-
workflows.push({
|
|
130
|
-
name: content.name || file.replace('.json', ''),
|
|
131
|
-
description: content.description || '',
|
|
132
|
-
scope,
|
|
133
|
-
path: filePath,
|
|
134
|
-
args: content.args,
|
|
135
|
-
stepCount: content.steps?.length || 0,
|
|
136
|
-
});
|
|
137
|
-
} catch {
|
|
138
|
-
// Skip invalid JSON files
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
} catch {
|
|
142
|
-
// Skip inaccessible directories
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return workflows;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Get detailed info about a workflow
|
|
152
|
-
* @param {string} name - Workflow name
|
|
153
|
-
* @returns {{ error?: string, name?: string, description?: string, args?: object, steps?: Array, path?: string }}
|
|
154
|
-
*/
|
|
155
|
-
function getWorkflowInfo(name) {
|
|
156
|
-
const resolved = resolveWorkflow(name);
|
|
157
|
-
|
|
158
|
-
if (resolved.type === 'not_found') {
|
|
159
|
-
return { error: `Workflow not found: ${name}` };
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (resolved.type === 'inline') {
|
|
163
|
-
return { error: 'Cannot get info for inline workflows' };
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
try {
|
|
167
|
-
const content = JSON.parse(fs.readFileSync(resolved.path, 'utf8'));
|
|
168
|
-
return {
|
|
169
|
-
name: content.name || name,
|
|
170
|
-
description: content.description || '',
|
|
171
|
-
args: content.args || {},
|
|
172
|
-
steps: content.steps || [],
|
|
173
|
-
path: resolved.path,
|
|
174
|
-
};
|
|
175
|
-
} catch (e) {
|
|
176
|
-
return { error: `Failed to parse workflow: ${e.message}` };
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Validate workflow args against schema
|
|
182
|
-
* @param {object} workflow - Workflow with args schema
|
|
183
|
-
* @param {object} providedArgs - User-provided args
|
|
184
|
-
* @returns {string[]} - Array of error messages
|
|
185
|
-
*/
|
|
186
|
-
function validateWorkflowArgs(workflow, providedArgs) {
|
|
187
|
-
const errors = [];
|
|
188
|
-
if (workflow.args) {
|
|
189
|
-
for (const [name, spec] of Object.entries(workflow.args)) {
|
|
190
|
-
if (spec.required && providedArgs[name] === undefined) {
|
|
191
|
-
errors.push(`Missing required argument: --${name}`);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
return errors;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Apply default values to workflow args
|
|
200
|
-
* @param {object} workflow - Workflow with args schema
|
|
201
|
-
* @param {object} providedArgs - User-provided args
|
|
202
|
-
* @returns {object} - Args with defaults applied
|
|
203
|
-
*/
|
|
204
|
-
function applyArgDefaults(workflow, providedArgs) {
|
|
205
|
-
const vars = { ...providedArgs };
|
|
206
|
-
if (workflow.args) {
|
|
207
|
-
for (const [name, spec] of Object.entries(workflow.args)) {
|
|
208
|
-
if (vars[name] === undefined && spec.default !== undefined) {
|
|
209
|
-
vars[name] = spec.default;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
return vars;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Validate a workflow JSON file
|
|
218
|
-
* @param {string} filePath - Path to workflow file
|
|
219
|
-
* @returns {{ valid: boolean, error?: string, workflow?: object }}
|
|
220
|
-
*/
|
|
221
|
-
function validateWorkflowFile(filePath) {
|
|
222
|
-
if (!fs.existsSync(filePath)) {
|
|
223
|
-
return { valid: false, error: `File not found: ${filePath}` };
|
|
81
|
+
async function runWithBrowserLock(lockOptions, endpoint, operation) {
|
|
82
|
+
let releaseBrowserLock = () => {};
|
|
83
|
+
if (!lockOptions.noLock) {
|
|
84
|
+
const lock = acquireBrowserLock(endpoint.key, SURF_TMP, {
|
|
85
|
+
timeoutMs: lockOptions.timeoutMs,
|
|
86
|
+
});
|
|
87
|
+
releaseBrowserLock = lock.release;
|
|
224
88
|
}
|
|
225
|
-
|
|
89
|
+
const release = () => {
|
|
90
|
+
const releaseCurrent = releaseBrowserLock;
|
|
91
|
+
releaseBrowserLock = () => {};
|
|
92
|
+
releaseCurrent();
|
|
93
|
+
};
|
|
94
|
+
process.once("exit", release);
|
|
226
95
|
try {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (!workflow.steps || !Array.isArray(workflow.steps)) {
|
|
232
|
-
return { valid: false, error: "Workflow must have a 'steps' array" };
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (workflow.steps.length === 0) {
|
|
236
|
-
return { valid: false, error: "Workflow has no steps" };
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Validate each step
|
|
240
|
-
for (let i = 0; i < workflow.steps.length; i++) {
|
|
241
|
-
const step = workflow.steps[i];
|
|
242
|
-
|
|
243
|
-
// Check for loops
|
|
244
|
-
if (step.repeat !== undefined || step.each !== undefined) {
|
|
245
|
-
if (!step.steps || !Array.isArray(step.steps)) {
|
|
246
|
-
return { valid: false, error: `Step ${i + 1}: loop must have a 'steps' array` };
|
|
247
|
-
}
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Regular step must have tool/cmd
|
|
252
|
-
if (!step.tool && !step.cmd) {
|
|
253
|
-
return { valid: false, error: `Step ${i + 1}: must have 'tool' field` };
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Validate args schema if present
|
|
258
|
-
if (workflow.args && typeof workflow.args !== 'object') {
|
|
259
|
-
return { valid: false, error: "'args' must be an object" };
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return { valid: true, workflow };
|
|
263
|
-
} catch (e) {
|
|
264
|
-
return { valid: false, error: `Invalid JSON: ${e.message}` };
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Format a step for display
|
|
270
|
-
* @param {object} step - Workflow step
|
|
271
|
-
* @param {number} indent - Indentation level
|
|
272
|
-
* @returns {string}
|
|
273
|
-
*/
|
|
274
|
-
function formatStep(step, indent = 0) {
|
|
275
|
-
const pad = ' '.repeat(indent);
|
|
276
|
-
|
|
277
|
-
if (step.repeat !== undefined) {
|
|
278
|
-
const lines = [`${pad}repeat ${step.repeat} times:`];
|
|
279
|
-
for (const s of step.steps || []) {
|
|
280
|
-
lines.push(formatStep(s, indent + 1));
|
|
281
|
-
}
|
|
282
|
-
if (step.until) {
|
|
283
|
-
lines.push(`${pad} until: ${step.until.tool || step.until.cmd}`);
|
|
284
|
-
}
|
|
285
|
-
return lines.join('\n');
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (step.each !== undefined) {
|
|
289
|
-
const lines = [`${pad}each ${step.each} as ${step.as || 'item'}:`];
|
|
290
|
-
for (const s of step.steps || []) {
|
|
291
|
-
lines.push(formatStep(s, indent + 1));
|
|
292
|
-
}
|
|
293
|
-
return lines.join('\n');
|
|
96
|
+
return await operation();
|
|
97
|
+
} finally {
|
|
98
|
+
process.removeListener("exit", release);
|
|
99
|
+
release();
|
|
294
100
|
}
|
|
295
|
-
|
|
296
|
-
const tool = step.tool || step.cmd;
|
|
297
|
-
const args = step.args || {};
|
|
298
|
-
const argStr = Object.entries(args)
|
|
299
|
-
.map(([k, v]) => `${k}=${JSON.stringify(v)}`)
|
|
300
|
-
.join(' ');
|
|
301
|
-
|
|
302
|
-
let line = `${pad}${tool}`;
|
|
303
|
-
if (argStr) line += ` ${argStr}`;
|
|
304
|
-
if (step.as) line += ` → ${step.as}`;
|
|
305
|
-
|
|
306
|
-
return line;
|
|
307
101
|
}
|
|
308
102
|
|
|
309
103
|
// Cross-platform image resize (macOS: sips, Linux: ImageMagick)
|
|
@@ -386,6 +180,45 @@ try {
|
|
|
386
180
|
process.exit(1);
|
|
387
181
|
}
|
|
388
182
|
|
|
183
|
+
if (args[0] === "oracle") {
|
|
184
|
+
handleOracleCli(args, {
|
|
185
|
+
endpoint,
|
|
186
|
+
cwd: process.cwd(),
|
|
187
|
+
withBrowserLock: (operation) => runWithBrowserLock(
|
|
188
|
+
parseBrowserLockOptions(args.includes("--no-lock")),
|
|
189
|
+
endpoint,
|
|
190
|
+
operation,
|
|
191
|
+
),
|
|
192
|
+
})
|
|
193
|
+
.then((result) => {
|
|
194
|
+
if (!result.handled) throw new Error("Oracle command was not handled");
|
|
195
|
+
if (result.value !== undefined) console.log(formatOracleOutput(result.value, result.json));
|
|
196
|
+
process.exit(0);
|
|
197
|
+
})
|
|
198
|
+
.catch((error) => {
|
|
199
|
+
console.error(formatOracleError(error, args.includes("--json")));
|
|
200
|
+
process.exit(1);
|
|
201
|
+
});
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (["playbook", "pb", "use"].includes(args[0])) {
|
|
206
|
+
if (playbookCommandNeedsBrowser(args)) {
|
|
207
|
+
installBrowserLock(parseBrowserLockOptions(args.includes("--no-lock")), endpoint);
|
|
208
|
+
}
|
|
209
|
+
handlePlaybookCli(args, { endpoint, cwd: process.cwd() })
|
|
210
|
+
.then((result) => {
|
|
211
|
+
if (!result.handled) throw new Error("Playbook command was not handled");
|
|
212
|
+
if (result.value !== undefined) console.log(formatPlaybookOutput(result.value, result.json));
|
|
213
|
+
process.exit(0);
|
|
214
|
+
})
|
|
215
|
+
.catch((error) => {
|
|
216
|
+
console.error(`Error: ${error.message}`);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
});
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
389
222
|
const ALIASES = {
|
|
390
223
|
snap: "screenshot",
|
|
391
224
|
read: "page.read",
|
|
@@ -951,6 +784,9 @@ const TOOLS = {
|
|
|
951
784
|
all: "Show all (no limit)",
|
|
952
785
|
v: "Verbose output",
|
|
953
786
|
vv: "Very verbose output",
|
|
787
|
+
"body-mode": "Response bodies: none, text, or all (default: text)",
|
|
788
|
+
"per-body-bytes": "Maximum captured bytes per response body",
|
|
789
|
+
"total-body-bytes": "Maximum captured response-body bytes per tab session",
|
|
954
790
|
clear: "Clear after reading",
|
|
955
791
|
stream: "Continuous output"
|
|
956
792
|
},
|
|
@@ -1015,9 +851,17 @@ const TOOLS = {
|
|
|
1015
851
|
"network.export": {
|
|
1016
852
|
desc: "Export captured requests",
|
|
1017
853
|
args: [],
|
|
1018
|
-
opts: {
|
|
854
|
+
opts: {
|
|
855
|
+
har: "Export as HAR 1.2",
|
|
856
|
+
jsonl: "Export as JSONL",
|
|
857
|
+
output: "Output file path",
|
|
858
|
+
"body-mode": "Response bodies: none, text, or all (default: text)",
|
|
859
|
+
"per-body-bytes": "Maximum captured bytes per response body",
|
|
860
|
+
"total-body-bytes": "Maximum captured response-body bytes per tab session",
|
|
861
|
+
},
|
|
1019
862
|
examples: [
|
|
1020
|
-
{ cmd: "network.export --jsonl --output /tmp/requests.jsonl", desc: "Export as JSONL" }
|
|
863
|
+
{ cmd: "network.export --jsonl --output /tmp/requests.jsonl", desc: "Export as JSONL" },
|
|
864
|
+
{ cmd: "network.export --har --output /tmp/requests.har", desc: "Export as HAR" },
|
|
1021
865
|
]
|
|
1022
866
|
},
|
|
1023
867
|
"network.path": {
|
|
@@ -1701,6 +1545,7 @@ Common Commands:
|
|
|
1701
1545
|
search <term> Search for text in page (alias: find)
|
|
1702
1546
|
window.new <url> Create isolated browser window
|
|
1703
1547
|
doctor Diagnose native host/socket setup
|
|
1548
|
+
oracle ask <prompt> Start a durable ChatGPT consult
|
|
1704
1549
|
wait <seconds> Wait N seconds
|
|
1705
1550
|
|
|
1706
1551
|
Quick Examples:
|
|
@@ -1760,6 +1605,13 @@ const showFullHelp = () => {
|
|
|
1760
1605
|
|
|
1761
1606
|
Usage: surf <command> [args] [options]
|
|
1762
1607
|
|
|
1608
|
+
Oracle:
|
|
1609
|
+
surf oracle <ask|status|result|follow|list>
|
|
1610
|
+
|
|
1611
|
+
Playbooks:
|
|
1612
|
+
surf playbook|pb <list|show|ops|run|record|suggest|save|client|trace|export|import>
|
|
1613
|
+
surf use <playbook> <op> [--arg value]
|
|
1614
|
+
|
|
1763
1615
|
`);
|
|
1764
1616
|
for (const [groupName, group] of Object.entries(TOOLS)) {
|
|
1765
1617
|
console.log(`${groupName.toUpperCase()} - ${group.desc}`);
|
|
@@ -2402,9 +2254,7 @@ if (args[0] === "do") {
|
|
|
2402
2254
|
|
|
2403
2255
|
// Process workflow file if loaded
|
|
2404
2256
|
if (workflow) {
|
|
2405
|
-
|
|
2406
|
-
throw new Error("Workflow must have a 'steps' array");
|
|
2407
|
-
}
|
|
2257
|
+
workflow = normalizeWorkflow(workflow);
|
|
2408
2258
|
|
|
2409
2259
|
// Validate required args
|
|
2410
2260
|
const argErrors = validateWorkflowArgs(workflow, workflowArgs);
|
|
@@ -2424,30 +2274,7 @@ if (args[0] === "do") {
|
|
|
2424
2274
|
process.exit(1);
|
|
2425
2275
|
}
|
|
2426
2276
|
|
|
2427
|
-
|
|
2428
|
-
// Also preserve loop steps as-is
|
|
2429
|
-
steps = workflow.steps.map(s => {
|
|
2430
|
-
if (s.repeat !== undefined || s.each !== undefined) {
|
|
2431
|
-
// Loop step - convert nested steps recursively
|
|
2432
|
-
const convertSteps = (stepsArr) => stepsArr.map(ns => {
|
|
2433
|
-
if (ns.repeat !== undefined || ns.each !== undefined) {
|
|
2434
|
-
// Recursively convert nested loop steps and until condition
|
|
2435
|
-
return {
|
|
2436
|
-
...ns,
|
|
2437
|
-
steps: convertSteps(ns.steps || []),
|
|
2438
|
-
until: ns.until ? { cmd: ns.until.tool || ns.until.cmd, args: ns.until.args || {} } : undefined
|
|
2439
|
-
};
|
|
2440
|
-
}
|
|
2441
|
-
return { cmd: ns.tool || ns.cmd, args: ns.args || {}, as: ns.as };
|
|
2442
|
-
});
|
|
2443
|
-
return {
|
|
2444
|
-
...s,
|
|
2445
|
-
steps: convertSteps(s.steps || []),
|
|
2446
|
-
until: s.until ? { cmd: s.until.tool || s.until.cmd, args: s.until.args || {} } : undefined
|
|
2447
|
-
};
|
|
2448
|
-
}
|
|
2449
|
-
return { cmd: s.tool || s.cmd, args: s.args || {}, as: s.as };
|
|
2450
|
-
});
|
|
2277
|
+
steps = workflow.steps;
|
|
2451
2278
|
}
|
|
2452
2279
|
} catch (e) {
|
|
2453
2280
|
console.error(`Error: Failed to parse workflow: ${e.message}`);
|
|
@@ -2948,9 +2775,9 @@ if (toolArgs["window-id"] !== undefined) {
|
|
|
2948
2775
|
globalOpts.windowId = wid;
|
|
2949
2776
|
delete toolArgs["window-id"];
|
|
2950
2777
|
}
|
|
2951
|
-
if (toolArgs["network-path"] !== undefined) {
|
|
2952
|
-
|
|
2953
|
-
|
|
2778
|
+
if (toolArgs["network-path"] !== undefined && typeof toolArgs["network-path"] !== "string") {
|
|
2779
|
+
console.error("Error: --network-path requires a directory");
|
|
2780
|
+
process.exit(1);
|
|
2954
2781
|
}
|
|
2955
2782
|
const wantJson = toolArgs.json === true;
|
|
2956
2783
|
delete toolArgs.json;
|