@orchagent/cli 0.3.102 → 0.3.104
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/commands/dag.js +4 -0
- package/dist/commands/health.js +2 -7
- package/dist/commands/init.js +4 -0
- package/dist/commands/publish.js +13 -3
- package/dist/commands/pull.js +17 -3
- package/dist/commands/run.js +42 -38
- package/dist/commands/schedule.js +3 -6
- package/dist/commands/trace.js +4 -0
- package/dist/lib/api.js +15 -11
- package/dist/lib/batch-publish.js +73 -0
- package/dist/lib/json-input.js +60 -0
- package/package.json +1 -1
package/dist/commands/dag.js
CHANGED
|
@@ -193,6 +193,10 @@ function registerDagCommand(program) {
|
|
|
193
193
|
throw new errors_1.CliError('Missing API key. Run `orch login` first.');
|
|
194
194
|
}
|
|
195
195
|
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
196
|
+
// Accept req_xxx format (gateway request_id shown in run output)
|
|
197
|
+
if (/^req_[0-9a-f]+$/i.test(runId)) {
|
|
198
|
+
runId = runId.slice(4);
|
|
199
|
+
}
|
|
196
200
|
// Resolve short run IDs
|
|
197
201
|
let resolvedRunId = runId;
|
|
198
202
|
if (isUuid(runId)) {
|
package/dist/commands/health.js
CHANGED
|
@@ -9,6 +9,7 @@ const config_1 = require("../lib/config");
|
|
|
9
9
|
const api_1 = require("../lib/api");
|
|
10
10
|
const agent_ref_1 = require("../lib/agent-ref");
|
|
11
11
|
const errors_1 = require("../lib/errors");
|
|
12
|
+
const json_input_1 = require("../lib/json-input");
|
|
12
13
|
const spinner_1 = require("../lib/spinner");
|
|
13
14
|
const output_1 = require("../lib/output");
|
|
14
15
|
const package_json_1 = __importDefault(require("../../package.json"));
|
|
@@ -194,13 +195,7 @@ function registerHealthCommand(program) {
|
|
|
194
195
|
const inputSchema = agentMeta.input_schema;
|
|
195
196
|
let body;
|
|
196
197
|
if (options.data) {
|
|
197
|
-
|
|
198
|
-
JSON.parse(options.data);
|
|
199
|
-
body = options.data;
|
|
200
|
-
}
|
|
201
|
-
catch {
|
|
202
|
-
throw new errors_1.CliError('Invalid JSON in --data option.');
|
|
203
|
-
}
|
|
198
|
+
body = await (0, json_input_1.resolveJsonBody)(options.data);
|
|
204
199
|
}
|
|
205
200
|
else {
|
|
206
201
|
const sample = generateSampleInput(inputSchema);
|
package/dist/commands/init.js
CHANGED
|
@@ -2242,6 +2242,10 @@ function registerInitCommand(program) {
|
|
|
2242
2242
|
process.stdout.write(` ${stepNum + 1}. Edit schema.json with your input/output schemas\n`);
|
|
2243
2243
|
process.stdout.write(` ${stepNum + 2}. Run: orchagent publish\n`);
|
|
2244
2244
|
}
|
|
2245
|
+
if (initMode.flavor === 'managed_loop') {
|
|
2246
|
+
process.stdout.write(`\n Note: supported_providers: ["any"] means anthropic, openai, or gemini\n`);
|
|
2247
|
+
process.stdout.write(` Your vault key determines which is used (set with: orch secrets set)\n`);
|
|
2248
|
+
}
|
|
2245
2249
|
process.stdout.write(AGENT_BUILDER_HINT);
|
|
2246
2250
|
});
|
|
2247
2251
|
}
|
package/dist/commands/publish.js
CHANGED
|
@@ -548,11 +548,21 @@ async function batchPublish(rootDir, options) {
|
|
|
548
548
|
catch {
|
|
549
549
|
// Non-critical — just won't show org prefix
|
|
550
550
|
}
|
|
551
|
-
|
|
552
|
-
process.stderr.write(plan);
|
|
551
|
+
// Dry-run: show ordering preview and exit (no subprocesses)
|
|
553
552
|
if (options.dryRun) {
|
|
554
|
-
|
|
553
|
+
const summary = (0, batch_publish_1.formatDryRunSummary)(sorted, orgSlug);
|
|
554
|
+
process.stderr.write(summary);
|
|
555
|
+
await (0, analytics_1.track)('cli_publish_all', {
|
|
556
|
+
total: sorted.length,
|
|
557
|
+
succeeded: 0,
|
|
558
|
+
failed: 0,
|
|
559
|
+
skipped: sorted.length,
|
|
560
|
+
dry_run: true,
|
|
561
|
+
});
|
|
562
|
+
return;
|
|
555
563
|
}
|
|
564
|
+
const plan = (0, batch_publish_1.formatPublishPlan)(sorted, orgSlug);
|
|
565
|
+
process.stderr.write(plan);
|
|
556
566
|
// Build the CLI args to forward (exclude --all)
|
|
557
567
|
const forwardArgs = [];
|
|
558
568
|
if (options.profile)
|
package/dist/commands/pull.js
CHANGED
|
@@ -50,7 +50,7 @@ function commandForEntrypoint(entrypoint) {
|
|
|
50
50
|
|| entrypoint.endsWith('.ts')) {
|
|
51
51
|
return `node ${entrypoint}`;
|
|
52
52
|
}
|
|
53
|
-
return `
|
|
53
|
+
return `python3 ${entrypoint}`;
|
|
54
54
|
}
|
|
55
55
|
// ─── Agent Resolution ───────────────────────────────────────────────────────
|
|
56
56
|
async function resolveAgent(config, org, agent, version, workspaceId) {
|
|
@@ -128,13 +128,21 @@ async function resolveAgent(config, org, agent, version, workspaceId) {
|
|
|
128
128
|
async function tryOwnerFallback(config, org, agent, version, workspaceId) {
|
|
129
129
|
try {
|
|
130
130
|
let match = findOwnerMatch(await (0, api_1.listMyAgents)(config, workspaceId), agent, version, org);
|
|
131
|
+
let effectiveWorkspaceId = workspaceId;
|
|
131
132
|
// Retry without workspace restriction to find agents in personal org
|
|
132
133
|
if (!match && workspaceId) {
|
|
133
134
|
match = findOwnerMatch(await (0, api_1.listMyAgents)(config, undefined), agent, version, org);
|
|
135
|
+
effectiveWorkspaceId = undefined;
|
|
134
136
|
}
|
|
135
137
|
if (!match)
|
|
136
138
|
return null;
|
|
137
|
-
|
|
139
|
+
// Pass workspace context so GET /agents/{id} uses the correct org scope.
|
|
140
|
+
// Without this header, the gateway scopes to the caller's personal org,
|
|
141
|
+
// which 404s when the agent lives in a team workspace.
|
|
142
|
+
const headers = {};
|
|
143
|
+
if (effectiveWorkspaceId)
|
|
144
|
+
headers['X-Workspace-Id'] = effectiveWorkspaceId;
|
|
145
|
+
const agentData = await (0, api_1.request)(config, 'GET', `/agents/${match.id}`, { headers });
|
|
138
146
|
return mapAgentToPullData(agentData);
|
|
139
147
|
}
|
|
140
148
|
catch {
|
|
@@ -164,7 +172,13 @@ async function resolveFromMyAgents(config, agent, version, org, workspaceId) {
|
|
|
164
172
|
return null;
|
|
165
173
|
target = found;
|
|
166
174
|
}
|
|
167
|
-
|
|
175
|
+
// Pass workspace context so GET /agents/{id} uses the correct org scope.
|
|
176
|
+
// Without this header, the gateway scopes to the caller's personal org,
|
|
177
|
+
// which 404s when the agent lives in a team workspace.
|
|
178
|
+
const headers = {};
|
|
179
|
+
if (workspaceId)
|
|
180
|
+
headers['X-Workspace-Id'] = workspaceId;
|
|
181
|
+
const agentData = await (0, api_1.request)(config, 'GET', `/agents/${target.id}`, { headers });
|
|
168
182
|
return mapAgentToPullData(agentData);
|
|
169
183
|
}
|
|
170
184
|
function mapAgentToPullData(agent) {
|
package/dist/commands/run.js
CHANGED
|
@@ -57,6 +57,7 @@ const config_1 = require("../lib/config");
|
|
|
57
57
|
const resolve_agent_1 = require("../lib/resolve-agent");
|
|
58
58
|
const api_1 = require("../lib/api");
|
|
59
59
|
const errors_1 = require("../lib/errors");
|
|
60
|
+
const json_input_1 = require("../lib/json-input");
|
|
60
61
|
const output_1 = require("../lib/output");
|
|
61
62
|
const spinner_1 = require("../lib/spinner");
|
|
62
63
|
const llm_1 = require("../lib/llm");
|
|
@@ -360,32 +361,6 @@ async function buildMultipartBody(filePaths, metadata) {
|
|
|
360
361
|
sourceLabel: filePaths.length === 1 ? filePaths[0] : `${filePaths.length} files`,
|
|
361
362
|
};
|
|
362
363
|
}
|
|
363
|
-
async function resolveJsonBody(input) {
|
|
364
|
-
let raw = input;
|
|
365
|
-
if (input.startsWith('@')) {
|
|
366
|
-
const source = input.slice(1);
|
|
367
|
-
if (!source) {
|
|
368
|
-
throw new errors_1.CliError('Invalid JSON input. Use a JSON string or @file.');
|
|
369
|
-
}
|
|
370
|
-
if (source === '-') {
|
|
371
|
-
const stdinData = await readStdin();
|
|
372
|
-
if (!stdinData) {
|
|
373
|
-
throw new errors_1.CliError('No stdin provided for JSON input.');
|
|
374
|
-
}
|
|
375
|
-
raw = stdinData.toString('utf8');
|
|
376
|
-
}
|
|
377
|
-
else {
|
|
378
|
-
await validateFilePath(source);
|
|
379
|
-
raw = await promises_1.default.readFile(source, 'utf8');
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
try {
|
|
383
|
-
return JSON.stringify(JSON.parse(raw));
|
|
384
|
-
}
|
|
385
|
-
catch {
|
|
386
|
-
throw (0, errors_1.jsonInputError)('data');
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
364
|
// ─── Keyed file & mount helpers ──────────────────────────────────────────────
|
|
390
365
|
const KEYED_FILE_KEY_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
391
366
|
function isKeyedFileArg(arg) {
|
|
@@ -494,7 +469,7 @@ async function buildInjectedPayload(options) {
|
|
|
494
469
|
let merged = {};
|
|
495
470
|
// 1. Start with --data
|
|
496
471
|
if (options.dataOption) {
|
|
497
|
-
const resolved = await resolveJsonBody(options.dataOption);
|
|
472
|
+
const resolved = await (0, json_input_1.resolveJsonBody)(options.dataOption);
|
|
498
473
|
merged = JSON.parse(resolved);
|
|
499
474
|
}
|
|
500
475
|
let totalBytes = 0;
|
|
@@ -1912,7 +1887,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1912
1887
|
// --estimate-only: show cost estimate and exit without running
|
|
1913
1888
|
if (options.estimate || options.estimateOnly) {
|
|
1914
1889
|
try {
|
|
1915
|
-
const est = await (0, api_1.getAgentCostEstimate)(resolved, org, agentName, version);
|
|
1890
|
+
const est = await (0, api_1.getAgentCostEstimate)(resolved, org, agentName, version, workspaceId);
|
|
1916
1891
|
const e = est.estimate;
|
|
1917
1892
|
if (e.sample_size === 0) {
|
|
1918
1893
|
process.stderr.write(chalk_1.default.yellow('\nNo run history available for cost estimation.\n'));
|
|
@@ -1952,12 +1927,19 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1952
1927
|
return;
|
|
1953
1928
|
}
|
|
1954
1929
|
}
|
|
1955
|
-
catch {
|
|
1956
|
-
//
|
|
1930
|
+
catch (err) {
|
|
1931
|
+
// Provide specific error messages based on failure type
|
|
1932
|
+
const detail = err instanceof api_1.ApiError && err.status === 404
|
|
1933
|
+
? 'Agent not found.'
|
|
1934
|
+
: err instanceof api_1.ApiError && err.status === 429
|
|
1935
|
+
? 'Rate limited — try again shortly.'
|
|
1936
|
+
: err instanceof api_1.ApiError
|
|
1937
|
+
? `API error (${err.status}).`
|
|
1938
|
+
: 'Network error — check your connection.';
|
|
1957
1939
|
if (options.estimateOnly) {
|
|
1958
|
-
throw new errors_1.CliError(
|
|
1940
|
+
throw new errors_1.CliError(`Could not fetch cost estimate: ${detail}`);
|
|
1959
1941
|
}
|
|
1960
|
-
process.stderr.write(chalk_1.default.gray(
|
|
1942
|
+
process.stderr.write(chalk_1.default.gray(`Could not fetch cost estimate: ${detail} Proceeding with run...\n\n`));
|
|
1961
1943
|
}
|
|
1962
1944
|
}
|
|
1963
1945
|
const endpoint = options.endpoint?.trim() || agentMeta.default_endpoint || 'analyze';
|
|
@@ -2069,7 +2051,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2069
2051
|
}
|
|
2070
2052
|
if (options.data && filePaths.length > 0) {
|
|
2071
2053
|
// Merge file content into --data
|
|
2072
|
-
const resolvedBody = await resolveJsonBody(options.data);
|
|
2054
|
+
const resolvedBody = await (0, json_input_1.resolveJsonBody)(options.data);
|
|
2073
2055
|
const bodyObj = JSON.parse(resolvedBody);
|
|
2074
2056
|
if (cloudEngine !== 'code_runtime') {
|
|
2075
2057
|
const fieldName = resolveFileField(options.fileField, agentMeta.input_schema);
|
|
@@ -2117,7 +2099,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2117
2099
|
}
|
|
2118
2100
|
}
|
|
2119
2101
|
else if (options.data) {
|
|
2120
|
-
const resolvedBody = await resolveJsonBody(options.data);
|
|
2102
|
+
const resolvedBody = await (0, json_input_1.resolveJsonBody)(options.data);
|
|
2121
2103
|
warnIfLocalPathReference(resolvedBody);
|
|
2122
2104
|
const parsedBody = JSON.parse(resolvedBody);
|
|
2123
2105
|
warnInputSchemaErrors(parsedBody, agentMeta.input_schema);
|
|
@@ -2573,7 +2555,22 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2573
2555
|
process.stdout.write(`${payload}\n`);
|
|
2574
2556
|
return;
|
|
2575
2557
|
}
|
|
2576
|
-
|
|
2558
|
+
// In verbose mode, strip stdout/stderr from the JSON payload since they'll
|
|
2559
|
+
// be displayed in dedicated colored sections below (avoids duplication)
|
|
2560
|
+
if (options.verbose && typeof payload === 'object' && payload !== null && 'metadata' in payload) {
|
|
2561
|
+
const payloadObj = payload;
|
|
2562
|
+
const meta = payloadObj.metadata;
|
|
2563
|
+
if (meta && (meta.stdout || meta.stderr)) {
|
|
2564
|
+
const { stdout: _s, stderr: _e, ...cleanMeta } = meta;
|
|
2565
|
+
(0, output_1.printJson)({ ...payloadObj, metadata: cleanMeta });
|
|
2566
|
+
}
|
|
2567
|
+
else {
|
|
2568
|
+
(0, output_1.printJson)(payload);
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
else {
|
|
2572
|
+
(0, output_1.printJson)(payload);
|
|
2573
|
+
}
|
|
2577
2574
|
// Display timing metadata on stderr (non-json mode only)
|
|
2578
2575
|
if (typeof payload === 'object' && payload !== null && 'metadata' in payload) {
|
|
2579
2576
|
const meta = payload.metadata;
|
|
@@ -2585,7 +2582,14 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2585
2582
|
if (stderr) {
|
|
2586
2583
|
process.stderr.write(chalk_1.default.bold.yellow('\n--- stderr ---') + '\n' + stderr + '\n');
|
|
2587
2584
|
}
|
|
2588
|
-
|
|
2585
|
+
// For code_runtime agents, stdout IS the data — skip if it would
|
|
2586
|
+
// duplicate what's already visible in the JSON data field
|
|
2587
|
+
const dataStr = typeof payload.data === 'string'
|
|
2588
|
+
? payload.data
|
|
2589
|
+
: JSON.stringify(payload.data);
|
|
2590
|
+
const stdoutDuplicatesData = isCodeRuntimeAgent && stdout && dataStr &&
|
|
2591
|
+
stdout.trim() === dataStr.trim();
|
|
2592
|
+
if (stdout && !stdoutDuplicatesData) {
|
|
2589
2593
|
process.stderr.write(chalk_1.default.bold.cyan('\n--- stdout ---') + '\n' + stdout + '\n');
|
|
2590
2594
|
}
|
|
2591
2595
|
if (!stderr && !stdout) {
|
|
@@ -2685,7 +2689,7 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2685
2689
|
return;
|
|
2686
2690
|
}
|
|
2687
2691
|
// Resolve @file.json / @- stdin syntax before parsing
|
|
2688
|
-
const resolvedInput = await resolveJsonBody(options.input);
|
|
2692
|
+
const resolvedInput = await (0, json_input_1.resolveJsonBody)(options.input);
|
|
2689
2693
|
let agentInputData;
|
|
2690
2694
|
try {
|
|
2691
2695
|
agentInputData = JSON.parse(resolvedInput);
|
|
@@ -2871,7 +2875,7 @@ function registerRunCommand(program) {
|
|
|
2871
2875
|
// Local-only options
|
|
2872
2876
|
.option('--download-only', 'Just download the agent, do not execute (local only)')
|
|
2873
2877
|
.option('--with-deps', 'Automatically download all dependencies (local only)')
|
|
2874
|
-
.option('--here', '
|
|
2878
|
+
.option('--here', 'Send current directory as path input, for tool agents with a path field (local only)')
|
|
2875
2879
|
.option('--path <dir>', 'Shorthand for --data \'{"path": "<dir>"}\' (local only)')
|
|
2876
2880
|
.addHelpText('after', `
|
|
2877
2881
|
Examples:
|
|
@@ -11,6 +11,7 @@ const promises_1 = __importDefault(require("readline/promises"));
|
|
|
11
11
|
const config_1 = require("../lib/config");
|
|
12
12
|
const api_1 = require("../lib/api");
|
|
13
13
|
const errors_1 = require("../lib/errors");
|
|
14
|
+
const json_input_1 = require("../lib/json-input");
|
|
14
15
|
const output_1 = require("../lib/output");
|
|
15
16
|
const agent_ref_1 = require("../lib/agent-ref");
|
|
16
17
|
const api_2 = require("../lib/api");
|
|
@@ -187,12 +188,8 @@ function registerScheduleCommand(program) {
|
|
|
187
188
|
const rawInput = options.data ?? options.input;
|
|
188
189
|
let inputData;
|
|
189
190
|
if (rawInput) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
catch {
|
|
194
|
-
throw new errors_1.CliError('Invalid JSON in --data. Use single quotes: --data \'{"key": "value"}\'');
|
|
195
|
-
}
|
|
191
|
+
const resolved = await (0, json_input_1.resolveJsonBody)(rawInput);
|
|
192
|
+
inputData = JSON.parse(resolved);
|
|
196
193
|
}
|
|
197
194
|
const scheduleType = options.webhook ? 'webhook' : 'cron';
|
|
198
195
|
const body = {
|
package/dist/commands/trace.js
CHANGED
|
@@ -105,6 +105,10 @@ function registerTraceCommand(program) {
|
|
|
105
105
|
throw new errors_1.CliError('Missing API key. Run `orch login` first.');
|
|
106
106
|
}
|
|
107
107
|
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
108
|
+
// Accept req_xxx format (gateway request_id shown in run output)
|
|
109
|
+
if (/^req_[0-9a-f]+$/i.test(runId)) {
|
|
110
|
+
runId = runId.slice(4);
|
|
111
|
+
}
|
|
108
112
|
// Resolve short run IDs
|
|
109
113
|
let resolvedRunId = runId;
|
|
110
114
|
if (isUuid(runId)) {
|
package/dist/lib/api.js
CHANGED
|
@@ -286,10 +286,11 @@ async function getPublicAgent(config, org, agent, version) {
|
|
|
286
286
|
}
|
|
287
287
|
async function getAgentCostEstimate(config, org, agent, version, workspaceId) {
|
|
288
288
|
const path = `/public/agents/${org}/${agent}/${version}/cost-estimate`;
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
289
|
+
if (config.apiKey) {
|
|
290
|
+
const headers = {};
|
|
291
|
+
if (workspaceId)
|
|
292
|
+
headers['X-Workspace-Id'] = workspaceId;
|
|
293
|
+
return request(config, 'GET', path, { headers });
|
|
293
294
|
}
|
|
294
295
|
return publicRequest(config, path);
|
|
295
296
|
}
|
|
@@ -380,15 +381,18 @@ async function getAgentWithFallback(config, org, agentName, version, workspaceId
|
|
|
380
381
|
if (!config.apiKey) {
|
|
381
382
|
throw new ApiError(`Agent '${org}/${agentName}@${version}' not found`, 404);
|
|
382
383
|
}
|
|
383
|
-
|
|
384
|
-
if (userOrg.slug !== org) {
|
|
385
|
-
throw new ApiError(`Agent '${org}/${agentName}@${version}' not found`, 404);
|
|
386
|
-
}
|
|
384
|
+
// Try authenticated lookup in the resolved workspace context
|
|
387
385
|
const myAgent = await getMyAgent(config, agentName, version, workspaceId);
|
|
388
|
-
if (
|
|
389
|
-
|
|
386
|
+
if (myAgent)
|
|
387
|
+
return myAgent;
|
|
388
|
+
// Fallback: if workspace was specified, also check personal org —
|
|
389
|
+
// handles cross-workspace lookups (e.g., team context looking up personal agent)
|
|
390
|
+
if (workspaceId) {
|
|
391
|
+
const personalAgent = await getMyAgent(config, agentName, version);
|
|
392
|
+
if (personalAgent)
|
|
393
|
+
return personalAgent;
|
|
390
394
|
}
|
|
391
|
-
|
|
395
|
+
throw new ApiError(`Agent '${org}/${agentName}@${version}' not found`, 404);
|
|
392
396
|
}
|
|
393
397
|
/**
|
|
394
398
|
* Resolve a workspace ID from an org slug.
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.discoverAgents = discoverAgents;
|
|
7
7
|
exports.topoSort = topoSort;
|
|
8
8
|
exports.formatPublishPlan = formatPublishPlan;
|
|
9
|
+
exports.formatDryRunSummary = formatDryRunSummary;
|
|
9
10
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
11
|
const path_1 = __importDefault(require("path"));
|
|
11
12
|
const chalk_1 = __importDefault(require("chalk"));
|
|
@@ -221,3 +222,75 @@ function formatPublishPlan(sorted, orgSlug) {
|
|
|
221
222
|
lines.push('');
|
|
222
223
|
return lines.join('\n');
|
|
223
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Format an enhanced dry-run summary for --all --dry-run.
|
|
227
|
+
* Shows publish ordering, local/external dependencies, and graph health.
|
|
228
|
+
*/
|
|
229
|
+
function formatDryRunSummary(sorted, orgSlug) {
|
|
230
|
+
const lines = [];
|
|
231
|
+
const localNames = new Set(sorted.map(a => a.name));
|
|
232
|
+
// Collect external deps (referenced but not in the project)
|
|
233
|
+
const externalDeps = new Map(); // ref → [agent names that reference it]
|
|
234
|
+
for (const agent of sorted) {
|
|
235
|
+
for (const ref of agent.dependencyRefs) {
|
|
236
|
+
const depName = ref.includes('/') ? ref.split('/')[1] : ref;
|
|
237
|
+
if (!localNames.has(depName)) {
|
|
238
|
+
const consumers = externalDeps.get(ref) || [];
|
|
239
|
+
consumers.push(agent.name);
|
|
240
|
+
externalDeps.set(ref, consumers);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// Publish order table
|
|
245
|
+
lines.push('');
|
|
246
|
+
lines.push(chalk_1.default.bold(` Publish order (${sorted.length} agent${sorted.length === 1 ? '' : 's'}):`));
|
|
247
|
+
lines.push('');
|
|
248
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
249
|
+
const agent = sorted[i];
|
|
250
|
+
const type = agent.isSkill ? 'skill' : 'agent';
|
|
251
|
+
const prefix = orgSlug ? `${orgSlug}/` : '';
|
|
252
|
+
// Separate local and external deps for clarity
|
|
253
|
+
const localDeps = [];
|
|
254
|
+
const extDeps = [];
|
|
255
|
+
for (const ref of agent.dependencyRefs) {
|
|
256
|
+
const depName = ref.includes('/') ? ref.split('/')[1] : ref;
|
|
257
|
+
if (localNames.has(depName)) {
|
|
258
|
+
localDeps.push(depName);
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
extDeps.push(ref);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
let depInfo = '';
|
|
265
|
+
if (localDeps.length > 0 || extDeps.length > 0) {
|
|
266
|
+
const parts = [];
|
|
267
|
+
if (localDeps.length > 0)
|
|
268
|
+
parts.push(localDeps.join(', '));
|
|
269
|
+
if (extDeps.length > 0)
|
|
270
|
+
parts.push(extDeps.map(d => `${d} ${chalk_1.default.yellow('(external)')}`).join(', '));
|
|
271
|
+
depInfo = ` ${chalk_1.default.gray('→')} ${parts.join(', ')}`;
|
|
272
|
+
}
|
|
273
|
+
lines.push(` ${chalk_1.default.bold(`${i + 1}.`)} ${prefix}${agent.name} ${chalk_1.default.gray(`[${type}]`)}${depInfo}`);
|
|
274
|
+
lines.push(` ${chalk_1.default.gray(agent.dirName + '/')}`);
|
|
275
|
+
}
|
|
276
|
+
// External dependencies section
|
|
277
|
+
if (externalDeps.size > 0) {
|
|
278
|
+
lines.push('');
|
|
279
|
+
lines.push(chalk_1.default.yellow(` External dependencies (must already be published):`));
|
|
280
|
+
for (const [ref, consumers] of externalDeps) {
|
|
281
|
+
lines.push(` ${ref} ${chalk_1.default.gray(`← ${consumers.join(', ')}`)}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Summary
|
|
285
|
+
lines.push('');
|
|
286
|
+
const skillCount = sorted.filter(a => a.isSkill).length;
|
|
287
|
+
const agentCount = sorted.length - skillCount;
|
|
288
|
+
const parts = [];
|
|
289
|
+
if (agentCount > 0)
|
|
290
|
+
parts.push(`${agentCount} agent${agentCount === 1 ? '' : 's'}`);
|
|
291
|
+
if (skillCount > 0)
|
|
292
|
+
parts.push(`${skillCount} skill${skillCount === 1 ? '' : 's'}`);
|
|
293
|
+
lines.push(chalk_1.default.green(` ✓ ${parts.join(', ')} ready to publish (no circular dependencies)`));
|
|
294
|
+
lines.push('');
|
|
295
|
+
return lines.join('\n');
|
|
296
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveJsonBody = resolveJsonBody;
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const errors_1 = require("./errors");
|
|
9
|
+
async function readStdin() {
|
|
10
|
+
if (process.stdin.isTTY)
|
|
11
|
+
return null;
|
|
12
|
+
const chunks = [];
|
|
13
|
+
for await (const chunk of process.stdin) {
|
|
14
|
+
chunks.push(Buffer.from(chunk));
|
|
15
|
+
}
|
|
16
|
+
if (!chunks.length)
|
|
17
|
+
return null;
|
|
18
|
+
return Buffer.concat(chunks);
|
|
19
|
+
}
|
|
20
|
+
async function validateFilePath(filePath) {
|
|
21
|
+
const stat = await promises_1.default.stat(filePath);
|
|
22
|
+
if (stat.isDirectory()) {
|
|
23
|
+
throw new errors_1.CliError(`Expected a file but got a directory: ${filePath}\n\n` +
|
|
24
|
+
`Provide a JSON file path, e.g. --data @input.json`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolve a --data value to a validated JSON string.
|
|
29
|
+
*
|
|
30
|
+
* Supports three input forms:
|
|
31
|
+
* - Plain JSON string: '{"key":"value"}'
|
|
32
|
+
* - File reference: @input.json
|
|
33
|
+
* - Stdin pipe: @-
|
|
34
|
+
*/
|
|
35
|
+
async function resolveJsonBody(input) {
|
|
36
|
+
let raw = input;
|
|
37
|
+
if (input.startsWith('@')) {
|
|
38
|
+
const source = input.slice(1);
|
|
39
|
+
if (!source) {
|
|
40
|
+
throw new errors_1.CliError('Invalid JSON input. Use a JSON string or @file.');
|
|
41
|
+
}
|
|
42
|
+
if (source === '-') {
|
|
43
|
+
const stdinData = await readStdin();
|
|
44
|
+
if (!stdinData) {
|
|
45
|
+
throw new errors_1.CliError('No stdin provided for JSON input.');
|
|
46
|
+
}
|
|
47
|
+
raw = stdinData.toString('utf8');
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
await validateFilePath(source);
|
|
51
|
+
raw = await promises_1.default.readFile(source, 'utf8');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
return JSON.stringify(JSON.parse(raw));
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
throw (0, errors_1.jsonInputError)('data');
|
|
59
|
+
}
|
|
60
|
+
}
|
package/package.json
CHANGED