berget 2.2.6 → 2.2.8
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/.github/workflows/publish.yml +2 -2
- package/.github/workflows/test.yml +10 -4
- package/.husky/pre-commit +1 -0
- package/.prettierignore +15 -0
- package/.prettierrc +7 -3
- package/CONTRIBUTING.md +38 -0
- package/README.md +2 -148
- package/dist/index.js +10 -11
- package/dist/package.json +30 -2
- package/dist/src/agents/app.js +28 -0
- package/dist/src/agents/backend.js +25 -0
- package/dist/src/agents/devops.js +34 -0
- package/dist/src/agents/frontend.js +25 -0
- package/dist/src/agents/fullstack.js +25 -0
- package/dist/src/agents/index.js +61 -0
- package/dist/src/agents/quality.js +70 -0
- package/dist/src/agents/security.js +26 -0
- package/dist/src/agents/types.js +2 -0
- package/dist/src/client.js +97 -117
- package/dist/src/commands/api-keys.js +75 -90
- package/dist/src/commands/auth.js +7 -16
- package/dist/src/commands/autocomplete.js +1 -1
- package/dist/src/commands/billing.js +6 -17
- package/dist/src/commands/chat.js +68 -101
- package/dist/src/commands/clusters.js +9 -18
- package/dist/src/commands/code/__tests__/auth-sync.test.js +351 -0
- package/dist/src/commands/code/__tests__/fake-api-key-service.js +13 -0
- package/dist/src/commands/code/__tests__/fake-auth-service.js +47 -0
- package/dist/src/commands/code/__tests__/fake-command-runner.js +21 -34
- package/dist/src/commands/code/__tests__/fake-file-store.js +20 -33
- package/dist/src/commands/code/__tests__/fake-prompter.js +83 -57
- package/dist/src/commands/code/__tests__/setup-flow.test.js +359 -92
- package/dist/src/commands/code/adapters/clack-prompter.js +15 -22
- package/dist/src/commands/code/adapters/fs-file-store.js +26 -40
- package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -37
- package/dist/src/commands/code/auth-sync.js +270 -0
- package/dist/src/commands/code/errors.js +12 -9
- package/dist/src/commands/code/ports/auth-services.js +2 -0
- package/dist/src/commands/code/setup.js +387 -281
- package/dist/src/commands/code.js +205 -332
- package/dist/src/commands/index.js +5 -5
- package/dist/src/commands/models.js +6 -17
- package/dist/src/commands/users.js +5 -16
- package/dist/src/constants/command-structure.js +104 -104
- package/dist/src/services/api-key-service.js +132 -157
- package/dist/src/services/auth-service.js +89 -342
- package/dist/src/services/browser-auth.js +268 -0
- package/dist/src/services/chat-service.js +371 -401
- package/dist/src/services/cluster-service.js +47 -62
- package/dist/src/services/collaborator-service.js +10 -25
- package/dist/src/services/flux-service.js +14 -29
- package/dist/src/services/helm-service.js +10 -25
- package/dist/src/services/kubectl-service.js +16 -33
- package/dist/src/utils/config-checker.js +3 -3
- package/dist/src/utils/config-loader.js +95 -95
- package/dist/src/utils/default-api-key.js +124 -134
- package/dist/src/utils/env-manager.js +55 -66
- package/dist/src/utils/error-handler.js +20 -21
- package/dist/src/utils/logger.js +72 -65
- package/dist/src/utils/markdown-renderer.js +27 -27
- package/dist/src/utils/opencode-validator.js +63 -68
- package/dist/src/utils/token-manager.js +74 -45
- package/dist/tests/commands/chat.test.js +16 -25
- package/dist/tests/commands/code.test.js +95 -104
- package/dist/tests/utils/config-loader.test.js +48 -48
- package/dist/tests/utils/env-manager.test.js +43 -52
- package/dist/tests/utils/opencode-validator.test.js +22 -21
- package/dist/vitest.config.js +1 -1
- package/eslint.config.mjs +67 -0
- package/index.ts +35 -42
- package/package.json +30 -2
- package/src/agents/app.ts +27 -0
- package/src/agents/backend.ts +24 -0
- package/src/agents/devops.ts +33 -0
- package/src/agents/frontend.ts +24 -0
- package/src/agents/fullstack.ts +24 -0
- package/src/agents/index.ts +73 -0
- package/src/agents/quality.ts +69 -0
- package/src/agents/security.ts +26 -0
- package/src/agents/types.ts +17 -0
- package/src/client.ts +118 -152
- package/src/commands/api-keys.ts +241 -333
- package/src/commands/auth.ts +22 -27
- package/src/commands/autocomplete.ts +9 -9
- package/src/commands/billing.ts +20 -24
- package/src/commands/chat.ts +248 -338
- package/src/commands/clusters.ts +27 -26
- package/src/commands/code/__tests__/auth-sync.test.ts +482 -0
- package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
- package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
- package/src/commands/code/__tests__/fake-command-runner.ts +45 -42
- package/src/commands/code/__tests__/fake-file-store.ts +32 -23
- package/src/commands/code/__tests__/fake-prompter.ts +116 -77
- package/src/commands/code/__tests__/setup-flow.test.ts +624 -268
- package/src/commands/code/adapters/clack-prompter.ts +53 -39
- package/src/commands/code/adapters/fs-file-store.ts +32 -27
- package/src/commands/code/adapters/spawn-command-runner.ts +38 -29
- package/src/commands/code/auth-sync.ts +329 -0
- package/src/commands/code/errors.ts +18 -18
- package/src/commands/code/ports/auth-services.ts +14 -0
- package/src/commands/code/ports/command-runner.ts +8 -4
- package/src/commands/code/ports/file-store.ts +5 -4
- package/src/commands/code/ports/prompter.ts +24 -18
- package/src/commands/code/setup.ts +570 -340
- package/src/commands/code.ts +338 -539
- package/src/commands/index.ts +20 -19
- package/src/commands/models.ts +28 -32
- package/src/commands/users.ts +15 -21
- package/src/constants/command-structure.ts +134 -157
- package/src/services/api-key-service.ts +105 -122
- package/src/services/auth-service.ts +99 -345
- package/src/services/browser-auth.ts +296 -0
- package/src/services/chat-service.ts +265 -299
- package/src/services/cluster-service.ts +42 -45
- package/src/services/collaborator-service.ts +14 -19
- package/src/services/flux-service.ts +23 -25
- package/src/services/helm-service.ts +19 -21
- package/src/services/kubectl-service.ts +17 -19
- package/src/types/api.d.ts +1905 -1907
- package/src/types/json.d.ts +2 -2
- package/src/utils/config-checker.ts +10 -10
- package/src/utils/config-loader.ts +162 -178
- package/src/utils/default-api-key.ts +114 -125
- package/src/utils/env-manager.ts +53 -57
- package/src/utils/error-handler.ts +61 -56
- package/src/utils/logger.ts +79 -73
- package/src/utils/markdown-renderer.ts +31 -31
- package/src/utils/opencode-validator.ts +85 -89
- package/src/utils/token-manager.ts +108 -87
- package/templates/agents/app.md +1 -0
- package/templates/agents/backend.md +1 -0
- package/templates/agents/devops.md +2 -0
- package/templates/agents/frontend.md +1 -0
- package/templates/agents/fullstack.md +1 -0
- package/templates/agents/quality.md +45 -40
- package/templates/agents/security.md +1 -0
- package/tests/commands/chat.test.ts +53 -62
- package/tests/commands/code.test.ts +265 -310
- package/tests/utils/config-loader.test.ts +189 -188
- package/tests/utils/env-manager.test.ts +110 -113
- package/tests/utils/opencode-validator.test.ts +52 -56
- package/tsconfig.json +4 -3
- package/vitest.config.ts +3 -3
- package/AGENTS.md +0 -374
- package/TODO.md +0 -19
|
@@ -3,28 +3,28 @@ 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.
|
|
6
|
+
exports.validateOpenCodeConfig = exports.fixOpenCodeConfig = void 0;
|
|
7
7
|
const ajv_1 = __importDefault(require("ajv"));
|
|
8
8
|
const ajv_formats_1 = __importDefault(require("ajv-formats"));
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
9
|
+
const node_fs_1 = require("node:fs");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const node_path_2 = require("node:path");
|
|
12
12
|
// Load the official OpenCode JSON Schema
|
|
13
|
-
const __dirname = (0,
|
|
14
|
-
const schemaPath = (0,
|
|
13
|
+
const __dirname = (0, node_path_2.dirname)(__filename);
|
|
14
|
+
const schemaPath = (0, node_path_1.join)(__dirname, '..', 'schemas', 'opencode-schema.json');
|
|
15
15
|
let ajv;
|
|
16
16
|
let openCodeSchema;
|
|
17
17
|
let validateFunction;
|
|
18
18
|
try {
|
|
19
|
-
const schemaContent = (0,
|
|
19
|
+
const schemaContent = (0, node_fs_1.readFileSync)(schemaPath, 'utf-8');
|
|
20
20
|
openCodeSchema = JSON.parse(schemaContent);
|
|
21
21
|
// Initialize AJV with formats and options
|
|
22
22
|
ajv = new ajv_1.default({
|
|
23
23
|
allErrors: true,
|
|
24
|
-
verbose: true,
|
|
25
|
-
strict: false,
|
|
26
24
|
allowUnionTypes: true,
|
|
27
25
|
removeAdditional: false,
|
|
26
|
+
strict: false,
|
|
27
|
+
verbose: true,
|
|
28
28
|
});
|
|
29
29
|
// Add JSON Schema formats
|
|
30
30
|
(0, ajv_formats_1.default)(ajv);
|
|
@@ -35,39 +35,11 @@ catch (error) {
|
|
|
35
35
|
console.error('Failed to load OpenCode schema:', error);
|
|
36
36
|
throw new Error('Could not initialize OpenCode validator');
|
|
37
37
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Validate OpenCode configuration against the official JSON Schema
|
|
40
|
-
*/
|
|
41
|
-
function validateOpenCodeConfig(config) {
|
|
42
|
-
var _a;
|
|
43
|
-
try {
|
|
44
|
-
if (!validateFunction) {
|
|
45
|
-
return { valid: false, errors: ['Schema validator not initialized'] };
|
|
46
|
-
}
|
|
47
|
-
const isValid = validateFunction(config);
|
|
48
|
-
if (isValid) {
|
|
49
|
-
return { valid: true };
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
const errors = ((_a = validateFunction.errors) === null || _a === void 0 ? void 0 : _a.map((err) => {
|
|
53
|
-
const path = err.instancePath || err.schemaPath || 'root';
|
|
54
|
-
const message = err.message || 'Unknown error';
|
|
55
|
-
return `${path}: ${message}`;
|
|
56
|
-
})) || ['Unknown validation error'];
|
|
57
|
-
return { valid: false, errors };
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
console.error('Validation error:', error);
|
|
62
|
-
return { valid: false, errors: ['Validation process failed'] };
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
exports.validateOpenCodeConfig = validateOpenCodeConfig;
|
|
66
38
|
/**
|
|
67
39
|
* Fix common OpenCode configuration issues
|
|
68
40
|
*/
|
|
69
41
|
function fixOpenCodeConfig(config) {
|
|
70
|
-
const fixed =
|
|
42
|
+
const fixed = { ...config };
|
|
71
43
|
// Fix tools.compact - should be boolean, not object
|
|
72
44
|
if (fixed.tools && typeof fixed.tools.compact === 'object') {
|
|
73
45
|
console.warn('⚠️ Converting tools.compact from object to boolean');
|
|
@@ -75,43 +47,39 @@ function fixOpenCodeConfig(config) {
|
|
|
75
47
|
fixed.tools.compact = true;
|
|
76
48
|
}
|
|
77
49
|
// Remove invalid properties
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
if (fixed[
|
|
81
|
-
console.warn(`⚠️ Removing invalid property: ${
|
|
82
|
-
delete fixed[
|
|
50
|
+
const invalidProperties = ['maxTokens', 'contextWindow'];
|
|
51
|
+
for (const property of invalidProperties) {
|
|
52
|
+
if (fixed[property] !== undefined) {
|
|
53
|
+
console.warn(`⚠️ Removing invalid property: ${property}`);
|
|
54
|
+
delete fixed[property];
|
|
83
55
|
}
|
|
84
|
-
}
|
|
56
|
+
}
|
|
85
57
|
// Fix provider models with invalid properties
|
|
86
58
|
if (fixed.provider) {
|
|
87
59
|
Object.values(fixed.provider).forEach((provider) => {
|
|
88
|
-
if (provider
|
|
60
|
+
if (provider?.models) {
|
|
89
61
|
Object.values(provider.models).forEach((model) => {
|
|
90
|
-
if (model &&
|
|
91
|
-
// Move maxTokens/contextWindow to proper structure if needed
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const newContext = Math.max(...contextValues);
|
|
102
|
-
if (!model.limit.context || newContext > model.limit.context) {
|
|
103
|
-
model.limit.context = newContext;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
// Set a reasonable default for output if not present
|
|
107
|
-
// (typically 1/4 to 1/8 of context window)
|
|
108
|
-
if (!model.limit.output && model.limit.context) {
|
|
109
|
-
model.limit.output = Math.floor(model.limit.context / 4);
|
|
62
|
+
if (model &&
|
|
63
|
+
typeof model === 'object' && // Move maxTokens/contextWindow to proper structure if needed
|
|
64
|
+
(model.maxTokens || model.contextWindow)) {
|
|
65
|
+
if (!model.limit)
|
|
66
|
+
model.limit = {};
|
|
67
|
+
// Use the larger of maxTokens/contextWindow for context
|
|
68
|
+
const contextValues = [model.maxTokens, model.contextWindow].filter(Boolean);
|
|
69
|
+
if (contextValues.length > 0) {
|
|
70
|
+
const newContext = Math.max(...contextValues);
|
|
71
|
+
if (!model.limit.context || newContext > model.limit.context) {
|
|
72
|
+
model.limit.context = newContext;
|
|
110
73
|
}
|
|
111
|
-
delete model.maxTokens;
|
|
112
|
-
delete model.contextWindow;
|
|
113
|
-
console.warn('⚠️ Moved maxTokens/contextWindow to limit.context/output');
|
|
114
74
|
}
|
|
75
|
+
// Set a reasonable default for output if not present
|
|
76
|
+
// (typically 1/4 to 1/8 of context window)
|
|
77
|
+
if (!model.limit.output && model.limit.context) {
|
|
78
|
+
model.limit.output = Math.floor(model.limit.context / 4);
|
|
79
|
+
}
|
|
80
|
+
delete model.maxTokens;
|
|
81
|
+
delete model.contextWindow;
|
|
82
|
+
console.warn('⚠️ Moved maxTokens/contextWindow to limit.context/output');
|
|
115
83
|
}
|
|
116
84
|
});
|
|
117
85
|
}
|
|
@@ -120,3 +88,30 @@ function fixOpenCodeConfig(config) {
|
|
|
120
88
|
return fixed;
|
|
121
89
|
}
|
|
122
90
|
exports.fixOpenCodeConfig = fixOpenCodeConfig;
|
|
91
|
+
/**
|
|
92
|
+
* Validate OpenCode configuration against the official JSON Schema
|
|
93
|
+
*/
|
|
94
|
+
function validateOpenCodeConfig(config) {
|
|
95
|
+
try {
|
|
96
|
+
if (!validateFunction) {
|
|
97
|
+
return { errors: ['Schema validator not initialized'], valid: false };
|
|
98
|
+
}
|
|
99
|
+
const isValid = validateFunction(config);
|
|
100
|
+
if (isValid) {
|
|
101
|
+
return { valid: true };
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
const errors = validateFunction.errors?.map((error) => {
|
|
105
|
+
const path = error.instancePath || error.schemaPath || 'root';
|
|
106
|
+
const message = error.message || 'Unknown error';
|
|
107
|
+
return `${path}: ${message}`;
|
|
108
|
+
}) || ['Unknown validation error'];
|
|
109
|
+
return { errors, valid: false };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
console.error('Validation error:', error);
|
|
114
|
+
return { errors: ['Validation process failed'], valid: false };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
exports.validateOpenCodeConfig = validateOpenCodeConfig;
|
|
@@ -24,16 +24,18 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.TokenManager = void 0;
|
|
27
|
-
const fs = __importStar(require("fs"));
|
|
28
|
-
const
|
|
29
|
-
const
|
|
27
|
+
const fs = __importStar(require("node:fs"));
|
|
28
|
+
const os = __importStar(require("node:os"));
|
|
29
|
+
const path = __importStar(require("node:path"));
|
|
30
30
|
const logger_1 = require("./logger");
|
|
31
31
|
/**
|
|
32
32
|
* Manages authentication tokens including refresh functionality
|
|
33
33
|
*/
|
|
34
34
|
class TokenManager {
|
|
35
|
+
static instance;
|
|
36
|
+
tokenData = null;
|
|
37
|
+
tokenFilePath;
|
|
35
38
|
constructor() {
|
|
36
|
-
this.tokenData = null;
|
|
37
39
|
// Set up token file path in user's home directory
|
|
38
40
|
const bergetDir = path.join(os.homedir(), '.berget');
|
|
39
41
|
if (!fs.existsSync(bergetDir)) {
|
|
@@ -49,40 +51,11 @@ class TokenManager {
|
|
|
49
51
|
return TokenManager.instance;
|
|
50
52
|
}
|
|
51
53
|
/**
|
|
52
|
-
*
|
|
53
|
-
*/
|
|
54
|
-
loadToken() {
|
|
55
|
-
try {
|
|
56
|
-
if (fs.existsSync(this.tokenFilePath)) {
|
|
57
|
-
const data = fs.readFileSync(this.tokenFilePath, 'utf8');
|
|
58
|
-
this.tokenData = JSON.parse(data);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
catch (error) {
|
|
62
|
-
logger_1.logger.error('Failed to load authentication token');
|
|
63
|
-
this.tokenData = null;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Save token data to file
|
|
54
|
+
* Clear all token data
|
|
68
55
|
*/
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
fs.writeFileSync(this.tokenFilePath, JSON.stringify(this.tokenData, null, 2));
|
|
73
|
-
// Set file permissions to be readable only by the owner
|
|
74
|
-
fs.chmodSync(this.tokenFilePath, 0o600);
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
// If token data is null, remove the file
|
|
78
|
-
if (fs.existsSync(this.tokenFilePath)) {
|
|
79
|
-
fs.unlinkSync(this.tokenFilePath);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
catch (error) {
|
|
84
|
-
logger_1.logger.error('Failed to save authentication token');
|
|
85
|
-
}
|
|
56
|
+
clearTokens() {
|
|
57
|
+
this.tokenData = null;
|
|
58
|
+
this.saveToken();
|
|
86
59
|
}
|
|
87
60
|
/**
|
|
88
61
|
* Get the current access token
|
|
@@ -134,34 +107,90 @@ class TokenManager {
|
|
|
134
107
|
* Set new token data
|
|
135
108
|
* @param accessToken The new access token
|
|
136
109
|
* @param refreshToken The new refresh token
|
|
137
|
-
* @param expiresIn Expiration time in seconds
|
|
110
|
+
* @param expiresIn Expiration time in seconds (fallback if JWT parsing fails)
|
|
138
111
|
*/
|
|
139
112
|
setTokens(accessToken, refreshToken, expiresIn) {
|
|
113
|
+
// Extract the actual expiry time from the JWT token
|
|
114
|
+
const jwtExpiresAt = extractJwtExpiresAt(accessToken);
|
|
115
|
+
const expiresAt = jwtExpiresAt > 0 ? jwtExpiresAt : Date.now() + expiresIn * 1000;
|
|
140
116
|
this.tokenData = {
|
|
141
117
|
access_token: accessToken,
|
|
118
|
+
expires_at: expiresAt,
|
|
142
119
|
refresh_token: refreshToken,
|
|
143
|
-
expires_at: Date.now() + expiresIn * 1000,
|
|
144
120
|
};
|
|
145
121
|
this.saveToken();
|
|
146
122
|
}
|
|
147
123
|
/**
|
|
148
124
|
* Update just the access token and its expiration
|
|
149
125
|
* @param accessToken The new access token
|
|
150
|
-
* @param expiresIn Expiration time in seconds
|
|
126
|
+
* @param expiresIn Expiration time in seconds (fallback if JWT parsing fails)
|
|
151
127
|
*/
|
|
152
128
|
updateAccessToken(accessToken, expiresIn) {
|
|
153
129
|
if (!this.tokenData)
|
|
154
130
|
return;
|
|
131
|
+
// Extract the actual expiry time from the JWT token
|
|
132
|
+
const jwtExpiresAt = extractJwtExpiresAt(accessToken);
|
|
133
|
+
const expiresAt = jwtExpiresAt > 0 ? jwtExpiresAt : Date.now() + expiresIn * 1000;
|
|
155
134
|
this.tokenData.access_token = accessToken;
|
|
156
|
-
this.tokenData.expires_at =
|
|
135
|
+
this.tokenData.expires_at = expiresAt;
|
|
157
136
|
this.saveToken();
|
|
158
137
|
}
|
|
159
138
|
/**
|
|
160
|
-
*
|
|
139
|
+
* Load token data from file
|
|
161
140
|
*/
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
141
|
+
loadToken() {
|
|
142
|
+
try {
|
|
143
|
+
if (fs.existsSync(this.tokenFilePath)) {
|
|
144
|
+
const data = fs.readFileSync(this.tokenFilePath, 'utf8');
|
|
145
|
+
this.tokenData = JSON.parse(data);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
logger_1.logger.error('Failed to load authentication token');
|
|
150
|
+
this.tokenData = null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Save token data to file
|
|
155
|
+
*/
|
|
156
|
+
saveToken() {
|
|
157
|
+
try {
|
|
158
|
+
if (this.tokenData) {
|
|
159
|
+
fs.writeFileSync(this.tokenFilePath, JSON.stringify(this.tokenData, null, 2));
|
|
160
|
+
// Set file permissions to be readable only by the owner
|
|
161
|
+
fs.chmodSync(this.tokenFilePath, 0o600);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
// If token data is null, remove the file
|
|
165
|
+
if (fs.existsSync(this.tokenFilePath)) {
|
|
166
|
+
fs.unlinkSync(this.tokenFilePath);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
logger_1.logger.error('Failed to save authentication token');
|
|
172
|
+
}
|
|
165
173
|
}
|
|
166
174
|
}
|
|
167
175
|
exports.TokenManager = TokenManager;
|
|
176
|
+
/**
|
|
177
|
+
* Extract the expiration time from a JWT token
|
|
178
|
+
* @param accessToken The JWT access token
|
|
179
|
+
* @returns The expiration timestamp in milliseconds, or 0 if invalid
|
|
180
|
+
*/
|
|
181
|
+
function extractJwtExpiresAt(accessToken) {
|
|
182
|
+
try {
|
|
183
|
+
const parts = accessToken.split('.');
|
|
184
|
+
if (parts.length !== 3)
|
|
185
|
+
return 0;
|
|
186
|
+
const payload = Buffer.from(parts[1], 'base64url').toString('utf8');
|
|
187
|
+
const decoded = JSON.parse(payload);
|
|
188
|
+
if (typeof decoded.exp === 'number') {
|
|
189
|
+
return decoded.exp * 1000; // JWT exp is in seconds, convert to milliseconds
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// If decoding fails, return 0 (treated as expired)
|
|
194
|
+
}
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
const vitest_1 = require("vitest");
|
|
13
3
|
const commander_1 = require("commander");
|
|
4
|
+
const vitest_1 = require("vitest");
|
|
14
5
|
const chat_1 = require("../../src/commands/chat");
|
|
15
6
|
const chat_service_1 = require("../../src/services/chat-service");
|
|
16
7
|
const default_api_key_1 = require("../../src/utils/default-api-key");
|
|
@@ -19,8 +10,8 @@ vitest_1.vi.mock('../../src/services/chat-service');
|
|
|
19
10
|
vitest_1.vi.mock('../../src/utils/default-api-key');
|
|
20
11
|
vitest_1.vi.mock('readline', () => ({
|
|
21
12
|
createInterface: vitest_1.vi.fn(() => ({
|
|
22
|
-
question: vitest_1.vi.fn(),
|
|
23
13
|
close: vitest_1.vi.fn(),
|
|
14
|
+
question: vitest_1.vi.fn(),
|
|
24
15
|
})),
|
|
25
16
|
}));
|
|
26
17
|
(0, vitest_1.describe)('Chat Commands', () => {
|
|
@@ -49,22 +40,22 @@ vitest_1.vi.mock('readline', () => ({
|
|
|
49
40
|
(0, vitest_1.describe)('chat run command', () => {
|
|
50
41
|
(0, vitest_1.it)('should use berget/glm-4.7 as default model', () => {
|
|
51
42
|
const chatCommand = program.commands.find((cmd) => cmd.name() === 'chat');
|
|
52
|
-
const runCommand = chatCommand
|
|
43
|
+
const runCommand = chatCommand?.commands.find((cmd) => cmd.name() === 'run');
|
|
53
44
|
(0, vitest_1.expect)(runCommand).toBeDefined();
|
|
54
45
|
// Check the help text which contains the default model
|
|
55
|
-
const helpText = runCommand
|
|
46
|
+
const helpText = runCommand?.helpInformation();
|
|
56
47
|
(0, vitest_1.expect)(helpText).toContain('glm-4.7');
|
|
57
48
|
});
|
|
58
49
|
(0, vitest_1.it)('should have streaming enabled by default', () => {
|
|
59
50
|
const chatCommand = program.commands.find((cmd) => cmd.name() === 'chat');
|
|
60
|
-
const runCommand = chatCommand
|
|
51
|
+
const runCommand = chatCommand?.commands.find((cmd) => cmd.name() === 'run');
|
|
61
52
|
(0, vitest_1.expect)(runCommand).toBeDefined();
|
|
62
53
|
// Check that the option is --no-stream (meaning streaming is default)
|
|
63
|
-
const streamOption = runCommand
|
|
54
|
+
const streamOption = runCommand?.options.find((opt) => opt.long === '--no-stream');
|
|
64
55
|
(0, vitest_1.expect)(streamOption).toBeDefined();
|
|
65
|
-
(0, vitest_1.expect)(streamOption
|
|
56
|
+
(0, vitest_1.expect)(streamOption?.description).toContain('Disable streaming');
|
|
66
57
|
});
|
|
67
|
-
(0, vitest_1.it)('should create completion with correct default options', () =>
|
|
58
|
+
(0, vitest_1.it)('should create completion with correct default options', async () => {
|
|
68
59
|
// Mock API key
|
|
69
60
|
process.env.BERGET_API_KEY = 'test-key';
|
|
70
61
|
// Mock successful completion
|
|
@@ -81,29 +72,29 @@ vitest_1.vi.mock('readline', () => ({
|
|
|
81
72
|
(0, vitest_1.expect)(mockChatService.createCompletion).not.toHaveBeenCalled();
|
|
82
73
|
// Clean up
|
|
83
74
|
delete process.env.BERGET_API_KEY;
|
|
84
|
-
})
|
|
75
|
+
});
|
|
85
76
|
});
|
|
86
77
|
(0, vitest_1.describe)('chat list command', () => {
|
|
87
|
-
(0, vitest_1.it)('should list available models', () =>
|
|
78
|
+
(0, vitest_1.it)('should list available models', async () => {
|
|
88
79
|
const mockModels = {
|
|
89
80
|
data: [
|
|
90
81
|
{
|
|
91
|
-
id: 'gpt-oss',
|
|
92
|
-
owned_by: 'openai',
|
|
93
82
|
active: true,
|
|
94
83
|
capabilities: {
|
|
95
|
-
vision: false,
|
|
96
84
|
function_calling: true,
|
|
97
85
|
json_mode: true,
|
|
86
|
+
vision: false,
|
|
98
87
|
},
|
|
88
|
+
id: 'gpt-oss',
|
|
89
|
+
owned_by: 'openai',
|
|
99
90
|
},
|
|
100
91
|
],
|
|
101
92
|
};
|
|
102
93
|
mockChatService.listModels.mockResolvedValue(mockModels);
|
|
103
94
|
const chatCommand = program.commands.find((cmd) => cmd.name() === 'chat');
|
|
104
|
-
const listCommand = chatCommand
|
|
95
|
+
const listCommand = chatCommand?.commands.find((cmd) => cmd.name() === 'list');
|
|
105
96
|
(0, vitest_1.expect)(listCommand).toBeDefined();
|
|
106
|
-
(0, vitest_1.expect)(listCommand
|
|
107
|
-
})
|
|
97
|
+
(0, vitest_1.expect)(listCommand?.description()).toBe('List available chat models');
|
|
98
|
+
});
|
|
108
99
|
});
|
|
109
100
|
});
|