berget 2.2.7 → 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 +6 -6
- package/.github/workflows/test.yml +1 -1
- package/.prettierrc +5 -3
- package/dist/index.js +24 -25
- package/dist/package.json +5 -3
- package/dist/src/agents/app.js +8 -8
- package/dist/src/agents/backend.js +3 -3
- package/dist/src/agents/devops.js +8 -8
- package/dist/src/agents/frontend.js +3 -3
- package/dist/src/agents/fullstack.js +3 -3
- package/dist/src/agents/index.js +18 -18
- package/dist/src/agents/quality.js +8 -8
- package/dist/src/agents/security.js +8 -8
- package/dist/src/client.js +115 -127
- package/dist/src/commands/api-keys.js +195 -202
- package/dist/src/commands/auth.js +16 -25
- package/dist/src/commands/autocomplete.js +8 -8
- package/dist/src/commands/billing.js +10 -19
- package/dist/src/commands/chat.js +139 -170
- package/dist/src/commands/clusters.js +21 -30
- package/dist/src/commands/code/__tests__/auth-sync.test.js +189 -186
- package/dist/src/commands/code/__tests__/fake-api-key-service.js +3 -13
- package/dist/src/commands/code/__tests__/fake-auth-service.js +21 -29
- package/dist/src/commands/code/__tests__/fake-command-runner.js +22 -33
- package/dist/src/commands/code/__tests__/fake-file-store.js +19 -41
- package/dist/src/commands/code/__tests__/fake-prompter.js +81 -97
- package/dist/src/commands/code/__tests__/setup-flow.test.js +295 -295
- package/dist/src/commands/code/adapters/clack-prompter.js +15 -32
- package/dist/src/commands/code/adapters/fs-file-store.js +25 -44
- package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -41
- package/dist/src/commands/code/auth-sync.js +215 -228
- package/dist/src/commands/code/errors.js +15 -12
- package/dist/src/commands/code/setup.js +390 -425
- package/dist/src/commands/code.js +279 -294
- package/dist/src/commands/index.js +5 -5
- package/dist/src/commands/models.js +16 -25
- package/dist/src/commands/users.js +9 -18
- package/dist/src/constants/command-structure.js +138 -138
- package/dist/src/services/api-key-service.js +132 -152
- package/dist/src/services/auth-service.js +81 -95
- package/dist/src/services/browser-auth.js +121 -131
- package/dist/src/services/chat-service.js +369 -386
- package/dist/src/services/cluster-service.js +47 -62
- package/dist/src/services/collaborator-service.js +9 -21
- package/dist/src/services/flux-service.js +13 -25
- package/dist/src/services/helm-service.js +9 -21
- package/dist/src/services/kubectl-service.js +15 -29
- package/dist/src/utils/config-checker.js +7 -7
- package/dist/src/utils/config-loader.js +109 -109
- package/dist/src/utils/default-api-key.js +129 -139
- package/dist/src/utils/env-manager.js +55 -66
- package/dist/src/utils/error-handler.js +62 -62
- package/dist/src/utils/logger.js +74 -67
- package/dist/src/utils/markdown-renderer.js +28 -28
- package/dist/src/utils/opencode-validator.js +67 -69
- package/dist/src/utils/token-manager.js +67 -65
- package/dist/tests/commands/chat.test.js +30 -39
- package/dist/tests/commands/code.test.js +186 -195
- package/dist/tests/utils/config-loader.test.js +107 -107
- package/dist/tests/utils/env-manager.test.js +81 -90
- package/dist/tests/utils/opencode-validator.test.js +42 -41
- package/dist/vitest.config.js +1 -1
- package/eslint.config.mjs +50 -30
- package/index.ts +30 -31
- package/package.json +5 -3
- package/src/agents/app.ts +9 -9
- package/src/agents/backend.ts +4 -4
- package/src/agents/devops.ts +9 -9
- package/src/agents/frontend.ts +4 -4
- package/src/agents/fullstack.ts +4 -4
- package/src/agents/index.ts +27 -25
- package/src/agents/quality.ts +9 -9
- package/src/agents/security.ts +9 -9
- package/src/agents/types.ts +10 -10
- package/src/client.ts +85 -77
- package/src/commands/api-keys.ts +190 -185
- package/src/commands/auth.ts +15 -14
- package/src/commands/autocomplete.ts +10 -10
- package/src/commands/billing.ts +13 -12
- package/src/commands/chat.ts +145 -142
- package/src/commands/clusters.ts +20 -19
- package/src/commands/code/__tests__/auth-sync.test.ts +176 -175
- package/src/commands/code/__tests__/fake-api-key-service.ts +2 -2
- package/src/commands/code/__tests__/fake-auth-service.ts +18 -18
- package/src/commands/code/__tests__/fake-command-runner.ts +28 -22
- package/src/commands/code/__tests__/fake-file-store.ts +15 -15
- package/src/commands/code/__tests__/fake-prompter.ts +86 -85
- package/src/commands/code/__tests__/setup-flow.test.ts +253 -251
- package/src/commands/code/adapters/clack-prompter.ts +32 -30
- package/src/commands/code/adapters/fs-file-store.ts +18 -17
- package/src/commands/code/adapters/spawn-command-runner.ts +20 -15
- package/src/commands/code/auth-sync.ts +210 -210
- package/src/commands/code/errors.ts +11 -11
- package/src/commands/code/ports/auth-services.ts +7 -7
- package/src/commands/code/ports/command-runner.ts +2 -2
- package/src/commands/code/ports/file-store.ts +3 -3
- package/src/commands/code/ports/prompter.ts +13 -13
- package/src/commands/code/setup.ts +408 -406
- package/src/commands/code.ts +288 -287
- package/src/commands/index.ts +11 -10
- package/src/commands/models.ts +19 -18
- package/src/commands/users.ts +11 -10
- package/src/constants/command-structure.ts +159 -159
- package/src/services/api-key-service.ts +85 -85
- package/src/services/auth-service.ts +55 -54
- package/src/services/browser-auth.ts +62 -62
- package/src/services/chat-service.ts +169 -170
- package/src/services/cluster-service.ts +28 -28
- package/src/services/collaborator-service.ts +6 -6
- package/src/services/flux-service.ts +17 -17
- package/src/services/helm-service.ts +11 -11
- package/src/services/kubectl-service.ts +12 -12
- package/src/types/api.d.ts +1933 -1933
- package/src/types/json.d.ts +1 -1
- package/src/utils/config-checker.ts +6 -6
- package/src/utils/config-loader.ts +130 -129
- package/src/utils/default-api-key.ts +81 -80
- package/src/utils/env-manager.ts +37 -37
- package/src/utils/error-handler.ts +64 -64
- package/src/utils/logger.ts +72 -66
- package/src/utils/markdown-renderer.ts +28 -28
- package/src/utils/opencode-validator.ts +72 -71
- package/src/utils/token-manager.ts +69 -68
- package/tests/commands/chat.test.ts +32 -31
- package/tests/commands/code.test.ts +182 -181
- package/tests/utils/config-loader.test.ts +111 -110
- package/tests/utils/env-manager.test.ts +83 -79
- package/tests/utils/opencode-validator.test.ts +43 -42
- package/tsconfig.json +2 -1
- package/vitest.config.ts +2 -2
|
@@ -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);
|
|
@@ -32,83 +32,54 @@ try {
|
|
|
32
32
|
validateFunction = ajv.compile(openCodeSchema);
|
|
33
33
|
}
|
|
34
34
|
catch (error) {
|
|
35
|
-
console.error(
|
|
36
|
-
throw new Error(
|
|
35
|
+
console.error('Failed to load OpenCode schema:', error);
|
|
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
|
-
if (fixed.tools && typeof fixed.tools.compact ===
|
|
73
|
-
console.warn(
|
|
44
|
+
if (fixed.tools && typeof fixed.tools.compact === 'object') {
|
|
45
|
+
console.warn('⚠️ Converting tools.compact from object to boolean');
|
|
74
46
|
// If it has properties, assume it should be enabled
|
|
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
|
-
}
|
|
102
|
-
}
|
|
103
|
-
// Set a reasonable default for output if not present
|
|
104
|
-
// (typically 1/4 to 1/8 of context window)
|
|
105
|
-
if (!model.limit.output && model.limit.context) {
|
|
106
|
-
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;
|
|
107
73
|
}
|
|
108
|
-
delete model.maxTokens;
|
|
109
|
-
delete model.contextWindow;
|
|
110
|
-
console.warn("⚠️ Moved maxTokens/contextWindow to limit.context/output");
|
|
111
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');
|
|
112
83
|
}
|
|
113
84
|
});
|
|
114
85
|
}
|
|
@@ -117,3 +88,30 @@ function fixOpenCodeConfig(config) {
|
|
|
117
88
|
return fixed;
|
|
118
89
|
}
|
|
119
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,43 +24,24 @@ 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
|
-
/**
|
|
32
|
-
* Extract the expiration time from a JWT token
|
|
33
|
-
* @param accessToken The JWT access token
|
|
34
|
-
* @returns The expiration timestamp in milliseconds, or 0 if invalid
|
|
35
|
-
*/
|
|
36
|
-
function extractJwtExpiresAt(accessToken) {
|
|
37
|
-
try {
|
|
38
|
-
const parts = accessToken.split(".");
|
|
39
|
-
if (parts.length !== 3)
|
|
40
|
-
return 0;
|
|
41
|
-
const payload = Buffer.from(parts[1], "base64url").toString("utf8");
|
|
42
|
-
const decoded = JSON.parse(payload);
|
|
43
|
-
if (typeof decoded.exp === "number") {
|
|
44
|
-
return decoded.exp * 1000; // JWT exp is in seconds, convert to milliseconds
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
catch (_a) {
|
|
48
|
-
// If decoding fails, return 0 (treated as expired)
|
|
49
|
-
}
|
|
50
|
-
return 0;
|
|
51
|
-
}
|
|
52
31
|
/**
|
|
53
32
|
* Manages authentication tokens including refresh functionality
|
|
54
33
|
*/
|
|
55
34
|
class TokenManager {
|
|
35
|
+
static instance;
|
|
36
|
+
tokenData = null;
|
|
37
|
+
tokenFilePath;
|
|
56
38
|
constructor() {
|
|
57
|
-
this.tokenData = null;
|
|
58
39
|
// Set up token file path in user's home directory
|
|
59
|
-
const bergetDir = path.join(os.homedir(),
|
|
40
|
+
const bergetDir = path.join(os.homedir(), '.berget');
|
|
60
41
|
if (!fs.existsSync(bergetDir)) {
|
|
61
42
|
fs.mkdirSync(bergetDir, { recursive: true });
|
|
62
43
|
}
|
|
63
|
-
this.tokenFilePath = path.join(bergetDir,
|
|
44
|
+
this.tokenFilePath = path.join(bergetDir, 'auth.json');
|
|
64
45
|
this.loadToken();
|
|
65
46
|
}
|
|
66
47
|
static getInstance() {
|
|
@@ -70,40 +51,11 @@ class TokenManager {
|
|
|
70
51
|
return TokenManager.instance;
|
|
71
52
|
}
|
|
72
53
|
/**
|
|
73
|
-
*
|
|
74
|
-
*/
|
|
75
|
-
loadToken() {
|
|
76
|
-
try {
|
|
77
|
-
if (fs.existsSync(this.tokenFilePath)) {
|
|
78
|
-
const data = fs.readFileSync(this.tokenFilePath, "utf8");
|
|
79
|
-
this.tokenData = JSON.parse(data);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
catch (_a) {
|
|
83
|
-
logger_1.logger.error("Failed to load authentication token");
|
|
84
|
-
this.tokenData = null;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Save token data to file
|
|
54
|
+
* Clear all token data
|
|
89
55
|
*/
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
fs.writeFileSync(this.tokenFilePath, JSON.stringify(this.tokenData, null, 2));
|
|
94
|
-
// Set file permissions to be readable only by the owner
|
|
95
|
-
fs.chmodSync(this.tokenFilePath, 0o600);
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
// If token data is null, remove the file
|
|
99
|
-
if (fs.existsSync(this.tokenFilePath)) {
|
|
100
|
-
fs.unlinkSync(this.tokenFilePath);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
catch (_a) {
|
|
105
|
-
logger_1.logger.error("Failed to save authentication token");
|
|
106
|
-
}
|
|
56
|
+
clearTokens() {
|
|
57
|
+
this.tokenData = null;
|
|
58
|
+
this.saveToken();
|
|
107
59
|
}
|
|
108
60
|
/**
|
|
109
61
|
* Get the current access token
|
|
@@ -163,8 +115,8 @@ class TokenManager {
|
|
|
163
115
|
const expiresAt = jwtExpiresAt > 0 ? jwtExpiresAt : Date.now() + expiresIn * 1000;
|
|
164
116
|
this.tokenData = {
|
|
165
117
|
access_token: accessToken,
|
|
166
|
-
refresh_token: refreshToken,
|
|
167
118
|
expires_at: expiresAt,
|
|
119
|
+
refresh_token: refreshToken,
|
|
168
120
|
};
|
|
169
121
|
this.saveToken();
|
|
170
122
|
}
|
|
@@ -184,11 +136,61 @@ class TokenManager {
|
|
|
184
136
|
this.saveToken();
|
|
185
137
|
}
|
|
186
138
|
/**
|
|
187
|
-
*
|
|
139
|
+
* Load token data from file
|
|
188
140
|
*/
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
+
}
|
|
192
173
|
}
|
|
193
174
|
}
|
|
194
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,29 +1,20 @@
|
|
|
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");
|
|
17
8
|
// Mock dependencies
|
|
18
|
-
vitest_1.vi.mock(
|
|
19
|
-
vitest_1.vi.mock(
|
|
20
|
-
vitest_1.vi.mock(
|
|
9
|
+
vitest_1.vi.mock('../../src/services/chat-service');
|
|
10
|
+
vitest_1.vi.mock('../../src/utils/default-api-key');
|
|
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
|
-
(0, vitest_1.describe)(
|
|
17
|
+
(0, vitest_1.describe)('Chat Commands', () => {
|
|
27
18
|
let program;
|
|
28
19
|
let mockChatService;
|
|
29
20
|
let mockDefaultApiKeyManager;
|
|
@@ -46,32 +37,32 @@ vitest_1.vi.mock("readline", () => ({
|
|
|
46
37
|
(0, vitest_1.afterEach)(() => {
|
|
47
38
|
vitest_1.vi.clearAllMocks();
|
|
48
39
|
});
|
|
49
|
-
(0, vitest_1.describe)(
|
|
50
|
-
(0, vitest_1.it)(
|
|
51
|
-
const chatCommand = program.commands.find(cmd => cmd.name() ===
|
|
52
|
-
const runCommand = chatCommand
|
|
40
|
+
(0, vitest_1.describe)('chat run command', () => {
|
|
41
|
+
(0, vitest_1.it)('should use berget/glm-4.7 as default model', () => {
|
|
42
|
+
const chatCommand = program.commands.find((cmd) => cmd.name() === 'chat');
|
|
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
|
|
56
|
-
(0, vitest_1.expect)(helpText).toContain(
|
|
46
|
+
const helpText = runCommand?.helpInformation();
|
|
47
|
+
(0, vitest_1.expect)(helpText).toContain('glm-4.7');
|
|
57
48
|
});
|
|
58
|
-
(0, vitest_1.it)(
|
|
59
|
-
const chatCommand = program.commands.find(cmd => cmd.name() ===
|
|
60
|
-
const runCommand = chatCommand
|
|
49
|
+
(0, vitest_1.it)('should have streaming enabled by default', () => {
|
|
50
|
+
const chatCommand = program.commands.find((cmd) => cmd.name() === 'chat');
|
|
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)(
|
|
58
|
+
(0, vitest_1.it)('should create completion with correct default options', async () => {
|
|
68
59
|
// Mock API key
|
|
69
|
-
process.env.BERGET_API_KEY =
|
|
60
|
+
process.env.BERGET_API_KEY = 'test-key';
|
|
70
61
|
// Mock successful completion
|
|
71
62
|
mockChatService.createCompletion.mockResolvedValue({
|
|
72
63
|
choices: [
|
|
73
64
|
{
|
|
74
|
-
message: { content:
|
|
65
|
+
message: { content: 'Test response' },
|
|
75
66
|
},
|
|
76
67
|
],
|
|
77
68
|
});
|
|
@@ -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
|
-
(0, vitest_1.describe)(
|
|
87
|
-
(0, vitest_1.it)(
|
|
77
|
+
(0, vitest_1.describe)('chat list command', () => {
|
|
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
|
-
const chatCommand = program.commands.find(cmd => cmd.name() ===
|
|
104
|
-
const listCommand = chatCommand
|
|
94
|
+
const chatCommand = program.commands.find((cmd) => cmd.name() === 'chat');
|
|
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
|
});
|