berget 1.4.0 → 2.0.1
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/.env.example +5 -0
- package/AGENTS.md +184 -0
- package/TODO.md +2 -0
- package/blog-post.md +176 -0
- package/dist/index.js +11 -8
- package/dist/package.json +7 -2
- package/dist/src/commands/api-keys.js +4 -2
- package/dist/src/commands/chat.js +21 -11
- package/dist/src/commands/code.js +1424 -0
- package/dist/src/commands/index.js +2 -0
- package/dist/src/constants/command-structure.js +12 -0
- package/dist/src/schemas/opencode-schema.json +1121 -0
- package/dist/src/services/cluster-service.js +1 -1
- package/dist/src/utils/default-api-key.js +2 -2
- package/dist/src/utils/env-manager.js +86 -0
- package/dist/src/utils/error-handler.js +10 -3
- package/dist/src/utils/markdown-renderer.js +4 -4
- package/dist/src/utils/opencode-validator.js +122 -0
- package/dist/src/utils/token-manager.js +2 -2
- package/dist/tests/commands/chat.test.js +20 -18
- package/dist/tests/commands/code.test.js +414 -0
- package/dist/tests/utils/env-manager.test.js +148 -0
- package/dist/tests/utils/opencode-validator.test.js +103 -0
- package/index.ts +67 -32
- package/opencode.json +182 -0
- package/package.json +7 -2
- package/src/client.ts +20 -20
- package/src/commands/api-keys.ts +93 -60
- package/src/commands/auth.ts +4 -2
- package/src/commands/billing.ts +6 -3
- package/src/commands/chat.ts +149 -107
- package/src/commands/clusters.ts +2 -2
- package/src/commands/code.ts +1696 -0
- package/src/commands/index.ts +2 -0
- package/src/commands/models.ts +3 -3
- package/src/commands/users.ts +2 -2
- package/src/constants/command-structure.ts +112 -58
- package/src/schemas/opencode-schema.json +991 -0
- package/src/services/api-key-service.ts +1 -1
- package/src/services/auth-service.ts +27 -25
- package/src/services/chat-service.ts +26 -23
- package/src/services/cluster-service.ts +5 -5
- package/src/services/collaborator-service.ts +3 -3
- package/src/services/flux-service.ts +2 -2
- package/src/services/helm-service.ts +2 -2
- package/src/services/kubectl-service.ts +3 -6
- package/src/types/api.d.ts +1032 -1010
- package/src/types/json.d.ts +3 -3
- package/src/utils/default-api-key.ts +54 -42
- package/src/utils/env-manager.ts +98 -0
- package/src/utils/error-handler.ts +24 -15
- package/src/utils/logger.ts +12 -12
- package/src/utils/markdown-renderer.ts +18 -18
- package/src/utils/opencode-validator.ts +134 -0
- package/src/utils/token-manager.ts +35 -23
- package/tests/commands/chat.test.ts +43 -31
- package/tests/commands/code.test.ts +505 -0
- package/tests/utils/env-manager.test.ts +199 -0
- package/tests/utils/opencode-validator.test.ts +118 -0
- package/tsconfig.json +8 -8
- package/-27b-it +0 -0
- package/examples/README.md +0 -95
- package/examples/ai-review.sh +0 -30
- package/examples/install-global-security-hook.sh +0 -170
- package/examples/security-check.sh +0 -102
- package/examples/smart-commit.sh +0 -26
package/index.ts
CHANGED
|
@@ -17,7 +17,8 @@ program
|
|
|
17
17
|
| |_/ / __/ | | (_| | __/ |_ | | | |_| |_
|
|
18
18
|
\\____/ \\___|_| \\__, |\\___|\\_\\_ \\_| |_/\\___/
|
|
19
19
|
__/ |
|
|
20
|
-
|___/ AI on European terms
|
|
20
|
+
|___/ AI on European terms
|
|
21
|
+
Version: ${version}`,
|
|
21
22
|
)
|
|
22
23
|
.version(version, '-v, --version')
|
|
23
24
|
.option('--local', 'Use local API endpoint (hidden)', false)
|
|
@@ -29,55 +30,89 @@ registerCommands(program)
|
|
|
29
30
|
// Check for .bergetconfig if not running a command
|
|
30
31
|
if (process.argv.length <= 2) {
|
|
31
32
|
checkBergetConfig()
|
|
32
|
-
|
|
33
|
+
|
|
33
34
|
// Show helpful welcome message
|
|
34
|
-
console.log(chalk.blue('\nWelcome to the Berget CLI!'))
|
|
35
|
-
console.log(chalk.blue('Common commands:'))
|
|
36
|
-
console.log(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
console.log(
|
|
40
|
-
|
|
35
|
+
console.log(chalk.blue('\nWelcome to the Berget CLI!'))
|
|
36
|
+
console.log(chalk.blue('Common commands:'))
|
|
37
|
+
console.log(
|
|
38
|
+
chalk.blue(` ${chalk.bold('berget auth login')} - Log in to Berget`),
|
|
39
|
+
)
|
|
40
|
+
console.log(
|
|
41
|
+
chalk.blue(
|
|
42
|
+
` ${chalk.bold('berget models list')} - List available AI models`,
|
|
43
|
+
),
|
|
44
|
+
)
|
|
45
|
+
console.log(
|
|
46
|
+
chalk.blue(
|
|
47
|
+
` ${chalk.bold('berget chat run')} - Start a chat session`,
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
console.log(
|
|
51
|
+
chalk.blue(
|
|
52
|
+
` ${chalk.bold(
|
|
53
|
+
'berget code init',
|
|
54
|
+
)} - Initialize AI coding assistant`,
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
console.log(
|
|
58
|
+
chalk.blue(
|
|
59
|
+
` ${chalk.bold('berget api-keys list')} - List your API keys`,
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
console.log(
|
|
63
|
+
chalk.blue(
|
|
64
|
+
`\nRun ${chalk.bold('berget --help')} for a complete list of commands.`,
|
|
65
|
+
),
|
|
66
|
+
)
|
|
41
67
|
}
|
|
42
68
|
|
|
43
69
|
// Add helpful suggestions for common command mistakes
|
|
44
70
|
const commonMistakes: Record<string, string> = {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
71
|
+
login: 'auth login',
|
|
72
|
+
logout: 'auth logout',
|
|
73
|
+
whoami: 'auth whoami',
|
|
48
74
|
'list-models': 'models list',
|
|
49
75
|
'list-keys': 'api-keys list',
|
|
50
76
|
'create-key': 'api-keys create',
|
|
51
77
|
'list-clusters': 'clusters list',
|
|
52
|
-
|
|
53
|
-
|
|
78
|
+
usage: 'billing usage',
|
|
79
|
+
init: 'code init',
|
|
80
|
+
}
|
|
54
81
|
|
|
55
82
|
// Add error handler for unknown commands
|
|
56
83
|
program.on('command:*', (operands) => {
|
|
57
|
-
const unknownCommand = operands[0] as string
|
|
58
|
-
console.error(chalk.red(`Error: unknown command '${unknownCommand}'`))
|
|
59
|
-
|
|
84
|
+
const unknownCommand = operands[0] as string
|
|
85
|
+
console.error(chalk.red(`Error: unknown command '${unknownCommand}'`))
|
|
86
|
+
|
|
60
87
|
// Check if this is a known mistake and suggest the correct command
|
|
61
88
|
if (unknownCommand in commonMistakes) {
|
|
62
|
-
console.log(
|
|
89
|
+
console.log(
|
|
90
|
+
chalk.yellow(
|
|
91
|
+
`Did you mean? ${chalk.bold(
|
|
92
|
+
`berget ${commonMistakes[unknownCommand]}`,
|
|
93
|
+
)}`,
|
|
94
|
+
),
|
|
95
|
+
)
|
|
63
96
|
} else {
|
|
64
97
|
// Try to find similar commands
|
|
65
|
-
const availableCommands = program.commands.map(cmd => cmd.name())
|
|
66
|
-
const similarCommands = availableCommands.filter(
|
|
67
|
-
cmd.includes(unknownCommand) || unknownCommand.includes(cmd)
|
|
68
|
-
)
|
|
69
|
-
|
|
98
|
+
const availableCommands = program.commands.map((cmd) => cmd.name())
|
|
99
|
+
const similarCommands = availableCommands.filter(
|
|
100
|
+
(cmd) => cmd.includes(unknownCommand) || unknownCommand.includes(cmd),
|
|
101
|
+
)
|
|
102
|
+
|
|
70
103
|
if (similarCommands.length > 0) {
|
|
71
|
-
console.log(chalk.yellow('Similar commands:'))
|
|
72
|
-
similarCommands.forEach(cmd => {
|
|
73
|
-
console.log(chalk.yellow(` ${chalk.bold(`berget ${cmd}`)}`))
|
|
74
|
-
})
|
|
104
|
+
console.log(chalk.yellow('Similar commands:'))
|
|
105
|
+
similarCommands.forEach((cmd) => {
|
|
106
|
+
console.log(chalk.yellow(` ${chalk.bold(`berget ${cmd}`)}`))
|
|
107
|
+
})
|
|
75
108
|
}
|
|
76
|
-
|
|
77
|
-
console.log(
|
|
109
|
+
|
|
110
|
+
console.log(
|
|
111
|
+
chalk.blue('\nRun `berget --help` for a list of available commands.'),
|
|
112
|
+
)
|
|
78
113
|
}
|
|
79
|
-
|
|
80
|
-
process.exit(1)
|
|
81
|
-
})
|
|
114
|
+
|
|
115
|
+
process.exit(1)
|
|
116
|
+
})
|
|
82
117
|
|
|
83
118
|
program.parse(process.argv)
|
package/opencode.json
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://opencode.ai/config.json",
|
|
3
|
+
"username": "berget-code",
|
|
4
|
+
"theme": "berget-dark",
|
|
5
|
+
"share": "manual",
|
|
6
|
+
"autoupdate": true,
|
|
7
|
+
"model": "berget/deepseek-r1",
|
|
8
|
+
"small_model": "berget/gpt-oss",
|
|
9
|
+
"agent": {
|
|
10
|
+
"fullstack": {
|
|
11
|
+
"model": "berget/deepseek-r1",
|
|
12
|
+
"temperature": 0.3,
|
|
13
|
+
"top_p": 0.9,
|
|
14
|
+
"mode": "primary",
|
|
15
|
+
"permission": {
|
|
16
|
+
"edit": "allow",
|
|
17
|
+
"bash": "allow",
|
|
18
|
+
"webfetch": "allow"
|
|
19
|
+
},
|
|
20
|
+
"description": "Router/coordinator agent for full-stack development with schema-driven architecture",
|
|
21
|
+
"prompt": "Voice: Scandinavian calm—precise, concise, confident; no fluff. You are Berget Code Fullstack agent. Act as a router and coordinator in a monorepo. Bottom-up schema: database → OpenAPI → generated types. Top-down types: API → UI → components. Use openapi-fetch and Zod at every boundary; compile-time errors are desired when contracts change. Routing rules: if task/paths match /apps/frontend or React (.tsx) → use frontend; if /apps/app or Expo/React Native → app; if /infra, /k8s, flux-system, kustomization.yaml, Helm values → devops; if /services, Koa routers, services/adapters/domain → backend. If ambiguous, remain fullstack and outline the end-to-end plan, then delegate subtasks to the right persona. Security: validate inputs; secrets via FluxCD SOPS/Sealed Secrets. Documentation is generated from code—never duplicated. CRITICAL: When all implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision."
|
|
22
|
+
},
|
|
23
|
+
"frontend": {
|
|
24
|
+
"model": "berget/deepseek-r1",
|
|
25
|
+
"temperature": 0.4,
|
|
26
|
+
"top_p": 0.9,
|
|
27
|
+
"mode": "primary",
|
|
28
|
+
"permission": {
|
|
29
|
+
"edit": "allow",
|
|
30
|
+
"bash": "deny",
|
|
31
|
+
"webfetch": "allow"
|
|
32
|
+
},
|
|
33
|
+
"note": "Bash access is denied for frontend persona to prevent shell command execution in UI environments. This restriction enforces security and architectural boundaries.",
|
|
34
|
+
"description": "Builds Scandinavian, type-safe UIs with React, Tailwind, Shadcn.",
|
|
35
|
+
"prompt": "You are Berget Code Frontend agent. Voice: Scandinavian calm—precise, concise, confident. React 18 + TypeScript. Tailwind + Shadcn UI only via the design system (index.css, tailwind.config.ts). Use semantic tokens for color/spacing/typography/motion; never ad-hoc classes or inline colors. Components are pure and responsive; props-first data; minimal global state (Zustand/Jotai). Accessibility and keyboard navigation mandatory. Mock data only at init under /data via typed hooks (e.g., useProducts() reading /data/products.json). Design: minimal, balanced, quiet motion. CRITICAL: When all frontend implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision."
|
|
36
|
+
},
|
|
37
|
+
"backend": {
|
|
38
|
+
"model": "berget/deepseek-r1",
|
|
39
|
+
"temperature": 0.3,
|
|
40
|
+
"top_p": 0.9,
|
|
41
|
+
"mode": "primary",
|
|
42
|
+
"permission": {
|
|
43
|
+
"edit": "allow",
|
|
44
|
+
"bash": "allow",
|
|
45
|
+
"webfetch": "allow"
|
|
46
|
+
},
|
|
47
|
+
"description": "Functional, modular Koa + TypeScript services; schema-first with code quality focus.",
|
|
48
|
+
"prompt": "You are Berget Code Backend agent. Voice: Scandinavian calm—precise, concise, confident. TypeScript + Koa. Prefer many small pure functions; avoid big try/catch blocks. Routes thin; logic in services/adapters/domain. Validate with Zod; auto-generate OpenAPI. Adapters isolate external systems; domain never depends on framework. Test with supertest; idempotent and stateless by default. Each microservice emits an OpenAPI contract; changes propagate upward to types. Code Quality & Refactoring Principles: Apply Single Responsibility Principle, fail fast with explicit errors, eliminate code duplication, remove nested complexity, use descriptive error codes, keep functions under 30 lines. Always leave code cleaner and more readable than you found it. CRITICAL: When all backend implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision."
|
|
49
|
+
},
|
|
50
|
+
"devops": {
|
|
51
|
+
"model": "berget/deepseek-r1",
|
|
52
|
+
"temperature": 0.3,
|
|
53
|
+
"top_p": 0.8,
|
|
54
|
+
"mode": "primary",
|
|
55
|
+
"permission": {
|
|
56
|
+
"edit": "allow",
|
|
57
|
+
"bash": "allow",
|
|
58
|
+
"webfetch": "allow"
|
|
59
|
+
},
|
|
60
|
+
"description": "Declarative GitOps infra with FluxCD, Kustomize, Helm, operators.",
|
|
61
|
+
"prompt": "You are Berget Code DevOps agent. Voice: Scandinavian calm—precise, concise, confident. Start simple: k8s/{deployment,service,ingress}. Add FluxCD sync to repo and image automation. Use Kustomize bases/overlays (staging, production). Add dependencies via Helm from upstream sources; prefer native operators when available (CloudNativePG, cert-manager, external-dns). SemVer with -rc tags keeps CI environments current. Observability with Prometheus/Grafana. No manual kubectl in production—Git is the source of truth."
|
|
62
|
+
},
|
|
63
|
+
"app": {
|
|
64
|
+
"model": "berget/deepseek-r1",
|
|
65
|
+
"temperature": 0.4,
|
|
66
|
+
"top_p": 0.9,
|
|
67
|
+
"mode": "primary",
|
|
68
|
+
"permission": {
|
|
69
|
+
"edit": "allow",
|
|
70
|
+
"bash": "deny",
|
|
71
|
+
"webfetch": "allow"
|
|
72
|
+
},
|
|
73
|
+
"note": "Bash access is denied for app persona to prevent shell command execution in mobile/Expo environments. This restriction enforces security and architectural boundaries.",
|
|
74
|
+
"description": "Expo + React Native apps; props-first, offline-aware, shared tokens.",
|
|
75
|
+
"prompt": "You are Berget Code App agent. Voice: Scandinavian calm—precise, concise, confident. Expo + React Native + TypeScript. Structure by components/hooks/services/navigation. Components are pure; data via props; refactor shared logic into hooks/stores. Share tokens with frontend. Mock data in /data via typed hooks; later replace with live APIs. Offline via SQLite/MMKV; notifications via Expo. Request permissions only when needed. Subtle, meaningful motion; light/dark parity."
|
|
76
|
+
},
|
|
77
|
+
"security": {
|
|
78
|
+
"model": "berget/deepseek-r1",
|
|
79
|
+
"temperature": 0.2,
|
|
80
|
+
"top_p": 0.8,
|
|
81
|
+
"mode": "subagent",
|
|
82
|
+
"permission": {
|
|
83
|
+
"edit": "deny",
|
|
84
|
+
"bash": "allow",
|
|
85
|
+
"webfetch": "allow"
|
|
86
|
+
},
|
|
87
|
+
"description": "Security specialist for pentesting, OWASP compliance, and vulnerability assessments.",
|
|
88
|
+
"prompt": "Voice: Scandinavian calm—precise, concise, confident. You are Berget Code Security agent. Expert in application security, penetration testing, and OWASP standards. Core responsibilities: Conduct security assessments and penetration tests, Validate OWASP Top 10 compliance, Review code for security vulnerabilities, Implement security headers and Content Security Policy (CSP), Audit API security, Check for sensitive data exposure, Validate input sanitization and output encoding, Assess dependency security and supply chain risks. Tools and techniques: OWASP ZAP, Burp Suite, security linters, dependency scanners, manual code review. Always provide specific, actionable security recommendations with priority levels."
|
|
89
|
+
},
|
|
90
|
+
"quality": {
|
|
91
|
+
"model": "berget/deepseek-r1",
|
|
92
|
+
"temperature": 0.1,
|
|
93
|
+
"top_p": 0.9,
|
|
94
|
+
"mode": "subagent",
|
|
95
|
+
"permission": {
|
|
96
|
+
"edit": "allow",
|
|
97
|
+
"bash": "allow",
|
|
98
|
+
"webfetch": "allow"
|
|
99
|
+
},
|
|
100
|
+
"description": "Quality assurance specialist for testing, building, and PR management.",
|
|
101
|
+
"prompt": "Voice: Scandinavian calm—precise, concise, confident. You are Berget Code Quality agent. Specialist in code quality assurance, testing, building, and pull request management.\n\nCore responsibilities:\n - Run comprehensive test suites (npm test, npm run test, jest, vitest)\n - Execute build processes (npm run build, webpack, vite, tsc)\n - Create and manage pull requests with proper descriptions\n - Monitor GitHub for Copilot/reviewer comments\n - Ensure code quality standards are met\n - Validate linting and formatting (npm run lint, prettier)\n - Check test coverage and performance benchmarks\n - Handle CI/CD pipeline validation\n\nCommon CLI commands:\n - npm test or npm run test (run test suite)\n - npm run build (build project)\n - npm run lint (run linting)\n - npm run format (format code)\n - npm run test:coverage (check coverage)\n - gh pr create (create pull request)\n - gh pr view --comments (check PR comments)\n - git add . && git commit -m \"message\" && git push (commit and push)\n\nPR Workflow:\n 1. Ensure all tests pass: npm test\n 2. Build successfully: npm run build\n 3. Create/update PR with clear description\n 4. Monitor for reviewer comments\n 5. Address feedback promptly\n 6. Update PR with fixes\n 7. Ensure CI checks pass\n\nAlways provide specific command examples and wait for processes to complete before proceeding."
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"command": {
|
|
105
|
+
"fullstack": {
|
|
106
|
+
"description": "Switch to Fullstack (router)",
|
|
107
|
+
"template": "{{input}}",
|
|
108
|
+
"agent": "fullstack"
|
|
109
|
+
},
|
|
110
|
+
"route": {
|
|
111
|
+
"description": "Let Fullstack auto-route to the right persona based on files/intent",
|
|
112
|
+
"template": "ROUTE {{input}}",
|
|
113
|
+
"agent": "fullstack",
|
|
114
|
+
"subtask": true
|
|
115
|
+
},
|
|
116
|
+
"frontend": {
|
|
117
|
+
"description": "Switch to Frontend persona",
|
|
118
|
+
"template": "{{input}}",
|
|
119
|
+
"agent": "frontend"
|
|
120
|
+
},
|
|
121
|
+
"backend": {
|
|
122
|
+
"description": "Switch to Backend persona",
|
|
123
|
+
"template": "{{input}}",
|
|
124
|
+
"agent": "backend"
|
|
125
|
+
},
|
|
126
|
+
"devops": {
|
|
127
|
+
"description": "Switch to DevOps persona",
|
|
128
|
+
"template": "{{input}}",
|
|
129
|
+
"agent": "devops"
|
|
130
|
+
},
|
|
131
|
+
"app": {
|
|
132
|
+
"description": "Switch to App persona",
|
|
133
|
+
"template": "{{input}}",
|
|
134
|
+
"agent": "app"
|
|
135
|
+
},
|
|
136
|
+
"quality": {
|
|
137
|
+
"description": "Switch to Quality agent for testing, building, and PR management",
|
|
138
|
+
"template": "{{input}}",
|
|
139
|
+
"agent": "quality"
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
"watcher": {
|
|
143
|
+
"ignore": [
|
|
144
|
+
"node_modules",
|
|
145
|
+
"dist",
|
|
146
|
+
".git",
|
|
147
|
+
"coverage"
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
"provider": {
|
|
151
|
+
"berget": {
|
|
152
|
+
"npm": "@ai-sdk/openai-compatible",
|
|
153
|
+
"name": "Berget AI",
|
|
154
|
+
"options": {
|
|
155
|
+
"baseURL": "https://api.berget.ai/v1"
|
|
156
|
+
},
|
|
157
|
+
"models": {
|
|
158
|
+
"deepseek-r1": {
|
|
159
|
+
"name": "GLM-4.6",
|
|
160
|
+
"limit": {
|
|
161
|
+
"output": 4000,
|
|
162
|
+
"context": 90000
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"gpt-oss": {
|
|
166
|
+
"name": "GPT-OSS",
|
|
167
|
+
"limit": {
|
|
168
|
+
"output": 4000,
|
|
169
|
+
"context": 128000
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"llama-8b": {
|
|
173
|
+
"name": "llama-3.1-8b",
|
|
174
|
+
"limit": {
|
|
175
|
+
"output": 4000,
|
|
176
|
+
"context": 128000
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "berget",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"berget": "dist/index.js"
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"description": "This is a cli command for interacting with the AI infrastructure provider Berget",
|
|
26
26
|
"devDependencies": {
|
|
27
|
+
"@types/dotenv": "^6.1.1",
|
|
27
28
|
"@types/marked": "^5.0.2",
|
|
28
29
|
"@types/marked-terminal": "^6.1.1",
|
|
29
30
|
"@types/node": "^20.11.20",
|
|
@@ -32,14 +33,18 @@
|
|
|
32
33
|
"vitest": "^1.0.0"
|
|
33
34
|
},
|
|
34
35
|
"dependencies": {
|
|
36
|
+
"ajv": "^8.17.1",
|
|
37
|
+
"ajv-formats": "^3.0.1",
|
|
35
38
|
"chalk": "^4.1.2",
|
|
36
39
|
"commander": "^12.0.0",
|
|
40
|
+
"dotenv": "^17.2.3",
|
|
37
41
|
"fs-extra": "^11.3.0",
|
|
38
42
|
"marked": "^9.1.6",
|
|
39
43
|
"marked-terminal": "^6.2.0",
|
|
40
44
|
"open": "^9.1.0",
|
|
41
45
|
"openapi-fetch": "^0.9.1",
|
|
42
46
|
"openapi-typescript": "^6.7.4",
|
|
43
|
-
"readline": "^1.3.0"
|
|
47
|
+
"readline": "^1.3.0",
|
|
48
|
+
"zod": "^4.1.12"
|
|
44
49
|
}
|
|
45
50
|
}
|
package/src/client.ts
CHANGED
|
@@ -36,7 +36,7 @@ export const getAuthToken = (): string | null => {
|
|
|
36
36
|
export const saveAuthToken = (
|
|
37
37
|
accessToken: string,
|
|
38
38
|
refreshToken: string,
|
|
39
|
-
expiresIn: number = 3600
|
|
39
|
+
expiresIn: number = 3600,
|
|
40
40
|
): void => {
|
|
41
41
|
const tokenManager = TokenManager.getInstance()
|
|
42
42
|
tokenManager.setTokens(accessToken, refreshToken, expiresIn)
|
|
@@ -53,7 +53,7 @@ export const createAuthenticatedClient = () => {
|
|
|
53
53
|
|
|
54
54
|
if (!tokenManager.getAccessToken()) {
|
|
55
55
|
logger.debug(
|
|
56
|
-
'No authentication token found. Please run `berget auth login` first.'
|
|
56
|
+
'No authentication token found. Please run `berget auth login` first.',
|
|
57
57
|
)
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -100,7 +100,7 @@ export const createAuthenticatedClient = () => {
|
|
|
100
100
|
let result
|
|
101
101
|
try {
|
|
102
102
|
result = await (target[prop as keyof typeof target] as Function)(
|
|
103
|
-
...args
|
|
103
|
+
...args,
|
|
104
104
|
)
|
|
105
105
|
} catch (requestError) {
|
|
106
106
|
logger.debug(
|
|
@@ -108,7 +108,7 @@ export const createAuthenticatedClient = () => {
|
|
|
108
108
|
requestError instanceof Error
|
|
109
109
|
? requestError.message
|
|
110
110
|
: String(requestError)
|
|
111
|
-
}
|
|
111
|
+
}`,
|
|
112
112
|
)
|
|
113
113
|
return {
|
|
114
114
|
error: {
|
|
@@ -171,7 +171,7 @@ export const createAuthenticatedClient = () => {
|
|
|
171
171
|
if (isAuthError && tokenManager.getRefreshToken()) {
|
|
172
172
|
logger.debug('Auth error detected, attempting token refresh')
|
|
173
173
|
logger.debug(
|
|
174
|
-
`Error details: ${JSON.stringify(result.error, null, 2)}
|
|
174
|
+
`Error details: ${JSON.stringify(result.error, null, 2)}`,
|
|
175
175
|
)
|
|
176
176
|
|
|
177
177
|
const refreshed = await refreshAccessToken(tokenManager)
|
|
@@ -185,7 +185,7 @@ export const createAuthenticatedClient = () => {
|
|
|
185
185
|
|
|
186
186
|
// Retry the request
|
|
187
187
|
return await (target[prop as keyof typeof target] as Function)(
|
|
188
|
-
...args
|
|
188
|
+
...args,
|
|
189
189
|
)
|
|
190
190
|
} else {
|
|
191
191
|
logger.debug('Token refresh failed')
|
|
@@ -211,7 +211,7 @@ export const createAuthenticatedClient = () => {
|
|
|
211
211
|
|
|
212
212
|
// Helper function to refresh the access token
|
|
213
213
|
async function refreshAccessToken(
|
|
214
|
-
tokenManager: TokenManager
|
|
214
|
+
tokenManager: TokenManager,
|
|
215
215
|
): Promise<boolean> {
|
|
216
216
|
try {
|
|
217
217
|
const refreshToken = tokenManager.getRefreshToken()
|
|
@@ -233,23 +233,23 @@ async function refreshAccessToken(
|
|
|
233
233
|
// Handle HTTP errors
|
|
234
234
|
if (!response.ok) {
|
|
235
235
|
logger.debug(
|
|
236
|
-
`Token refresh error: HTTP ${response.status} ${response.statusText}
|
|
236
|
+
`Token refresh error: HTTP ${response.status} ${response.statusText}`,
|
|
237
237
|
)
|
|
238
238
|
|
|
239
239
|
// Check if the refresh token itself is expired or invalid
|
|
240
240
|
if (response.status === 401 || response.status === 403) {
|
|
241
241
|
console.warn(
|
|
242
242
|
chalk.yellow(
|
|
243
|
-
'Your refresh token has expired. Please run `berget auth login` again.'
|
|
244
|
-
)
|
|
243
|
+
'Your refresh token has expired. Please run `berget auth login` again.',
|
|
244
|
+
),
|
|
245
245
|
)
|
|
246
246
|
// Clear tokens if unauthorized - they're invalid
|
|
247
247
|
tokenManager.clearTokens()
|
|
248
248
|
} else {
|
|
249
249
|
console.warn(
|
|
250
250
|
chalk.yellow(
|
|
251
|
-
`Failed to refresh token: ${response.status} ${response.statusText}
|
|
252
|
-
)
|
|
251
|
+
`Failed to refresh token: ${response.status} ${response.statusText}`,
|
|
252
|
+
),
|
|
253
253
|
)
|
|
254
254
|
}
|
|
255
255
|
return false
|
|
@@ -259,7 +259,7 @@ async function refreshAccessToken(
|
|
|
259
259
|
const contentType = response.headers.get('content-type')
|
|
260
260
|
if (!contentType || !contentType.includes('application/json')) {
|
|
261
261
|
console.warn(
|
|
262
|
-
chalk.yellow(`Unexpected content type in response: ${contentType}`)
|
|
262
|
+
chalk.yellow(`Unexpected content type in response: ${contentType}`),
|
|
263
263
|
)
|
|
264
264
|
return false
|
|
265
265
|
}
|
|
@@ -270,8 +270,8 @@ async function refreshAccessToken(
|
|
|
270
270
|
if (!data || !data.token) {
|
|
271
271
|
console.warn(
|
|
272
272
|
chalk.yellow(
|
|
273
|
-
'Invalid token response. Please run `berget auth login` again.'
|
|
274
|
-
)
|
|
273
|
+
'Invalid token response. Please run `berget auth login` again.',
|
|
274
|
+
),
|
|
275
275
|
)
|
|
276
276
|
return false
|
|
277
277
|
}
|
|
@@ -286,7 +286,7 @@ async function refreshAccessToken(
|
|
|
286
286
|
tokenManager.setTokens(
|
|
287
287
|
data.token,
|
|
288
288
|
data.refresh_token,
|
|
289
|
-
data.expires_in || 3600
|
|
289
|
+
data.expires_in || 3600,
|
|
290
290
|
)
|
|
291
291
|
logger.debug('Refresh token also updated')
|
|
292
292
|
}
|
|
@@ -297,8 +297,8 @@ async function refreshAccessToken(
|
|
|
297
297
|
fetchError instanceof Error
|
|
298
298
|
? fetchError.message
|
|
299
299
|
: String(fetchError)
|
|
300
|
-
}
|
|
301
|
-
)
|
|
300
|
+
}`,
|
|
301
|
+
),
|
|
302
302
|
)
|
|
303
303
|
return false
|
|
304
304
|
}
|
|
@@ -309,8 +309,8 @@ async function refreshAccessToken(
|
|
|
309
309
|
chalk.yellow(
|
|
310
310
|
`Failed to refresh authentication token: ${
|
|
311
311
|
error instanceof Error ? error.message : String(error)
|
|
312
|
-
}
|
|
313
|
-
)
|
|
312
|
+
}`,
|
|
313
|
+
),
|
|
314
314
|
)
|
|
315
315
|
return false
|
|
316
316
|
}
|