@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,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkApiKeyPresent = checkApiKeyPresent;
|
|
4
|
+
exports.checkApiKeyValid = checkApiKeyValid;
|
|
5
|
+
exports.runAuthChecks = runAuthChecks;
|
|
6
|
+
const config_1 = require("../../config");
|
|
7
|
+
const api_1 = require("../../api");
|
|
8
|
+
/**
|
|
9
|
+
* Check if API key is configured (in config file or env var).
|
|
10
|
+
*/
|
|
11
|
+
async function checkApiKeyPresent() {
|
|
12
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
13
|
+
const fileConfig = await (0, config_1.loadConfig)();
|
|
14
|
+
if (config.apiKey) {
|
|
15
|
+
// Determine source of the API key
|
|
16
|
+
let source = 'unknown';
|
|
17
|
+
if (process.env.ORCHAGENT_API_KEY) {
|
|
18
|
+
source = 'ORCHAGENT_API_KEY environment variable';
|
|
19
|
+
}
|
|
20
|
+
else if (fileConfig.api_key) {
|
|
21
|
+
source = '~/.orchagent/config.json';
|
|
22
|
+
}
|
|
23
|
+
// Get key prefix for verbose output (mask most of the key)
|
|
24
|
+
const keyPrefix = config.apiKey.slice(0, 12) + '...';
|
|
25
|
+
return {
|
|
26
|
+
category: 'authentication',
|
|
27
|
+
name: 'api_key_present',
|
|
28
|
+
status: 'success',
|
|
29
|
+
message: 'API key configured',
|
|
30
|
+
details: { source, keyPrefix },
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
category: 'authentication',
|
|
35
|
+
name: 'api_key_present',
|
|
36
|
+
status: 'error',
|
|
37
|
+
message: 'No API key configured',
|
|
38
|
+
fix: 'Run `orch login` or set ORCHAGENT_API_KEY environment variable',
|
|
39
|
+
details: { configured: false },
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if API key is valid by calling the /org endpoint.
|
|
44
|
+
*/
|
|
45
|
+
async function checkApiKeyValid() {
|
|
46
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
47
|
+
if (!config.apiKey) {
|
|
48
|
+
return {
|
|
49
|
+
category: 'authentication',
|
|
50
|
+
name: 'api_key_valid',
|
|
51
|
+
status: 'error',
|
|
52
|
+
message: 'Cannot validate API key (not configured)',
|
|
53
|
+
details: { reason: 'no api key' },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const org = await (0, api_1.getOrg)(config);
|
|
58
|
+
return {
|
|
59
|
+
category: 'authentication',
|
|
60
|
+
name: 'api_key_valid',
|
|
61
|
+
status: 'success',
|
|
62
|
+
message: `API key valid (logged in as: ${org.name})`,
|
|
63
|
+
details: {
|
|
64
|
+
orgName: org.name,
|
|
65
|
+
orgSlug: org.slug,
|
|
66
|
+
apiUrl: config.apiUrl,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
if (err instanceof api_1.ApiError) {
|
|
72
|
+
if (err.status === 401) {
|
|
73
|
+
return {
|
|
74
|
+
category: 'authentication',
|
|
75
|
+
name: 'api_key_valid',
|
|
76
|
+
status: 'error',
|
|
77
|
+
message: 'API key is invalid or expired',
|
|
78
|
+
fix: 'Run `orch login` to get a new key',
|
|
79
|
+
details: { error: err.message, status: err.status },
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
category: 'authentication',
|
|
84
|
+
name: 'api_key_valid',
|
|
85
|
+
status: 'error',
|
|
86
|
+
message: `API key validation failed (${err.status})`,
|
|
87
|
+
fix: 'Check your network connection and try again',
|
|
88
|
+
details: { error: err.message, status: err.status },
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
category: 'authentication',
|
|
93
|
+
name: 'api_key_valid',
|
|
94
|
+
status: 'error',
|
|
95
|
+
message: 'Could not validate API key',
|
|
96
|
+
fix: 'Check your network connection and try again',
|
|
97
|
+
details: { error: err instanceof Error ? err.message : 'unknown error' },
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Run all auth checks.
|
|
103
|
+
*/
|
|
104
|
+
async function runAuthChecks() {
|
|
105
|
+
const results = [];
|
|
106
|
+
// First check if key is present
|
|
107
|
+
const keyPresent = await checkApiKeyPresent();
|
|
108
|
+
results.push(keyPresent);
|
|
109
|
+
// Only validate key if it's present
|
|
110
|
+
if (keyPresent.status === 'success') {
|
|
111
|
+
const keyValid = await checkApiKeyValid();
|
|
112
|
+
results.push(keyValid);
|
|
113
|
+
}
|
|
114
|
+
return results;
|
|
115
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
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.checkConfigExists = checkConfigExists;
|
|
7
|
+
exports.checkConfigPermissions = checkConfigPermissions;
|
|
8
|
+
exports.runConfigChecks = runConfigChecks;
|
|
9
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const os_1 = __importDefault(require("os"));
|
|
12
|
+
const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.orchagent');
|
|
13
|
+
const CONFIG_PATH = path_1.default.join(CONFIG_DIR, 'config.json');
|
|
14
|
+
/**
|
|
15
|
+
* Check if config file exists.
|
|
16
|
+
*/
|
|
17
|
+
async function checkConfigExists() {
|
|
18
|
+
try {
|
|
19
|
+
await promises_1.default.access(CONFIG_PATH);
|
|
20
|
+
return {
|
|
21
|
+
category: 'configuration',
|
|
22
|
+
name: 'config_exists',
|
|
23
|
+
status: 'success',
|
|
24
|
+
message: `Config file exists (~/.orchagent/config.json)`,
|
|
25
|
+
details: { path: CONFIG_PATH },
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return {
|
|
30
|
+
category: 'configuration',
|
|
31
|
+
name: 'config_exists',
|
|
32
|
+
status: 'warning',
|
|
33
|
+
message: 'Config file not found',
|
|
34
|
+
fix: 'Run `orch login` to create config',
|
|
35
|
+
details: { path: CONFIG_PATH, exists: false },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Check config file permissions (should be 600 for security).
|
|
41
|
+
*/
|
|
42
|
+
async function checkConfigPermissions() {
|
|
43
|
+
try {
|
|
44
|
+
const stats = await promises_1.default.stat(CONFIG_PATH);
|
|
45
|
+
// On Windows, file permissions work differently
|
|
46
|
+
if (process.platform === 'win32') {
|
|
47
|
+
return {
|
|
48
|
+
category: 'configuration',
|
|
49
|
+
name: 'config_permissions',
|
|
50
|
+
status: 'success',
|
|
51
|
+
message: 'Config file permissions (Windows)',
|
|
52
|
+
details: { platform: 'win32', note: 'permissions check skipped on Windows' },
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// Check if permissions are 600 (owner read/write only)
|
|
56
|
+
// mode & 0o777 gives us the permission bits
|
|
57
|
+
const mode = stats.mode & 0o777;
|
|
58
|
+
if (mode === 0o600) {
|
|
59
|
+
return {
|
|
60
|
+
category: 'configuration',
|
|
61
|
+
name: 'config_permissions',
|
|
62
|
+
status: 'success',
|
|
63
|
+
message: 'Config file permissions (600)',
|
|
64
|
+
details: { mode: mode.toString(8), expected: '600' },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// Check if it's too permissive (world or group readable)
|
|
68
|
+
const worldReadable = (mode & 0o004) !== 0;
|
|
69
|
+
const groupReadable = (mode & 0o040) !== 0;
|
|
70
|
+
if (worldReadable || groupReadable) {
|
|
71
|
+
return {
|
|
72
|
+
category: 'configuration',
|
|
73
|
+
name: 'config_permissions',
|
|
74
|
+
status: 'warning',
|
|
75
|
+
message: `Config file permissions too open (${mode.toString(8)})`,
|
|
76
|
+
fix: 'Run `chmod 600 ~/.orchagent/config.json`',
|
|
77
|
+
details: {
|
|
78
|
+
mode: mode.toString(8),
|
|
79
|
+
expected: '600',
|
|
80
|
+
worldReadable,
|
|
81
|
+
groupReadable,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
category: 'configuration',
|
|
87
|
+
name: 'config_permissions',
|
|
88
|
+
status: 'success',
|
|
89
|
+
message: `Config file permissions (${mode.toString(8)})`,
|
|
90
|
+
details: { mode: mode.toString(8), expected: '600' },
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
if (err.code === 'ENOENT') {
|
|
95
|
+
// Config doesn't exist, skip permissions check
|
|
96
|
+
return {
|
|
97
|
+
category: 'configuration',
|
|
98
|
+
name: 'config_permissions',
|
|
99
|
+
status: 'warning',
|
|
100
|
+
message: 'Config file permissions (file not found)',
|
|
101
|
+
details: { error: 'file not found' },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
category: 'configuration',
|
|
106
|
+
name: 'config_permissions',
|
|
107
|
+
status: 'warning',
|
|
108
|
+
message: 'Could not check config permissions',
|
|
109
|
+
details: { error: err instanceof Error ? err.message : 'unknown error' },
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Run all config checks.
|
|
115
|
+
*/
|
|
116
|
+
async function runConfigChecks() {
|
|
117
|
+
const results = await Promise.all([checkConfigExists(), checkConfigPermissions()]);
|
|
118
|
+
return results;
|
|
119
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkGatewayHealth = checkGatewayHealth;
|
|
4
|
+
exports.runConnectivityChecks = runConnectivityChecks;
|
|
5
|
+
const config_1 = require("../../config");
|
|
6
|
+
const LATENCY_WARNING_MS = 2000;
|
|
7
|
+
/**
|
|
8
|
+
* Check if gateway is reachable by pinging /health endpoint.
|
|
9
|
+
* Also measures response time.
|
|
10
|
+
*/
|
|
11
|
+
async function checkGatewayHealth() {
|
|
12
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
13
|
+
const healthUrl = `${config.apiUrl.replace(/\/$/, '')}/health`;
|
|
14
|
+
const startTime = Date.now();
|
|
15
|
+
try {
|
|
16
|
+
const response = await fetch(healthUrl, {
|
|
17
|
+
signal: AbortSignal.timeout(10000),
|
|
18
|
+
});
|
|
19
|
+
const latency = Date.now() - startTime;
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
return [
|
|
22
|
+
{
|
|
23
|
+
category: 'connectivity',
|
|
24
|
+
name: 'gateway_reachable',
|
|
25
|
+
status: 'error',
|
|
26
|
+
message: `Gateway returned ${response.status}`,
|
|
27
|
+
fix: 'Check if OrchAgent API is operational at https://status.orchagent.io',
|
|
28
|
+
details: {
|
|
29
|
+
url: healthUrl,
|
|
30
|
+
status: response.status,
|
|
31
|
+
latency,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
// Parse hostname for display
|
|
37
|
+
const apiHost = new URL(config.apiUrl).host;
|
|
38
|
+
const results = [
|
|
39
|
+
{
|
|
40
|
+
category: 'connectivity',
|
|
41
|
+
name: 'gateway_reachable',
|
|
42
|
+
status: 'success',
|
|
43
|
+
message: `Gateway reachable (${apiHost})`,
|
|
44
|
+
details: {
|
|
45
|
+
url: healthUrl,
|
|
46
|
+
status: response.status,
|
|
47
|
+
host: apiHost,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
// Add latency check
|
|
52
|
+
if (latency > LATENCY_WARNING_MS) {
|
|
53
|
+
results.push({
|
|
54
|
+
category: 'connectivity',
|
|
55
|
+
name: 'response_time',
|
|
56
|
+
status: 'warning',
|
|
57
|
+
message: `Response time: ${latency}ms (high latency)`,
|
|
58
|
+
fix: 'High latency detected. Check network or try a different region.',
|
|
59
|
+
details: { latency, threshold: LATENCY_WARNING_MS },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
results.push({
|
|
64
|
+
category: 'connectivity',
|
|
65
|
+
name: 'response_time',
|
|
66
|
+
status: 'success',
|
|
67
|
+
message: `Response time: ${latency}ms`,
|
|
68
|
+
details: { latency, threshold: LATENCY_WARNING_MS },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return results;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
const latency = Date.now() - startTime;
|
|
75
|
+
const isTimeout = err instanceof Error && (err.name === 'AbortError' || err.name === 'TimeoutError');
|
|
76
|
+
if (isTimeout) {
|
|
77
|
+
return [
|
|
78
|
+
{
|
|
79
|
+
category: 'connectivity',
|
|
80
|
+
name: 'gateway_reachable',
|
|
81
|
+
status: 'error',
|
|
82
|
+
message: 'Gateway connection timed out',
|
|
83
|
+
fix: 'Check network, firewall, or proxy settings',
|
|
84
|
+
details: { url: healthUrl, error: 'timeout', latency },
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
return [
|
|
89
|
+
{
|
|
90
|
+
category: 'connectivity',
|
|
91
|
+
name: 'gateway_reachable',
|
|
92
|
+
status: 'error',
|
|
93
|
+
message: 'Cannot reach gateway',
|
|
94
|
+
fix: 'Check network, firewall, or proxy settings',
|
|
95
|
+
details: {
|
|
96
|
+
url: healthUrl,
|
|
97
|
+
error: err instanceof Error ? err.message : 'unknown error',
|
|
98
|
+
latency,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Run all connectivity checks.
|
|
106
|
+
*/
|
|
107
|
+
async function runConnectivityChecks() {
|
|
108
|
+
return checkGatewayHealth();
|
|
109
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
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.checkNodeVersion = checkNodeVersion;
|
|
7
|
+
exports.checkCliVersion = checkCliVersion;
|
|
8
|
+
exports.checkGitAvailable = checkGitAvailable;
|
|
9
|
+
exports.runEnvironmentChecks = runEnvironmentChecks;
|
|
10
|
+
const child_process_1 = require("child_process");
|
|
11
|
+
const package_json_1 = __importDefault(require("../../../../package.json"));
|
|
12
|
+
const REQUIRED_NODE_MAJOR = 18;
|
|
13
|
+
/**
|
|
14
|
+
* Check if Node.js version is 18+.
|
|
15
|
+
*/
|
|
16
|
+
async function checkNodeVersion() {
|
|
17
|
+
const version = process.version;
|
|
18
|
+
const major = parseInt(version.slice(1).split('.')[0], 10);
|
|
19
|
+
if (major >= REQUIRED_NODE_MAJOR) {
|
|
20
|
+
return {
|
|
21
|
+
category: 'environment',
|
|
22
|
+
name: 'node_version',
|
|
23
|
+
status: 'success',
|
|
24
|
+
message: `Node.js ${version} (${REQUIRED_NODE_MAJOR}+ required)`,
|
|
25
|
+
details: { version, required: `${REQUIRED_NODE_MAJOR}.0.0` },
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
category: 'environment',
|
|
30
|
+
name: 'node_version',
|
|
31
|
+
status: 'error',
|
|
32
|
+
message: `Node.js ${version} is too old (${REQUIRED_NODE_MAJOR}+ required)`,
|
|
33
|
+
fix: `Install Node.js ${REQUIRED_NODE_MAJOR}+ from https://nodejs.org`,
|
|
34
|
+
details: { version, required: `${REQUIRED_NODE_MAJOR}.0.0` },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Check if CLI is up to date by comparing with npm registry.
|
|
39
|
+
*/
|
|
40
|
+
async function checkCliVersion() {
|
|
41
|
+
const installedVersion = package_json_1.default.version;
|
|
42
|
+
try {
|
|
43
|
+
// Fetch latest version from npm registry
|
|
44
|
+
const response = await fetch('https://registry.npmjs.org/@orchagent/cli/latest', { signal: AbortSignal.timeout(5000) });
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
return {
|
|
47
|
+
category: 'environment',
|
|
48
|
+
name: 'cli_version',
|
|
49
|
+
status: 'warning',
|
|
50
|
+
message: `CLI v${installedVersion} (could not check for updates)`,
|
|
51
|
+
details: { installed: installedVersion, error: 'npm registry unreachable' },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const data = (await response.json());
|
|
55
|
+
const latestVersion = data.version;
|
|
56
|
+
if (installedVersion === latestVersion) {
|
|
57
|
+
return {
|
|
58
|
+
category: 'environment',
|
|
59
|
+
name: 'cli_version',
|
|
60
|
+
status: 'success',
|
|
61
|
+
message: `CLI v${installedVersion} (up to date)`,
|
|
62
|
+
details: { installed: installedVersion, latest: latestVersion },
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// Compare versions (simple semver comparison)
|
|
66
|
+
const installedParts = installedVersion.split('.').map(Number);
|
|
67
|
+
const latestParts = latestVersion.split('.').map(Number);
|
|
68
|
+
let isOutdated = false;
|
|
69
|
+
for (let i = 0; i < 3; i++) {
|
|
70
|
+
if ((latestParts[i] || 0) > (installedParts[i] || 0)) {
|
|
71
|
+
isOutdated = true;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
if ((latestParts[i] || 0) < (installedParts[i] || 0)) {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (isOutdated) {
|
|
79
|
+
return {
|
|
80
|
+
category: 'environment',
|
|
81
|
+
name: 'cli_version',
|
|
82
|
+
status: 'warning',
|
|
83
|
+
message: `CLI v${installedVersion} (v${latestVersion} available)`,
|
|
84
|
+
fix: 'Run `npm update -g @orchagent/cli` to update',
|
|
85
|
+
details: { installed: installedVersion, latest: latestVersion },
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
category: 'environment',
|
|
90
|
+
name: 'cli_version',
|
|
91
|
+
status: 'success',
|
|
92
|
+
message: `CLI v${installedVersion}`,
|
|
93
|
+
details: { installed: installedVersion, latest: latestVersion },
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
return {
|
|
98
|
+
category: 'environment',
|
|
99
|
+
name: 'cli_version',
|
|
100
|
+
status: 'warning',
|
|
101
|
+
message: `CLI v${installedVersion} (could not check for updates)`,
|
|
102
|
+
details: {
|
|
103
|
+
installed: installedVersion,
|
|
104
|
+
error: err instanceof Error ? err.message : 'unknown error',
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Check if git is available in PATH.
|
|
111
|
+
* Note: Uses execSync with hardcoded command string - no user input, safe from injection.
|
|
112
|
+
*/
|
|
113
|
+
async function checkGitAvailable() {
|
|
114
|
+
try {
|
|
115
|
+
const output = (0, child_process_1.execSync)('git --version', {
|
|
116
|
+
encoding: 'utf-8',
|
|
117
|
+
timeout: 5000,
|
|
118
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
119
|
+
});
|
|
120
|
+
const versionMatch = output.match(/git version (\S+)/);
|
|
121
|
+
const version = versionMatch ? versionMatch[1] : 'unknown';
|
|
122
|
+
return {
|
|
123
|
+
category: 'environment',
|
|
124
|
+
name: 'git_available',
|
|
125
|
+
status: 'success',
|
|
126
|
+
message: `Git available (${version})`,
|
|
127
|
+
details: { version },
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return {
|
|
132
|
+
category: 'environment',
|
|
133
|
+
name: 'git_available',
|
|
134
|
+
status: 'warning',
|
|
135
|
+
message: 'Git not found in PATH',
|
|
136
|
+
fix: 'Install git from https://git-scm.com',
|
|
137
|
+
details: { available: false },
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Run all environment checks.
|
|
143
|
+
*/
|
|
144
|
+
async function runEnvironmentChecks() {
|
|
145
|
+
const results = await Promise.all([
|
|
146
|
+
checkNodeVersion(),
|
|
147
|
+
checkCliVersion(),
|
|
148
|
+
checkGitAvailable(),
|
|
149
|
+
]);
|
|
150
|
+
return results;
|
|
151
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkServerLlmKeys = checkServerLlmKeys;
|
|
4
|
+
exports.checkLocalLlmEnvVars = checkLocalLlmEnvVars;
|
|
5
|
+
exports.runLlmChecks = runLlmChecks;
|
|
6
|
+
const config_1 = require("../../config");
|
|
7
|
+
const api_1 = require("../../api");
|
|
8
|
+
// Common LLM provider environment variables
|
|
9
|
+
const LLM_ENV_VARS = [
|
|
10
|
+
{ name: 'OPENAI_API_KEY', provider: 'OpenAI' },
|
|
11
|
+
{ name: 'ANTHROPIC_API_KEY', provider: 'Anthropic' },
|
|
12
|
+
{ name: 'GOOGLE_API_KEY', provider: 'Google' },
|
|
13
|
+
{ name: 'GEMINI_API_KEY', provider: 'Gemini' },
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Check if LLM keys are configured on the server.
|
|
17
|
+
*/
|
|
18
|
+
async function checkServerLlmKeys() {
|
|
19
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
20
|
+
if (!config.apiKey) {
|
|
21
|
+
return {
|
|
22
|
+
category: 'llm',
|
|
23
|
+
name: 'server_llm_keys',
|
|
24
|
+
status: 'warning',
|
|
25
|
+
message: 'Cannot check server LLM keys (not logged in)',
|
|
26
|
+
details: { reason: 'no api key' },
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const keys = await (0, api_1.fetchLlmKeys)(config);
|
|
31
|
+
if (keys.length === 0) {
|
|
32
|
+
return {
|
|
33
|
+
category: 'llm',
|
|
34
|
+
name: 'server_llm_keys',
|
|
35
|
+
status: 'warning',
|
|
36
|
+
message: 'No LLM keys configured on server',
|
|
37
|
+
fix: 'Run `orch keys add <provider>` or add keys at orchagent.io/settings',
|
|
38
|
+
details: { count: 0, providers: [] },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const providers = keys.map((k) => k.provider);
|
|
42
|
+
return {
|
|
43
|
+
category: 'llm',
|
|
44
|
+
name: 'server_llm_keys',
|
|
45
|
+
status: 'success',
|
|
46
|
+
message: `Server LLM keys configured (${providers.join(', ')})`,
|
|
47
|
+
details: { count: keys.length, providers },
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
if (err instanceof api_1.ApiError && err.status === 401) {
|
|
52
|
+
return {
|
|
53
|
+
category: 'llm',
|
|
54
|
+
name: 'server_llm_keys',
|
|
55
|
+
status: 'warning',
|
|
56
|
+
message: 'Cannot check server LLM keys (auth failed)',
|
|
57
|
+
details: { error: err.message },
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
category: 'llm',
|
|
62
|
+
name: 'server_llm_keys',
|
|
63
|
+
status: 'warning',
|
|
64
|
+
message: 'Could not check server LLM keys',
|
|
65
|
+
details: { error: err instanceof Error ? err.message : 'unknown error' },
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check if common LLM provider API keys are set in environment.
|
|
71
|
+
*/
|
|
72
|
+
async function checkLocalLlmEnvVars() {
|
|
73
|
+
const configuredProviders = [];
|
|
74
|
+
const details = {};
|
|
75
|
+
for (const { name, provider } of LLM_ENV_VARS) {
|
|
76
|
+
const isSet = !!process.env[name];
|
|
77
|
+
details[name] = isSet;
|
|
78
|
+
if (isSet) {
|
|
79
|
+
configuredProviders.push(provider);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (configuredProviders.length === 0) {
|
|
83
|
+
return {
|
|
84
|
+
category: 'llm',
|
|
85
|
+
name: 'local_llm_env',
|
|
86
|
+
status: 'warning',
|
|
87
|
+
message: 'No local LLM API keys found in environment',
|
|
88
|
+
fix: 'Set OPENAI_API_KEY, ANTHROPIC_API_KEY, or similar for local runs',
|
|
89
|
+
details,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
// Deduplicate (Google and Gemini might both be set)
|
|
93
|
+
const uniqueProviders = [...new Set(configuredProviders)];
|
|
94
|
+
return {
|
|
95
|
+
category: 'llm',
|
|
96
|
+
name: 'local_llm_env',
|
|
97
|
+
status: 'success',
|
|
98
|
+
message: `Local LLM keys found (${uniqueProviders.join(', ')})`,
|
|
99
|
+
details,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Run all LLM configuration checks.
|
|
104
|
+
*/
|
|
105
|
+
async function runLlmChecks() {
|
|
106
|
+
const results = await Promise.all([checkServerLlmKeys(), checkLocalLlmEnvVars()]);
|
|
107
|
+
return results;
|
|
108
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./types"), exports);
|
|
18
|
+
__exportStar(require("./runner"), exports);
|
|
19
|
+
__exportStar(require("./output"), exports);
|