kubeagent 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/LICENSE +72 -0
- package/README.md +154 -0
- package/dist/auth.d.ts +23 -0
- package/dist/auth.js +162 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +447 -0
- package/dist/config.d.ts +50 -0
- package/dist/config.js +79 -0
- package/dist/debug.d.ts +10 -0
- package/dist/debug.js +18 -0
- package/dist/diagnoser/index.d.ts +17 -0
- package/dist/diagnoser/index.js +251 -0
- package/dist/diagnoser/tools.d.ts +119 -0
- package/dist/diagnoser/tools.js +108 -0
- package/dist/kb/loader.d.ts +1 -0
- package/dist/kb/loader.js +41 -0
- package/dist/kb/writer.d.ts +11 -0
- package/dist/kb/writer.js +36 -0
- package/dist/kubectl-config.d.ts +7 -0
- package/dist/kubectl-config.js +47 -0
- package/dist/kubectl.d.ts +13 -0
- package/dist/kubectl.js +57 -0
- package/dist/monitor/checks.d.ts +71 -0
- package/dist/monitor/checks.js +167 -0
- package/dist/monitor/index.d.ts +7 -0
- package/dist/monitor/index.js +126 -0
- package/dist/monitor/types.d.ts +11 -0
- package/dist/monitor/types.js +1 -0
- package/dist/notify/index.d.ts +5 -0
- package/dist/notify/index.js +40 -0
- package/dist/notify/setup.d.ts +4 -0
- package/dist/notify/setup.js +88 -0
- package/dist/notify/slack.d.ts +4 -0
- package/dist/notify/slack.js +76 -0
- package/dist/notify/telegram.d.ts +8 -0
- package/dist/notify/telegram.js +63 -0
- package/dist/notify/webhook.d.ts +3 -0
- package/dist/notify/webhook.js +49 -0
- package/dist/onboard/cluster-scan.d.ts +42 -0
- package/dist/onboard/cluster-scan.js +103 -0
- package/dist/onboard/code-scan.d.ts +9 -0
- package/dist/onboard/code-scan.js +114 -0
- package/dist/onboard/index.d.ts +1 -0
- package/dist/onboard/index.js +328 -0
- package/dist/onboard/interview.d.ts +12 -0
- package/dist/onboard/interview.js +71 -0
- package/dist/onboard/project-matcher.d.ts +25 -0
- package/dist/onboard/project-matcher.js +149 -0
- package/dist/orchestrator.d.ts +3 -0
- package/dist/orchestrator.js +222 -0
- package/dist/proxy-client.d.ts +15 -0
- package/dist/proxy-client.js +72 -0
- package/dist/render.d.ts +5 -0
- package/dist/render.js +143 -0
- package/dist/verifier.d.ts +9 -0
- package/dist/verifier.js +17 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
KubeAgent Commercial License
|
|
2
|
+
Copyright (c) 2025 KubeAgent (kubeagent.net). All rights reserved.
|
|
3
|
+
|
|
4
|
+
TERMS AND CONDITIONS
|
|
5
|
+
|
|
6
|
+
1. DEFINITIONS
|
|
7
|
+
|
|
8
|
+
"Software" means the KubeAgent software and associated documentation files.
|
|
9
|
+
"Licensor" means KubeAgent (kubeagent.net).
|
|
10
|
+
"You" means the individual or legal entity exercising rights under this License.
|
|
11
|
+
"Commercial Use" means any use of the Software for commercial advantage or
|
|
12
|
+
monetary compensation, including use within a business or organization.
|
|
13
|
+
|
|
14
|
+
2. GRANT OF LICENSE
|
|
15
|
+
|
|
16
|
+
Subject to the terms of this License, Licensor grants You a non-exclusive,
|
|
17
|
+
non-transferable, limited license to:
|
|
18
|
+
|
|
19
|
+
a. Use the Software for personal, non-commercial evaluation purposes.
|
|
20
|
+
b. Access and modify the source code for the purpose of contributing to the
|
|
21
|
+
official KubeAgent repository via pull request.
|
|
22
|
+
|
|
23
|
+
3. RESTRICTIONS
|
|
24
|
+
|
|
25
|
+
You may NOT, without explicit written permission from the Licensor:
|
|
26
|
+
|
|
27
|
+
a. Use the Software for Commercial Use.
|
|
28
|
+
b. Distribute, sublicense, sell, resell, transfer, assign, or otherwise
|
|
29
|
+
commercially exploit or make available the Software or any derivative works.
|
|
30
|
+
c. Copy or use the Software for any purpose other than as permitted in Section 2.
|
|
31
|
+
d. Remove or alter any proprietary notices, labels, or marks on the Software.
|
|
32
|
+
e. Use the KubeAgent name, logo, or trademarks to endorse or promote products
|
|
33
|
+
derived from this Software without prior written consent.
|
|
34
|
+
|
|
35
|
+
4. COMMERCIAL LICENSING
|
|
36
|
+
|
|
37
|
+
Commercial use of the Software requires a separate commercial license agreement
|
|
38
|
+
with the Licensor. To obtain a commercial license, please contact:
|
|
39
|
+
|
|
40
|
+
Email: hello@kubeagent.net
|
|
41
|
+
Website: https://kubeagent.net
|
|
42
|
+
|
|
43
|
+
Paid plans that include a commercial license are available at:
|
|
44
|
+
https://kubeagent.net/#pricing
|
|
45
|
+
|
|
46
|
+
5. CONTRIBUTIONS
|
|
47
|
+
|
|
48
|
+
By submitting a pull request or patch to the official KubeAgent repository,
|
|
49
|
+
You grant the Licensor a perpetual, worldwide, non-exclusive, royalty-free,
|
|
50
|
+
irrevocable license to use, reproduce, modify, distribute, and sublicense
|
|
51
|
+
your contribution as part of the Software under any license the Licensor chooses.
|
|
52
|
+
|
|
53
|
+
6. DISCLAIMER OF WARRANTIES
|
|
54
|
+
|
|
55
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
56
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
57
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
58
|
+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
|
|
59
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM,
|
|
60
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
61
|
+
THE SOFTWARE.
|
|
62
|
+
|
|
63
|
+
7. TERMINATION
|
|
64
|
+
|
|
65
|
+
This License is effective until terminated. Your rights under this License
|
|
66
|
+
terminate automatically if You fail to comply with any of its terms. Upon
|
|
67
|
+
termination, You must destroy all copies of the Software in your possession.
|
|
68
|
+
|
|
69
|
+
8. GOVERNING LAW
|
|
70
|
+
|
|
71
|
+
This License shall be governed by and construed in accordance with applicable
|
|
72
|
+
law, without regard to conflict of law principles.
|
package/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# KubeAgent
|
|
2
|
+
|
|
3
|
+
AI-powered Kubernetes monitoring, diagnosis, and remediation CLI. Uses Claude to investigate cluster issues, determine root causes, and apply fixes — with human approval for risky actions.
|
|
4
|
+
|
|
5
|
+
Built for solo DevOps engineers and small teams who want an intelligent on-call assistant.
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Monitor (kubectl polling) --> Diagnoser (Claude API) --> Verifier (re-check)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
1. **Monitor** polls your cluster for issues (CrashLoopBackOff, OOM, ImagePullBackOff, node pressure, etc.) — no LLM involved.
|
|
14
|
+
2. **Diagnoser** sends issues to Claude with your cluster's knowledge base as context. Claude investigates using kubectl tools (logs, describe, events) in an agentic loop.
|
|
15
|
+
3. **Verifier** re-checks the cluster after a fix to confirm the issue is resolved.
|
|
16
|
+
|
|
17
|
+
Safe actions (pod restart, rollout restart, scale up) are auto-applied. Risky actions (rollback, delete, scale to zero) require your approval.
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Install dependencies
|
|
23
|
+
npm install
|
|
24
|
+
|
|
25
|
+
# Set your API key
|
|
26
|
+
export ANTHROPIC_API_KEY=sk-...
|
|
27
|
+
|
|
28
|
+
# Check cluster health (no LLM)
|
|
29
|
+
npx tsx src/cli.ts status
|
|
30
|
+
|
|
31
|
+
# Onboard — scan cluster, detect tech stacks, build knowledge base
|
|
32
|
+
npx tsx src/cli.ts onboard
|
|
33
|
+
|
|
34
|
+
# Watch mode — continuous monitoring with auto-remediation
|
|
35
|
+
npx tsx src/cli.ts watch
|
|
36
|
+
|
|
37
|
+
# One-shot diagnosis of a specific resource
|
|
38
|
+
npx tsx src/cli.ts diagnose my-pod -n production
|
|
39
|
+
|
|
40
|
+
# Ask a freeform question about your cluster
|
|
41
|
+
npx tsx src/cli.ts "why is the retime API returning 503s?"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Install Globally
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm run build
|
|
48
|
+
npm link
|
|
49
|
+
kubeagent status
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Commands
|
|
53
|
+
|
|
54
|
+
| Command | Description | Uses LLM |
|
|
55
|
+
|---------|-------------|----------|
|
|
56
|
+
| `status` | Quick cluster health check | No |
|
|
57
|
+
| `onboard` | Scan cluster + codebases, interview, generate knowledge base | Yes |
|
|
58
|
+
| `watch` | Continuous monitoring with auto-remediation | Yes |
|
|
59
|
+
| `diagnose <resource>` | One-shot diagnosis of a pod/deployment/service | Yes |
|
|
60
|
+
| `<prompt>` | Freeform question about your cluster | Yes |
|
|
61
|
+
|
|
62
|
+
### Common Options
|
|
63
|
+
|
|
64
|
+
- `-c, --context <name>` — Kubernetes context (defaults to current)
|
|
65
|
+
- `-n, --namespace <name>` — Namespace (for `diagnose`)
|
|
66
|
+
- `-i, --interval <seconds>` — Check interval for `watch` (default: 60)
|
|
67
|
+
|
|
68
|
+
## Knowledge Base
|
|
69
|
+
|
|
70
|
+
KubeAgent builds a per-cluster knowledge base at `~/.kubeagent/clusters/<context>/` during onboarding:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
~/.kubeagent/
|
|
74
|
+
config.yaml # Global config (clusters, webhooks, intervals)
|
|
75
|
+
clusters/
|
|
76
|
+
hetzner-prod/
|
|
77
|
+
cluster.md # Nodes, namespaces, deployments, services
|
|
78
|
+
projects/
|
|
79
|
+
retime.md # Tech stack, dependencies, notes
|
|
80
|
+
dove.md
|
|
81
|
+
runbooks/ # Custom runbooks (manually added)
|
|
82
|
+
incidents/ # Auto-logged incident reports
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
This context is injected into Claude's system prompt during diagnosis, giving it awareness of your specific infrastructure.
|
|
86
|
+
|
|
87
|
+
## Action Safety
|
|
88
|
+
|
|
89
|
+
Actions are classified into tiers:
|
|
90
|
+
|
|
91
|
+
| Tier | Actions | Behavior |
|
|
92
|
+
|------|---------|----------|
|
|
93
|
+
| **Safe** | `get_logs`, `describe_resource`, `get_events`, `restart_pod`, `rollout_restart`, `scale_deployment` | Auto-executed |
|
|
94
|
+
| **Risky** | `rollback_deployment`, `scale_to_zero`, `delete_resource`, `edit_configmap`, `apply_manifest` | Requires approval |
|
|
95
|
+
| **Never** | `delete namespace`, `delete node`, etc. | Refused |
|
|
96
|
+
|
|
97
|
+
## Notifications
|
|
98
|
+
|
|
99
|
+
Configure webhooks in `~/.kubeagent/config.yaml` to get alerts in Slack, Mattermost, or Discord:
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
webhooks:
|
|
103
|
+
- url: https://hooks.slack.com/services/T.../B.../xxx
|
|
104
|
+
type: slack
|
|
105
|
+
severityFilter: critical
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Architecture
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
src/
|
|
112
|
+
cli.ts # Commander.js CLI entry point
|
|
113
|
+
config.ts # YAML config (~/.kubeagent/config.yaml)
|
|
114
|
+
kubectl.ts # kubectl exec wrapper
|
|
115
|
+
orchestrator.ts # Monitor -> Diagnose -> Verify pipeline
|
|
116
|
+
verifier.ts # Post-fix verification
|
|
117
|
+
monitor/
|
|
118
|
+
types.ts # Issue types (Severity, IssueKind, Issue)
|
|
119
|
+
checks.ts # Pod and node issue detection
|
|
120
|
+
index.ts # Polling loop
|
|
121
|
+
diagnoser/
|
|
122
|
+
tools.ts # Zod-defined kubectl tools for Claude
|
|
123
|
+
index.ts # Claude API agentic loop
|
|
124
|
+
kb/
|
|
125
|
+
loader.ts # Read KB into system prompt
|
|
126
|
+
writer.ts # Write cluster/project/runbook/incident markdown
|
|
127
|
+
notify/
|
|
128
|
+
index.ts # Notification dispatcher
|
|
129
|
+
webhook.ts # Slack/Mattermost/Discord webhooks
|
|
130
|
+
onboard/
|
|
131
|
+
cluster-scan.ts # Auto-discover K8s resources
|
|
132
|
+
code-scan.ts # Detect tech stack from project files
|
|
133
|
+
interview.ts # Claude-generated clarifying questions
|
|
134
|
+
index.ts # Full onboarding flow
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Requirements
|
|
138
|
+
|
|
139
|
+
- Node.js >= 20
|
|
140
|
+
- `kubectl` configured with cluster access
|
|
141
|
+
- `ANTHROPIC_API_KEY` environment variable (for LLM features)
|
|
142
|
+
|
|
143
|
+
## Development
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npm test # Run all tests
|
|
147
|
+
npm run test:watch # Watch mode
|
|
148
|
+
npm run build # Compile TypeScript
|
|
149
|
+
npx tsx src/cli.ts # Run without building
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface AuthState {
|
|
2
|
+
serverUrl: string;
|
|
3
|
+
appUrl: string;
|
|
4
|
+
token: string;
|
|
5
|
+
email?: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function loadAuth(): AuthState | null;
|
|
10
|
+
export declare function saveAuth(auth: AuthState): void;
|
|
11
|
+
export declare function clearAuth(): void;
|
|
12
|
+
export declare function loginBrowser(serverUrl: string, appUrl: string): Promise<AuthState>;
|
|
13
|
+
export declare function createApiKey(auth: AuthState, name: string): Promise<{
|
|
14
|
+
key: string;
|
|
15
|
+
prefix: string;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function showAccount(auth: AuthState): Promise<{
|
|
18
|
+
balance: {
|
|
19
|
+
monthlyRemaining: number;
|
|
20
|
+
extraRemaining: number;
|
|
21
|
+
totalRemaining: number;
|
|
22
|
+
};
|
|
23
|
+
}>;
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { randomBytes } from "node:crypto";
|
|
5
|
+
import { configDir } from "./config.js";
|
|
6
|
+
import { dbg } from "./debug.js";
|
|
7
|
+
function authPath() {
|
|
8
|
+
return join(configDir(), "auth.json");
|
|
9
|
+
}
|
|
10
|
+
export function loadAuth() {
|
|
11
|
+
const path = authPath();
|
|
12
|
+
if (!existsSync(path)) {
|
|
13
|
+
dbg("auth", "no auth file found", { path });
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const auth = JSON.parse(readFileSync(path, "utf-8"));
|
|
18
|
+
dbg("auth", "loaded", {
|
|
19
|
+
serverUrl: auth.serverUrl,
|
|
20
|
+
email: auth.email,
|
|
21
|
+
keyPrefix: auth.apiKey ? auth.apiKey.slice(0, 12) + "..." : "(none)",
|
|
22
|
+
hasToken: !!auth.token,
|
|
23
|
+
});
|
|
24
|
+
return auth;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
dbg("auth", "failed to parse auth file");
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function saveAuth(auth) {
|
|
32
|
+
const dir = configDir();
|
|
33
|
+
if (!existsSync(dir))
|
|
34
|
+
mkdirSync(dir, { recursive: true });
|
|
35
|
+
writeFileSync(authPath(), JSON.stringify(auth, null, 2));
|
|
36
|
+
}
|
|
37
|
+
export function clearAuth() {
|
|
38
|
+
const path = authPath();
|
|
39
|
+
if (existsSync(path))
|
|
40
|
+
unlinkSync(path);
|
|
41
|
+
}
|
|
42
|
+
// Browser-based login flow (like Tailscale / GitHub CLI):
|
|
43
|
+
// 1. Generate random state + start local HTTP listener on a free port
|
|
44
|
+
// 2. Open browser to <appUrl>/auth/cli?state=<s>&port=<p>
|
|
45
|
+
// 3. User logs in (if needed) and clicks "Authorize"
|
|
46
|
+
// 4. Server redirects to http://localhost:<p>/callback?token=...&email=...
|
|
47
|
+
// 5. CLI saves token and resolves
|
|
48
|
+
export async function loginBrowser(serverUrl, appUrl) {
|
|
49
|
+
const state = randomBytes(16).toString("hex");
|
|
50
|
+
const { token, email, name } = await new Promise((resolve, reject) => {
|
|
51
|
+
const server = createServer((req, res) => {
|
|
52
|
+
if (!req.url?.startsWith("/callback")) {
|
|
53
|
+
res.end();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const url = new URL(req.url, "http://localhost");
|
|
57
|
+
const receivedState = url.searchParams.get("state");
|
|
58
|
+
const token = url.searchParams.get("token");
|
|
59
|
+
const email = url.searchParams.get("email") ?? "";
|
|
60
|
+
const name = url.searchParams.get("name") ?? "";
|
|
61
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
62
|
+
res.end(`<!DOCTYPE html>
|
|
63
|
+
<html lang="en">
|
|
64
|
+
<head>
|
|
65
|
+
<meta charset="UTF-8" />
|
|
66
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
67
|
+
<title>KubeAgent — Authorized</title>
|
|
68
|
+
<style>
|
|
69
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
70
|
+
body { background: #0a0a0a; color: #e5e5e5; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; display: flex; align-items: center; justify-content: center; min-height: 100vh; }
|
|
71
|
+
.card { background: #141414; border: 1px solid #262626; border-radius: 12px; padding: 2.5rem 3rem; text-align: center; max-width: 420px; width: 90%; }
|
|
72
|
+
.icon { font-size: 3rem; margin-bottom: 1.25rem; }
|
|
73
|
+
h1 { font-size: 1.4rem; font-weight: 700; margin-bottom: 0.6rem; color: #f5f5f5; }
|
|
74
|
+
p { color: #737373; font-size: 0.95rem; line-height: 1.6; }
|
|
75
|
+
.check { display: inline-flex; align-items: center; justify-content: center; width: 64px; height: 64px; border-radius: 50%; background: #052e16; margin-bottom: 1.25rem; }
|
|
76
|
+
.check svg { width: 32px; height: 32px; stroke: #34d399; }
|
|
77
|
+
</style>
|
|
78
|
+
<script>setTimeout(() => window.close(), 2000);</script>
|
|
79
|
+
</head>
|
|
80
|
+
<body>
|
|
81
|
+
<div class="card">
|
|
82
|
+
<div class="check">
|
|
83
|
+
<svg fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor">
|
|
84
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" />
|
|
85
|
+
</svg>
|
|
86
|
+
</div>
|
|
87
|
+
<h1>CLI Authorized</h1>
|
|
88
|
+
<p>You're logged in. Return to your terminal — this tab will close automatically.</p>
|
|
89
|
+
</div>
|
|
90
|
+
</body>
|
|
91
|
+
</html>`);
|
|
92
|
+
server.close();
|
|
93
|
+
if (receivedState !== state || !token) {
|
|
94
|
+
reject(new Error("Invalid callback — state mismatch or missing token"));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
resolve({ token, email, name });
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
server.listen(0, "127.0.0.1", async () => {
|
|
101
|
+
const port = server.address().port;
|
|
102
|
+
const authUrl = `${appUrl}/auth/cli?state=${state}&port=${port}`;
|
|
103
|
+
// Open browser cross-platform
|
|
104
|
+
const { execSync } = await import("node:child_process");
|
|
105
|
+
const cmd = process.platform === "darwin" ? `open "${authUrl}"`
|
|
106
|
+
: process.platform === "win32" ? `start "" "${authUrl}"`
|
|
107
|
+
: `xdg-open "${authUrl}"`;
|
|
108
|
+
try {
|
|
109
|
+
execSync(cmd);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// fallback: just print the URL
|
|
113
|
+
}
|
|
114
|
+
console.log(`\nOpening browser for authentication...`);
|
|
115
|
+
console.log(`If the browser didn't open, visit:\n ${authUrl}\n`);
|
|
116
|
+
});
|
|
117
|
+
// Timeout after 5 minutes
|
|
118
|
+
const timeoutId = setTimeout(() => {
|
|
119
|
+
server.close();
|
|
120
|
+
reject(new Error("Login timed out (5 minutes)"));
|
|
121
|
+
}, 5 * 60 * 1000);
|
|
122
|
+
// Unref so the timeout doesn't keep Node alive if something else exits first
|
|
123
|
+
timeoutId.unref();
|
|
124
|
+
});
|
|
125
|
+
const auth = { serverUrl, appUrl, token, email, name };
|
|
126
|
+
saveAuth(auth);
|
|
127
|
+
return auth;
|
|
128
|
+
}
|
|
129
|
+
export async function createApiKey(auth, name) {
|
|
130
|
+
const url = `${auth.serverUrl}/keys`;
|
|
131
|
+
let res;
|
|
132
|
+
try {
|
|
133
|
+
res = await fetch(url, {
|
|
134
|
+
method: "POST",
|
|
135
|
+
headers: {
|
|
136
|
+
"Content-Type": "application/json",
|
|
137
|
+
Authorization: `Bearer ${auth.token}`,
|
|
138
|
+
},
|
|
139
|
+
body: JSON.stringify({ name }),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
throw new Error(`Could not reach server at ${url} — is it running?\n (${err.message})`);
|
|
144
|
+
}
|
|
145
|
+
if (!res.ok) {
|
|
146
|
+
const body = await res.json().catch(() => ({}));
|
|
147
|
+
throw new Error(body.error ?? `Failed to create API key (${res.status})`);
|
|
148
|
+
}
|
|
149
|
+
const data = (await res.json());
|
|
150
|
+
auth.apiKey = data.key;
|
|
151
|
+
saveAuth(auth);
|
|
152
|
+
return data;
|
|
153
|
+
}
|
|
154
|
+
export async function showAccount(auth) {
|
|
155
|
+
const res = await fetch(`${auth.serverUrl}/v1/balance`, {
|
|
156
|
+
headers: { Authorization: `ApiKey ${auth.apiKey ?? auth.token}` },
|
|
157
|
+
});
|
|
158
|
+
if (!res.ok) {
|
|
159
|
+
throw new Error(`Failed to fetch account info (${res.status})`);
|
|
160
|
+
}
|
|
161
|
+
return { balance: (await res.json()) };
|
|
162
|
+
}
|
package/dist/cli.d.ts
ADDED