@orchagent/cli 0.2.11 → 0.2.13
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/call.js +37 -10
- package/dist/commands/delete.js +12 -0
- package/dist/commands/doctor.js +27 -0
- package/dist/commands/github.js +543 -0
- package/dist/commands/index.js +6 -2
- package/dist/commands/publish.js +86 -2
- package/dist/commands/run.js +7 -2
- package/dist/commands/skill.js +10 -7
- package/dist/commands/status.js +97 -0
- package/dist/commands/workspace.js +133 -0
- package/dist/lib/api.js +30 -5
- package/dist/lib/auth-errors.js +27 -0
- package/dist/lib/browser-auth.js +5 -8
- package/dist/lib/bundle.js +48 -0
- package/dist/lib/doctor/checks/auth.js +115 -0
- package/dist/lib/doctor/checks/config.js +119 -0
- package/dist/lib/doctor/checks/connectivity.js +109 -0
- package/dist/lib/doctor/checks/environment.js +151 -0
- package/dist/lib/doctor/checks/llm.js +108 -0
- package/dist/lib/doctor/index.js +19 -0
- package/dist/lib/doctor/output.js +105 -0
- package/dist/lib/doctor/runner.js +68 -0
- package/dist/lib/doctor/types.js +2 -0
- package/dist/lib/errors.js +41 -1
- package/dist/lib/llm-errors.js +79 -0
- package/dist/lib/llm.js +4 -3
- package/package.json +1 -1
|
@@ -0,0 +1,105 @@
|
|
|
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.printHumanOutput = printHumanOutput;
|
|
7
|
+
exports.formatJsonOutput = formatJsonOutput;
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
// Status symbols
|
|
10
|
+
const SYMBOLS = {
|
|
11
|
+
success: chalk_1.default.green('\u2713'), // checkmark
|
|
12
|
+
warning: chalk_1.default.yellow('\u26a0'), // warning sign
|
|
13
|
+
error: chalk_1.default.red('\u2717'), // X mark
|
|
14
|
+
};
|
|
15
|
+
// Category display names
|
|
16
|
+
const CATEGORY_NAMES = {
|
|
17
|
+
environment: 'Environment',
|
|
18
|
+
configuration: 'Configuration',
|
|
19
|
+
connectivity: 'Connectivity',
|
|
20
|
+
authentication: 'Authentication',
|
|
21
|
+
llm: 'LLM Configuration',
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Group check results by category.
|
|
25
|
+
*/
|
|
26
|
+
function groupByCategory(results) {
|
|
27
|
+
const groups = new Map();
|
|
28
|
+
// Define category order
|
|
29
|
+
const categoryOrder = [
|
|
30
|
+
'environment',
|
|
31
|
+
'configuration',
|
|
32
|
+
'connectivity',
|
|
33
|
+
'authentication',
|
|
34
|
+
'llm',
|
|
35
|
+
];
|
|
36
|
+
// Initialize groups in order
|
|
37
|
+
for (const cat of categoryOrder) {
|
|
38
|
+
groups.set(cat, []);
|
|
39
|
+
}
|
|
40
|
+
// Add results to groups
|
|
41
|
+
for (const result of results) {
|
|
42
|
+
const existing = groups.get(result.category) || [];
|
|
43
|
+
existing.push(result);
|
|
44
|
+
groups.set(result.category, existing);
|
|
45
|
+
}
|
|
46
|
+
// Remove empty groups
|
|
47
|
+
for (const [key, value] of groups) {
|
|
48
|
+
if (value.length === 0) {
|
|
49
|
+
groups.delete(key);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return groups;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Print human-readable output to stdout.
|
|
56
|
+
*/
|
|
57
|
+
function printHumanOutput(results, summary, verbose) {
|
|
58
|
+
// Header
|
|
59
|
+
process.stdout.write('\n');
|
|
60
|
+
process.stdout.write(chalk_1.default.bold('OrchAgent Doctor\n'));
|
|
61
|
+
process.stdout.write('================\n\n');
|
|
62
|
+
// Group and print results
|
|
63
|
+
const groups = groupByCategory(results);
|
|
64
|
+
for (const [category, checks] of groups) {
|
|
65
|
+
const displayName = CATEGORY_NAMES[category] || category;
|
|
66
|
+
process.stdout.write(chalk_1.default.bold(`${displayName}\n`));
|
|
67
|
+
for (const check of checks) {
|
|
68
|
+
const symbol = SYMBOLS[check.status];
|
|
69
|
+
process.stdout.write(` ${symbol} ${check.message}\n`);
|
|
70
|
+
// Show fix suggestion for warnings/errors
|
|
71
|
+
if (check.fix && check.status !== 'success') {
|
|
72
|
+
process.stdout.write(chalk_1.default.dim(` \u2192 ${check.fix}\n`));
|
|
73
|
+
}
|
|
74
|
+
// Show details in verbose mode
|
|
75
|
+
if (verbose && check.details) {
|
|
76
|
+
for (const [key, value] of Object.entries(check.details)) {
|
|
77
|
+
const displayValue = typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
78
|
+
process.stdout.write(chalk_1.default.dim(` ${key}: ${displayValue}\n`));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
process.stdout.write('\n');
|
|
83
|
+
}
|
|
84
|
+
// Summary line
|
|
85
|
+
const summaryParts = [];
|
|
86
|
+
if (summary.passed > 0) {
|
|
87
|
+
summaryParts.push(chalk_1.default.green(`${summary.passed} passed`));
|
|
88
|
+
}
|
|
89
|
+
if (summary.warnings > 0) {
|
|
90
|
+
summaryParts.push(chalk_1.default.yellow(`${summary.warnings} warning${summary.warnings > 1 ? 's' : ''}`));
|
|
91
|
+
}
|
|
92
|
+
if (summary.errors > 0) {
|
|
93
|
+
summaryParts.push(chalk_1.default.red(`${summary.errors} error${summary.errors > 1 ? 's' : ''}`));
|
|
94
|
+
}
|
|
95
|
+
process.stdout.write(`Summary: ${summaryParts.join(', ')}\n`);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Format results as JSON output.
|
|
99
|
+
*/
|
|
100
|
+
function formatJsonOutput(results, summary) {
|
|
101
|
+
return {
|
|
102
|
+
summary,
|
|
103
|
+
checks: results,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runAllChecks = runAllChecks;
|
|
4
|
+
exports.calculateSummary = calculateSummary;
|
|
5
|
+
const environment_1 = require("./checks/environment");
|
|
6
|
+
const config_1 = require("./checks/config");
|
|
7
|
+
const auth_1 = require("./checks/auth");
|
|
8
|
+
const connectivity_1 = require("./checks/connectivity");
|
|
9
|
+
const llm_1 = require("./checks/llm");
|
|
10
|
+
/**
|
|
11
|
+
* Run all diagnostic checks in order.
|
|
12
|
+
* Checks are organized by category and run in a logical sequence.
|
|
13
|
+
*/
|
|
14
|
+
async function runAllChecks() {
|
|
15
|
+
const results = [];
|
|
16
|
+
// Environment checks (no dependencies)
|
|
17
|
+
const envResults = await (0, environment_1.runEnvironmentChecks)();
|
|
18
|
+
results.push(...envResults);
|
|
19
|
+
// Config checks (no dependencies)
|
|
20
|
+
const configResults = await (0, config_1.runConfigChecks)();
|
|
21
|
+
results.push(...configResults);
|
|
22
|
+
// Connectivity checks (test gateway before auth)
|
|
23
|
+
const connectivityResults = await (0, connectivity_1.runConnectivityChecks)();
|
|
24
|
+
results.push(...connectivityResults);
|
|
25
|
+
// Check if gateway is reachable before running auth/LLM checks
|
|
26
|
+
const gatewayOk = connectivityResults.some((r) => r.name === 'gateway_reachable' && r.status === 'success');
|
|
27
|
+
if (gatewayOk) {
|
|
28
|
+
// Auth checks (requires gateway)
|
|
29
|
+
const authResults = await (0, auth_1.runAuthChecks)();
|
|
30
|
+
results.push(...authResults);
|
|
31
|
+
// LLM checks (requires auth for server keys)
|
|
32
|
+
const llmResults = await (0, llm_1.runLlmChecks)();
|
|
33
|
+
results.push(...llmResults);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Add placeholder results when gateway is unreachable
|
|
37
|
+
results.push({
|
|
38
|
+
category: 'authentication',
|
|
39
|
+
name: 'api_key_present',
|
|
40
|
+
status: 'warning',
|
|
41
|
+
message: 'Skipped (gateway unreachable)',
|
|
42
|
+
details: { skipped: true, reason: 'gateway unreachable' },
|
|
43
|
+
});
|
|
44
|
+
results.push({
|
|
45
|
+
category: 'llm',
|
|
46
|
+
name: 'server_llm_keys',
|
|
47
|
+
status: 'warning',
|
|
48
|
+
message: 'Skipped (gateway unreachable)',
|
|
49
|
+
details: { skipped: true, reason: 'gateway unreachable' },
|
|
50
|
+
});
|
|
51
|
+
// Still check local LLM env vars since they don't require network
|
|
52
|
+
const localLlmCheck = (await (0, llm_1.runLlmChecks)()).find((r) => r.name === 'local_llm_env');
|
|
53
|
+
if (localLlmCheck) {
|
|
54
|
+
results.push(localLlmCheck);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return results;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Calculate summary statistics from check results.
|
|
61
|
+
*/
|
|
62
|
+
function calculateSummary(results) {
|
|
63
|
+
return {
|
|
64
|
+
passed: results.filter((r) => r.status === 'success').length,
|
|
65
|
+
warnings: results.filter((r) => r.status === 'warning').length,
|
|
66
|
+
errors: results.filter((r) => r.status === 'error').length,
|
|
67
|
+
};
|
|
68
|
+
}
|
package/dist/lib/errors.js
CHANGED
|
@@ -33,13 +33,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.CliError = void 0;
|
|
36
|
+
exports.NetworkError = exports.ExitCodes = exports.CliError = void 0;
|
|
37
37
|
exports.formatError = formatError;
|
|
38
38
|
exports.exitWithError = exitWithError;
|
|
39
|
+
exports.jsonInputError = jsonInputError;
|
|
39
40
|
const Sentry = __importStar(require("@sentry/node"));
|
|
40
41
|
const analytics_1 = require("./analytics");
|
|
41
42
|
class CliError extends Error {
|
|
42
43
|
exitCode;
|
|
44
|
+
cause;
|
|
43
45
|
constructor(message, exitCode = 1) {
|
|
44
46
|
super(message);
|
|
45
47
|
this.exitCode = exitCode;
|
|
@@ -73,3 +75,41 @@ async function exitWithError(err) {
|
|
|
73
75
|
}
|
|
74
76
|
process.exit(1);
|
|
75
77
|
}
|
|
78
|
+
exports.ExitCodes = {
|
|
79
|
+
SUCCESS: 0,
|
|
80
|
+
GENERAL_ERROR: 1,
|
|
81
|
+
AUTH_ERROR: 2,
|
|
82
|
+
PERMISSION_DENIED: 3,
|
|
83
|
+
NOT_FOUND: 4,
|
|
84
|
+
INVALID_INPUT: 5,
|
|
85
|
+
RATE_LIMITED: 6,
|
|
86
|
+
TIMEOUT: 7,
|
|
87
|
+
SERVER_ERROR: 8,
|
|
88
|
+
NETWORK_ERROR: 9,
|
|
89
|
+
};
|
|
90
|
+
class NetworkError extends CliError {
|
|
91
|
+
constructor(url, cause) {
|
|
92
|
+
const host = new URL(url).host;
|
|
93
|
+
super(`Unable to connect to ${host}\n\n` +
|
|
94
|
+
'Possible causes:\n' +
|
|
95
|
+
' - Network connectivity issues\n' +
|
|
96
|
+
' - Service temporarily unavailable\n' +
|
|
97
|
+
' - Firewall or proxy blocking the request\n\n' +
|
|
98
|
+
'Check status at: https://status.orchagent.io', exports.ExitCodes.NETWORK_ERROR);
|
|
99
|
+
this.cause = cause;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.NetworkError = NetworkError;
|
|
103
|
+
function jsonInputError(flag) {
|
|
104
|
+
return new CliError(`Invalid JSON in --${flag} option.\n\n` +
|
|
105
|
+
'Common causes:\n' +
|
|
106
|
+
' - Shell special characters (!, $, `) need escaping\n' +
|
|
107
|
+
' - Missing or mismatched quotes\n\n' +
|
|
108
|
+
'Shell-specific tips:\n' +
|
|
109
|
+
' - Bash/Zsh: Use single quotes: --data \'{"key": "value"}\'\n' +
|
|
110
|
+
' - PowerShell: Use double quotes and escape: --data "{\\"key\\": \\"value\\"}"\n' +
|
|
111
|
+
' - Any shell: Use a file: --data @input.json\n\n' +
|
|
112
|
+
'Alternatives:\n' +
|
|
113
|
+
` - Use a file: --${flag} @input.json\n` +
|
|
114
|
+
` - Use stdin: echo '{"key":"value"}' | orch call agent --${flag} @-`, exports.ExitCodes.INVALID_INPUT);
|
|
115
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseLlmError = parseLlmError;
|
|
4
|
+
const errors_1 = require("./errors");
|
|
5
|
+
// Safe fallback messages by status code
|
|
6
|
+
const FALLBACKS = {
|
|
7
|
+
openai: {
|
|
8
|
+
401: 'Invalid OpenAI API key. Check your OPENAI_API_KEY.',
|
|
9
|
+
403: 'Access denied. Your API key may not have permission for this model.',
|
|
10
|
+
429: 'Rate limit exceeded. Wait a moment and try again.',
|
|
11
|
+
500: 'OpenAI service error. Try again later.',
|
|
12
|
+
502: 'OpenAI is temporarily unavailable.',
|
|
13
|
+
503: 'OpenAI is overloaded. Try again later.',
|
|
14
|
+
},
|
|
15
|
+
anthropic: {
|
|
16
|
+
401: 'Invalid Anthropic API key. Check your ANTHROPIC_API_KEY.',
|
|
17
|
+
403: 'Access denied. Your API key may not have permission for this model.',
|
|
18
|
+
429: 'Rate limit exceeded. Wait a moment and try again.',
|
|
19
|
+
500: 'Anthropic service error. Try again later.',
|
|
20
|
+
502: 'Anthropic is temporarily unavailable.',
|
|
21
|
+
503: 'Anthropic is overloaded. Try again later.',
|
|
22
|
+
},
|
|
23
|
+
gemini: {
|
|
24
|
+
401: 'Invalid Gemini API key. Check your GEMINI_API_KEY.',
|
|
25
|
+
403: 'Access denied. Your API key may not have permission for this model.',
|
|
26
|
+
429: 'Rate limit exceeded. Wait a moment and try again.',
|
|
27
|
+
500: 'Gemini service error. Try again later.',
|
|
28
|
+
502: 'Gemini is temporarily unavailable.',
|
|
29
|
+
503: 'Gemini is overloaded. Try again later.',
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
const DEFAULT = 'LLM provider error. Check your API key and try again.';
|
|
33
|
+
function isHtml(text) {
|
|
34
|
+
const t = text.trim().toLowerCase();
|
|
35
|
+
return t.startsWith('<!doctype') || t.startsWith('<html');
|
|
36
|
+
}
|
|
37
|
+
function sanitize(msg) {
|
|
38
|
+
if (msg.length > 200)
|
|
39
|
+
msg = msg.slice(0, 200) + '...';
|
|
40
|
+
return msg.replace(/https?:\/\/[^\s]+/g, '[URL]').trim();
|
|
41
|
+
}
|
|
42
|
+
function parseOpenAI(text, status) {
|
|
43
|
+
try {
|
|
44
|
+
const p = JSON.parse(text);
|
|
45
|
+
if (p.error?.message)
|
|
46
|
+
return sanitize(p.error.message);
|
|
47
|
+
}
|
|
48
|
+
catch { }
|
|
49
|
+
return FALLBACKS.openai[status] || DEFAULT;
|
|
50
|
+
}
|
|
51
|
+
function parseAnthropic(text, status) {
|
|
52
|
+
try {
|
|
53
|
+
const p = JSON.parse(text);
|
|
54
|
+
const msg = p.error?.message || p.message;
|
|
55
|
+
if (msg)
|
|
56
|
+
return sanitize(msg);
|
|
57
|
+
}
|
|
58
|
+
catch { }
|
|
59
|
+
return FALLBACKS.anthropic[status] || DEFAULT;
|
|
60
|
+
}
|
|
61
|
+
function parseGemini(text, status) {
|
|
62
|
+
try {
|
|
63
|
+
const p = JSON.parse(text);
|
|
64
|
+
if (p.error?.message)
|
|
65
|
+
return sanitize(p.error.message);
|
|
66
|
+
}
|
|
67
|
+
catch { }
|
|
68
|
+
return FALLBACKS.gemini[status] || DEFAULT;
|
|
69
|
+
}
|
|
70
|
+
function parseLlmError(provider, text, status) {
|
|
71
|
+
if (isHtml(text)) {
|
|
72
|
+
return new errors_1.CliError(`${provider} error: ${FALLBACKS[provider][status] || DEFAULT}`);
|
|
73
|
+
}
|
|
74
|
+
const msg = provider === 'openai' ? parseOpenAI(text, status)
|
|
75
|
+
: provider === 'anthropic' ? parseAnthropic(text, status)
|
|
76
|
+
: parseGemini(text, status);
|
|
77
|
+
const display = provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
78
|
+
return new errors_1.CliError(`${display} API error: ${msg}`);
|
|
79
|
+
}
|
package/dist/lib/llm.js
CHANGED
|
@@ -47,6 +47,7 @@ exports.buildPrompt = buildPrompt;
|
|
|
47
47
|
exports.callLlm = callLlm;
|
|
48
48
|
exports.validateProvider = validateProvider;
|
|
49
49
|
const errors_1 = require("./errors");
|
|
50
|
+
const llm_errors_1 = require("./llm-errors");
|
|
50
51
|
// Environment variable names for each provider
|
|
51
52
|
exports.PROVIDER_ENV_VARS = {
|
|
52
53
|
openai: 'OPENAI_API_KEY',
|
|
@@ -181,7 +182,7 @@ async function callOpenAI(apiKey, model, prompt, outputSchema) {
|
|
|
181
182
|
});
|
|
182
183
|
if (!response.ok) {
|
|
183
184
|
const text = await response.text();
|
|
184
|
-
throw
|
|
185
|
+
throw (0, llm_errors_1.parseLlmError)('openai', text, response.status);
|
|
185
186
|
}
|
|
186
187
|
const data = (await response.json());
|
|
187
188
|
const content = data.choices?.[0]?.message?.content || '';
|
|
@@ -208,7 +209,7 @@ async function callAnthropic(apiKey, model, prompt, _outputSchema) {
|
|
|
208
209
|
});
|
|
209
210
|
if (!response.ok) {
|
|
210
211
|
const text = await response.text();
|
|
211
|
-
throw
|
|
212
|
+
throw (0, llm_errors_1.parseLlmError)('anthropic', text, response.status);
|
|
212
213
|
}
|
|
213
214
|
const data = (await response.json());
|
|
214
215
|
const content = data.content?.[0]?.text || '';
|
|
@@ -230,7 +231,7 @@ async function callGemini(apiKey, model, prompt, _outputSchema) {
|
|
|
230
231
|
});
|
|
231
232
|
if (!response.ok) {
|
|
232
233
|
const text = await response.text();
|
|
233
|
-
throw
|
|
234
|
+
throw (0, llm_errors_1.parseLlmError)('gemini', text, response.status);
|
|
234
235
|
}
|
|
235
236
|
const data = (await response.json());
|
|
236
237
|
const content = data.candidates?.[0]?.content?.parts?.[0]?.text || '';
|