kybernesis 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/README.md +143 -0
- package/dist/api.d.ts +44 -0
- package/dist/api.js +65 -0
- package/dist/api.js.map +1 -0
- package/dist/auth.d.ts +25 -0
- package/dist/auth.js +238 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +114 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/delete.d.ts +1 -0
- package/dist/commands/delete.js +34 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +17 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/login.d.ts +1 -0
- package/dist/commands/login.js +9 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +1 -0
- package/dist/commands/logout.js +6 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/save.d.ts +1 -0
- package/dist/commands/save.js +21 -0
- package/dist/commands/save.js.map +1 -0
- package/dist/commands/search.d.ts +1 -0
- package/dist/commands/search.js +28 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/stats.d.ts +1 -0
- package/dist/commands/stats.js +17 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +29 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/switch.d.ts +1 -0
- package/dist/commands/switch.js +8 -0
- package/dist/commands/switch.js.map +1 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Kybernesis CLI
|
|
2
|
+
|
|
3
|
+
A command-line interface for interacting with your [Kybernesis](https://kybernesis.ai) memory workspace. Authenticate once via browser, then search, save, list, and manage memories directly from your terminal.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### From source (recommended for now)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone https://github.com/KybernesisAI/kybernesis-cli.git
|
|
11
|
+
cd kybernesis-cli
|
|
12
|
+
npm install
|
|
13
|
+
npm run build
|
|
14
|
+
npm install -g .
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### From npm (coming soon)
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g kybernesis
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
### 1. Log in
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
kybernesis login
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This opens your browser to authenticate via Kybernesis. Sign in with your account, pick a workspace, and the CLI stores your credentials locally at `~/.config/kybernesis/auth.json`.
|
|
32
|
+
|
|
33
|
+
### 2. Check your status
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
kybernesis status
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Shows your current workspace, user ID, token expiry, and server connection status.
|
|
40
|
+
|
|
41
|
+
### 3. Search your memories
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
kybernesis search "react patterns"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Returns matching memories with titles, summaries, tags, and metadata.
|
|
48
|
+
|
|
49
|
+
### 4. Save a memory
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
kybernesis save "We decided to use Zustand for global state management" \
|
|
53
|
+
--title "State management decision" \
|
|
54
|
+
--tags react,zustand,state
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 5. List recent memories
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
kybernesis list
|
|
61
|
+
kybernesis list --limit 50
|
|
62
|
+
kybernesis list --limit 20 --offset 40
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 6. View statistics
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
kybernesis stats
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Shows total memory count, breakdown by tier (hot/warm/archive) and source (upload/chat/connector).
|
|
72
|
+
|
|
73
|
+
### 7. Delete a memory
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
kybernesis delete <memoryId>
|
|
77
|
+
kybernesis delete <memoryId> -y # skip confirmation
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 8. Switch workspace
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
kybernesis switch
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Clears your current session and re-opens the browser to authenticate with a different workspace.
|
|
87
|
+
|
|
88
|
+
### 9. Log out
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
kybernesis logout
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Removes all stored credentials from your machine.
|
|
95
|
+
|
|
96
|
+
## All Commands
|
|
97
|
+
|
|
98
|
+
| Command | Description |
|
|
99
|
+
|---------|-------------|
|
|
100
|
+
| `kybernesis login` | Authenticate via browser (OAuth 2.1 + PKCE) |
|
|
101
|
+
| `kybernesis logout` | Clear stored credentials |
|
|
102
|
+
| `kybernesis status` | Show auth & workspace info |
|
|
103
|
+
| `kybernesis search <query>` | Search memories |
|
|
104
|
+
| `kybernesis save <content>` | Save a new memory (`--title`, `--tags`) |
|
|
105
|
+
| `kybernesis list` | List recent memories (`--limit`, `--offset`) |
|
|
106
|
+
| `kybernesis delete <id>` | Delete a memory (with confirmation, or `-y`) |
|
|
107
|
+
| `kybernesis stats` | Memory statistics |
|
|
108
|
+
| `kybernesis switch` | Re-authenticate to a different workspace |
|
|
109
|
+
| `kybernesis help` | Show help |
|
|
110
|
+
| `kybernesis version` | Show version |
|
|
111
|
+
|
|
112
|
+
## How Authentication Works
|
|
113
|
+
|
|
114
|
+
1. `kybernesis login` registers a dynamic OAuth client (one-time) and starts a local callback server on a random port
|
|
115
|
+
2. Your browser opens to `api.kybernesis.ai/authorize` where you sign in via Clerk and pick a workspace
|
|
116
|
+
3. After authorization, the browser redirects to `http://127.0.0.1:<port>/callback` with an auth code
|
|
117
|
+
4. The CLI exchanges the code for access + refresh tokens using PKCE (no client secret needed)
|
|
118
|
+
5. Tokens are stored at `~/.config/kybernesis/auth.json` with `chmod 600` (owner-only read/write)
|
|
119
|
+
6. On subsequent commands, tokens are auto-refreshed when they expire — no re-login needed unless the refresh token expires (30 days)
|
|
120
|
+
|
|
121
|
+
## Using with Claude Code
|
|
122
|
+
|
|
123
|
+
The CLI is designed to work seamlessly with Claude Code. Claude can call it via Bash to interact with your memory workspace without needing an MCP connection:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Claude can search your memories
|
|
127
|
+
kybernesis search "deployment checklist"
|
|
128
|
+
|
|
129
|
+
# Claude can save context for later
|
|
130
|
+
kybernesis save "The API rate limit is 100 req/min per org" --tags api,limits
|
|
131
|
+
|
|
132
|
+
# Claude can check workspace stats
|
|
133
|
+
kybernesis stats
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Requirements
|
|
137
|
+
|
|
138
|
+
- Node.js 18 or later
|
|
139
|
+
- A [Kybernesis](https://kybernesis.ai) account
|
|
140
|
+
|
|
141
|
+
## License
|
|
142
|
+
|
|
143
|
+
MIT
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export interface MemoryResult {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
summary: string;
|
|
5
|
+
tags: string[];
|
|
6
|
+
source: string;
|
|
7
|
+
layer: string;
|
|
8
|
+
priority: number;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
updatedAt: string;
|
|
11
|
+
}
|
|
12
|
+
export interface MemoryStats {
|
|
13
|
+
total: number;
|
|
14
|
+
byTier: {
|
|
15
|
+
hot: number;
|
|
16
|
+
warm: number;
|
|
17
|
+
archive: number;
|
|
18
|
+
};
|
|
19
|
+
bySource: {
|
|
20
|
+
upload: number;
|
|
21
|
+
chat: number;
|
|
22
|
+
connector: number;
|
|
23
|
+
};
|
|
24
|
+
recentlyAccessed: number;
|
|
25
|
+
}
|
|
26
|
+
export interface UserInfo {
|
|
27
|
+
valid: boolean;
|
|
28
|
+
userId: string;
|
|
29
|
+
orgId: string;
|
|
30
|
+
keyPrefix: string;
|
|
31
|
+
scopes: string[];
|
|
32
|
+
}
|
|
33
|
+
export declare function search(query: string, limit?: number): Promise<MemoryResult[]>;
|
|
34
|
+
export declare function save(content: string, opts?: {
|
|
35
|
+
title?: string;
|
|
36
|
+
tags?: string[];
|
|
37
|
+
source?: string;
|
|
38
|
+
}): Promise<MemoryResult>;
|
|
39
|
+
export declare function list(limit?: number, offset?: number): Promise<MemoryResult[]>;
|
|
40
|
+
export declare function deleteMemory(memoryId: string): Promise<{
|
|
41
|
+
deleted: boolean;
|
|
42
|
+
}>;
|
|
43
|
+
export declare function stats(): Promise<MemoryStats>;
|
|
44
|
+
export declare function validate(): Promise<UserInfo>;
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { getToken, loadTokens } from './auth.js';
|
|
2
|
+
const API_BASE = 'https://api.kybernesis.ai';
|
|
3
|
+
async function rpc(tool, input) {
|
|
4
|
+
const token = await getToken();
|
|
5
|
+
const tokens = await loadTokens();
|
|
6
|
+
// Auto-fill orgId from stored tokens if not provided
|
|
7
|
+
if (tokens?.orgId && !('orgId' in input)) {
|
|
8
|
+
input.orgId = tokens.orgId;
|
|
9
|
+
}
|
|
10
|
+
const response = await fetch(`${API_BASE}/rpc/${tool}`, {
|
|
11
|
+
method: 'POST',
|
|
12
|
+
headers: {
|
|
13
|
+
'Content-Type': 'application/json',
|
|
14
|
+
Authorization: `Bearer ${token}`,
|
|
15
|
+
},
|
|
16
|
+
body: JSON.stringify({ input }),
|
|
17
|
+
});
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
const text = await response.text();
|
|
20
|
+
throw new Error(`API error (${response.status}): ${text}`);
|
|
21
|
+
}
|
|
22
|
+
return response.json();
|
|
23
|
+
}
|
|
24
|
+
export async function search(query, limit = 10) {
|
|
25
|
+
const result = await rpc('searchMemory', {
|
|
26
|
+
query,
|
|
27
|
+
limit,
|
|
28
|
+
});
|
|
29
|
+
return result.results;
|
|
30
|
+
}
|
|
31
|
+
export async function save(content, opts) {
|
|
32
|
+
const result = await rpc('addMemory', {
|
|
33
|
+
content,
|
|
34
|
+
title: opts?.title,
|
|
35
|
+
tags: opts?.tags,
|
|
36
|
+
source: opts?.source ?? 'chat',
|
|
37
|
+
});
|
|
38
|
+
return result.memory;
|
|
39
|
+
}
|
|
40
|
+
export async function list(limit = 20, offset = 0) {
|
|
41
|
+
const result = await rpc('listMemories', {
|
|
42
|
+
limit,
|
|
43
|
+
offset,
|
|
44
|
+
});
|
|
45
|
+
return result.results;
|
|
46
|
+
}
|
|
47
|
+
export async function deleteMemory(memoryId) {
|
|
48
|
+
return rpc('deleteMemory', { memoryId });
|
|
49
|
+
}
|
|
50
|
+
export async function stats() {
|
|
51
|
+
const result = await rpc('stats', {});
|
|
52
|
+
return result.stats;
|
|
53
|
+
}
|
|
54
|
+
export async function validate() {
|
|
55
|
+
const token = await getToken();
|
|
56
|
+
const response = await fetch(`${API_BASE}/v1/user/validate`, {
|
|
57
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
58
|
+
});
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
const text = await response.text();
|
|
61
|
+
throw new Error(`API error (${response.status}): ${text}`);
|
|
62
|
+
}
|
|
63
|
+
return response.json();
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface AuthTokens {
|
|
2
|
+
clientId: string;
|
|
3
|
+
accessToken: string;
|
|
4
|
+
refreshToken: string;
|
|
5
|
+
expiresAt: number;
|
|
6
|
+
workspace: string;
|
|
7
|
+
orgId: string;
|
|
8
|
+
userId: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function loadTokens(): Promise<AuthTokens | null>;
|
|
11
|
+
export declare function clearTokens(): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Start the OAuth login flow:
|
|
14
|
+
* 1. Register client if needed
|
|
15
|
+
* 2. Start local callback server
|
|
16
|
+
* 3. Open browser to authorize
|
|
17
|
+
* 4. Wait for callback
|
|
18
|
+
* 5. Exchange code for tokens
|
|
19
|
+
* 6. Save tokens
|
|
20
|
+
*/
|
|
21
|
+
export declare function login(): Promise<AuthTokens>;
|
|
22
|
+
/**
|
|
23
|
+
* Get a valid access token, refreshing if needed.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getToken(): Promise<string>;
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { randomBytes, createHash } from 'node:crypto';
|
|
3
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { homedir } from 'node:os';
|
|
6
|
+
const API_BASE = 'https://api.kybernesis.ai';
|
|
7
|
+
const CONFIG_DIR = join(homedir(), '.config', 'kybernesis');
|
|
8
|
+
const AUTH_FILE = join(CONFIG_DIR, 'auth.json');
|
|
9
|
+
function base64url(buffer) {
|
|
10
|
+
return buffer.toString('base64url');
|
|
11
|
+
}
|
|
12
|
+
function generateCodeVerifier() {
|
|
13
|
+
return base64url(randomBytes(32));
|
|
14
|
+
}
|
|
15
|
+
function generateCodeChallenge(verifier) {
|
|
16
|
+
return base64url(createHash('sha256').update(verifier).digest());
|
|
17
|
+
}
|
|
18
|
+
function generateState() {
|
|
19
|
+
return base64url(randomBytes(16));
|
|
20
|
+
}
|
|
21
|
+
async function ensureConfigDir() {
|
|
22
|
+
await mkdir(CONFIG_DIR, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
export async function loadTokens() {
|
|
25
|
+
try {
|
|
26
|
+
const data = await readFile(AUTH_FILE, 'utf-8');
|
|
27
|
+
return JSON.parse(data);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function saveTokens(tokens) {
|
|
34
|
+
await ensureConfigDir();
|
|
35
|
+
await writeFile(AUTH_FILE, JSON.stringify(tokens, null, 2), { mode: 0o600 });
|
|
36
|
+
}
|
|
37
|
+
export async function clearTokens() {
|
|
38
|
+
const { unlink } = await import('node:fs/promises');
|
|
39
|
+
try {
|
|
40
|
+
await unlink(AUTH_FILE);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Already gone
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Register a dynamic OAuth client (DCR) if we don't have a stored clientId.
|
|
48
|
+
*/
|
|
49
|
+
async function registerClient() {
|
|
50
|
+
const response = await fetch(`${API_BASE}/register`, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: { 'Content-Type': 'application/json' },
|
|
53
|
+
body: JSON.stringify({
|
|
54
|
+
client_name: 'Kybernesis CLI',
|
|
55
|
+
redirect_uris: ['http://127.0.0.1/callback'],
|
|
56
|
+
grant_types: ['authorization_code', 'refresh_token'],
|
|
57
|
+
response_types: ['code'],
|
|
58
|
+
token_endpoint_auth_method: 'none',
|
|
59
|
+
}),
|
|
60
|
+
});
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
const text = await response.text();
|
|
63
|
+
throw new Error(`Client registration failed: ${response.status} ${text}`);
|
|
64
|
+
}
|
|
65
|
+
const data = (await response.json());
|
|
66
|
+
return data.client_id;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Start the OAuth login flow:
|
|
70
|
+
* 1. Register client if needed
|
|
71
|
+
* 2. Start local callback server
|
|
72
|
+
* 3. Open browser to authorize
|
|
73
|
+
* 4. Wait for callback
|
|
74
|
+
* 5. Exchange code for tokens
|
|
75
|
+
* 6. Save tokens
|
|
76
|
+
*/
|
|
77
|
+
export async function login() {
|
|
78
|
+
// Check for existing client_id
|
|
79
|
+
const existing = await loadTokens();
|
|
80
|
+
let clientId = existing?.clientId;
|
|
81
|
+
if (!clientId) {
|
|
82
|
+
process.stdout.write('Registering CLI client...\n');
|
|
83
|
+
clientId = await registerClient();
|
|
84
|
+
}
|
|
85
|
+
const codeVerifier = generateCodeVerifier();
|
|
86
|
+
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
87
|
+
const state = generateState();
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const server = createServer(async (req, res) => {
|
|
90
|
+
try {
|
|
91
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
92
|
+
if (url.pathname !== '/callback') {
|
|
93
|
+
res.writeHead(404);
|
|
94
|
+
res.end('Not found');
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const code = url.searchParams.get('code');
|
|
98
|
+
const returnedState = url.searchParams.get('state');
|
|
99
|
+
const error = url.searchParams.get('error');
|
|
100
|
+
if (error) {
|
|
101
|
+
const desc = url.searchParams.get('error_description') || error;
|
|
102
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
103
|
+
res.end(`<html><body><h2>Authorization Failed</h2><p>${desc}</p><p>You can close this tab.</p></body></html>`);
|
|
104
|
+
server.close();
|
|
105
|
+
reject(new Error(`Authorization failed: ${desc}`));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (!code || returnedState !== state) {
|
|
109
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
110
|
+
res.end('<html><body><h2>Invalid callback</h2><p>Missing code or state mismatch.</p></body></html>');
|
|
111
|
+
server.close();
|
|
112
|
+
reject(new Error('Invalid callback: missing code or state mismatch'));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Exchange code for tokens
|
|
116
|
+
const port = server.address().port;
|
|
117
|
+
const tokenResponse = await fetch(`${API_BASE}/token`, {
|
|
118
|
+
method: 'POST',
|
|
119
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
120
|
+
body: new URLSearchParams({
|
|
121
|
+
grant_type: 'authorization_code',
|
|
122
|
+
client_id: clientId,
|
|
123
|
+
code,
|
|
124
|
+
code_verifier: codeVerifier,
|
|
125
|
+
redirect_uri: `http://127.0.0.1:${port}/callback`,
|
|
126
|
+
}),
|
|
127
|
+
});
|
|
128
|
+
if (!tokenResponse.ok) {
|
|
129
|
+
const text = await tokenResponse.text();
|
|
130
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
131
|
+
res.end(`<html><body><h2>Token Exchange Failed</h2><p>${text}</p></body></html>`);
|
|
132
|
+
server.close();
|
|
133
|
+
reject(new Error(`Token exchange failed: ${tokenResponse.status} ${text}`));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const tokenData = (await tokenResponse.json());
|
|
137
|
+
// Validate the token to get user/org info
|
|
138
|
+
const validateResponse = await fetch(`${API_BASE}/v1/user/validate`, {
|
|
139
|
+
headers: { Authorization: `Bearer ${tokenData.access_token}` },
|
|
140
|
+
});
|
|
141
|
+
let userId = '';
|
|
142
|
+
let orgId = '';
|
|
143
|
+
let workspace = '';
|
|
144
|
+
if (validateResponse.ok) {
|
|
145
|
+
const userInfo = (await validateResponse.json());
|
|
146
|
+
userId = userInfo.userId;
|
|
147
|
+
orgId = userInfo.orgId;
|
|
148
|
+
workspace = userInfo.workspace || 'default';
|
|
149
|
+
}
|
|
150
|
+
const tokens = {
|
|
151
|
+
clientId: clientId,
|
|
152
|
+
accessToken: tokenData.access_token,
|
|
153
|
+
refreshToken: tokenData.refresh_token,
|
|
154
|
+
expiresAt: Date.now() + tokenData.expires_in * 1000,
|
|
155
|
+
workspace,
|
|
156
|
+
orgId,
|
|
157
|
+
userId,
|
|
158
|
+
};
|
|
159
|
+
await saveTokens(tokens);
|
|
160
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
161
|
+
res.end('<html><body style="font-family:system-ui;text-align:center;padding-top:80px">' +
|
|
162
|
+
'<h2>Authenticated!</h2>' +
|
|
163
|
+
'<p>You can close this tab and return to the terminal.</p>' +
|
|
164
|
+
'</body></html>');
|
|
165
|
+
server.close();
|
|
166
|
+
resolve(tokens);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
server.close();
|
|
170
|
+
reject(err);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
// Listen on a random available port
|
|
174
|
+
server.listen(0, '127.0.0.1', async () => {
|
|
175
|
+
const port = server.address().port;
|
|
176
|
+
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
177
|
+
const authorizeUrl = new URL(`${API_BASE}/authorize`);
|
|
178
|
+
authorizeUrl.searchParams.set('client_id', clientId);
|
|
179
|
+
authorizeUrl.searchParams.set('redirect_uri', redirectUri);
|
|
180
|
+
authorizeUrl.searchParams.set('code_challenge', codeChallenge);
|
|
181
|
+
authorizeUrl.searchParams.set('code_challenge_method', 'S256');
|
|
182
|
+
authorizeUrl.searchParams.set('state', state);
|
|
183
|
+
authorizeUrl.searchParams.set('response_type', 'code');
|
|
184
|
+
authorizeUrl.searchParams.set('scope', 'read write');
|
|
185
|
+
process.stdout.write(`\nOpening browser to authenticate...\n`);
|
|
186
|
+
process.stdout.write(`If the browser doesn't open, visit:\n${authorizeUrl.toString()}\n\n`);
|
|
187
|
+
process.stdout.write('Waiting for authorization...\n');
|
|
188
|
+
try {
|
|
189
|
+
const open = (await import('open')).default;
|
|
190
|
+
await open(authorizeUrl.toString());
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// If open fails, user has the URL printed above
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
// Timeout after 5 minutes
|
|
197
|
+
setTimeout(() => {
|
|
198
|
+
server.close();
|
|
199
|
+
reject(new Error('Login timed out after 5 minutes'));
|
|
200
|
+
}, 5 * 60 * 1000);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Get a valid access token, refreshing if needed.
|
|
205
|
+
*/
|
|
206
|
+
export async function getToken() {
|
|
207
|
+
const tokens = await loadTokens();
|
|
208
|
+
if (!tokens) {
|
|
209
|
+
throw new Error('Not logged in. Run `kybernesis login` first.');
|
|
210
|
+
}
|
|
211
|
+
// If token expires in less than 5 minutes, refresh
|
|
212
|
+
if (tokens.expiresAt < Date.now() + 5 * 60 * 1000) {
|
|
213
|
+
try {
|
|
214
|
+
const response = await fetch(`${API_BASE}/token`, {
|
|
215
|
+
method: 'POST',
|
|
216
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
217
|
+
body: new URLSearchParams({
|
|
218
|
+
grant_type: 'refresh_token',
|
|
219
|
+
client_id: tokens.clientId,
|
|
220
|
+
refresh_token: tokens.refreshToken,
|
|
221
|
+
}),
|
|
222
|
+
});
|
|
223
|
+
if (!response.ok) {
|
|
224
|
+
throw new Error(`Refresh failed: ${response.status}`);
|
|
225
|
+
}
|
|
226
|
+
const data = (await response.json());
|
|
227
|
+
tokens.accessToken = data.access_token;
|
|
228
|
+
tokens.refreshToken = data.refresh_token;
|
|
229
|
+
tokens.expiresAt = Date.now() + data.expires_in * 1000;
|
|
230
|
+
await saveTokens(tokens);
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
throw new Error('Session expired. Run `kybernesis login` to re-authenticate.');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return tokens.accessToken;
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA6C,MAAM,WAAW,CAAC;AACpF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,QAAQ,GAAG,2BAA2B,CAAC;AAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;AAC5D,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AAYhD,SAAS,SAAS,CAAC,MAAc;IAC/B,OAAO,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,oBAAoB;IAC3B,OAAO,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,qBAAqB,CAAC,QAAgB;IAC7C,OAAO,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,aAAa;IACpB,OAAO,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,KAAK,UAAU,eAAe;IAC5B,MAAM,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,MAAkB;IAC1C,MAAM,eAAe,EAAE,CAAC;IACxB,MAAM,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;IACpD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,eAAe;IACjB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,cAAc;IAC3B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,WAAW,EAAE;QACnD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,WAAW,EAAE,gBAAgB;YAC7B,aAAa,EAAE,CAAC,2BAA2B,CAAC;YAC5C,WAAW,EAAE,CAAC,oBAAoB,EAAE,eAAe,CAAC;YACpD,cAAc,EAAE,CAAC,MAAM,CAAC;YACxB,0BAA0B,EAAE,MAAM;SACnC,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA0B,CAAC;IAC9D,OAAO,IAAI,CAAC,SAAS,CAAC;AACxB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,+BAA+B;IAC/B,MAAM,QAAQ,GAAG,MAAM,UAAU,EAAE,CAAC;IACpC,IAAI,QAAQ,GAAG,QAAQ,EAAE,QAAQ,CAAC;IAElC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACpD,QAAQ,GAAG,MAAM,cAAc,EAAE,CAAC;IACpC,CAAC;IAED,MAAM,YAAY,GAAG,oBAAoB,EAAE,CAAC;IAC5C,MAAM,aAAa,GAAG,qBAAqB,CAAC,YAAY,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAE9B,OAAO,IAAI,OAAO,CAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACjD,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,GAAoB,EAAE,GAAmB,EAAE,EAAE;YAC9E,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAI,EAAE,UAAU,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;gBAE5D,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;oBACjC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;oBACnB,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;oBACrB,OAAO;gBACT,CAAC;gBAED,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC1C,MAAM,aAAa,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBACpD,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAE5C,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,KAAK,CAAC;oBAChE,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;oBACpD,GAAG,CAAC,GAAG,CAAC,+CAA+C,IAAI,kDAAkD,CAAC,CAAC;oBAC/G,MAAM,CAAC,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC,CAAC;oBACnD,OAAO;gBACT,CAAC;gBAED,IAAI,CAAC,IAAI,IAAI,aAAa,KAAK,KAAK,EAAE,CAAC;oBACrC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;oBACpD,GAAG,CAAC,GAAG,CAAC,2FAA2F,CAAC,CAAC;oBACrG,MAAM,CAAC,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC,CAAC;oBACtE,OAAO;gBACT,CAAC;gBAED,2BAA2B;gBAC3B,MAAM,IAAI,GAAI,MAAM,CAAC,OAAO,EAAuB,CAAC,IAAI,CAAC;gBACzD,MAAM,aAAa,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,QAAQ,EAAE;oBACrD,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;oBAChE,IAAI,EAAE,IAAI,eAAe,CAAC;wBACxB,UAAU,EAAE,oBAAoB;wBAChC,SAAS,EAAE,QAAS;wBACpB,IAAI;wBACJ,aAAa,EAAE,YAAY;wBAC3B,YAAY,EAAE,oBAAoB,IAAI,WAAW;qBAClD,CAAC;iBACH,CAAC,CAAC;gBAEH,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC;oBACtB,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,CAAC;oBACxC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;oBACpD,GAAG,CAAC,GAAG,CAAC,gDAAgD,IAAI,oBAAoB,CAAC,CAAC;oBAClF,MAAM,CAAC,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,IAAI,KAAK,CAAC,0BAA0B,aAAa,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;oBAC5E,OAAO;gBACT,CAAC;gBAED,MAAM,SAAS,GAAG,CAAC,MAAM,aAAa,CAAC,IAAI,EAAE,CAI5C,CAAC;gBAEF,0CAA0C;gBAC1C,MAAM,gBAAgB,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,mBAAmB,EAAE;oBACnE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,SAAS,CAAC,YAAY,EAAE,EAAE;iBAC/D,CAAC,CAAC;gBAEH,IAAI,MAAM,GAAG,EAAE,CAAC;gBAChB,IAAI,KAAK,GAAG,EAAE,CAAC;gBACf,IAAI,SAAS,GAAG,EAAE,CAAC;gBAEnB,IAAI,gBAAgB,CAAC,EAAE,EAAE,CAAC;oBACxB,MAAM,QAAQ,GAAG,CAAC,MAAM,gBAAgB,CAAC,IAAI,EAAE,CAI9C,CAAC;oBACF,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;oBACzB,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;oBACvB,SAAS,GAAG,QAAQ,CAAC,SAAS,IAAI,SAAS,CAAC;gBAC9C,CAAC;gBAED,MAAM,MAAM,GAAe;oBACzB,QAAQ,EAAE,QAAS;oBACnB,WAAW,EAAE,SAAS,CAAC,YAAY;oBACnC,YAAY,EAAE,SAAS,CAAC,aAAa;oBACrC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,UAAU,GAAG,IAAI;oBACnD,SAAS;oBACT,KAAK;oBACL,MAAM;iBACP,CAAC;gBAEF,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;gBAEzB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;gBACpD,GAAG,CAAC,GAAG,CACL,+EAA+E;oBAC/E,yBAAyB;oBACzB,2DAA2D;oBAC3D,gBAAgB,CACjB,CAAC;gBAEF,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,MAAM,CAAC,CAAC;YAClB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,oCAAoC;QACpC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,IAAI,EAAE;YACvC,MAAM,IAAI,GAAI,MAAM,CAAC,OAAO,EAAuB,CAAC,IAAI,CAAC;YACzD,MAAM,WAAW,GAAG,oBAAoB,IAAI,WAAW,CAAC;YAExD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,GAAG,QAAQ,YAAY,CAAC,CAAC;YACtD,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,QAAS,CAAC,CAAC;YACtD,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;YAC3D,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;YAC/D,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;YAC/D,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAC9C,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;YACvD,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YAErD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;YAC/D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wCAAwC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC5F,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;YAEvD,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;gBAC5C,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,CAAC;YACtC,CAAC;YAAC,MAAM,CAAC;gBACP,gDAAgD;YAClD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,0BAA0B;QAC1B,UAAU,CAAC,GAAG,EAAE;YACd,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC,CAAC;QACvD,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAElC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IAED,mDAAmD;IACnD,IAAI,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;QAClD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,QAAQ,EAAE;gBAChD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;gBAChE,IAAI,EAAE,IAAI,eAAe,CAAC;oBACxB,UAAU,EAAE,eAAe;oBAC3B,SAAS,EAAE,MAAM,CAAC,QAAQ;oBAC1B,aAAa,EAAE,MAAM,CAAC,YAAY;iBACnC,CAAC;aACH,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YACxD,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAIlC,CAAC;YAEF,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC;YACvC,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC;YACzC,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YAEvD,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,WAAW,CAAC;AAC5B,CAAC"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { loginCommand } from './commands/login.js';
|
|
3
|
+
import { logoutCommand } from './commands/logout.js';
|
|
4
|
+
import { statusCommand } from './commands/status.js';
|
|
5
|
+
import { searchCommand } from './commands/search.js';
|
|
6
|
+
import { saveCommand } from './commands/save.js';
|
|
7
|
+
import { listCommand } from './commands/list.js';
|
|
8
|
+
import { deleteCommand } from './commands/delete.js';
|
|
9
|
+
import { statsCommand } from './commands/stats.js';
|
|
10
|
+
import { switchCommand } from './commands/switch.js';
|
|
11
|
+
const VERSION = '0.1.0';
|
|
12
|
+
function printHelp() {
|
|
13
|
+
process.stdout.write(`
|
|
14
|
+
kybernesis v${VERSION} — CLI for your Kybernesis memory workspace
|
|
15
|
+
|
|
16
|
+
Usage: kybernesis <command> [options]
|
|
17
|
+
|
|
18
|
+
Commands:
|
|
19
|
+
login Authenticate via browser (OAuth)
|
|
20
|
+
logout Clear stored credentials
|
|
21
|
+
status Show current auth & workspace info
|
|
22
|
+
search <query> Search your memories
|
|
23
|
+
save <content> Save a new memory
|
|
24
|
+
--title <title> Memory title
|
|
25
|
+
--tags <t1,t2> Comma-separated tags
|
|
26
|
+
list List recent memories
|
|
27
|
+
--limit <n> Number of results (default: 20)
|
|
28
|
+
--offset <n> Skip first n results
|
|
29
|
+
delete <memoryId> Delete a memory (with confirmation)
|
|
30
|
+
stats Show memory statistics
|
|
31
|
+
switch Re-authenticate to a different workspace
|
|
32
|
+
help Show this help message
|
|
33
|
+
version Show version
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
kybernesis login
|
|
37
|
+
kybernesis search "react patterns"
|
|
38
|
+
kybernesis save "We use Zustand for state" --tags react,state --title "State management"
|
|
39
|
+
kybernesis list --limit 50
|
|
40
|
+
kybernesis stats
|
|
41
|
+
|
|
42
|
+
`);
|
|
43
|
+
}
|
|
44
|
+
function parseArgs(args) {
|
|
45
|
+
const flags = {};
|
|
46
|
+
const positional = [];
|
|
47
|
+
for (let i = 0; i < args.length; i++) {
|
|
48
|
+
const arg = args[i];
|
|
49
|
+
if (arg.startsWith('--') && i + 1 < args.length) {
|
|
50
|
+
flags[arg.slice(2)] = args[++i];
|
|
51
|
+
}
|
|
52
|
+
else if (arg === '-y') {
|
|
53
|
+
flags['yes'] = 'true';
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
positional.push(arg);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return { flags, positional };
|
|
60
|
+
}
|
|
61
|
+
async function main() {
|
|
62
|
+
const [command, ...rest] = process.argv.slice(2);
|
|
63
|
+
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
|
64
|
+
printHelp();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (command === 'version' || command === '--version' || command === '-v') {
|
|
68
|
+
process.stdout.write(`kybernesis v${VERSION}\n`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const { flags, positional } = parseArgs(rest);
|
|
72
|
+
try {
|
|
73
|
+
switch (command) {
|
|
74
|
+
case 'login':
|
|
75
|
+
await loginCommand();
|
|
76
|
+
break;
|
|
77
|
+
case 'logout':
|
|
78
|
+
await logoutCommand();
|
|
79
|
+
break;
|
|
80
|
+
case 'status':
|
|
81
|
+
await statusCommand();
|
|
82
|
+
break;
|
|
83
|
+
case 'search':
|
|
84
|
+
await searchCommand(positional.join(' '), flags);
|
|
85
|
+
break;
|
|
86
|
+
case 'save':
|
|
87
|
+
await saveCommand(positional.join(' '), flags);
|
|
88
|
+
break;
|
|
89
|
+
case 'list':
|
|
90
|
+
await listCommand(flags);
|
|
91
|
+
break;
|
|
92
|
+
case 'delete':
|
|
93
|
+
await deleteCommand(positional[0], flags);
|
|
94
|
+
break;
|
|
95
|
+
case 'stats':
|
|
96
|
+
await statsCommand();
|
|
97
|
+
break;
|
|
98
|
+
case 'switch':
|
|
99
|
+
await switchCommand();
|
|
100
|
+
break;
|
|
101
|
+
default:
|
|
102
|
+
process.stderr.write(`Unknown command: ${command}\n`);
|
|
103
|
+
process.stderr.write(`Run "kybernesis help" for usage.\n`);
|
|
104
|
+
process.exitCode = 1;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
109
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
110
|
+
process.exitCode = 1;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
main();
|
|
114
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function deleteCommand(memoryId: string | undefined, flags: Record<string, string>): Promise<void>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline';
|
|
2
|
+
import { deleteMemory } from '../api.js';
|
|
3
|
+
async function confirm(prompt) {
|
|
4
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
rl.question(`${prompt} (y/N) `, (answer) => {
|
|
7
|
+
rl.close();
|
|
8
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export async function deleteCommand(memoryId, flags) {
|
|
13
|
+
if (!memoryId) {
|
|
14
|
+
process.stderr.write('Usage: kybernesis delete <memoryId>\n');
|
|
15
|
+
process.exitCode = 1;
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (flags.yes !== 'true') {
|
|
19
|
+
const proceed = await confirm(`Delete memory ${memoryId}?`);
|
|
20
|
+
if (!proceed) {
|
|
21
|
+
process.stdout.write('Cancelled.\n');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const result = await deleteMemory(memoryId);
|
|
26
|
+
if (result.deleted) {
|
|
27
|
+
process.stdout.write(`Deleted memory ${memoryId}\n`);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
process.stderr.write(`Memory ${memoryId} not found or could not be deleted.\n`);
|
|
31
|
+
process.exitCode = 1;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.js","sourceRoot":"","sources":["../../src/commands/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,KAAK,UAAU,OAAO,CAAC,MAAc;IACnC,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAE7E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,EAAE,CAAC,QAAQ,CAAC,GAAG,MAAM,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE;YACzC,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,GAAG,IAAI,MAAM,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,CAAC;QAC1E,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAA4B,EAAE,KAA6B;IAC7F,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC9D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,IAAI,KAAK,CAAC,GAAG,KAAK,MAAM,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,iBAAiB,QAAQ,GAAG,CAAC,CAAC;QAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;YACrC,OAAO;QACT,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;IAE5C,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kBAAkB,QAAQ,IAAI,CAAC,CAAC;IACvD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,QAAQ,uCAAuC,CAAC,CAAC;QAChF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function listCommand(flags: Record<string, string>): Promise<void>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { list } from '../api.js';
|
|
2
|
+
export async function listCommand(flags) {
|
|
3
|
+
const limit = flags.limit ? parseInt(flags.limit, 10) : 20;
|
|
4
|
+
const offset = flags.offset ? parseInt(flags.offset, 10) : 0;
|
|
5
|
+
const results = await list(limit, offset);
|
|
6
|
+
if (results.length === 0) {
|
|
7
|
+
process.stdout.write('No memories found.\n');
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
process.stdout.write(`Showing ${results.length} memor${results.length === 1 ? 'y' : 'ies'}${offset > 0 ? ` (offset ${offset})` : ''}:\n\n`);
|
|
11
|
+
for (const memory of results) {
|
|
12
|
+
const tags = memory.tags?.length ? ` [${memory.tags.join(', ')}]` : '';
|
|
13
|
+
process.stdout.write(` ${memory.id} ${memory.title}${tags}\n`);
|
|
14
|
+
process.stdout.write(` ${memory.layer} | ${memory.source} | ${memory.createdAt}\n`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAA6B;IAC7D,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE7D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAE1C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC7C,OAAO;IACT,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,OAAO,CAAC,MAAM,SAAS,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAE5I,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,EAAE,KAAK,MAAM,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC;QACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,KAAK,MAAM,MAAM,CAAC,MAAM,MAAM,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;IACzF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loginCommand(): Promise<void>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { login } from '../auth.js';
|
|
2
|
+
export async function loginCommand() {
|
|
3
|
+
const tokens = await login();
|
|
4
|
+
process.stdout.write(`\nLogged in successfully!\n`);
|
|
5
|
+
process.stdout.write(` Workspace: ${tokens.workspace || tokens.orgId}\n`);
|
|
6
|
+
process.stdout.write(` User: ${tokens.userId}\n`);
|
|
7
|
+
process.stdout.write(` Org: ${tokens.orgId}\n\n`);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,MAAM,GAAG,MAAM,KAAK,EAAE,CAAC;IAE7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACpD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;IAC3E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IACxD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,MAAM,CAAC,KAAK,MAAM,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function logoutCommand(): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,WAAW,EAAE,CAAC;IACpB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function saveCommand(content: string, flags: Record<string, string>): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { save } from '../api.js';
|
|
2
|
+
export async function saveCommand(content, flags) {
|
|
3
|
+
if (!content.trim()) {
|
|
4
|
+
process.stderr.write('Usage: kybernesis save <content> [--title <title>] [--tags <t1,t2>]\n');
|
|
5
|
+
process.exitCode = 1;
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const tags = flags.tags ? flags.tags.split(',').map((t) => t.trim()) : undefined;
|
|
9
|
+
const memory = await save(content, {
|
|
10
|
+
title: flags.title,
|
|
11
|
+
tags,
|
|
12
|
+
});
|
|
13
|
+
process.stdout.write(`Memory saved: ${memory.id}\n`);
|
|
14
|
+
if (memory.title) {
|
|
15
|
+
process.stdout.write(` Title: ${memory.title}\n`);
|
|
16
|
+
}
|
|
17
|
+
if (memory.tags?.length) {
|
|
18
|
+
process.stdout.write(` Tags: ${memory.tags.join(', ')}\n`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=save.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save.js","sourceRoot":"","sources":["../../src/commands/save.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe,EAAE,KAA6B;IAC9E,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QACpB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uEAAuE,CAAC,CAAC;QAC9F,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAEjF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE;QACjC,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI;KACL,CAAC,CAAC;IAEH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IACrD,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;IACrD,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function searchCommand(query: string, flags: Record<string, string>): Promise<void>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { search } from '../api.js';
|
|
2
|
+
export async function searchCommand(query, flags) {
|
|
3
|
+
if (!query.trim()) {
|
|
4
|
+
process.stderr.write('Usage: kybernesis search <query>\n');
|
|
5
|
+
process.exitCode = 1;
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const limit = flags.limit ? parseInt(flags.limit, 10) : 10;
|
|
9
|
+
const results = await search(query, limit);
|
|
10
|
+
if (results.length === 0) {
|
|
11
|
+
process.stdout.write('No memories found.\n');
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
process.stdout.write(`Found ${results.length} memor${results.length === 1 ? 'y' : 'ies'}:\n\n`);
|
|
15
|
+
for (const memory of results) {
|
|
16
|
+
process.stdout.write(` ${memory.id}\n`);
|
|
17
|
+
process.stdout.write(` ${memory.title}\n`);
|
|
18
|
+
if (memory.summary) {
|
|
19
|
+
process.stdout.write(` ${memory.summary.slice(0, 120)}\n`);
|
|
20
|
+
}
|
|
21
|
+
if (memory.tags?.length) {
|
|
22
|
+
process.stdout.write(` tags: ${memory.tags.join(', ')}\n`);
|
|
23
|
+
}
|
|
24
|
+
process.stdout.write(` ${memory.layer} | ${memory.source} | ${memory.createdAt}\n`);
|
|
25
|
+
process.stdout.write('\n');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/commands/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAa,EAAE,KAA6B;IAC9E,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAC3D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAE3C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC7C,OAAO;IACT,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,OAAO,CAAC,MAAM,SAAS,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC;IAEhG,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;QACzC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;QAC5C,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9D,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;YACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,KAAK,MAAM,MAAM,CAAC,MAAM,MAAM,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;QACrF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function statsCommand(): Promise<void>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { stats } from '../api.js';
|
|
2
|
+
export async function statsCommand() {
|
|
3
|
+
const s = await stats();
|
|
4
|
+
process.stdout.write(`Memory Statistics\n`);
|
|
5
|
+
process.stdout.write(`─────────────────\n`);
|
|
6
|
+
process.stdout.write(`Total memories: ${s.total}\n`);
|
|
7
|
+
process.stdout.write(`Recently accessed: ${s.recentlyAccessed}\n\n`);
|
|
8
|
+
process.stdout.write(`By Tier:\n`);
|
|
9
|
+
process.stdout.write(` Hot: ${s.byTier.hot}\n`);
|
|
10
|
+
process.stdout.write(` Warm: ${s.byTier.warm}\n`);
|
|
11
|
+
process.stdout.write(` Archive: ${s.byTier.archive}\n\n`);
|
|
12
|
+
process.stdout.write(`By Source:\n`);
|
|
13
|
+
process.stdout.write(` Upload: ${s.bySource.upload}\n`);
|
|
14
|
+
process.stdout.write(` Chat: ${s.bySource.chat}\n`);
|
|
15
|
+
process.stdout.write(` Connector: ${s.bySource.connector}\n`);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/commands/stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,CAAC,GAAG,MAAM,KAAK,EAAE,CAAC;IAExB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACzD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,gBAAgB,MAAM,CAAC,CAAC;IACtE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC;IACtD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC;IAC3D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,QAAQ,CAAC,MAAM,IAAI,CAAC,CAAC;IAC5D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,CAAC;IAC1D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,QAAQ,CAAC,SAAS,IAAI,CAAC,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function statusCommand(): Promise<void>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { loadTokens } from '../auth.js';
|
|
2
|
+
import { validate } from '../api.js';
|
|
3
|
+
export async function statusCommand() {
|
|
4
|
+
const tokens = await loadTokens();
|
|
5
|
+
if (!tokens) {
|
|
6
|
+
process.stdout.write('Not logged in. Run `kybernesis login` to authenticate.\n');
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
process.stdout.write(`Workspace: ${tokens.workspace || tokens.orgId}\n`);
|
|
10
|
+
process.stdout.write(`User: ${tokens.userId}\n`);
|
|
11
|
+
process.stdout.write(`Org ID: ${tokens.orgId}\n`);
|
|
12
|
+
const expiresIn = tokens.expiresAt - Date.now();
|
|
13
|
+
if (expiresIn > 0) {
|
|
14
|
+
const minutes = Math.floor(expiresIn / 60000);
|
|
15
|
+
process.stdout.write(`Token: valid (expires in ${minutes}m)\n`);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
process.stdout.write(`Token: expired (will auto-refresh on next command)\n`);
|
|
19
|
+
}
|
|
20
|
+
// Verify token is actually valid server-side
|
|
21
|
+
try {
|
|
22
|
+
const info = await validate();
|
|
23
|
+
process.stdout.write(`Server: connected (scopes: ${info.scopes.join(', ')})\n`);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
process.stdout.write(`Server: unable to validate token\n`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAElC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0DAA0D,CAAC,CAAC;QACjF,OAAO;IACT,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;IAC1E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IACvD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;IAEtD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAChD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QAClB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC;QAC9C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iCAAiC,OAAO,MAAM,CAAC,CAAC;IACvE,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;IACpF,CAAC;IAED,6CAA6C;IAC7C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,QAAQ,EAAE,CAAC;QAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;IACjE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function switchCommand(): Promise<void>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { clearTokens } from '../auth.js';
|
|
2
|
+
import { loginCommand } from './login.js';
|
|
3
|
+
export async function switchCommand() {
|
|
4
|
+
process.stdout.write('Switching workspace — clearing current session...\n');
|
|
5
|
+
await clearTokens();
|
|
6
|
+
await loginCommand();
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=switch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"switch.js","sourceRoot":"","sources":["../../src/commands/switch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;IAC5E,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,YAAY,EAAE,CAAC;AACvB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kybernesis",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for Kybernesis — interact with your memory workspace from the terminal",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"kybernesis": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsc --watch",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"kybernesis",
|
|
19
|
+
"memory",
|
|
20
|
+
"ai",
|
|
21
|
+
"cli"
|
|
22
|
+
],
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/KybernesisAI/kybernesis-cli.git"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"open": "^10.1.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^20.11.0",
|
|
36
|
+
"typescript": "^5.4.0"
|
|
37
|
+
}
|
|
38
|
+
}
|