@outputai/cli 0.2.1-next.23c3ed0.0 → 0.2.1-next.2ddcc3e.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/bin/run.js +2 -2
- package/dist/assets/docker/docker-compose-dev.yml +1 -1
- package/dist/generated/framework_version.json +1 -1
- package/dist/utils/credentials_loader.d.ts +1 -0
- package/dist/utils/credentials_loader.js +18 -0
- package/dist/utils/credentials_loader.spec.d.ts +1 -0
- package/dist/utils/credentials_loader.spec.js +84 -0
- package/package.json +4 -4
package/bin/run.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
import { execute } from '@oclif/core';
|
|
4
4
|
import { loadEnvironment } from '../dist/utils/env_loader.js';
|
|
5
5
|
import { bootstrapProxy } from '../dist/utils/proxy.js';
|
|
6
|
-
import {
|
|
6
|
+
import { loadCredentialRefs } from '../dist/utils/credentials_loader.js';
|
|
7
7
|
|
|
8
8
|
// Load environment variables from .env files before executing CLI
|
|
9
9
|
loadEnvironment();
|
|
10
10
|
bootstrapProxy();
|
|
11
|
-
|
|
11
|
+
loadCredentialRefs();
|
|
12
12
|
|
|
13
13
|
await execute( { dir: import.meta.url } );
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const loadCredentialRefs: () => void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { InvalidCredentialsKeyError, MalformedCredentialsKeyError, MissingKeyError, resolveCredentialRefs } from '@outputai/credentials';
|
|
2
|
+
const isCredentialsConfigError = (error) => error instanceof MissingKeyError ||
|
|
3
|
+
error instanceof InvalidCredentialsKeyError ||
|
|
4
|
+
error instanceof MalformedCredentialsKeyError;
|
|
5
|
+
export const loadCredentialRefs = () => {
|
|
6
|
+
try {
|
|
7
|
+
resolveCredentialRefs();
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
if (isCredentialsConfigError(error)) {
|
|
11
|
+
console.error(`Error: ${error.message}`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import * as credentials from '@outputai/credentials';
|
|
3
|
+
import { loadCredentialRefs } from './credentials_loader.js';
|
|
4
|
+
vi.mock('@outputai/credentials', async () => {
|
|
5
|
+
const actual = await vi.importActual('@outputai/credentials');
|
|
6
|
+
return {
|
|
7
|
+
...actual,
|
|
8
|
+
resolveCredentialRefs: vi.fn()
|
|
9
|
+
};
|
|
10
|
+
});
|
|
11
|
+
describe('loadCredentialRefs', () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
vi.clearAllMocks();
|
|
14
|
+
});
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
vi.restoreAllMocks();
|
|
17
|
+
});
|
|
18
|
+
it('should call resolveCredentialRefs without errors when no credentials are misconfigured', () => {
|
|
19
|
+
vi.mocked(credentials.resolveCredentialRefs).mockReturnValue([]);
|
|
20
|
+
expect(() => loadCredentialRefs()).not.toThrow();
|
|
21
|
+
expect(credentials.resolveCredentialRefs).toHaveBeenCalledTimes(1);
|
|
22
|
+
});
|
|
23
|
+
it('should print a clean error message and exit on MissingKeyError without dumping a stack trace', () => {
|
|
24
|
+
vi.mocked(credentials.resolveCredentialRefs).mockImplementation(() => {
|
|
25
|
+
throw new credentials.MissingKeyError();
|
|
26
|
+
});
|
|
27
|
+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
28
|
+
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => undefined));
|
|
29
|
+
loadCredentialRefs();
|
|
30
|
+
expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
|
|
31
|
+
const printedMessage = consoleErrorSpy.mock.calls[0]?.[0];
|
|
32
|
+
expect(printedMessage).toContain('No credentials key found');
|
|
33
|
+
expect(printedMessage).toContain('OUTPUT_CREDENTIALS_KEY');
|
|
34
|
+
expect(printedMessage).toContain('config/credentials.key');
|
|
35
|
+
expect(printedMessage).not.toContain(' at ');
|
|
36
|
+
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
37
|
+
});
|
|
38
|
+
it('should include the environment-specific hints in the printed message when an environment is set', () => {
|
|
39
|
+
vi.mocked(credentials.resolveCredentialRefs).mockImplementation(() => {
|
|
40
|
+
throw new credentials.MissingKeyError('production');
|
|
41
|
+
});
|
|
42
|
+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
43
|
+
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => undefined));
|
|
44
|
+
loadCredentialRefs();
|
|
45
|
+
const printedMessage = consoleErrorSpy.mock.calls[0]?.[0];
|
|
46
|
+
expect(printedMessage).toContain('OUTPUT_CREDENTIALS_KEY_PRODUCTION');
|
|
47
|
+
expect(printedMessage).toContain('config/credentials/production.key');
|
|
48
|
+
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
49
|
+
});
|
|
50
|
+
it('should print a clean error message and exit on InvalidCredentialsKeyError', () => {
|
|
51
|
+
vi.mocked(credentials.resolveCredentialRefs).mockImplementation(() => {
|
|
52
|
+
throw new credentials.InvalidCredentialsKeyError('config/credentials.yml.enc');
|
|
53
|
+
});
|
|
54
|
+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
55
|
+
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => undefined));
|
|
56
|
+
loadCredentialRefs();
|
|
57
|
+
expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
|
|
58
|
+
const printedMessage = consoleErrorSpy.mock.calls[0]?.[0];
|
|
59
|
+
expect(printedMessage).toContain('Failed to decrypt config/credentials.yml.enc');
|
|
60
|
+
expect(printedMessage).toContain('does not match');
|
|
61
|
+
expect(printedMessage).not.toContain(' at ');
|
|
62
|
+
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
63
|
+
});
|
|
64
|
+
it('should print a clean error message and exit on MalformedCredentialsKeyError', () => {
|
|
65
|
+
vi.mocked(credentials.resolveCredentialRefs).mockImplementation(() => {
|
|
66
|
+
throw new credentials.MalformedCredentialsKeyError('config/credentials.yml.enc', 'hex string expected, got unpadded hex of length 55');
|
|
67
|
+
});
|
|
68
|
+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
69
|
+
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => undefined));
|
|
70
|
+
loadCredentialRefs();
|
|
71
|
+
const printedMessage = consoleErrorSpy.mock.calls[0]?.[0];
|
|
72
|
+
expect(printedMessage).toContain('is malformed');
|
|
73
|
+
expect(printedMessage).toContain('must be exactly 64 hex characters');
|
|
74
|
+
expect(printedMessage).toContain('unpadded hex of length 55');
|
|
75
|
+
expect(printedMessage).not.toContain(' at ');
|
|
76
|
+
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
77
|
+
});
|
|
78
|
+
it('should rethrow unexpected errors so they are not silently swallowed', () => {
|
|
79
|
+
vi.mocked(credentials.resolveCredentialRefs).mockImplementation(() => {
|
|
80
|
+
throw new Error('something else broke');
|
|
81
|
+
});
|
|
82
|
+
expect(() => loadCredentialRefs()).toThrow('something else broke');
|
|
83
|
+
});
|
|
84
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outputai/cli",
|
|
3
|
-
"version": "0.2.1-next.
|
|
3
|
+
"version": "0.2.1-next.2ddcc3e.0",
|
|
4
4
|
"description": "CLI for Output.ai workflow generation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"semver": "7.7.4",
|
|
37
37
|
"undici": "8.0.2",
|
|
38
38
|
"yaml": "^2.8.3",
|
|
39
|
-
"@outputai/credentials": "0.2.1-next.
|
|
40
|
-
"@outputai/evals": "0.2.1-next.
|
|
41
|
-
"@outputai/llm": "0.2.1-next.
|
|
39
|
+
"@outputai/credentials": "0.2.1-next.2ddcc3e.0",
|
|
40
|
+
"@outputai/evals": "0.2.1-next.2ddcc3e.0",
|
|
41
|
+
"@outputai/llm": "0.2.1-next.2ddcc3e.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/cli-progress": "3.11.6",
|