@orchagent/cli 0.3.119 → 0.3.120
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/AGENT_GUIDE.md +34 -0
- package/dist/commands/agents.js +15 -2
- package/dist/commands/context.js +76 -0
- package/dist/commands/describe.js +131 -0
- package/dist/commands/index.js +6 -0
- package/dist/commands/info.js +8 -2
- package/dist/commands/login.js +18 -6
- package/dist/commands/logs.js +35 -1
- package/dist/commands/publish.js +107 -12
- package/dist/commands/run.js +39 -9
- package/dist/commands/schedule.js +37 -5
- package/dist/commands/schema.js +78 -0
- package/dist/commands/secrets.js +17 -2
- package/dist/commands/service.js +20 -3
- package/dist/commands/storage.js +9 -4
- package/dist/index.js +21 -2
- package/dist/lib/agent-ref.js +3 -0
- package/dist/lib/command-introspection.js +250 -0
- package/dist/lib/errors.js +125 -18
- package/dist/lib/list-options.js +69 -0
- package/dist/lib/llm.js +35 -0
- package/dist/lib/output.js +29 -0
- package/dist/lib/sanitize.js +99 -0
- package/dist/lib/validate.js +17 -0
- package/package.json +3 -2
package/AGENT_GUIDE.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: orchagent-cli
|
|
3
|
+
discovery:
|
|
4
|
+
full_context: "orch context"
|
|
5
|
+
command_contract: "orch describe <command> --json"
|
|
6
|
+
output:
|
|
7
|
+
preferred: --json
|
|
8
|
+
non_tty_behavior: commands with --json auto-enable JSON when stdout is non-TTY
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# orchagent CLI Agent Guide
|
|
12
|
+
|
|
13
|
+
Use this file as an entry point for AI agents and automation tools.
|
|
14
|
+
|
|
15
|
+
## Discovery
|
|
16
|
+
|
|
17
|
+
- Run `orch context` to get a full, machine-readable command index in YAML-frontmatter markdown format.
|
|
18
|
+
- Run `orch describe <command> --json` to get argument, flag, mutation, and example metadata for one command.
|
|
19
|
+
|
|
20
|
+
## Auth
|
|
21
|
+
|
|
22
|
+
- Set `ORCHAGENT_API_KEY` in the environment.
|
|
23
|
+
- Use `orch login` to create or store credentials locally.
|
|
24
|
+
|
|
25
|
+
## I/O Conventions
|
|
26
|
+
|
|
27
|
+
- Prefer `--json` for stable parsing.
|
|
28
|
+
- For JSON payloads, prefer `--data @file.json` or `--data @-` (stdin) for reliability.
|
|
29
|
+
- Use explicit agent references (`org/name@version`) when possible.
|
|
30
|
+
|
|
31
|
+
## Safety
|
|
32
|
+
|
|
33
|
+
- Prefer `--dry-run` for mutating commands when available.
|
|
34
|
+
- Inspect command contracts with `orch describe` before executing side effects.
|
package/dist/commands/agents.js
CHANGED
|
@@ -42,6 +42,7 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
42
42
|
const config_1 = require("../lib/config");
|
|
43
43
|
const api_1 = require("../lib/api");
|
|
44
44
|
const output_1 = require("../lib/output");
|
|
45
|
+
const list_options_1 = require("../lib/list-options");
|
|
45
46
|
/**
|
|
46
47
|
* Given a list of agents, return only the latest version of each agent name.
|
|
47
48
|
* "Latest" = highest created_at timestamp (most recently published).
|
|
@@ -73,6 +74,9 @@ function registerAgentsCommand(program) {
|
|
|
73
74
|
.option('--filter <text>', 'Filter by name')
|
|
74
75
|
.option('--all-versions', 'Show all versions (default: latest only)')
|
|
75
76
|
.option('--json', 'Output raw JSON')
|
|
77
|
+
.option('--fields <fields>', 'Comma-separated fields to include in JSON output (implies --json)')
|
|
78
|
+
.option('--limit <n>', 'Maximum number of items to return')
|
|
79
|
+
.option('--offset <n>', 'Number of items to skip')
|
|
76
80
|
.action(async (options) => {
|
|
77
81
|
const config = await (0, config_1.getResolvedConfig)();
|
|
78
82
|
// Resolve workspace context
|
|
@@ -95,8 +99,17 @@ function registerAgentsCommand(program) {
|
|
|
95
99
|
displayAgents = grouped.agents;
|
|
96
100
|
versionCounts = grouped.versionCounts;
|
|
97
101
|
}
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
// Apply client-side limit/offset
|
|
103
|
+
const limit = (0, list_options_1.parseIntOption)(options.limit);
|
|
104
|
+
const offset = (0, list_options_1.parseIntOption)(options.offset);
|
|
105
|
+
if (limit != null || offset != null) {
|
|
106
|
+
displayAgents = (0, list_options_1.applyLimitOffset)(displayAgents, limit, offset);
|
|
107
|
+
}
|
|
108
|
+
// --fields implies --json
|
|
109
|
+
const useJson = options.json || !!options.fields;
|
|
110
|
+
if (useJson) {
|
|
111
|
+
const fields = options.fields ? (0, list_options_1.parseFields)(options.fields) : undefined;
|
|
112
|
+
(0, output_1.printJson)(fields ? (0, list_options_1.filterFields)(displayAgents, fields) : displayAgents);
|
|
100
113
|
return;
|
|
101
114
|
}
|
|
102
115
|
if (displayAgents.length === 0) {
|
|
@@ -0,0 +1,76 @@
|
|
|
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.buildContextDocument = buildContextDocument;
|
|
7
|
+
exports.registerContextCommand = registerContextCommand;
|
|
8
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
9
|
+
const package_json_1 = __importDefault(require("../../package.json"));
|
|
10
|
+
const command_introspection_1 = require("../lib/command-introspection");
|
|
11
|
+
function toContextCommands(program) {
|
|
12
|
+
return (0, command_introspection_1.buildCliCommandMetadata)(program).map((command) => ({
|
|
13
|
+
name: command.name,
|
|
14
|
+
description: command.description,
|
|
15
|
+
flags: (0, command_introspection_1.collectFlagNames)(command),
|
|
16
|
+
mutations: command.mutations,
|
|
17
|
+
...(command.dryRun ? { dry_run: true } : {}),
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
function buildGuideBody(commands) {
|
|
21
|
+
const commandIndex = commands
|
|
22
|
+
.map((command) => `- \`${command.name}\` - ${command.description || 'No description available'}`)
|
|
23
|
+
.join('\n');
|
|
24
|
+
const mutatingCommands = commands
|
|
25
|
+
.filter((command) => command.mutations)
|
|
26
|
+
.map((command) => `- \`${command.name}\`${command.dry_run ? ' (supports --dry-run)' : ''}`)
|
|
27
|
+
.join('\n');
|
|
28
|
+
return `# orchagent CLI - Agent Guide
|
|
29
|
+
|
|
30
|
+
## Authentication
|
|
31
|
+
Set \`ORCHAGENT_API_KEY\` in the environment. You can create a key with \`orch login\` or in the orchagent dashboard.
|
|
32
|
+
|
|
33
|
+
## Discovery
|
|
34
|
+
- Use \`orch context\` for a full command index plus machine-readable frontmatter.
|
|
35
|
+
- Use \`orch describe <command> --json\` for detailed metadata on one command.
|
|
36
|
+
|
|
37
|
+
## Output Conventions
|
|
38
|
+
- Prefer \`--json\` when your caller needs structured output.
|
|
39
|
+
- Commands that support \`--json\` automatically switch to JSON in non-TTY output.
|
|
40
|
+
- For JSON inputs, prefer \`--data @file.json\` or \`--data @-\` (stdin) over shell-escaped inline blobs.
|
|
41
|
+
|
|
42
|
+
## Common Patterns
|
|
43
|
+
- Use explicit agent references when possible: \`org/name@version\`.
|
|
44
|
+
- Use \`--dry-run\` before mutating operations whenever available.
|
|
45
|
+
- For large automation flows, inspect command contracts with \`orch describe\` before execution.
|
|
46
|
+
|
|
47
|
+
## Top-Level Commands
|
|
48
|
+
${commandIndex}
|
|
49
|
+
|
|
50
|
+
## Mutating Commands
|
|
51
|
+
${mutatingCommands || '- None detected'}
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
function buildContextDocument(program) {
|
|
55
|
+
const commands = toContextCommands(program);
|
|
56
|
+
const version = program.version() || package_json_1.default.version;
|
|
57
|
+
const frontmatter = {
|
|
58
|
+
name: 'orchagent-cli',
|
|
59
|
+
version,
|
|
60
|
+
commands,
|
|
61
|
+
};
|
|
62
|
+
const body = buildGuideBody(commands).trim();
|
|
63
|
+
return `---\n${yaml_1.default.stringify(frontmatter).trim()}\n---\n\n${body}\n`;
|
|
64
|
+
}
|
|
65
|
+
function registerContextCommand(program) {
|
|
66
|
+
program
|
|
67
|
+
.command('context')
|
|
68
|
+
.description('Print an embedded CLI guide for AI agents (YAML frontmatter + markdown)')
|
|
69
|
+
.addHelpText('after', `
|
|
70
|
+
Examples:
|
|
71
|
+
orch context
|
|
72
|
+
`)
|
|
73
|
+
.action(() => {
|
|
74
|
+
process.stdout.write(buildContextDocument(program));
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerDescribeCommand = registerDescribeCommand;
|
|
4
|
+
const command_introspection_1 = require("../lib/command-introspection");
|
|
5
|
+
const errors_1 = require("../lib/errors");
|
|
6
|
+
const output_1 = require("../lib/output");
|
|
7
|
+
function toDescribeOutput(command) {
|
|
8
|
+
return {
|
|
9
|
+
command: command.path,
|
|
10
|
+
description: command.description,
|
|
11
|
+
usage: `orch ${command.usage}`,
|
|
12
|
+
arguments: command.arguments.map((argument) => ({
|
|
13
|
+
name: argument.name,
|
|
14
|
+
required: argument.required,
|
|
15
|
+
variadic: argument.variadic,
|
|
16
|
+
format: argument.format,
|
|
17
|
+
...(argument.description ? { description: argument.description } : {}),
|
|
18
|
+
})),
|
|
19
|
+
flags: command.flags.map((flag) => ({
|
|
20
|
+
name: flag.name,
|
|
21
|
+
type: flag.type,
|
|
22
|
+
required: flag.required,
|
|
23
|
+
...(flag.valueRequired ? { value_required: true } : {}),
|
|
24
|
+
description: flag.description,
|
|
25
|
+
...(flag.short ? { short: flag.short } : {}),
|
|
26
|
+
...(flag.alias ? { alias: flag.alias } : {}),
|
|
27
|
+
...(flag.choices ? { choices: flag.choices } : {}),
|
|
28
|
+
...(flag.defaultValue !== undefined ? { default: flag.defaultValue } : {}),
|
|
29
|
+
})),
|
|
30
|
+
mutations: command.mutations,
|
|
31
|
+
dry_run: command.dryRun,
|
|
32
|
+
examples: command.examples,
|
|
33
|
+
aliases: command.aliases,
|
|
34
|
+
...(command.subcommands.length > 0
|
|
35
|
+
? {
|
|
36
|
+
subcommands: command.subcommands.map((subcommand) => ({
|
|
37
|
+
command: subcommand.path,
|
|
38
|
+
description: subcommand.description,
|
|
39
|
+
usage: `orch ${subcommand.usage}`,
|
|
40
|
+
mutations: subcommand.mutations,
|
|
41
|
+
dry_run: subcommand.dryRun,
|
|
42
|
+
})),
|
|
43
|
+
}
|
|
44
|
+
: {}),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function renderMarkdown(command) {
|
|
48
|
+
const argumentLines = command.arguments.length > 0
|
|
49
|
+
? command.arguments
|
|
50
|
+
.map((argument) => `- \`${argument.name}\` (${argument.required ? 'required' : 'optional'}, ${argument.format})${argument.description ? ` - ${argument.description}` : ''}`)
|
|
51
|
+
.join('\n')
|
|
52
|
+
: '- None';
|
|
53
|
+
const flagLines = command.flags.length > 0
|
|
54
|
+
? command.flags
|
|
55
|
+
.map((flag) => {
|
|
56
|
+
const parts = [`\`${flag.name}\``];
|
|
57
|
+
if (flag.alias)
|
|
58
|
+
parts.push(`alias: \`${flag.alias}\``);
|
|
59
|
+
if (flag.short)
|
|
60
|
+
parts.push(`short: \`${flag.short}\``);
|
|
61
|
+
parts.push(`type: ${flag.type}`);
|
|
62
|
+
return `- ${parts.join(', ')} - ${flag.description}`;
|
|
63
|
+
})
|
|
64
|
+
.join('\n')
|
|
65
|
+
: '- None';
|
|
66
|
+
const subcommandLines = command.subcommands.length > 0
|
|
67
|
+
? command.subcommands
|
|
68
|
+
.map((subcommand) => `- \`${subcommand.path}\` - ${subcommand.description}`)
|
|
69
|
+
.join('\n')
|
|
70
|
+
: '- None';
|
|
71
|
+
const exampleLines = command.examples.length > 0
|
|
72
|
+
? command.examples.map((example) => `- \`${example}\``).join('\n')
|
|
73
|
+
: '- None';
|
|
74
|
+
return `# orch describe: ${command.path}
|
|
75
|
+
|
|
76
|
+
## Description
|
|
77
|
+
${command.description || 'No description available'}
|
|
78
|
+
|
|
79
|
+
## Usage
|
|
80
|
+
\`orch ${command.usage}\`
|
|
81
|
+
|
|
82
|
+
## Mutations
|
|
83
|
+
${command.mutations ? 'Yes' : 'No'}
|
|
84
|
+
|
|
85
|
+
## Dry Run
|
|
86
|
+
${command.dryRun ? 'Supported' : 'Not supported'}
|
|
87
|
+
|
|
88
|
+
## Arguments
|
|
89
|
+
${argumentLines}
|
|
90
|
+
|
|
91
|
+
## Flags
|
|
92
|
+
${flagLines}
|
|
93
|
+
|
|
94
|
+
## Subcommands
|
|
95
|
+
${subcommandLines}
|
|
96
|
+
|
|
97
|
+
## Examples
|
|
98
|
+
${exampleLines}
|
|
99
|
+
`;
|
|
100
|
+
}
|
|
101
|
+
function resolveCommand(program, commandPath) {
|
|
102
|
+
const allCommands = (0, command_introspection_1.buildCliCommandMetadata)(program);
|
|
103
|
+
const match = (0, command_introspection_1.findCommandMetadata)(allCommands, commandPath);
|
|
104
|
+
if (match)
|
|
105
|
+
return match;
|
|
106
|
+
const query = commandPath.join(' ');
|
|
107
|
+
const err = new errors_1.CliError(`Unknown command "${query}"`, errors_1.ExitCodes.NOT_FOUND);
|
|
108
|
+
err.code = errors_1.ErrorCodes.NOT_FOUND;
|
|
109
|
+
err.hint = 'Run "orch context" to list available commands.';
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
112
|
+
function registerDescribeCommand(program) {
|
|
113
|
+
program
|
|
114
|
+
.command('describe <command...>')
|
|
115
|
+
.description('Show machine-readable metadata for a single command')
|
|
116
|
+
.option('--json', 'Output machine-readable JSON (default)')
|
|
117
|
+
.option('--markdown', 'Output markdown instead of JSON')
|
|
118
|
+
.addHelpText('after', `
|
|
119
|
+
Examples:
|
|
120
|
+
orch describe run --json
|
|
121
|
+
orch describe schedule create --json
|
|
122
|
+
`)
|
|
123
|
+
.action((commandPath, options) => {
|
|
124
|
+
const command = resolveCommand(program, commandPath);
|
|
125
|
+
if (options.markdown) {
|
|
126
|
+
process.stdout.write(renderMarkdown(command));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
(0, output_1.printJson)(toDescribeOutput(command));
|
|
130
|
+
});
|
|
131
|
+
}
|
package/dist/commands/index.js
CHANGED
|
@@ -46,6 +46,9 @@ const dag_1 = require("./dag");
|
|
|
46
46
|
const completion_1 = require("./completion");
|
|
47
47
|
const validate_1 = require("./validate");
|
|
48
48
|
const storage_1 = require("./storage");
|
|
49
|
+
const schema_1 = require("./schema");
|
|
50
|
+
const context_1 = require("./context");
|
|
51
|
+
const describe_1 = require("./describe");
|
|
49
52
|
function registerCommands(program) {
|
|
50
53
|
(0, login_1.registerLoginCommand)(program);
|
|
51
54
|
(0, logout_1.registerLogoutCommand)(program);
|
|
@@ -92,4 +95,7 @@ function registerCommands(program) {
|
|
|
92
95
|
(0, completion_1.registerCompletionCommand)(program);
|
|
93
96
|
(0, validate_1.registerValidateCommand)(program);
|
|
94
97
|
(0, storage_1.registerStorageCommand)(program);
|
|
98
|
+
(0, schema_1.registerSchemaCommand)(program);
|
|
99
|
+
(0, context_1.registerContextCommand)(program);
|
|
100
|
+
(0, describe_1.registerDescribeCommand)(program);
|
|
95
101
|
}
|
package/dist/commands/info.js
CHANGED
|
@@ -3,12 +3,14 @@ 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.getAgentInfo = getAgentInfo;
|
|
6
7
|
exports.registerInfoCommand = registerInfoCommand;
|
|
7
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
9
|
const config_1 = require("../lib/config");
|
|
9
10
|
const api_1 = require("../lib/api");
|
|
10
11
|
const agent_ref_1 = require("../lib/agent-ref");
|
|
11
12
|
const errors_1 = require("../lib/errors");
|
|
13
|
+
const list_options_1 = require("../lib/list-options");
|
|
12
14
|
function formatSchema(schema, indent = ' ') {
|
|
13
15
|
const lines = [];
|
|
14
16
|
const props = schema.properties || {};
|
|
@@ -150,6 +152,7 @@ function registerInfoCommand(program) {
|
|
|
150
152
|
.command('info <agent>')
|
|
151
153
|
.description('Show agent details including inputs and outputs')
|
|
152
154
|
.option('--json', 'Output as JSON')
|
|
155
|
+
.option('--fields <fields>', 'Comma-separated fields to include in JSON output (implies --json)')
|
|
153
156
|
.action(async (agentArg, options) => {
|
|
154
157
|
const config = await (0, config_1.getResolvedConfig)();
|
|
155
158
|
const parsed = (0, agent_ref_1.parseAgentRef)(agentArg);
|
|
@@ -163,13 +166,16 @@ function registerInfoCommand(program) {
|
|
|
163
166
|
const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, org);
|
|
164
167
|
// Fetch agent metadata
|
|
165
168
|
const agentData = await getAgentInfo(config, org, agent, version, workspaceId);
|
|
166
|
-
|
|
169
|
+
// --fields implies --json
|
|
170
|
+
if (options.json || options.fields) {
|
|
167
171
|
// Don't expose internal routing URLs in JSON output
|
|
168
172
|
const output = { ...agentData };
|
|
169
173
|
if (output.url?.includes('.internal')) {
|
|
170
174
|
delete output.url;
|
|
171
175
|
}
|
|
172
|
-
|
|
176
|
+
const fields = options.fields ? (0, list_options_1.parseFields)(options.fields) : undefined;
|
|
177
|
+
const data = fields ? (0, list_options_1.filterFields)(output, fields) : output;
|
|
178
|
+
process.stdout.write(JSON.stringify(data, null, 2) + '\n');
|
|
173
179
|
return;
|
|
174
180
|
}
|
|
175
181
|
// Display agent info
|
package/dist/commands/login.js
CHANGED
|
@@ -27,10 +27,12 @@ function registerLoginCommand(program) {
|
|
|
27
27
|
.option('--key <key>', 'API key (for CI/CD, non-interactive)')
|
|
28
28
|
.option('--port <port>', `Localhost port for browser callback (default: ${DEFAULT_AUTH_PORT})`, String(DEFAULT_AUTH_PORT))
|
|
29
29
|
.action(async (options) => {
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
// If key provided via --key flag, use key-based flow (for CI/CD)
|
|
31
|
+
// Note: ORCHAGENT_API_KEY env var is intentionally NOT checked here —
|
|
32
|
+
// it's for runtime API auth, not for login. Otherwise `orch login`
|
|
33
|
+
// can never reach the browser flow when the env var is set.
|
|
34
|
+
if (options.key) {
|
|
35
|
+
await keyBasedLogin(options.key);
|
|
34
36
|
return;
|
|
35
37
|
}
|
|
36
38
|
// Check if running in non-interactive mode (no TTY)
|
|
@@ -61,13 +63,18 @@ async function keyBasedLogin(apiKey) {
|
|
|
61
63
|
...existing,
|
|
62
64
|
api_key: apiKey,
|
|
63
65
|
api_url: resolved.apiUrl,
|
|
64
|
-
default_org:
|
|
66
|
+
default_org: org.slug,
|
|
65
67
|
};
|
|
66
68
|
// Clear workspace from previous account — workspaces are account-specific
|
|
67
69
|
delete nextConfig.workspace;
|
|
68
70
|
await (0, config_1.saveConfig)(nextConfig);
|
|
69
71
|
await (0, analytics_1.track)('cli_login', { method: 'key' });
|
|
70
72
|
process.stdout.write(`✓ Logged in to ${org.slug}\n`);
|
|
73
|
+
if (process.env.ORCHAGENT_API_KEY) {
|
|
74
|
+
process.stderr.write('\nWarning: ORCHAGENT_API_KEY is set in your environment.\n' +
|
|
75
|
+
'The env var overrides your login credentials. Unset it with:\n' +
|
|
76
|
+
' unset ORCHAGENT_API_KEY\n');
|
|
77
|
+
}
|
|
71
78
|
if (isFirstLogin) {
|
|
72
79
|
process.stdout.write('\n Tip: Run `orch doctor` to verify your setup.\n\n');
|
|
73
80
|
}
|
|
@@ -86,13 +93,18 @@ async function browserBasedLogin(port) {
|
|
|
86
93
|
...existing,
|
|
87
94
|
api_key: result.apiKey,
|
|
88
95
|
api_url: resolved.apiUrl,
|
|
89
|
-
default_org:
|
|
96
|
+
default_org: result.orgSlug,
|
|
90
97
|
};
|
|
91
98
|
// Clear workspace from previous account — workspaces are account-specific
|
|
92
99
|
delete nextConfig.workspace;
|
|
93
100
|
await (0, config_1.saveConfig)(nextConfig);
|
|
94
101
|
await (0, analytics_1.track)('cli_login', { method: 'browser' });
|
|
95
102
|
process.stdout.write(`\n✓ Logged in to ${result.orgSlug}\n`);
|
|
103
|
+
if (process.env.ORCHAGENT_API_KEY) {
|
|
104
|
+
process.stderr.write('\nWarning: ORCHAGENT_API_KEY is set in your environment.\n' +
|
|
105
|
+
'The env var overrides your login credentials. Unset it with:\n' +
|
|
106
|
+
' unset ORCHAGENT_API_KEY\n');
|
|
107
|
+
}
|
|
96
108
|
if (isFirstLogin) {
|
|
97
109
|
process.stdout.write('\n Tip: Run `orch doctor` to verify your setup.\n\n');
|
|
98
110
|
}
|
package/dist/commands/logs.js
CHANGED
|
@@ -10,6 +10,7 @@ const config_1 = require("../lib/config");
|
|
|
10
10
|
const api_1 = require("../lib/api");
|
|
11
11
|
const errors_1 = require("../lib/errors");
|
|
12
12
|
const output_1 = require("../lib/output");
|
|
13
|
+
const list_options_1 = require("../lib/list-options");
|
|
13
14
|
// ============================================
|
|
14
15
|
// HELPERS
|
|
15
16
|
// ============================================
|
|
@@ -102,6 +103,16 @@ async function findServiceForAgent(config, workspaceId, agentName) {
|
|
|
102
103
|
return null;
|
|
103
104
|
}
|
|
104
105
|
}
|
|
106
|
+
/** Find all active (non-deleted) always-on services in the workspace. */
|
|
107
|
+
async function findActiveServices(config, workspaceId) {
|
|
108
|
+
try {
|
|
109
|
+
const result = await (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/services?limit=100`);
|
|
110
|
+
return result.services.filter((s) => s.current_state !== 'deleted');
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
105
116
|
/** Fetch and display logs from an always-on service. */
|
|
106
117
|
async function showServiceLogs(config, workspaceId, service, limit, json) {
|
|
107
118
|
const params = new URLSearchParams();
|
|
@@ -140,8 +151,10 @@ function registerLogsCommand(program) {
|
|
|
140
151
|
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
141
152
|
.option('--status <status>', 'Filter by status: running, completed, failed, timeout')
|
|
142
153
|
.option('--limit <n>', 'Number of runs to show (default: 20)', '20')
|
|
154
|
+
.option('--offset <n>', 'Number of runs to skip')
|
|
143
155
|
.option('--live', 'Show live logs from always-on service (skips run history)')
|
|
144
156
|
.option('--json', 'Output as JSON')
|
|
157
|
+
.option('--fields <fields>', 'Comma-separated fields to include in JSON output (implies --json)')
|
|
145
158
|
.action(async (target, options) => {
|
|
146
159
|
const config = await (0, config_1.getResolvedConfig)();
|
|
147
160
|
if (!config.apiKey) {
|
|
@@ -208,12 +221,19 @@ async function listRuns(config, workspaceId, agentName, options) {
|
|
|
208
221
|
params.set('status', options.status);
|
|
209
222
|
const limit = parseInt(options.limit ?? '20', 10);
|
|
210
223
|
params.set('limit', String(Math.min(Math.max(1, limit), 200)));
|
|
224
|
+
if (options.offset) {
|
|
225
|
+
const offset = parseInt(options.offset, 10);
|
|
226
|
+
if (!isNaN(offset) && offset > 0)
|
|
227
|
+
params.set('offset', String(offset));
|
|
228
|
+
}
|
|
211
229
|
const qs = params.toString() ? `?${params.toString()}` : '';
|
|
212
230
|
const [result, service] = await Promise.all([
|
|
213
231
|
(0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/runs${qs}`),
|
|
214
232
|
servicePromise,
|
|
215
233
|
]);
|
|
216
|
-
|
|
234
|
+
// --fields implies --json
|
|
235
|
+
const useJson = options.json || !!options.fields;
|
|
236
|
+
if (useJson) {
|
|
217
237
|
const payload = { ...result };
|
|
218
238
|
if (service) {
|
|
219
239
|
payload.service = {
|
|
@@ -223,15 +243,29 @@ async function listRuns(config, workspaceId, agentName, options) {
|
|
|
223
243
|
health_status: service.health_status,
|
|
224
244
|
};
|
|
225
245
|
}
|
|
246
|
+
if (options.fields) {
|
|
247
|
+
const fields = (0, list_options_1.parseFields)(options.fields);
|
|
248
|
+
payload.runs = (0, list_options_1.filterFields)(payload.runs, fields);
|
|
249
|
+
}
|
|
226
250
|
(0, output_1.printJson)(payload);
|
|
227
251
|
return;
|
|
228
252
|
}
|
|
229
253
|
if (result.runs.length === 0 && !service) {
|
|
230
254
|
if (agentName) {
|
|
231
255
|
process.stdout.write(`No runs found for agent '${agentName}'.\n`);
|
|
256
|
+
process.stdout.write(chalk_1.default.gray(`\nIf this is an always-on service, try: orch logs ${agentName} --live\n`));
|
|
232
257
|
}
|
|
233
258
|
else {
|
|
234
259
|
process.stdout.write('No runs found in this workspace.\n');
|
|
260
|
+
// Check if the workspace has active services the user might want logs for
|
|
261
|
+
const activeServices = await findActiveServices(config, workspaceId);
|
|
262
|
+
if (activeServices.length > 0) {
|
|
263
|
+
process.stdout.write(chalk_1.default.yellow('\nThis workspace has always-on services. To view service logs:\n'));
|
|
264
|
+
for (const svc of activeServices) {
|
|
265
|
+
process.stdout.write(` ${chalk_1.default.cyan('orch logs ' + svc.agent_name + ' --live')} (${svc.service_name})\n`);
|
|
266
|
+
}
|
|
267
|
+
process.stdout.write(chalk_1.default.gray('\nOr use: orch service logs <service-id>\n'));
|
|
268
|
+
}
|
|
235
269
|
}
|
|
236
270
|
return;
|
|
237
271
|
}
|
package/dist/commands/publish.js
CHANGED
|
@@ -43,6 +43,8 @@ exports.scanReservedPort = scanReservedPort;
|
|
|
43
43
|
exports.detectSdkCompatible = detectSdkCompatible;
|
|
44
44
|
exports.checkDependencies = checkDependencies;
|
|
45
45
|
exports.checkWorkspaceLlmKeys = checkWorkspaceLlmKeys;
|
|
46
|
+
exports.prePublishSecretsCheck = prePublishSecretsCheck;
|
|
47
|
+
exports.checkRequiredSecrets = checkRequiredSecrets;
|
|
46
48
|
exports.batchPublish = batchPublish;
|
|
47
49
|
exports.registerPublishCommand = registerPublishCommand;
|
|
48
50
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
@@ -58,6 +60,7 @@ const analytics_1 = require("../lib/analytics");
|
|
|
58
60
|
const bundle_1 = require("../lib/bundle");
|
|
59
61
|
const key_store_1 = require("../lib/key-store");
|
|
60
62
|
const batch_publish_1 = require("../lib/batch-publish");
|
|
63
|
+
const llm_1 = require("../lib/llm");
|
|
61
64
|
/**
|
|
62
65
|
* Extract template placeholders from a prompt template.
|
|
63
66
|
* Matches double-brace patterns like {{variable}}.
|
|
@@ -517,6 +520,97 @@ async function checkWorkspaceLlmKeys(config, workspaceId, workspaceSlug, executi
|
|
|
517
520
|
` Cloud runs will fail until you add keys.\n` +
|
|
518
521
|
` Add a key: ${chalk_1.default.cyan(`orch secrets set ${exampleSecretName} <key>`)}\n`);
|
|
519
522
|
}
|
|
523
|
+
/**
|
|
524
|
+
* Pre-publish check: verify that all required_secrets exist in the workspace
|
|
525
|
+
* BEFORE publishing. This lets users set missing secrets before wasting time
|
|
526
|
+
* on the publish step (DX-3).
|
|
527
|
+
*
|
|
528
|
+
* In TTY mode: warns and asks for confirmation to continue.
|
|
529
|
+
* In non-TTY mode: warns but does not block (for CI/CD pipelines).
|
|
530
|
+
* API errors are silently swallowed (non-fatal).
|
|
531
|
+
*
|
|
532
|
+
* Returns the list of missing secret names (empty if all present or check failed).
|
|
533
|
+
*/
|
|
534
|
+
async function prePublishSecretsCheck(config, workspaceId, workspaceSlug, requiredSecrets) {
|
|
535
|
+
if (!requiredSecrets.length)
|
|
536
|
+
return [];
|
|
537
|
+
let secrets;
|
|
538
|
+
try {
|
|
539
|
+
const result = await (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/secrets`);
|
|
540
|
+
secrets = result.secrets;
|
|
541
|
+
if (!Array.isArray(secrets))
|
|
542
|
+
return [];
|
|
543
|
+
}
|
|
544
|
+
catch {
|
|
545
|
+
return []; // Can't reach API — skip pre-check, gateway will catch at runtime
|
|
546
|
+
}
|
|
547
|
+
const existingNames = new Set(secrets.map(s => s.name));
|
|
548
|
+
const missing = requiredSecrets.filter(name => !existingNames.has(name));
|
|
549
|
+
if (missing.length === 0)
|
|
550
|
+
return [];
|
|
551
|
+
// Show prominent warning about missing secrets
|
|
552
|
+
process.stderr.write(chalk_1.default.yellow.bold(`\n⚠ ${missing.length} required secret${missing.length === 1 ? '' : 's'} missing from workspace '${workspaceSlug}':\n`));
|
|
553
|
+
for (const name of missing) {
|
|
554
|
+
process.stderr.write(` ${chalk_1.default.yellow('•')} ${chalk_1.default.bold(name)}\n`);
|
|
555
|
+
}
|
|
556
|
+
process.stderr.write(`\n Set them now:\n`);
|
|
557
|
+
for (const name of missing) {
|
|
558
|
+
process.stderr.write(` ${chalk_1.default.cyan(`orch secrets set ${name} <value>`)}\n`);
|
|
559
|
+
}
|
|
560
|
+
process.stderr.write(chalk_1.default.gray(`\n Without these secrets, the agent will fail at runtime.\n`));
|
|
561
|
+
// In TTY mode, ask user to confirm they want to continue publishing
|
|
562
|
+
if (process.stdin.isTTY && process.stderr.isTTY) {
|
|
563
|
+
const readline = await Promise.resolve().then(() => __importStar(require('readline/promises')));
|
|
564
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
565
|
+
const answer = await rl.question(chalk_1.default.yellow('\n Publish anyway? (y/N): '));
|
|
566
|
+
rl.close();
|
|
567
|
+
if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
|
|
568
|
+
process.stderr.write('\nPublish cancelled. Set the missing secrets first.\n');
|
|
569
|
+
process.exit(0);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
process.stderr.write(chalk_1.default.gray(` (non-interactive — continuing with publish)\n\n`));
|
|
574
|
+
}
|
|
575
|
+
return missing;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* After a successful publish, check whether the workspace has all the secrets
|
|
579
|
+
* declared in required_secrets. Warns about any missing ones so the user can
|
|
580
|
+
* set them before running the agent.
|
|
581
|
+
*
|
|
582
|
+
* Best-effort: API errors are silently swallowed (the agent is already published).
|
|
583
|
+
*/
|
|
584
|
+
async function checkRequiredSecrets(config, workspaceId, workspaceSlug, requiredSecrets) {
|
|
585
|
+
if (!requiredSecrets.length)
|
|
586
|
+
return;
|
|
587
|
+
let secrets;
|
|
588
|
+
try {
|
|
589
|
+
const result = await (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/secrets`);
|
|
590
|
+
secrets = result.secrets;
|
|
591
|
+
if (!Array.isArray(secrets))
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
catch {
|
|
595
|
+
return; // Can't reach API — fall back to showing all secrets as instructions
|
|
596
|
+
}
|
|
597
|
+
const existingNames = new Set(secrets.map(s => s.name));
|
|
598
|
+
const missing = requiredSecrets.filter(name => !existingNames.has(name));
|
|
599
|
+
if (missing.length === 0) {
|
|
600
|
+
// All secrets present — brief confirmation
|
|
601
|
+
process.stderr.write(chalk_1.default.green(`\n✓ All ${requiredSecrets.length} required secret${requiredSecrets.length === 1 ? '' : 's'} found in workspace '${workspaceSlug}'\n`));
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
// Some secrets missing — warn with set commands
|
|
605
|
+
process.stderr.write(chalk_1.default.yellow(`\n⚠ ${missing.length} required secret${missing.length === 1 ? '' : 's'} not set in workspace '${workspaceSlug}':\n`));
|
|
606
|
+
for (const name of missing) {
|
|
607
|
+
process.stderr.write(` ${chalk_1.default.yellow(name)}\n`);
|
|
608
|
+
}
|
|
609
|
+
process.stderr.write(`\n Set before running:\n`);
|
|
610
|
+
for (const name of missing) {
|
|
611
|
+
process.stderr.write(` ${chalk_1.default.cyan(`orch secrets set ${name} <value>`)}\n`);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
520
614
|
/**
|
|
521
615
|
* Batch publish all agents found in subdirectories, in dependency order.
|
|
522
616
|
* Discovers orchagent.json/SKILL.md in immediate subdirectories,
|
|
@@ -883,6 +977,13 @@ function registerPublishCommand(program) {
|
|
|
883
977
|
` ${chalk_1.default.cyan(`"default_models": { "anthropic": "${modelVal}" }`)}\n\n` +
|
|
884
978
|
` The model resolution order is: caller --model flag → agent default_models → platform default.\n\n`));
|
|
885
979
|
}
|
|
980
|
+
// DX-17: Validate model IDs in default_models
|
|
981
|
+
if (manifest.default_models && typeof manifest.default_models === 'object') {
|
|
982
|
+
const modelWarnings = (0, llm_1.validateModelIds)(manifest.default_models);
|
|
983
|
+
for (const w of modelWarnings) {
|
|
984
|
+
process.stderr.write(chalk_1.default.yellow(`Warning: ${w.message}\n`));
|
|
985
|
+
}
|
|
986
|
+
}
|
|
886
987
|
// Auto-migrate inline schemas to schema.json
|
|
887
988
|
const schemaPath = path_1.default.join(cwd, 'schema.json');
|
|
888
989
|
let schemaFileExists = false;
|
|
@@ -1336,6 +1437,9 @@ function registerPublishCommand(program) {
|
|
|
1336
1437
|
` env var to detect the reserved port.\n\n`);
|
|
1337
1438
|
}
|
|
1338
1439
|
}
|
|
1440
|
+
// Pre-publish: check required secrets exist in workspace (DX-3)
|
|
1441
|
+
// Fail fast — warn before spending time on the publish API call
|
|
1442
|
+
await prePublishSecretsCheck(config, workspaceId || org.id, org.slug, manifest.required_secrets || []);
|
|
1339
1443
|
// Create the agent (server auto-assigns version)
|
|
1340
1444
|
let result;
|
|
1341
1445
|
try {
|
|
@@ -1542,18 +1646,9 @@ function registerPublishCommand(program) {
|
|
|
1542
1646
|
}
|
|
1543
1647
|
// Warn if workspace has no LLM vault keys for this agent's providers (UX-13-01)
|
|
1544
1648
|
await checkWorkspaceLlmKeys(config, workspaceId || org.id, org.slug, executionEngine, supportedProviders);
|
|
1545
|
-
//
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
for (const secret of manifest.required_secrets) {
|
|
1549
|
-
process.stdout.write(` ${secret}\n`);
|
|
1550
|
-
}
|
|
1551
|
-
process.stdout.write(`\nSet secrets before running:\n`);
|
|
1552
|
-
for (const secret of manifest.required_secrets) {
|
|
1553
|
-
process.stdout.write(` orch secrets set ${secret} <value>\n`);
|
|
1554
|
-
}
|
|
1555
|
-
process.stdout.write(`\nView existing secrets: ${chalk_1.default.cyan('orch secrets list')}\n`);
|
|
1556
|
-
}
|
|
1649
|
+
// Post-publish: confirm secrets status (DX-13, DX-3)
|
|
1650
|
+
// Shows green check when all present, yellow warning when missing
|
|
1651
|
+
await checkRequiredSecrets(config, workspaceId || org.id, org.slug, manifest.required_secrets || []);
|
|
1557
1652
|
// Show security review result if available
|
|
1558
1653
|
const secReview = result.security_review;
|
|
1559
1654
|
if (secReview?.verdict) {
|