@orchagent/cli 0.1.0
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/README.md +18 -0
- package/dist/commands/agents.js +26 -0
- package/dist/commands/call.js +264 -0
- package/dist/commands/fork.js +42 -0
- package/dist/commands/index.js +33 -0
- package/dist/commands/info.js +88 -0
- package/dist/commands/init.js +101 -0
- package/dist/commands/keys.js +172 -0
- package/dist/commands/llm-config.js +40 -0
- package/dist/commands/login.js +94 -0
- package/dist/commands/publish.js +192 -0
- package/dist/commands/publish.test.js +475 -0
- package/dist/commands/run.js +421 -0
- package/dist/commands/run.test.js +330 -0
- package/dist/commands/search.js +46 -0
- package/dist/commands/skill.js +141 -0
- package/dist/commands/star.js +41 -0
- package/dist/commands/whoami.js +17 -0
- package/dist/index.js +55 -0
- package/dist/lib/analytics.js +27 -0
- package/dist/lib/api.js +179 -0
- package/dist/lib/api.test.js +230 -0
- package/dist/lib/browser-auth.js +278 -0
- package/dist/lib/bundle.js +213 -0
- package/dist/lib/config.js +54 -0
- package/dist/lib/config.test.js +144 -0
- package/dist/lib/errors.js +75 -0
- package/dist/lib/llm.js +252 -0
- package/dist/lib/output.js +50 -0
- package/dist/types.js +2 -0
- package/package.json +59 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Code bundling utilities for hosted code agents.
|
|
4
|
+
*
|
|
5
|
+
* Creates zip bundles from project directories for upload to OrchAgent.
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.createCodeBundle = createCodeBundle;
|
|
12
|
+
exports.detectEntrypoint = detectEntrypoint;
|
|
13
|
+
exports.validateBundle = validateBundle;
|
|
14
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
const archiver_1 = __importDefault(require("archiver"));
|
|
17
|
+
const fs_1 = require("fs");
|
|
18
|
+
/** Default patterns to exclude from bundles */
|
|
19
|
+
const DEFAULT_EXCLUDES = [
|
|
20
|
+
// Python
|
|
21
|
+
'**/__pycache__/**',
|
|
22
|
+
'**/*.pyc',
|
|
23
|
+
'**/*.pyo',
|
|
24
|
+
'**/*.pyd',
|
|
25
|
+
'.Python',
|
|
26
|
+
'venv/**',
|
|
27
|
+
'.venv/**',
|
|
28
|
+
'env/**',
|
|
29
|
+
'.env',
|
|
30
|
+
'**/*.egg-info/**',
|
|
31
|
+
'dist/**',
|
|
32
|
+
'build/**',
|
|
33
|
+
'.eggs/**',
|
|
34
|
+
'.mypy_cache/**',
|
|
35
|
+
'.pytest_cache/**',
|
|
36
|
+
'.ruff_cache/**',
|
|
37
|
+
'pyproject.toml',
|
|
38
|
+
'setup.py',
|
|
39
|
+
'setup.cfg',
|
|
40
|
+
// Node.js
|
|
41
|
+
'node_modules/**',
|
|
42
|
+
'npm-debug.log',
|
|
43
|
+
'yarn-error.log',
|
|
44
|
+
'package.json',
|
|
45
|
+
'package-lock.json',
|
|
46
|
+
'yarn.lock',
|
|
47
|
+
'bun.lockb',
|
|
48
|
+
'tsconfig.json',
|
|
49
|
+
// Git
|
|
50
|
+
'.git/**',
|
|
51
|
+
'.gitignore',
|
|
52
|
+
'.gitattributes',
|
|
53
|
+
// IDE
|
|
54
|
+
'.idea/**',
|
|
55
|
+
'.vscode/**',
|
|
56
|
+
'*.swp',
|
|
57
|
+
'*.swo',
|
|
58
|
+
'.DS_Store',
|
|
59
|
+
// Documentation
|
|
60
|
+
'README.md',
|
|
61
|
+
'README.rst',
|
|
62
|
+
'README.txt',
|
|
63
|
+
'CHANGELOG.md',
|
|
64
|
+
'LICENSE',
|
|
65
|
+
'LICENSE.md',
|
|
66
|
+
'LICENSE.txt',
|
|
67
|
+
'docs/**',
|
|
68
|
+
// Docker
|
|
69
|
+
'Dockerfile',
|
|
70
|
+
'docker-compose.yml',
|
|
71
|
+
'docker-compose.yaml',
|
|
72
|
+
'.dockerignore',
|
|
73
|
+
// CI/CD
|
|
74
|
+
'.github/**',
|
|
75
|
+
'.gitlab-ci.yml',
|
|
76
|
+
'.travis.yml',
|
|
77
|
+
'.circleci/**',
|
|
78
|
+
// Test files
|
|
79
|
+
'tests/**',
|
|
80
|
+
'test/**',
|
|
81
|
+
'*_test.py',
|
|
82
|
+
'test_*.py',
|
|
83
|
+
'*.test.js',
|
|
84
|
+
'*.test.ts',
|
|
85
|
+
'*.spec.js',
|
|
86
|
+
'*.spec.ts',
|
|
87
|
+
'__tests__/**',
|
|
88
|
+
'conftest.py',
|
|
89
|
+
'pytest.ini',
|
|
90
|
+
'.coveragerc',
|
|
91
|
+
'coverage/**',
|
|
92
|
+
// OrchAgent
|
|
93
|
+
'orchagent.json',
|
|
94
|
+
'bundle.zip',
|
|
95
|
+
'*.zip',
|
|
96
|
+
// Scripts and misc
|
|
97
|
+
'scripts/**',
|
|
98
|
+
'Makefile',
|
|
99
|
+
'.editorconfig',
|
|
100
|
+
'.pre-commit-config.yaml',
|
|
101
|
+
];
|
|
102
|
+
/**
|
|
103
|
+
* Create a code bundle from a project directory.
|
|
104
|
+
*
|
|
105
|
+
* @param sourceDir - The directory to bundle
|
|
106
|
+
* @param outputPath - Path for the output zip file
|
|
107
|
+
* @param options - Bundle options
|
|
108
|
+
* @returns Promise that resolves with bundle metadata
|
|
109
|
+
*/
|
|
110
|
+
async function createCodeBundle(sourceDir, outputPath, options = {}) {
|
|
111
|
+
const excludePatterns = [...DEFAULT_EXCLUDES, ...(options.exclude || [])];
|
|
112
|
+
// Verify source directory exists
|
|
113
|
+
const stat = await promises_1.default.stat(sourceDir);
|
|
114
|
+
if (!stat.isDirectory()) {
|
|
115
|
+
throw new Error(`Source path is not a directory: ${sourceDir}`);
|
|
116
|
+
}
|
|
117
|
+
// Verify entrypoint exists if specified
|
|
118
|
+
const entrypoint = options.entrypoint || 'main.py';
|
|
119
|
+
const entrypointPath = path_1.default.join(sourceDir, entrypoint);
|
|
120
|
+
try {
|
|
121
|
+
await promises_1.default.access(entrypointPath);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
throw new Error(`Entrypoint file not found: ${entrypoint}`);
|
|
125
|
+
}
|
|
126
|
+
// Create output directory if needed
|
|
127
|
+
const outputDir = path_1.default.dirname(outputPath);
|
|
128
|
+
await promises_1.default.mkdir(outputDir, { recursive: true });
|
|
129
|
+
return new Promise((resolve, reject) => {
|
|
130
|
+
const output = (0, fs_1.createWriteStream)(outputPath);
|
|
131
|
+
const archive = (0, archiver_1.default)('zip', { zlib: { level: 9 } });
|
|
132
|
+
let fileCount = 0;
|
|
133
|
+
output.on('close', () => {
|
|
134
|
+
resolve({
|
|
135
|
+
path: outputPath,
|
|
136
|
+
sizeBytes: archive.pointer(),
|
|
137
|
+
fileCount,
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
archive.on('error', (err) => {
|
|
141
|
+
reject(err);
|
|
142
|
+
});
|
|
143
|
+
archive.on('entry', () => {
|
|
144
|
+
fileCount++;
|
|
145
|
+
});
|
|
146
|
+
archive.pipe(output);
|
|
147
|
+
// Add directory contents with exclusions
|
|
148
|
+
archive.glob('**/*', {
|
|
149
|
+
cwd: sourceDir,
|
|
150
|
+
ignore: excludePatterns,
|
|
151
|
+
dot: false,
|
|
152
|
+
});
|
|
153
|
+
archive.finalize();
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Detect the project type and suggest an entrypoint.
|
|
158
|
+
*
|
|
159
|
+
* @param projectDir - The project directory to analyze
|
|
160
|
+
* @returns Detected entrypoint or null if not found
|
|
161
|
+
*/
|
|
162
|
+
async function detectEntrypoint(projectDir) {
|
|
163
|
+
// Check common Python entrypoints
|
|
164
|
+
const pythonEntrypoints = ['main.py', 'app.py', 'agent.py', 'run.py', '__main__.py'];
|
|
165
|
+
for (const entry of pythonEntrypoints) {
|
|
166
|
+
try {
|
|
167
|
+
await promises_1.default.access(path_1.default.join(projectDir, entry));
|
|
168
|
+
return entry;
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// Continue checking
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Check common JS/TS entrypoints
|
|
175
|
+
const jsEntrypoints = ['main.js', 'index.js', 'agent.js', 'main.ts', 'index.ts', 'agent.ts'];
|
|
176
|
+
for (const entry of jsEntrypoints) {
|
|
177
|
+
try {
|
|
178
|
+
await promises_1.default.access(path_1.default.join(projectDir, entry));
|
|
179
|
+
return entry;
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// Continue checking
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Validate a bundle before upload.
|
|
189
|
+
*
|
|
190
|
+
* @param bundlePath - Path to the zip file
|
|
191
|
+
* @param maxSizeBytes - Maximum allowed size
|
|
192
|
+
* @returns Validation result
|
|
193
|
+
*/
|
|
194
|
+
async function validateBundle(bundlePath, maxSizeBytes = 50 * 1024 * 1024) {
|
|
195
|
+
try {
|
|
196
|
+
const stat = await promises_1.default.stat(bundlePath);
|
|
197
|
+
if (stat.size > maxSizeBytes) {
|
|
198
|
+
return {
|
|
199
|
+
valid: false,
|
|
200
|
+
error: `Bundle exceeds maximum size of ${maxSizeBytes / (1024 * 1024)}MB`,
|
|
201
|
+
sizeBytes: stat.size,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
return { valid: true, sizeBytes: stat.size };
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
return {
|
|
208
|
+
valid: false,
|
|
209
|
+
error: `Failed to read bundle: ${err}`,
|
|
210
|
+
sizeBytes: 0,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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.loadConfig = loadConfig;
|
|
7
|
+
exports.saveConfig = saveConfig;
|
|
8
|
+
exports.getResolvedConfig = getResolvedConfig;
|
|
9
|
+
exports.getConfigPath = getConfigPath;
|
|
10
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const os_1 = __importDefault(require("os"));
|
|
13
|
+
const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.orchagent');
|
|
14
|
+
const CONFIG_PATH = path_1.default.join(CONFIG_DIR, 'config.json');
|
|
15
|
+
const DEFAULT_API_URL = 'https://api.orchagent.io';
|
|
16
|
+
async function loadConfig() {
|
|
17
|
+
try {
|
|
18
|
+
const raw = await promises_1.default.readFile(CONFIG_PATH, 'utf-8');
|
|
19
|
+
return JSON.parse(raw);
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
if (err.code === 'ENOENT') {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function saveConfig(config) {
|
|
29
|
+
await promises_1.default.mkdir(CONFIG_DIR, { recursive: true });
|
|
30
|
+
const payload = `${JSON.stringify(config, null, 2)}\n`;
|
|
31
|
+
await promises_1.default.writeFile(CONFIG_PATH, payload, { mode: 0o600 });
|
|
32
|
+
await promises_1.default.chmod(CONFIG_PATH, 0o600);
|
|
33
|
+
}
|
|
34
|
+
async function getResolvedConfig(overrides = {}) {
|
|
35
|
+
const fileConfig = await loadConfig();
|
|
36
|
+
const apiKey = overrides.api_key ??
|
|
37
|
+
process.env.ORCHAGENT_API_KEY ??
|
|
38
|
+
fileConfig.api_key;
|
|
39
|
+
const apiUrl = overrides.api_url ??
|
|
40
|
+
process.env.ORCHAGENT_API_URL ??
|
|
41
|
+
fileConfig.api_url ??
|
|
42
|
+
DEFAULT_API_URL;
|
|
43
|
+
const defaultOrg = overrides.default_org ??
|
|
44
|
+
process.env.ORCHAGENT_DEFAULT_ORG ??
|
|
45
|
+
fileConfig.default_org;
|
|
46
|
+
return {
|
|
47
|
+
apiKey,
|
|
48
|
+
apiUrl,
|
|
49
|
+
defaultOrg,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function getConfigPath() {
|
|
53
|
+
return CONFIG_PATH;
|
|
54
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for config loading and saving.
|
|
4
|
+
*
|
|
5
|
+
* These tests cover the security-critical config management:
|
|
6
|
+
* - Loading config from file
|
|
7
|
+
* - Saving config with proper permissions
|
|
8
|
+
* - Config resolution priority (overrides > env > file)
|
|
9
|
+
*/
|
|
10
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
const vitest_1 = require("vitest");
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
const os_1 = __importDefault(require("os"));
|
|
17
|
+
// Mock fs/promises
|
|
18
|
+
vitest_1.vi.mock('fs/promises', () => ({
|
|
19
|
+
default: {
|
|
20
|
+
readFile: vitest_1.vi.fn(),
|
|
21
|
+
writeFile: vitest_1.vi.fn(),
|
|
22
|
+
mkdir: vitest_1.vi.fn(),
|
|
23
|
+
chmod: vitest_1.vi.fn(),
|
|
24
|
+
},
|
|
25
|
+
}));
|
|
26
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
27
|
+
const config_1 = require("./config");
|
|
28
|
+
(0, vitest_1.describe)('loadConfig', () => {
|
|
29
|
+
(0, vitest_1.beforeEach)(() => {
|
|
30
|
+
vitest_1.vi.resetAllMocks();
|
|
31
|
+
});
|
|
32
|
+
(0, vitest_1.it)('returns empty object when config file missing', async () => {
|
|
33
|
+
const error = new Error('ENOENT');
|
|
34
|
+
error.code = 'ENOENT';
|
|
35
|
+
vitest_1.vi.mocked(promises_1.default.readFile).mockRejectedValueOnce(error);
|
|
36
|
+
const config = await (0, config_1.loadConfig)();
|
|
37
|
+
(0, vitest_1.expect)(config).toEqual({});
|
|
38
|
+
});
|
|
39
|
+
(0, vitest_1.it)('parses JSON config file', async () => {
|
|
40
|
+
const configData = {
|
|
41
|
+
api_key: 'sk_test_123',
|
|
42
|
+
api_url: 'https://custom.api.com',
|
|
43
|
+
default_org: 'my-org',
|
|
44
|
+
};
|
|
45
|
+
vitest_1.vi.mocked(promises_1.default.readFile).mockResolvedValueOnce(JSON.stringify(configData));
|
|
46
|
+
const config = await (0, config_1.loadConfig)();
|
|
47
|
+
(0, vitest_1.expect)(config).toEqual(configData);
|
|
48
|
+
});
|
|
49
|
+
(0, vitest_1.it)('throws on non-ENOENT errors', async () => {
|
|
50
|
+
const error = new Error('Permission denied');
|
|
51
|
+
error.code = 'EACCES';
|
|
52
|
+
vitest_1.vi.mocked(promises_1.default.readFile).mockRejectedValueOnce(error);
|
|
53
|
+
await (0, vitest_1.expect)((0, config_1.loadConfig)()).rejects.toThrow('Permission denied');
|
|
54
|
+
});
|
|
55
|
+
(0, vitest_1.it)('reads from ~/.orchagent/config.json', async () => {
|
|
56
|
+
vitest_1.vi.mocked(promises_1.default.readFile).mockResolvedValueOnce('{}');
|
|
57
|
+
await (0, config_1.loadConfig)();
|
|
58
|
+
(0, vitest_1.expect)(promises_1.default.readFile).toHaveBeenCalledWith(path_1.default.join(os_1.default.homedir(), '.orchagent', 'config.json'), 'utf-8');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
(0, vitest_1.describe)('saveConfig', () => {
|
|
62
|
+
(0, vitest_1.beforeEach)(() => {
|
|
63
|
+
vitest_1.vi.resetAllMocks();
|
|
64
|
+
vitest_1.vi.mocked(promises_1.default.mkdir).mockResolvedValue(undefined);
|
|
65
|
+
vitest_1.vi.mocked(promises_1.default.writeFile).mockResolvedValue(undefined);
|
|
66
|
+
vitest_1.vi.mocked(promises_1.default.chmod).mockResolvedValue(undefined);
|
|
67
|
+
});
|
|
68
|
+
(0, vitest_1.it)('creates config directory if missing', async () => {
|
|
69
|
+
await (0, config_1.saveConfig)({ api_key: 'sk_test' });
|
|
70
|
+
(0, vitest_1.expect)(promises_1.default.mkdir).toHaveBeenCalledWith(path_1.default.join(os_1.default.homedir(), '.orchagent'), { recursive: true });
|
|
71
|
+
});
|
|
72
|
+
(0, vitest_1.it)('writes JSON with pretty formatting', async () => {
|
|
73
|
+
const config = { api_key: 'sk_test_123' };
|
|
74
|
+
await (0, config_1.saveConfig)(config);
|
|
75
|
+
(0, vitest_1.expect)(promises_1.default.writeFile).toHaveBeenCalledWith(path_1.default.join(os_1.default.homedir(), '.orchagent', 'config.json'), vitest_1.expect.stringContaining('"api_key": "sk_test_123"'), { mode: 0o600 });
|
|
76
|
+
});
|
|
77
|
+
(0, vitest_1.it)('sets restrictive file permissions (0600)', async () => {
|
|
78
|
+
await (0, config_1.saveConfig)({ api_key: 'sk_test' });
|
|
79
|
+
// First via writeFile options
|
|
80
|
+
(0, vitest_1.expect)(promises_1.default.writeFile).toHaveBeenCalledWith(vitest_1.expect.any(String), vitest_1.expect.any(String), { mode: 0o600 });
|
|
81
|
+
// Then explicitly with chmod
|
|
82
|
+
(0, vitest_1.expect)(promises_1.default.chmod).toHaveBeenCalledWith(path_1.default.join(os_1.default.homedir(), '.orchagent', 'config.json'), 0o600);
|
|
83
|
+
});
|
|
84
|
+
(0, vitest_1.it)('adds trailing newline to file', async () => {
|
|
85
|
+
await (0, config_1.saveConfig)({ api_key: 'test' });
|
|
86
|
+
const writeCall = vitest_1.vi.mocked(promises_1.default.writeFile).mock.calls[0];
|
|
87
|
+
const content = writeCall[1];
|
|
88
|
+
(0, vitest_1.expect)(content.endsWith('\n')).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
(0, vitest_1.describe)('getResolvedConfig', () => {
|
|
92
|
+
const originalEnv = process.env;
|
|
93
|
+
(0, vitest_1.beforeEach)(() => {
|
|
94
|
+
vitest_1.vi.resetAllMocks();
|
|
95
|
+
process.env = { ...originalEnv };
|
|
96
|
+
// Default: no config file
|
|
97
|
+
const error = new Error('ENOENT');
|
|
98
|
+
error.code = 'ENOENT';
|
|
99
|
+
vitest_1.vi.mocked(promises_1.default.readFile).mockRejectedValue(error);
|
|
100
|
+
});
|
|
101
|
+
(0, vitest_1.afterEach)(() => {
|
|
102
|
+
process.env = originalEnv;
|
|
103
|
+
});
|
|
104
|
+
(0, vitest_1.it)('uses default API URL when not configured', async () => {
|
|
105
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
106
|
+
(0, vitest_1.expect)(config.apiUrl).toBe('https://api.orchagent.com');
|
|
107
|
+
});
|
|
108
|
+
(0, vitest_1.it)('reads API key from file config', async () => {
|
|
109
|
+
vitest_1.vi.mocked(promises_1.default.readFile).mockResolvedValueOnce(JSON.stringify({ api_key: 'sk_from_file' }));
|
|
110
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
111
|
+
(0, vitest_1.expect)(config.apiKey).toBe('sk_from_file');
|
|
112
|
+
});
|
|
113
|
+
(0, vitest_1.it)('prioritizes env vars over file config', async () => {
|
|
114
|
+
process.env.ORCHAGENT_API_KEY = 'sk_from_env';
|
|
115
|
+
vitest_1.vi.mocked(promises_1.default.readFile).mockResolvedValueOnce(JSON.stringify({ api_key: 'sk_from_file' }));
|
|
116
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
117
|
+
(0, vitest_1.expect)(config.apiKey).toBe('sk_from_env');
|
|
118
|
+
});
|
|
119
|
+
(0, vitest_1.it)('prioritizes overrides over env vars', async () => {
|
|
120
|
+
process.env.ORCHAGENT_API_KEY = 'sk_from_env';
|
|
121
|
+
const config = await (0, config_1.getResolvedConfig)({ api_key: 'sk_override' });
|
|
122
|
+
(0, vitest_1.expect)(config.apiKey).toBe('sk_override');
|
|
123
|
+
});
|
|
124
|
+
(0, vitest_1.it)('resolves API URL from env var', async () => {
|
|
125
|
+
process.env.ORCHAGENT_API_URL = 'https://custom.api.com';
|
|
126
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
127
|
+
(0, vitest_1.expect)(config.apiUrl).toBe('https://custom.api.com');
|
|
128
|
+
});
|
|
129
|
+
(0, vitest_1.it)('resolves default org from env var', async () => {
|
|
130
|
+
process.env.ORCHAGENT_DEFAULT_ORG = 'my-org';
|
|
131
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
132
|
+
(0, vitest_1.expect)(config.defaultOrg).toBe('my-org');
|
|
133
|
+
});
|
|
134
|
+
(0, vitest_1.it)('returns undefined apiKey when not set anywhere', async () => {
|
|
135
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
136
|
+
(0, vitest_1.expect)(config.apiKey).toBeUndefined();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
(0, vitest_1.describe)('getConfigPath', () => {
|
|
140
|
+
(0, vitest_1.it)('returns path to config file', () => {
|
|
141
|
+
const configPath = (0, config_1.getConfigPath)();
|
|
142
|
+
(0, vitest_1.expect)(configPath).toBe(path_1.default.join(os_1.default.homedir(), '.orchagent', 'config.json'));
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CliError = void 0;
|
|
37
|
+
exports.formatError = formatError;
|
|
38
|
+
exports.exitWithError = exitWithError;
|
|
39
|
+
const Sentry = __importStar(require("@sentry/node"));
|
|
40
|
+
const analytics_1 = require("./analytics");
|
|
41
|
+
class CliError extends Error {
|
|
42
|
+
exitCode;
|
|
43
|
+
constructor(message, exitCode = 1) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.exitCode = exitCode;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.CliError = CliError;
|
|
49
|
+
function formatError(err) {
|
|
50
|
+
if (err instanceof CliError) {
|
|
51
|
+
return err.message;
|
|
52
|
+
}
|
|
53
|
+
if (err instanceof Error) {
|
|
54
|
+
const anyErr = err;
|
|
55
|
+
if (anyErr.status && anyErr.payload) {
|
|
56
|
+
return `${anyErr.message} (status ${anyErr.status})`;
|
|
57
|
+
}
|
|
58
|
+
return anyErr.message;
|
|
59
|
+
}
|
|
60
|
+
return String(err);
|
|
61
|
+
}
|
|
62
|
+
async function exitWithError(err) {
|
|
63
|
+
const message = formatError(err);
|
|
64
|
+
// Report to Sentry if it's a real error (not a CliError which is expected)
|
|
65
|
+
if (!(err instanceof CliError) && process.env.SENTRY_DSN) {
|
|
66
|
+
Sentry.captureException(err);
|
|
67
|
+
}
|
|
68
|
+
// Flush PostHog before exiting
|
|
69
|
+
await (0, analytics_1.shutdownPostHog)();
|
|
70
|
+
process.stderr.write(`${message}\n`);
|
|
71
|
+
if (err instanceof CliError) {
|
|
72
|
+
process.exit(err.exitCode);
|
|
73
|
+
}
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|