suzi-cli 0.1.28 → 0.1.30
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/CLAUDE.md +0 -2
- package/README.md +9 -16
- package/dist/commands/accounts.d.ts.map +1 -1
- package/dist/commands/accounts.js +23 -4
- package/dist/commands/accounts.js.map +1 -1
- package/dist/commands/agent.d.ts +7 -0
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +94 -54
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/env.js +41 -21
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/feedback.d.ts.map +1 -1
- package/dist/commands/feedback.js +14 -1
- package/dist/commands/feedback.js.map +1 -1
- package/dist/commands/import.d.ts.map +1 -1
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install-hooks.js.map +1 -1
- package/dist/commands/list-actions.d.ts.map +1 -1
- package/dist/commands/list-actions.js +65 -32
- package/dist/commands/list-actions.js.map +1 -1
- package/dist/commands/list-triggers.d.ts.map +1 -1
- package/dist/commands/list-triggers.js +69 -43
- package/dist/commands/list-triggers.js.map +1 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +74 -68
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/portfolio.d.ts.map +1 -1
- package/dist/commands/portfolio.js +72 -17
- package/dist/commands/portfolio.js.map +1 -1
- package/dist/commands/preferences.d.ts.map +1 -1
- package/dist/commands/preferences.js +8 -0
- package/dist/commands/preferences.js.map +1 -1
- package/dist/commands/run.d.ts +33 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +805 -174
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/run.test.d.ts +2 -0
- package/dist/commands/run.test.d.ts.map +1 -0
- package/dist/commands/run.test.js +84 -0
- package/dist/commands/run.test.js.map +1 -0
- package/dist/commands/share.js.map +1 -1
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +78 -41
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/transactions.d.ts.map +1 -1
- package/dist/commands/transactions.js +1 -58
- package/dist/commands/transactions.js.map +1 -1
- package/dist/index.js +186 -55
- package/dist/index.js.map +1 -1
- package/dist/lib/actions-cache.d.ts +2 -0
- package/dist/lib/actions-cache.d.ts.map +1 -1
- package/dist/lib/actions-cache.js +9 -1
- package/dist/lib/actions-cache.js.map +1 -1
- package/dist/lib/api.d.ts +15 -5
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +17 -16
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/session-registry.d.ts +1 -1
- package/dist/lib/session-registry.d.ts.map +1 -1
- package/dist/lib/session-registry.js +1 -1
- package/dist/lib/session-registry.js.map +1 -1
- package/dist/lib/skills.d.ts +20 -11
- package/dist/lib/skills.d.ts.map +1 -1
- package/dist/lib/skills.js +77 -53
- package/dist/lib/skills.js.map +1 -1
- package/dist/lib/skills.test.d.ts +2 -0
- package/dist/lib/skills.test.d.ts.map +1 -0
- package/dist/lib/skills.test.js +102 -0
- package/dist/lib/skills.test.js.map +1 -0
- package/dist/lib/tooling-preflight.d.ts.map +1 -1
- package/dist/lib/tooling-preflight.js +0 -9
- package/dist/lib/tooling-preflight.js.map +1 -1
- package/dist/utils/agent-picker.d.ts.map +1 -1
- package/dist/utils/agent-picker.js +7 -41
- package/dist/utils/agent-picker.js.map +1 -1
- package/dist/utils/resolver.d.ts.map +1 -1
- package/dist/utils/resolver.js +11 -7
- package/dist/utils/resolver.js.map +1 -1
- package/dist/utils/tty.d.ts.map +1 -1
- package/dist/utils/tty.js.map +1 -1
- package/dist/utils/ui.d.ts +2 -0
- package/dist/utils/ui.d.ts.map +1 -1
- package/dist/utils/ui.js +28 -7
- package/dist/utils/ui.js.map +1 -1
- package/package.json +1 -1
- package/TEST_CHECKLIST.md +0 -243
- package/dist/commands/memory.d.ts +0 -3
- package/dist/commands/memory.d.ts.map +0 -1
- package/dist/commands/memory.js +0 -196
- package/dist/commands/memory.js.map +0 -1
- package/dist/commands/prompt-suggestions.d.ts +0 -3
- package/dist/commands/prompt-suggestions.d.ts.map +0 -1
- package/dist/commands/prompt-suggestions.js +0 -161
- package/dist/commands/prompt-suggestions.js.map +0 -1
- package/dist/commands/subagents.d.ts +0 -3
- package/dist/commands/subagents.d.ts.map +0 -1
- package/dist/commands/subagents.js +0 -206
- package/dist/commands/subagents.js.map +0 -1
package/dist/commands/run.js
CHANGED
|
@@ -3,59 +3,76 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseDynamicArgs = parseDynamicArgs;
|
|
7
|
+
exports.validateParamsAgainstSchema = validateParamsAgainstSchema;
|
|
8
|
+
exports.maybeHandleRunActionHelp = maybeHandleRunActionHelp;
|
|
9
|
+
exports.executeRunCommand = executeRunCommand;
|
|
6
10
|
exports.registerRunCommand = registerRunCommand;
|
|
11
|
+
const commander_1 = require("commander");
|
|
7
12
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
13
|
const api_1 = require("../lib/api");
|
|
14
|
+
const api_errors_1 = require("../lib/api-errors");
|
|
15
|
+
const actions_cache_1 = require("../lib/actions-cache");
|
|
9
16
|
const ui_1 = require("../utils/ui");
|
|
10
17
|
const tty_1 = require("../utils/tty");
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const
|
|
18
|
+
const KNOWN_OPTIONS = new Set(['params', 'json', 'output', 'dry-run', 'no-input', 'help']);
|
|
19
|
+
const KNOWN_WITH_VALUE = new Set(['params', 'output']);
|
|
20
|
+
const TRUTHY = new Set(['true', 'yes', 'on']);
|
|
21
|
+
const FALSY = new Set(['false', 'no', 'off']);
|
|
15
22
|
/**
|
|
16
|
-
* Convert kebab-case to camelCase.
|
|
17
|
-
* e.g. "token-id"
|
|
23
|
+
* Convert kebab-case to camelCase. Passthrough if no hyphens.
|
|
24
|
+
* e.g. "token-id" -> "tokenId", "tokenID" -> "tokenID"
|
|
18
25
|
*/
|
|
19
26
|
function kebabToCamel(str) {
|
|
20
27
|
return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
21
28
|
}
|
|
29
|
+
function camelToKebab(str) {
|
|
30
|
+
return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
31
|
+
}
|
|
22
32
|
/**
|
|
23
33
|
* Resolve a param key against the action's JSON Schema property names.
|
|
24
34
|
* Uses case-insensitive matching so "tokenid" resolves to "tokenID".
|
|
25
35
|
*/
|
|
26
36
|
function resolveParamKey(inputKey, schemaProps) {
|
|
27
|
-
// Exact match
|
|
28
37
|
if (schemaProps.includes(inputKey))
|
|
29
38
|
return inputKey;
|
|
30
|
-
// Case-insensitive match
|
|
31
39
|
const lower = inputKey.toLowerCase();
|
|
32
|
-
const match = schemaProps.find((
|
|
40
|
+
const match = schemaProps.find((prop) => prop.toLowerCase() === lower);
|
|
33
41
|
if (match)
|
|
34
42
|
return match;
|
|
35
43
|
return inputKey;
|
|
36
44
|
}
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
function getSchemaVariants(schema) {
|
|
46
|
+
if (Array.isArray(schema.anyOf)) {
|
|
47
|
+
return schema.anyOf.filter((variant) => Boolean(variant) && typeof variant === 'object' && !Array.isArray(variant));
|
|
48
|
+
}
|
|
49
|
+
if (Array.isArray(schema.oneOf)) {
|
|
50
|
+
return schema.oneOf.filter((variant) => Boolean(variant) && typeof variant === 'object' && !Array.isArray(variant));
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(schema.type)) {
|
|
53
|
+
return schema.type
|
|
54
|
+
.filter((type) => typeof type === 'string')
|
|
55
|
+
.map((type) => ({ type }));
|
|
56
|
+
}
|
|
57
|
+
return [schema];
|
|
58
|
+
}
|
|
39
59
|
/**
|
|
40
60
|
* Heuristic type coercion for CLI string values.
|
|
41
61
|
*/
|
|
42
62
|
function coerceValue(raw) {
|
|
43
|
-
// JSON objects/arrays
|
|
44
63
|
if ((raw.startsWith('{') && raw.endsWith('}')) || (raw.startsWith('[') && raw.endsWith(']'))) {
|
|
45
64
|
try {
|
|
46
65
|
return JSON.parse(raw);
|
|
47
66
|
}
|
|
48
67
|
catch {
|
|
49
|
-
|
|
68
|
+
// Keep as string.
|
|
50
69
|
}
|
|
51
70
|
}
|
|
52
|
-
// Numbers first — "1" and "0" should be numbers, not booleans
|
|
53
71
|
if (/^-?\d+(\.\d+)?$/.test(raw) && raw.length <= 15) {
|
|
54
72
|
const num = Number(raw);
|
|
55
|
-
if (!isNaN(num) && isFinite(num))
|
|
73
|
+
if (!Number.isNaN(num) && Number.isFinite(num))
|
|
56
74
|
return num;
|
|
57
75
|
}
|
|
58
|
-
// Booleans (only non-numeric truthy/falsy values)
|
|
59
76
|
if (TRUTHY.has(raw.toLowerCase()))
|
|
60
77
|
return true;
|
|
61
78
|
if (FALSY.has(raw.toLowerCase()))
|
|
@@ -63,106 +80,371 @@ function coerceValue(raw) {
|
|
|
63
80
|
return raw;
|
|
64
81
|
}
|
|
65
82
|
/**
|
|
66
|
-
* Schema-aware type coercion: uses the JSON Schema type to decide.
|
|
83
|
+
* Schema-aware type coercion: uses the JSON Schema type (and anyOf/oneOf) to decide.
|
|
67
84
|
*/
|
|
68
85
|
function coerceWithSchema(raw, propSchema) {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (type === 'object' || type === 'array') {
|
|
79
|
-
try {
|
|
80
|
-
return JSON.parse(raw);
|
|
86
|
+
const variants = getSchemaVariants(propSchema);
|
|
87
|
+
for (const variant of variants) {
|
|
88
|
+
const type = variant.type;
|
|
89
|
+
if (type === 'number' || type === 'integer') {
|
|
90
|
+
const num = Number(raw);
|
|
91
|
+
if (!Number.isNaN(num)) {
|
|
92
|
+
return type === 'integer' ? Math.trunc(num) : num;
|
|
93
|
+
}
|
|
94
|
+
continue;
|
|
81
95
|
}
|
|
82
|
-
|
|
96
|
+
if (type === 'boolean') {
|
|
97
|
+
const lower = raw.toLowerCase();
|
|
98
|
+
if (TRUTHY.has(lower))
|
|
99
|
+
return true;
|
|
100
|
+
if (FALSY.has(lower))
|
|
101
|
+
return false;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (type === 'object' || type === 'array') {
|
|
105
|
+
try {
|
|
106
|
+
return JSON.parse(raw);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (type === 'string') {
|
|
83
113
|
return raw;
|
|
84
114
|
}
|
|
85
115
|
}
|
|
86
116
|
return coerceValue(raw);
|
|
87
117
|
}
|
|
118
|
+
function isNumericToken(token) {
|
|
119
|
+
return /^-?\d+(\.\d+)?$/.test(token);
|
|
120
|
+
}
|
|
121
|
+
function isOptionToken(token) {
|
|
122
|
+
if (!token.startsWith('-'))
|
|
123
|
+
return false;
|
|
124
|
+
return !isNumericToken(token);
|
|
125
|
+
}
|
|
88
126
|
/**
|
|
89
|
-
* Parse dynamic
|
|
127
|
+
* Parse dynamic params from process.argv, skipping run-command options.
|
|
128
|
+
* Supports both `--amount 1` and `amount 1`.
|
|
90
129
|
*/
|
|
91
130
|
function parseDynamicArgs(argv) {
|
|
92
131
|
const runIdx = argv.indexOf('run');
|
|
93
|
-
if (runIdx === -1)
|
|
94
|
-
return {};
|
|
95
|
-
|
|
132
|
+
if (runIdx === -1) {
|
|
133
|
+
return { params: {}, positionalPairs: [], strayTokens: [] };
|
|
134
|
+
}
|
|
96
135
|
const args = argv.slice(runIdx + 2);
|
|
97
136
|
const params = {};
|
|
137
|
+
const positionalPairs = [];
|
|
138
|
+
const strayTokens = [];
|
|
98
139
|
let i = 0;
|
|
99
140
|
while (i < args.length) {
|
|
100
141
|
const arg = args[i];
|
|
101
|
-
if (
|
|
142
|
+
if (arg === '--') {
|
|
102
143
|
i++;
|
|
103
144
|
continue;
|
|
104
145
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
i++;
|
|
109
|
-
if (KNOWN_WITH_VALUE.has(rawKey) && i < args.length && !args[i].startsWith('--')) {
|
|
146
|
+
if (arg.startsWith('--')) {
|
|
147
|
+
const rawKey = arg.slice(2);
|
|
148
|
+
if (KNOWN_OPTIONS.has(rawKey)) {
|
|
110
149
|
i++;
|
|
150
|
+
if (KNOWN_WITH_VALUE.has(rawKey) &&
|
|
151
|
+
i < args.length &&
|
|
152
|
+
!isOptionToken(args[i])) {
|
|
153
|
+
i++;
|
|
154
|
+
}
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const camelKey = kebabToCamel(rawKey);
|
|
158
|
+
const nextArg = args[i + 1];
|
|
159
|
+
if (nextArg === undefined || isOptionToken(nextArg)) {
|
|
160
|
+
params[camelKey] = true;
|
|
161
|
+
i++;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
params[camelKey] = coerceValue(nextArg);
|
|
165
|
+
i += 2;
|
|
111
166
|
}
|
|
112
167
|
continue;
|
|
113
168
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (nextArg === undefined || nextArg.startsWith('--')) {
|
|
117
|
-
params[camelKey] = true;
|
|
169
|
+
if (arg.startsWith('-')) {
|
|
170
|
+
strayTokens.push(arg);
|
|
118
171
|
i++;
|
|
172
|
+
continue;
|
|
119
173
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
174
|
+
const nextArg = args[i + 1];
|
|
175
|
+
if (nextArg === undefined || isOptionToken(nextArg)) {
|
|
176
|
+
strayTokens.push(arg);
|
|
177
|
+
i++;
|
|
178
|
+
continue;
|
|
123
179
|
}
|
|
180
|
+
const camelKey = kebabToCamel(arg);
|
|
181
|
+
params[camelKey] = coerceValue(nextArg);
|
|
182
|
+
positionalPairs.push({ key: camelKey, value: nextArg });
|
|
183
|
+
i += 2;
|
|
124
184
|
}
|
|
125
|
-
return params;
|
|
185
|
+
return { params, positionalPairs, strayTokens };
|
|
126
186
|
}
|
|
127
187
|
/**
|
|
128
188
|
* Apply schema-aware key resolution and type coercion to params.
|
|
129
189
|
*/
|
|
130
190
|
function refineParamsWithSchema(params, action) {
|
|
131
191
|
const schemaProps = (0, actions_cache_1.getSchemaProperties)(action);
|
|
132
|
-
if (schemaProps.length === 0)
|
|
133
|
-
return params;
|
|
192
|
+
if (schemaProps.length === 0) {
|
|
193
|
+
return { params, unknownKeys: [] };
|
|
194
|
+
}
|
|
134
195
|
const paramSchema = action.parameters;
|
|
135
196
|
const refined = {};
|
|
136
|
-
|
|
197
|
+
const unknownKeys = [];
|
|
198
|
+
const allowsAdditionalProperties = paramSchema.additionalProperties === true;
|
|
199
|
+
for (const [key, value] of Object.entries(params)) {
|
|
137
200
|
const resolvedKey = resolveParamKey(key, schemaProps);
|
|
138
201
|
const propDef = paramSchema.properties?.[resolvedKey];
|
|
139
|
-
if (propDef
|
|
140
|
-
|
|
202
|
+
if (!propDef) {
|
|
203
|
+
if (allowsAdditionalProperties) {
|
|
204
|
+
refined[key] = value;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
unknownKeys.push(key);
|
|
208
|
+
}
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (typeof value === 'string') {
|
|
212
|
+
refined[resolvedKey] = coerceWithSchema(value, propDef);
|
|
141
213
|
}
|
|
142
214
|
else {
|
|
143
|
-
refined[resolvedKey] =
|
|
215
|
+
refined[resolvedKey] = value;
|
|
144
216
|
}
|
|
145
217
|
}
|
|
146
|
-
return refined;
|
|
218
|
+
return { params: refined, unknownKeys };
|
|
147
219
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
function
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
220
|
+
function uniqueStrings(values) {
|
|
221
|
+
return Array.from(new Set(values));
|
|
222
|
+
}
|
|
223
|
+
function schemaTypeLabel(schema) {
|
|
224
|
+
const enumValues = Array.isArray(schema.enum) ? schema.enum : undefined;
|
|
225
|
+
if (enumValues && enumValues.length > 0) {
|
|
226
|
+
return uniqueStrings(enumValues.map((value) => JSON.stringify(value))).join(' | ');
|
|
227
|
+
}
|
|
228
|
+
if (Object.prototype.hasOwnProperty.call(schema, 'const')) {
|
|
229
|
+
return JSON.stringify(schema.const);
|
|
230
|
+
}
|
|
231
|
+
const labels = getSchemaVariants(schema)
|
|
232
|
+
.map((variant) => {
|
|
233
|
+
const type = variant.type;
|
|
234
|
+
if (type)
|
|
235
|
+
return type;
|
|
236
|
+
if (variant.properties && typeof variant.properties === 'object')
|
|
237
|
+
return 'object';
|
|
238
|
+
if (variant.items)
|
|
239
|
+
return 'array';
|
|
240
|
+
return 'unknown';
|
|
159
241
|
})
|
|
160
|
-
.
|
|
161
|
-
|
|
242
|
+
.filter(Boolean);
|
|
243
|
+
return uniqueStrings(labels).join(' | ') || 'unknown';
|
|
244
|
+
}
|
|
245
|
+
function schemaConstraintLabel(schema) {
|
|
246
|
+
const variants = getSchemaVariants(schema);
|
|
247
|
+
const parts = [];
|
|
248
|
+
const numericVariant = variants.find((variant) => {
|
|
249
|
+
const type = variant.type;
|
|
250
|
+
return type === 'number' || type === 'integer';
|
|
251
|
+
});
|
|
252
|
+
if (numericVariant) {
|
|
253
|
+
if (typeof numericVariant.exclusiveMinimum === 'number') {
|
|
254
|
+
parts.push(`> ${numericVariant.exclusiveMinimum}`);
|
|
255
|
+
}
|
|
256
|
+
else if (typeof numericVariant.minimum === 'number') {
|
|
257
|
+
parts.push(`>= ${numericVariant.minimum}`);
|
|
258
|
+
}
|
|
259
|
+
if (typeof numericVariant.exclusiveMaximum === 'number') {
|
|
260
|
+
parts.push(`< ${numericVariant.exclusiveMaximum}`);
|
|
261
|
+
}
|
|
262
|
+
else if (typeof numericVariant.maximum === 'number') {
|
|
263
|
+
parts.push(`<= ${numericVariant.maximum}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const stringVariant = variants.find((variant) => variant.type === 'string');
|
|
267
|
+
if (stringVariant) {
|
|
268
|
+
if (typeof stringVariant.minLength === 'number') {
|
|
269
|
+
parts.push(`min length ${stringVariant.minLength}`);
|
|
270
|
+
}
|
|
271
|
+
if (typeof stringVariant.maxLength === 'number') {
|
|
272
|
+
parts.push(`max length ${stringVariant.maxLength}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const arrayVariant = variants.find((variant) => variant.type === 'array');
|
|
276
|
+
if (arrayVariant) {
|
|
277
|
+
if (typeof arrayVariant.minItems === 'number') {
|
|
278
|
+
parts.push(`min items ${arrayVariant.minItems}`);
|
|
279
|
+
}
|
|
280
|
+
if (typeof arrayVariant.maxItems === 'number') {
|
|
281
|
+
parts.push(`max items ${arrayVariant.maxItems}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return parts.length > 0 ? parts.join(', ') : undefined;
|
|
285
|
+
}
|
|
286
|
+
function getActionParameterFields(action) {
|
|
287
|
+
const paramSchema = action.parameters;
|
|
288
|
+
const required = new Set(paramSchema.required ?? []);
|
|
289
|
+
return Object.entries(paramSchema.properties ?? {}).map(([name, schema]) => ({
|
|
290
|
+
name,
|
|
291
|
+
required: required.has(name),
|
|
292
|
+
typeLabel: schemaTypeLabel(schema),
|
|
293
|
+
constraints: schemaConstraintLabel(schema),
|
|
294
|
+
schema,
|
|
295
|
+
}));
|
|
296
|
+
}
|
|
297
|
+
function exampleValueForSchema(schema) {
|
|
298
|
+
const enumValues = Array.isArray(schema.enum) ? schema.enum : undefined;
|
|
299
|
+
if (enumValues && enumValues.length > 0) {
|
|
300
|
+
return enumValues[0];
|
|
301
|
+
}
|
|
302
|
+
if (Object.prototype.hasOwnProperty.call(schema, 'const')) {
|
|
303
|
+
return schema.const;
|
|
304
|
+
}
|
|
305
|
+
const variants = getSchemaVariants(schema).sort((left, right) => {
|
|
306
|
+
const order = ['integer', 'number', 'boolean', 'object', 'array', 'string'];
|
|
307
|
+
const leftIndex = order.indexOf(left.type ?? 'string');
|
|
308
|
+
const rightIndex = order.indexOf(right.type ?? 'string');
|
|
309
|
+
return (leftIndex === -1 ? order.length : leftIndex) - (rightIndex === -1 ? order.length : rightIndex);
|
|
310
|
+
});
|
|
311
|
+
for (const variant of variants) {
|
|
312
|
+
switch (variant.type) {
|
|
313
|
+
case 'number':
|
|
314
|
+
case 'integer':
|
|
315
|
+
return 1;
|
|
316
|
+
case 'boolean':
|
|
317
|
+
return true;
|
|
318
|
+
case 'object':
|
|
319
|
+
return {};
|
|
320
|
+
case 'array':
|
|
321
|
+
return [];
|
|
322
|
+
case 'string':
|
|
323
|
+
return 'value';
|
|
324
|
+
default:
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return 'value';
|
|
329
|
+
}
|
|
330
|
+
function formatExampleCliValue(value) {
|
|
331
|
+
if (typeof value === 'string')
|
|
332
|
+
return value;
|
|
333
|
+
return JSON.stringify(value);
|
|
334
|
+
}
|
|
335
|
+
function buildExampleObject(fields) {
|
|
336
|
+
return Object.fromEntries(fields.map((field) => [field.name, exampleValueForSchema(field.schema)]));
|
|
337
|
+
}
|
|
338
|
+
function buildActionExamples(action, command = 'suzi run') {
|
|
339
|
+
const fields = getActionParameterFields(action);
|
|
340
|
+
const requiredFields = fields.filter((field) => field.required);
|
|
341
|
+
const primaryFields = requiredFields.length > 0 ? requiredFields : fields.slice(0, 1);
|
|
342
|
+
const examples = [];
|
|
343
|
+
if (primaryFields.length === 0) {
|
|
344
|
+
examples.push(`${command} ${action.name}`);
|
|
345
|
+
return examples;
|
|
346
|
+
}
|
|
347
|
+
const flagArgs = primaryFields
|
|
348
|
+
.map((field) => `--${camelToKebab(field.name)} ${formatExampleCliValue(exampleValueForSchema(field.schema))}`)
|
|
349
|
+
.join(' ');
|
|
350
|
+
examples.push(`${command} ${action.name} ${flagArgs}`);
|
|
351
|
+
const positionalArgs = primaryFields
|
|
352
|
+
.map((field) => `${field.name} ${formatExampleCliValue(exampleValueForSchema(field.schema))}`)
|
|
353
|
+
.join(' ');
|
|
354
|
+
examples.push(`${command} ${action.name} ${positionalArgs}`);
|
|
355
|
+
const paramsJson = JSON.stringify(buildExampleObject(primaryFields));
|
|
356
|
+
examples.push(`${command} ${action.name} --params '${paramsJson}'`);
|
|
357
|
+
return uniqueStrings(examples);
|
|
358
|
+
}
|
|
359
|
+
function primitiveEquals(left, right) {
|
|
360
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
361
|
+
}
|
|
362
|
+
function matchesSchema(value, schema) {
|
|
363
|
+
const variants = getSchemaVariants(schema);
|
|
364
|
+
if (variants.length > 1) {
|
|
365
|
+
return variants.some((variant) => matchesSchema(value, variant));
|
|
366
|
+
}
|
|
367
|
+
const variant = variants[0] ?? schema;
|
|
368
|
+
if (Array.isArray(variant.enum)) {
|
|
369
|
+
return variant.enum.some((candidate) => primitiveEquals(candidate, value));
|
|
370
|
+
}
|
|
371
|
+
if (Object.prototype.hasOwnProperty.call(variant, 'const')) {
|
|
372
|
+
return primitiveEquals(variant.const, value);
|
|
373
|
+
}
|
|
374
|
+
const type = variant.type;
|
|
375
|
+
if (!type)
|
|
376
|
+
return true;
|
|
377
|
+
switch (type) {
|
|
378
|
+
case 'number':
|
|
379
|
+
return (typeof value === 'number' &&
|
|
380
|
+
Number.isFinite(value) &&
|
|
381
|
+
(typeof variant.minimum !== 'number' || value >= variant.minimum) &&
|
|
382
|
+
(typeof variant.maximum !== 'number' || value <= variant.maximum) &&
|
|
383
|
+
(typeof variant.exclusiveMinimum !== 'number' || value > variant.exclusiveMinimum) &&
|
|
384
|
+
(typeof variant.exclusiveMaximum !== 'number' || value < variant.exclusiveMaximum));
|
|
385
|
+
case 'integer':
|
|
386
|
+
return (typeof value === 'number' &&
|
|
387
|
+
Number.isInteger(value) &&
|
|
388
|
+
(typeof variant.minimum !== 'number' || value >= variant.minimum) &&
|
|
389
|
+
(typeof variant.maximum !== 'number' || value <= variant.maximum) &&
|
|
390
|
+
(typeof variant.exclusiveMinimum !== 'number' || value > variant.exclusiveMinimum) &&
|
|
391
|
+
(typeof variant.exclusiveMaximum !== 'number' || value < variant.exclusiveMaximum));
|
|
392
|
+
case 'boolean':
|
|
393
|
+
return typeof value === 'boolean';
|
|
394
|
+
case 'string':
|
|
395
|
+
return (typeof value === 'string' &&
|
|
396
|
+
(typeof variant.minLength !== 'number' || value.length >= variant.minLength) &&
|
|
397
|
+
(typeof variant.maxLength !== 'number' || value.length <= variant.maxLength));
|
|
398
|
+
case 'object':
|
|
399
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
400
|
+
case 'array':
|
|
401
|
+
return Array.isArray(value);
|
|
402
|
+
case 'null':
|
|
403
|
+
return value === null;
|
|
404
|
+
default:
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function describeExpectedValue(schema, key) {
|
|
409
|
+
const typeLabel = schemaTypeLabel(schema);
|
|
410
|
+
const constraints = schemaConstraintLabel(schema);
|
|
411
|
+
const base = constraints ? `${typeLabel} (${constraints})` : typeLabel;
|
|
412
|
+
if (typeLabel === 'boolean') {
|
|
413
|
+
return 'boolean (true or false)';
|
|
414
|
+
}
|
|
415
|
+
if (typeLabel.includes('object') || typeLabel.includes('array')) {
|
|
416
|
+
return `${base}; pass JSON via --${camelToKebab(key)} '${JSON.stringify(exampleValueForSchema(schema))}' or --params '{...}'`;
|
|
417
|
+
}
|
|
418
|
+
return base;
|
|
419
|
+
}
|
|
420
|
+
function validateParamsAgainstSchema(params, action) {
|
|
421
|
+
const paramSchema = action.parameters;
|
|
422
|
+
const properties = paramSchema.properties ?? {};
|
|
423
|
+
const required = new Set(paramSchema.required ?? []);
|
|
424
|
+
const missingRequired = Array.from(required).filter((key) => params[key] === undefined);
|
|
425
|
+
const invalidFields = [];
|
|
426
|
+
for (const [key, value] of Object.entries(params)) {
|
|
427
|
+
const schema = properties[key];
|
|
428
|
+
if (!schema)
|
|
429
|
+
continue;
|
|
430
|
+
if (!matchesSchema(value, schema)) {
|
|
431
|
+
invalidFields.push({
|
|
432
|
+
key,
|
|
433
|
+
message: `Expected ${describeExpectedValue(schema, key)}.`,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return { missingRequired, invalidFields };
|
|
438
|
+
}
|
|
439
|
+
function printCliApiIssues(issues) {
|
|
440
|
+
if (issues.length === 0)
|
|
441
|
+
return;
|
|
442
|
+
console.log();
|
|
443
|
+
console.log(ui_1.colors.error(' Issues:'));
|
|
444
|
+
for (const issue of issues) {
|
|
445
|
+
console.log(` ${ui_1.colors.error('✕')} ${(0, api_errors_1.formatCliApiErrorIssue)(issue)}`);
|
|
446
|
+
}
|
|
162
447
|
}
|
|
163
|
-
/**
|
|
164
|
-
* Pretty-print the action result.
|
|
165
|
-
*/
|
|
166
448
|
function formatPretty(actionName, result) {
|
|
167
449
|
(0, ui_1.header)(`Result: ${actionName}`);
|
|
168
450
|
console.log();
|
|
@@ -183,137 +465,486 @@ function formatPretty(actionName, result) {
|
|
|
183
465
|
return;
|
|
184
466
|
}
|
|
185
467
|
const obj = result;
|
|
186
|
-
for (const [key,
|
|
187
|
-
if (
|
|
468
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
469
|
+
if (value === null || value === undefined)
|
|
188
470
|
continue;
|
|
189
|
-
if (typeof
|
|
190
|
-
console.log(` ${ui_1.colors.muted(key + ':')} ${JSON.stringify(
|
|
471
|
+
if (typeof value === 'object') {
|
|
472
|
+
console.log(` ${ui_1.colors.muted(key + ':')} ${JSON.stringify(value, null, 2).split('\n').join('\n ')}`);
|
|
191
473
|
}
|
|
192
474
|
else {
|
|
193
|
-
(0, ui_1.label)(key, String(
|
|
475
|
+
(0, ui_1.label)(key, String(value));
|
|
194
476
|
}
|
|
195
477
|
}
|
|
196
478
|
}
|
|
197
|
-
function
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
479
|
+
function getCanonicalRunCommand() {
|
|
480
|
+
return 'suzi run';
|
|
481
|
+
}
|
|
482
|
+
function getRunHelpPayload(action) {
|
|
483
|
+
const fields = getActionParameterFields(action);
|
|
484
|
+
return {
|
|
485
|
+
action: action.name,
|
|
486
|
+
description: action.description,
|
|
487
|
+
usage: `${getCanonicalRunCommand()} ${action.name} [--param value] [--params '{"key":"value"}'] [--json]`,
|
|
488
|
+
parameters: fields.map((field) => ({
|
|
489
|
+
name: field.name,
|
|
490
|
+
required: field.required,
|
|
491
|
+
type: field.typeLabel,
|
|
492
|
+
...(field.constraints ? { constraints: field.constraints } : {}),
|
|
493
|
+
})),
|
|
494
|
+
examples: buildActionExamples(action),
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
function printActionParameters(action) {
|
|
498
|
+
const fields = getActionParameterFields(action);
|
|
499
|
+
if (fields.length === 0) {
|
|
500
|
+
(0, ui_1.info)(`Action ${chalk_1.default.cyan(action.name)} takes no parameters.`);
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
console.log(chalk_1.default.bold('Parameters:'));
|
|
504
|
+
for (const field of fields) {
|
|
505
|
+
const requiredText = field.required ? chalk_1.default.yellow('required') : ui_1.colors.muted('optional');
|
|
506
|
+
const constraintText = field.constraints ? ` ${ui_1.colors.muted(`(${field.constraints})`)}` : '';
|
|
507
|
+
console.log(` ${chalk_1.default.cyan(field.name)} ${ui_1.colors.muted(field.typeLabel)} ${requiredText}${constraintText}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
function printActionExamples(action) {
|
|
511
|
+
console.log();
|
|
512
|
+
console.log(chalk_1.default.bold('Examples:'));
|
|
513
|
+
for (const example of buildActionExamples(action)) {
|
|
514
|
+
console.log(` ${ui_1.colors.primary('$')} ${chalk_1.default.white(example)}`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function printRunActionHelp(action, jsonMode, sourceCommand) {
|
|
518
|
+
if (jsonMode) {
|
|
519
|
+
(0, tty_1.outputJson)({ success: true, data: getRunHelpPayload(action) });
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
if (sourceCommand === 'agents run') {
|
|
523
|
+
(0, ui_1.warn)('`suzi agents run` is deprecated. Use `suzi run` instead.');
|
|
524
|
+
console.log();
|
|
525
|
+
}
|
|
526
|
+
(0, ui_1.header)(`Run ${action.name}`);
|
|
527
|
+
console.log();
|
|
528
|
+
console.log(` ${action.description}`);
|
|
529
|
+
console.log();
|
|
530
|
+
console.log(chalk_1.default.bold('Usage:'));
|
|
531
|
+
console.log(` ${ui_1.colors.primary(getCanonicalRunCommand())} ${chalk_1.default.white(action.name)} ${ui_1.colors.muted('[options]')}`);
|
|
532
|
+
console.log();
|
|
533
|
+
printActionParameters(action);
|
|
534
|
+
printActionExamples(action);
|
|
535
|
+
console.log();
|
|
536
|
+
(0, ui_1.info)(`Use ${chalk_1.default.cyan(`suzi list-actions --protocol ${action.protocol} --schema ${action.name.split('.')[1]}`)} to inspect the raw schema.`);
|
|
537
|
+
console.log();
|
|
538
|
+
}
|
|
539
|
+
function printGenericRunExamples() {
|
|
540
|
+
console.log();
|
|
541
|
+
console.log(chalk_1.default.bold('Examples:'));
|
|
542
|
+
console.log(` ${ui_1.colors.primary('$')} ${chalk_1.default.white('suzi run polymarket.deposit --amount 1')}`);
|
|
543
|
+
console.log(` ${ui_1.colors.primary('$')} ${chalk_1.default.white('suzi run polymarket.deposit amount 1')}`);
|
|
544
|
+
console.log(` ${ui_1.colors.primary('$')} ${chalk_1.default.white(`suzi run polymarket.deposit --params '{"amount":1}'`)}`);
|
|
545
|
+
}
|
|
546
|
+
function emitRunFailure(message, options) {
|
|
547
|
+
const code = options.code ?? 'VALIDATION_FAILED';
|
|
548
|
+
if (options.jsonMode) {
|
|
549
|
+
(0, tty_1.outputJson)({
|
|
550
|
+
success: false,
|
|
551
|
+
error: { code, message },
|
|
552
|
+
...(options.data ? { data: options.data } : {}),
|
|
553
|
+
});
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
(0, ui_1.emitCliError)(message, code, false);
|
|
557
|
+
options.printTextDetails?.();
|
|
558
|
+
}
|
|
559
|
+
function emitSchemaAwareFailure(message, action, options) {
|
|
560
|
+
const helpPayload = getRunHelpPayload(action);
|
|
561
|
+
const notes = options.notes ?? [];
|
|
562
|
+
const extraIssues = options.extraIssues ?? [];
|
|
563
|
+
emitRunFailure(message, {
|
|
564
|
+
jsonMode: options.jsonMode,
|
|
565
|
+
code: options.code,
|
|
566
|
+
data: {
|
|
567
|
+
action: helpPayload.action,
|
|
568
|
+
parameters: helpPayload.parameters,
|
|
569
|
+
examples: helpPayload.examples,
|
|
570
|
+
...(notes.length > 0 ? { notes } : {}),
|
|
571
|
+
...(extraIssues.length > 0 ? { issues: extraIssues } : {}),
|
|
572
|
+
},
|
|
573
|
+
printTextDetails: () => {
|
|
574
|
+
console.log();
|
|
575
|
+
printActionParameters(action);
|
|
576
|
+
if (extraIssues.length > 0) {
|
|
577
|
+
console.log();
|
|
578
|
+
console.log(ui_1.colors.error('Issues:'));
|
|
579
|
+
for (const issue of extraIssues) {
|
|
580
|
+
console.log(` ${ui_1.colors.error('✕')} ${issue.key}: ${issue.message}`);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
if (notes.length > 0) {
|
|
584
|
+
console.log();
|
|
585
|
+
for (const note of notes) {
|
|
586
|
+
(0, ui_1.info)(note);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
printActionExamples(action);
|
|
590
|
+
},
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
function suggestActions(input, actions) {
|
|
594
|
+
const lower = input.toLowerCase();
|
|
595
|
+
return actions
|
|
596
|
+
.filter((action) => {
|
|
597
|
+
if (action.name.toLowerCase().includes(lower))
|
|
598
|
+
return true;
|
|
599
|
+
const actionPart = action.name.split('.')[1]?.toLowerCase();
|
|
600
|
+
return actionPart ? lower.includes(actionPart) : false;
|
|
601
|
+
})
|
|
602
|
+
.slice(0, 5)
|
|
603
|
+
.map((action) => action.name);
|
|
604
|
+
}
|
|
605
|
+
function looksLikeJsonLiteral(value) {
|
|
606
|
+
return ((value.startsWith('{') && value.endsWith('}')) ||
|
|
607
|
+
(value.startsWith('[') && value.endsWith(']')));
|
|
608
|
+
}
|
|
609
|
+
function buildStrayTokenNotes(strayTokens) {
|
|
610
|
+
if (strayTokens.length === 0)
|
|
611
|
+
return [];
|
|
612
|
+
if (strayTokens.length === 1 && looksLikeJsonLiteral(strayTokens[0])) {
|
|
613
|
+
return [
|
|
614
|
+
`Unexpected JSON argument ${strayTokens[0]}. --json controls output; use --params '${strayTokens[0]}' for input.`,
|
|
615
|
+
];
|
|
616
|
+
}
|
|
617
|
+
if (strayTokens.length === 1) {
|
|
618
|
+
return [
|
|
619
|
+
`Could not interpret "${strayTokens[0]}". If it is a parameter name, pass a value after it or use --params '{...}'.`,
|
|
620
|
+
];
|
|
621
|
+
}
|
|
622
|
+
return [
|
|
623
|
+
`Could not interpret these extra tokens: ${strayTokens.join(' ')}.`,
|
|
624
|
+
'Use --param value pairs or --params \'{...}\' for action input.',
|
|
625
|
+
];
|
|
626
|
+
}
|
|
627
|
+
function hasLegacyJsonOutputFlag(args) {
|
|
628
|
+
return args.some((arg, index) => arg === '--output' && args[index + 1] === 'json');
|
|
629
|
+
}
|
|
630
|
+
async function loadActionForHelp(actionName) {
|
|
631
|
+
const cached = (0, actions_cache_1.getCachedActionByName)(actionName);
|
|
632
|
+
if (cached)
|
|
633
|
+
return cached;
|
|
634
|
+
try {
|
|
635
|
+
return await (0, actions_cache_1.getActionByName)(actionName);
|
|
636
|
+
}
|
|
637
|
+
catch {
|
|
638
|
+
return undefined;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
async function maybeHandleRunActionHelp(argv = process.argv) {
|
|
642
|
+
const args = argv.slice(2);
|
|
643
|
+
const helpRequested = args.includes('--help') || args.includes('-h');
|
|
644
|
+
if (!helpRequested)
|
|
645
|
+
return false;
|
|
646
|
+
let action;
|
|
647
|
+
let sourceCommand;
|
|
648
|
+
if (args[0] === 'run' && args[1] && !args[1].startsWith('-')) {
|
|
649
|
+
action = args[1];
|
|
650
|
+
sourceCommand = 'run';
|
|
651
|
+
}
|
|
652
|
+
else if (args[0] === 'agents' && args[1] === 'run' && args[2] && !args[2].startsWith('-')) {
|
|
653
|
+
action = args[2];
|
|
654
|
+
sourceCommand = 'agents run';
|
|
655
|
+
}
|
|
656
|
+
if (!action || !sourceCommand) {
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
const jsonMode = args.includes('--json') || hasLegacyJsonOutputFlag(args);
|
|
660
|
+
const actionMeta = await loadActionForHelp(action);
|
|
661
|
+
if (!actionMeta) {
|
|
662
|
+
emitRunFailure(`Action "${action}" was not found.`, {
|
|
663
|
+
jsonMode,
|
|
664
|
+
code: 'NOT_FOUND',
|
|
665
|
+
data: {
|
|
666
|
+
action,
|
|
667
|
+
},
|
|
668
|
+
printTextDetails: () => {
|
|
669
|
+
(0, ui_1.info)(`Run ${chalk_1.default.cyan('suzi list-actions')} to see all available actions.`);
|
|
670
|
+
},
|
|
671
|
+
});
|
|
672
|
+
return true;
|
|
673
|
+
}
|
|
674
|
+
printRunActionHelp(actionMeta, jsonMode, sourceCommand);
|
|
675
|
+
return true;
|
|
676
|
+
}
|
|
677
|
+
async function executeRunCommand(action, opts, argv = process.argv, sourceCommand = 'run') {
|
|
678
|
+
const jsonMode = Boolean(opts.json || opts.output === 'json');
|
|
679
|
+
if (sourceCommand === 'agents run' && !jsonMode) {
|
|
680
|
+
(0, ui_1.warn)('`suzi agents run` is deprecated. Use `suzi run` instead.');
|
|
681
|
+
}
|
|
682
|
+
if (!action) {
|
|
683
|
+
emitRunFailure(`Missing action. Expected ${getCanonicalRunCommand()} <protocol.action>.`, {
|
|
684
|
+
jsonMode,
|
|
685
|
+
code: 'INPUT_REQUIRED',
|
|
686
|
+
data: {
|
|
687
|
+
examples: [
|
|
688
|
+
'suzi run polymarket.deposit --amount 1',
|
|
689
|
+
'suzi run polymarket.deposit --params \'{"amount":1}\'',
|
|
690
|
+
],
|
|
691
|
+
},
|
|
692
|
+
printTextDetails: () => {
|
|
693
|
+
printGenericRunExamples();
|
|
694
|
+
console.log();
|
|
695
|
+
(0, ui_1.info)(`Run ${chalk_1.default.cyan('suzi list-actions')} to see all available actions.`);
|
|
696
|
+
},
|
|
697
|
+
});
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
const [protocol, actionName] = action.split('.', 2);
|
|
701
|
+
if (!protocol || !actionName) {
|
|
702
|
+
emitRunFailure('Action name must be in "protocol.action" format (e.g. polymarket.deposit).', {
|
|
703
|
+
jsonMode,
|
|
704
|
+
code: 'VALIDATION_FAILED',
|
|
705
|
+
data: {
|
|
706
|
+
action,
|
|
707
|
+
},
|
|
708
|
+
printTextDetails: () => {
|
|
709
|
+
printGenericRunExamples();
|
|
710
|
+
console.log();
|
|
711
|
+
(0, ui_1.info)(`Run ${chalk_1.default.cyan('suzi list-actions')} to see all available actions.`);
|
|
712
|
+
},
|
|
713
|
+
});
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
if (!(0, ui_1.requireAuth)(jsonMode))
|
|
717
|
+
return;
|
|
718
|
+
const rawParams = parseDynamicArgs(argv);
|
|
719
|
+
let params;
|
|
720
|
+
if (opts.params) {
|
|
721
|
+
if (Object.keys(rawParams.params).length > 0 || rawParams.strayTokens.length > 0) {
|
|
722
|
+
const notes = [
|
|
723
|
+
'Do not mix --params with individual action arguments.',
|
|
724
|
+
'Choose either --params \'{...}\' or --param value pairs.',
|
|
725
|
+
];
|
|
726
|
+
if (rawParams.strayTokens.length > 0) {
|
|
727
|
+
notes.push(...buildStrayTokenNotes(rawParams.strayTokens));
|
|
728
|
+
}
|
|
729
|
+
emitRunFailure('Conflicting action input: received --params together with additional action arguments.', {
|
|
730
|
+
jsonMode,
|
|
731
|
+
code: 'VALIDATION_FAILED',
|
|
732
|
+
data: {
|
|
733
|
+
action,
|
|
734
|
+
notes,
|
|
735
|
+
},
|
|
736
|
+
printTextDetails: () => {
|
|
737
|
+
for (const note of notes) {
|
|
738
|
+
(0, ui_1.info)(note);
|
|
739
|
+
}
|
|
740
|
+
},
|
|
741
|
+
});
|
|
742
|
+
return;
|
|
210
743
|
}
|
|
211
|
-
|
|
744
|
+
try {
|
|
745
|
+
params = JSON.parse(opts.params);
|
|
746
|
+
}
|
|
747
|
+
catch (err) {
|
|
748
|
+
emitRunFailure(`Invalid JSON in --params: ${err instanceof Error ? err.message : String(err)}`, {
|
|
749
|
+
jsonMode,
|
|
750
|
+
code: 'VALIDATION_FAILED',
|
|
751
|
+
data: {
|
|
752
|
+
action,
|
|
753
|
+
example: `${getCanonicalRunCommand()} ${action} --params '{"amount":1}'`,
|
|
754
|
+
},
|
|
755
|
+
printTextDetails: () => {
|
|
756
|
+
(0, ui_1.info)('Pass a JSON object to --params, for example --params \'{"amount":1}\'.');
|
|
757
|
+
},
|
|
758
|
+
});
|
|
212
759
|
return;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
760
|
+
}
|
|
761
|
+
if (typeof params !== 'object' || params === null || Array.isArray(params)) {
|
|
762
|
+
emitRunFailure('--params must be a JSON object (e.g. \'{"key":"value"}\').', {
|
|
763
|
+
jsonMode,
|
|
764
|
+
code: 'VALIDATION_FAILED',
|
|
765
|
+
});
|
|
218
766
|
return;
|
|
219
767
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
params = rawParams.params;
|
|
771
|
+
}
|
|
772
|
+
let actionMeta;
|
|
773
|
+
try {
|
|
774
|
+
actionMeta = await (0, actions_cache_1.getActionByName)(action);
|
|
775
|
+
}
|
|
776
|
+
catch {
|
|
777
|
+
// Proceed without local schema metadata.
|
|
778
|
+
}
|
|
779
|
+
if (rawParams.strayTokens.length > 0) {
|
|
780
|
+
const notes = buildStrayTokenNotes(rawParams.strayTokens);
|
|
781
|
+
if (actionMeta) {
|
|
782
|
+
emitSchemaAwareFailure('Could not parse one or more action parameters.', actionMeta, {
|
|
783
|
+
jsonMode,
|
|
784
|
+
code: 'VALIDATION_FAILED',
|
|
785
|
+
notes,
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
emitRunFailure('Could not parse one or more action parameters.', {
|
|
790
|
+
jsonMode,
|
|
791
|
+
code: 'VALIDATION_FAILED',
|
|
792
|
+
data: {
|
|
793
|
+
action,
|
|
794
|
+
notes,
|
|
795
|
+
},
|
|
796
|
+
printTextDetails: () => {
|
|
797
|
+
for (const note of notes) {
|
|
798
|
+
(0, ui_1.info)(note);
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
if (actionMeta) {
|
|
806
|
+
const refined = refineParamsWithSchema(params, actionMeta);
|
|
807
|
+
params = refined.params;
|
|
808
|
+
if (refined.unknownKeys.length > 0) {
|
|
809
|
+
emitSchemaAwareFailure(`Unknown parameter(s): ${refined.unknownKeys.map((key) => `--${key}`).join(', ')}`, actionMeta, {
|
|
810
|
+
jsonMode,
|
|
811
|
+
code: 'VALIDATION_FAILED',
|
|
812
|
+
notes: [
|
|
813
|
+
`Use suzi list-actions --protocol ${protocol} --schema ${actionName} to inspect the raw schema.`,
|
|
814
|
+
],
|
|
815
|
+
});
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
const validation = validateParamsAgainstSchema(params, actionMeta);
|
|
819
|
+
if (validation.missingRequired.length > 0 || validation.invalidFields.length > 0) {
|
|
820
|
+
const messageParts = [];
|
|
821
|
+
if (validation.missingRequired.length > 0) {
|
|
822
|
+
messageParts.push(`Missing required parameter(s): ${validation.missingRequired.join(', ')}`);
|
|
229
823
|
}
|
|
230
|
-
if (
|
|
231
|
-
(
|
|
232
|
-
return;
|
|
824
|
+
if (validation.invalidFields.length > 0) {
|
|
825
|
+
messageParts.push(...validation.invalidFields.map((issue) => `${issue.key}: ${issue.message}`));
|
|
233
826
|
}
|
|
827
|
+
emitSchemaAwareFailure(messageParts.join('. '), actionMeta, {
|
|
828
|
+
jsonMode,
|
|
829
|
+
code: 'VALIDATION_FAILED',
|
|
830
|
+
notes: rawParams.positionalPairs.length > 0
|
|
831
|
+
? ['Bare key/value pairs are supported, but the canonical form is --param value or --params \'{...}\'.']
|
|
832
|
+
: undefined,
|
|
833
|
+
extraIssues: validation.invalidFields,
|
|
834
|
+
});
|
|
835
|
+
return;
|
|
234
836
|
}
|
|
235
|
-
|
|
236
|
-
|
|
837
|
+
}
|
|
838
|
+
if (!actionMeta) {
|
|
839
|
+
if (!jsonMode) {
|
|
840
|
+
(0, ui_1.warn)(`Action "${action}" not found in local cache. Attempting execution anyway.`);
|
|
237
841
|
}
|
|
238
|
-
// Look up action metadata for key resolution and coercion
|
|
239
|
-
let actionMeta;
|
|
240
842
|
try {
|
|
241
|
-
|
|
843
|
+
const allActions = await (0, actions_cache_1.getActions)();
|
|
844
|
+
const suggestions = suggestActions(action, allActions);
|
|
845
|
+
if (!jsonMode && suggestions.length > 0) {
|
|
846
|
+
(0, ui_1.info)(`Did you mean: ${suggestions.map((suggestion) => chalk_1.default.cyan(suggestion)).join(', ')}?`);
|
|
847
|
+
}
|
|
242
848
|
}
|
|
243
849
|
catch {
|
|
244
|
-
//
|
|
850
|
+
// Ignore suggestion failures.
|
|
245
851
|
}
|
|
246
|
-
|
|
247
|
-
|
|
852
|
+
}
|
|
853
|
+
if (opts.dryRun) {
|
|
854
|
+
if (jsonMode) {
|
|
855
|
+
(0, tty_1.outputJson)({
|
|
856
|
+
success: true,
|
|
857
|
+
data: {
|
|
858
|
+
action,
|
|
859
|
+
...(actionMeta ? { description: actionMeta.description } : {}),
|
|
860
|
+
request: { name: action, params },
|
|
861
|
+
},
|
|
862
|
+
});
|
|
863
|
+
return;
|
|
248
864
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const suggestions = suggestActions(action, allActions);
|
|
255
|
-
if (suggestions.length > 0) {
|
|
256
|
-
(0, ui_1.info)(`Did you mean: ${suggestions.map((s) => chalk_1.default.cyan(s)).join(', ')}?`);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
catch {
|
|
260
|
-
// Ignore suggestion failures
|
|
261
|
-
}
|
|
865
|
+
(0, ui_1.header)('Dry Run');
|
|
866
|
+
console.log();
|
|
867
|
+
(0, ui_1.label)('action', action);
|
|
868
|
+
if (actionMeta) {
|
|
869
|
+
(0, ui_1.label)('description', actionMeta.description);
|
|
262
870
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
871
|
+
console.log();
|
|
872
|
+
console.log(` ${ui_1.colors.muted('Request payload:')}`);
|
|
873
|
+
console.log(JSON.stringify({ name: action, params }, null, 2));
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
const spinner = (0, tty_1.createSpinner)(`Executing ${chalk_1.default.bold(action)}...`, jsonMode);
|
|
877
|
+
try {
|
|
878
|
+
const resp = await (0, api_1.post)('/api/actions/execute', { name: action, params });
|
|
879
|
+
if (!resp.ok || !resp.data?.ok) {
|
|
880
|
+
spinner?.fail('Action failed');
|
|
881
|
+
const parsed = (0, api_errors_1.parseCliApiError)(resp.data);
|
|
882
|
+
const summary = parsed.summary || `API returned status ${resp.status}`;
|
|
883
|
+
if (jsonMode) {
|
|
884
|
+
(0, tty_1.outputJson)({
|
|
885
|
+
success: false,
|
|
886
|
+
error: {
|
|
887
|
+
code: resp.status === 400 ? 'VALIDATION_FAILED' : 'INTERNAL_ERROR',
|
|
888
|
+
message: summary,
|
|
889
|
+
},
|
|
890
|
+
data: {
|
|
891
|
+
error: parsed.raw,
|
|
892
|
+
...(parsed.issues.length > 0 ? { issues: parsed.issues } : {}),
|
|
893
|
+
...(parsed.detailsText ? { details: parsed.detailsText } : {}),
|
|
894
|
+
},
|
|
895
|
+
});
|
|
270
896
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
// Execute
|
|
277
|
-
const spinner = (0, tty_1.createSpinner)(`Executing ${chalk_1.default.bold(action)}...`, isJson);
|
|
278
|
-
try {
|
|
279
|
-
const resp = await (0, api_1.post)('/api/actions/execute', { name: action, params });
|
|
280
|
-
if (!resp.ok || !resp.data?.ok) {
|
|
281
|
-
spinner?.fail(`Action failed`);
|
|
282
|
-
const msg = resp.data?.message || resp.data?.error || `API returned status ${resp.status}`;
|
|
283
|
-
if (isJson) {
|
|
284
|
-
(0, tty_1.outputJson)({ success: false, error: msg });
|
|
897
|
+
else {
|
|
898
|
+
(0, ui_1.emitCliError)(summary, resp.status === 400 ? 'VALIDATION_FAILED' : 'INTERNAL_ERROR', false);
|
|
899
|
+
if (parsed.issues.length > 0) {
|
|
900
|
+
printCliApiIssues(parsed.issues);
|
|
285
901
|
}
|
|
286
|
-
else {
|
|
287
|
-
(
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
902
|
+
else if (parsed.detailsText) {
|
|
903
|
+
console.log(ui_1.colors.muted(` ${parsed.detailsText}`));
|
|
904
|
+
}
|
|
905
|
+
if (resp.status === 400) {
|
|
906
|
+
console.log();
|
|
907
|
+
(0, ui_1.info)(`Run ${chalk_1.default.cyan(`${getCanonicalRunCommand()} ${action} --help`)} for parameters and examples.`);
|
|
291
908
|
}
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
if (isJson) {
|
|
295
|
-
(0, tty_1.outputJson)({ success: true, data: resp.data.result });
|
|
296
|
-
}
|
|
297
|
-
else {
|
|
298
|
-
spinner?.succeed(`${action} executed successfully`);
|
|
299
|
-
console.log();
|
|
300
|
-
formatPretty(action, resp.data.result);
|
|
301
909
|
}
|
|
910
|
+
return;
|
|
302
911
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if (isJson) {
|
|
306
|
-
(0, tty_1.outputJson)({ success: false, error: { code: 'NETWORK_ERROR', message } });
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
spinner?.fail('Request failed');
|
|
310
|
-
(0, ui_1.error)(message);
|
|
311
|
-
}
|
|
912
|
+
if (jsonMode) {
|
|
913
|
+
(0, tty_1.outputJson)({ success: true, data: resp.data.result });
|
|
312
914
|
}
|
|
313
|
-
|
|
915
|
+
else {
|
|
916
|
+
spinner?.succeed(`${action} executed successfully`);
|
|
314
917
|
console.log();
|
|
315
|
-
(
|
|
918
|
+
formatPretty(action, resp.data.result);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
catch (err) {
|
|
922
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
923
|
+
if (jsonMode) {
|
|
924
|
+
(0, tty_1.outputJson)({ success: false, error: { code: 'NETWORK_ERROR', message } });
|
|
316
925
|
}
|
|
926
|
+
else {
|
|
927
|
+
spinner?.fail('Request failed');
|
|
928
|
+
(0, ui_1.emitCliError)(message, 'NETWORK_ERROR', false);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
if (!jsonMode) {
|
|
932
|
+
console.log();
|
|
933
|
+
(0, ui_1.divider)();
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
function registerRunCommand(program) {
|
|
937
|
+
program
|
|
938
|
+
.command('run [action]')
|
|
939
|
+
.description('Execute an action directly (e.g. suzi run polymarket.deposit --amount 1)')
|
|
940
|
+
.allowUnknownOption()
|
|
941
|
+
.allowExcessArguments(true)
|
|
942
|
+
.option('--params <json>', 'Pass action parameters as a JSON object')
|
|
943
|
+
.option('--json', 'Output as JSON')
|
|
944
|
+
.addOption(new commander_1.Option('--output <format>', 'Deprecated output format alias').choices(['pretty', 'json']).hideHelp())
|
|
945
|
+
.option('--dry-run', 'Preview the request payload without executing')
|
|
946
|
+
.action(async (action, opts) => {
|
|
947
|
+
await executeRunCommand(action, opts, process.argv, 'run');
|
|
317
948
|
});
|
|
318
949
|
}
|
|
319
950
|
//# sourceMappingURL=run.js.map
|