cohvu 0.6.2 → 1.0.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/dist/api.d.ts +128 -0
- package/dist/api.js +221 -0
- package/dist/api.js.map +1 -0
- package/dist/auth.js +2 -2
- package/dist/auth.js.map +1 -1
- package/dist/index.js +83 -825
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +1 -0
- package/dist/proxy.js +151 -0
- package/dist/proxy.js.map +1 -0
- package/dist/setup.js +1 -1
- package/dist/setup.js.map +1 -1
- package/dist/tui/ansi.d.ts +41 -0
- package/dist/tui/ansi.js +120 -0
- package/dist/tui/ansi.js.map +1 -0
- package/dist/tui/dashboard.d.ts +1 -0
- package/dist/tui/dashboard.js +1051 -0
- package/dist/tui/dashboard.js.map +1 -0
- package/dist/tui/keys.d.ts +31 -0
- package/dist/tui/keys.js +53 -0
- package/dist/tui/keys.js.map +1 -0
- package/dist/tui/platform-detect.d.ts +2 -0
- package/dist/tui/platform-detect.js +102 -0
- package/dist/tui/platform-detect.js.map +1 -0
- package/dist/tui/render.d.ts +5 -0
- package/dist/tui/render.js +139 -0
- package/dist/tui/render.js.map +1 -0
- package/dist/tui/state.d.ts +215 -0
- package/dist/tui/state.js +206 -0
- package/dist/tui/state.js.map +1 -0
- package/dist/tui/views/billing.d.ts +3 -0
- package/dist/tui/views/billing.js +127 -0
- package/dist/tui/views/billing.js.map +1 -0
- package/dist/tui/views/knowledge.d.ts +3 -0
- package/dist/tui/views/knowledge.js +195 -0
- package/dist/tui/views/knowledge.js.map +1 -0
- package/dist/tui/views/modals.d.ts +3 -0
- package/dist/tui/views/modals.js +192 -0
- package/dist/tui/views/modals.js.map +1 -0
- package/dist/tui/views/project.d.ts +3 -0
- package/dist/tui/views/project.js +76 -0
- package/dist/tui/views/project.js.map +1 -0
- package/dist/tui/views/team.d.ts +3 -0
- package/dist/tui/views/team.js +100 -0
- package/dist/tui/views/team.js.map +1 -0
- package/dist/tui/views/you.d.ts +3 -0
- package/dist/tui/views/you.js +74 -0
- package/dist/tui/views/you.js.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,123 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
-
// Cohvu CLI —
|
|
3
|
+
// Cohvu CLI — one command.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
// npx cohvu login — sign in with Google, auto-configure all platforms
|
|
7
|
-
// npx cohvu logout — remove saved credentials
|
|
8
|
-
// npx cohvu setup — regenerate platform configs and instruction files
|
|
9
|
-
// npx cohvu status — show account, project, and billing info
|
|
10
|
-
// npx cohvu create — create a project (free trial or checkout)
|
|
11
|
-
// npx cohvu rename — rename a project
|
|
12
|
-
// npx cohvu memories — browse or search project memories
|
|
13
|
-
// npx cohvu forget — search and remove specific memories
|
|
14
|
-
// npx cohvu billing — check billing status or manage subscription
|
|
15
|
-
// npx cohvu invite — show invite links, regenerate agent link
|
|
16
|
-
// npx cohvu members — manage project members
|
|
17
|
-
// npx cohvu delete — delete a root project (interactive)
|
|
18
|
-
// npx cohvu clear — clear all knowledge from a project (interactive)
|
|
19
|
-
// npx cohvu switch — switch active project (when you have multiple)
|
|
20
|
-
// npx cohvu — start MCP proxy (used by agent MCP config)
|
|
5
|
+
// npx cohvu — TUI dashboard (interactive) or MCP proxy (piped by agent)
|
|
21
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
23
|
-
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
24
|
-
const auth_1 = require("./auth");
|
|
25
|
-
const setup_1 = require("./setup");
|
|
26
7
|
const fs_1 = require("fs");
|
|
27
8
|
const path_1 = require("path");
|
|
28
9
|
const os_1 = require("os");
|
|
29
10
|
const readline_1 = require("readline");
|
|
30
11
|
const child_process_1 = require("child_process");
|
|
12
|
+
const auth_1 = require("./auth");
|
|
13
|
+
const setup_1 = require("./setup");
|
|
14
|
+
const proxy_1 = require("./proxy");
|
|
15
|
+
const api_1 = require("./api");
|
|
16
|
+
const dashboard_1 = require("./tui/dashboard");
|
|
17
|
+
const ansi_1 = require("./tui/ansi");
|
|
31
18
|
const DEFAULT_BASE_URL = "https://api.cohvu.com";
|
|
32
19
|
const TOKENS_FILE = (0, path_1.join)((0, os_1.homedir)(), ".cohvu", "tokens.json");
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
process.stderr.write(`Login failed: ${error}\n`);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
else if (command === "logout") {
|
|
41
|
-
logout();
|
|
42
|
-
}
|
|
43
|
-
else if (command === "setup") {
|
|
44
|
-
setup().catch((error) => {
|
|
45
|
-
process.stderr.write(`Setup failed: ${error}\n`);
|
|
46
|
-
process.exit(1);
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
else if (command === "create") {
|
|
50
|
-
createProject().catch((error) => {
|
|
51
|
-
process.stderr.write(`Create failed: ${error}\n`);
|
|
52
|
-
process.exit(1);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
else if (command === "billing") {
|
|
56
|
-
billing().catch((error) => {
|
|
57
|
-
process.stderr.write(`Billing failed: ${error}\n`);
|
|
58
|
-
process.exit(1);
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
else if (command === "invite") {
|
|
62
|
-
invite().catch((error) => {
|
|
63
|
-
process.stderr.write(`Invite failed: ${error}\n`);
|
|
64
|
-
process.exit(1);
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
else if (command === "members") {
|
|
68
|
-
members().catch((error) => {
|
|
69
|
-
process.stderr.write(`Members failed: ${error}\n`);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
else if (command === "delete") {
|
|
74
|
-
deleteProject().catch((error) => {
|
|
75
|
-
process.stderr.write(`Delete failed: ${error}\n`);
|
|
76
|
-
process.exit(1);
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
else if (command === "clear") {
|
|
80
|
-
clearProject().catch((error) => {
|
|
81
|
-
process.stderr.write(`Clear failed: ${error}\n`);
|
|
82
|
-
process.exit(1);
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
else if (command === "switch") {
|
|
86
|
-
switchProject().catch((error) => {
|
|
87
|
-
process.stderr.write(`Switch failed: ${error}\n`);
|
|
88
|
-
process.exit(1);
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
else if (command === "memories") {
|
|
92
|
-
browseMemories().catch((error) => {
|
|
93
|
-
process.stderr.write(`Memories failed: ${error}\n`);
|
|
94
|
-
process.exit(1);
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
else if (command === "forget") {
|
|
98
|
-
forgetMemory().catch((error) => {
|
|
99
|
-
process.stderr.write(`Forget failed: ${error}\n`);
|
|
100
|
-
process.exit(1);
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
else if (command === "status") {
|
|
104
|
-
status().catch((error) => {
|
|
105
|
-
process.stderr.write(`Status failed: ${error}\n`);
|
|
106
|
-
process.exit(1);
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
else if (command === "rename") {
|
|
110
|
-
renameProject().catch((error) => {
|
|
111
|
-
process.stderr.write(`Rename failed: ${error}\n`);
|
|
20
|
+
if (process.stdin.isTTY) {
|
|
21
|
+
enterDashboard().catch((error) => {
|
|
22
|
+
process.stderr.write(`Failed: ${error}\n`);
|
|
112
23
|
process.exit(1);
|
|
113
24
|
});
|
|
114
25
|
}
|
|
115
26
|
else {
|
|
116
|
-
proxy().catch((error) => {
|
|
27
|
+
(0, proxy_1.proxy)().catch((error) => {
|
|
117
28
|
process.stderr.write(`Cohvu CLI failed: ${error}\n`);
|
|
118
29
|
process.exit(1);
|
|
119
30
|
});
|
|
120
31
|
}
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Helpers
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
121
35
|
function prompt(question) {
|
|
122
36
|
const rl = (0, readline_1.createInterface)({ input: process.stdin, output: process.stdout });
|
|
123
37
|
return new Promise((resolve) => {
|
|
@@ -133,746 +47,90 @@ function openBrowser(url) {
|
|
|
133
47
|
: process.platform === "win32"
|
|
134
48
|
? "start"
|
|
135
49
|
: "xdg-open";
|
|
136
|
-
(0, child_process_1.
|
|
137
|
-
}
|
|
138
|
-
async function fetchMe(baseUrl, accessToken) {
|
|
139
|
-
const res = await fetch(`${baseUrl}/v1/auth/me`, {
|
|
140
|
-
headers: { Authorization: `Bearer ${accessToken}` },
|
|
141
|
-
});
|
|
142
|
-
if (!res.ok)
|
|
143
|
-
throw new Error(`Failed to fetch account info: ${res.status}`);
|
|
144
|
-
return (await res.json());
|
|
145
|
-
}
|
|
146
|
-
async function resolveProject(baseUrl, accessToken, label) {
|
|
147
|
-
const me = await fetchMe(baseUrl, accessToken);
|
|
148
|
-
const rootProjects = me.projects.filter((s) => s.is_root);
|
|
149
|
-
if (rootProjects.length === 0) {
|
|
150
|
-
console.log("You have no projects. Run `npx cohvu create` first.");
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
if (rootProjects.length === 1)
|
|
154
|
-
return rootProjects[0];
|
|
155
|
-
console.log(`\nYour projects:\n`);
|
|
156
|
-
rootProjects.forEach((s, i) => {
|
|
157
|
-
console.log(` ${i + 1}. ${s.name} (${s.slug})`);
|
|
158
|
-
});
|
|
159
|
-
const choice = await prompt(`\n${label} (1-${rootProjects.length}): `);
|
|
160
|
-
const index = parseInt(choice, 10) - 1;
|
|
161
|
-
if (isNaN(index) || index < 0 || index >= rootProjects.length) {
|
|
162
|
-
console.log("Canceled.");
|
|
163
|
-
return null;
|
|
164
|
-
}
|
|
165
|
-
return rootProjects[index];
|
|
50
|
+
(0, child_process_1.execFile)(cmd, [url], () => { });
|
|
166
51
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// ---------------------------------------------------------------------------
|
|
170
|
-
async function login() {
|
|
171
|
-
const baseUrl = process.env.COHVU_API_URL ?? DEFAULT_BASE_URL;
|
|
172
|
-
const isFirstLogin = !(0, fs_1.existsSync)(TOKENS_FILE);
|
|
173
|
-
await (0, auth_1.deviceAuthFlow)(baseUrl);
|
|
174
|
-
const results = await (0, setup_1.runSetup)();
|
|
175
|
-
(0, setup_1.printSetupResults)(results);
|
|
176
|
-
// Fetch user info and show project status
|
|
177
|
-
try {
|
|
178
|
-
const auth = await (0, auth_1.getAuthToken)(baseUrl);
|
|
179
|
-
const me = await fetchMe(baseUrl, auth.accessToken);
|
|
180
|
-
const rootProjects = me.projects.filter((s) => s.is_root);
|
|
181
|
-
// Find active project
|
|
182
|
-
const active = me.user.active_project_id
|
|
183
|
-
? rootProjects.find((p) => p.project_id === me.user.active_project_id)
|
|
184
|
-
: rootProjects[0] ?? null;
|
|
185
|
-
if (active) {
|
|
186
|
-
const status = projectStatusLabel(active);
|
|
187
|
-
console.log(` ✓ Project: ${active.name}${status ? ` (${status})` : ""}`);
|
|
188
|
-
}
|
|
189
|
-
console.log(`\nYou're ready. Open any agent and start working.\n`);
|
|
190
|
-
if (rootProjects.length === 0) {
|
|
191
|
-
const answer = await prompt("Create your first project? (Y/n): ");
|
|
192
|
-
if (answer.toLowerCase() !== "n") {
|
|
193
|
-
await doCreateProject(baseUrl, auth.accessToken);
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
console.log("Run `npx cohvu create` when you're ready.\n");
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
if (isFirstLogin) {
|
|
200
|
-
printCliReference();
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
catch {
|
|
204
|
-
// Non-fatal — setup already succeeded
|
|
205
|
-
console.log(`\nYou're ready. Open any agent and start working.\n`);
|
|
206
|
-
if (isFirstLogin) {
|
|
207
|
-
printCliReference();
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
function projectStatusLabel(project) {
|
|
212
|
-
if (project.trial_ends_at) {
|
|
213
|
-
const ends = new Date(project.trial_ends_at);
|
|
214
|
-
const now = new Date();
|
|
215
|
-
const daysLeft = Math.ceil((ends.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
|
|
216
|
-
if (daysLeft > 0)
|
|
217
|
-
return `trial ends in ${daysLeft} day${daysLeft === 1 ? "" : "s"}`;
|
|
218
|
-
return "trial ended";
|
|
219
|
-
}
|
|
220
|
-
if (project.billing_status === "active")
|
|
221
|
-
return "active";
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
function printCliReference() {
|
|
225
|
-
console.log(" npx cohvu status show account and project info");
|
|
226
|
-
console.log(" npx cohvu create create a new project");
|
|
227
|
-
console.log(" npx cohvu rename rename a project");
|
|
228
|
-
console.log(" npx cohvu memories browse or search project memories");
|
|
229
|
-
console.log(" npx cohvu forget search and remove specific memories");
|
|
230
|
-
console.log(" npx cohvu invite invite teammates and get your agent link");
|
|
231
|
-
console.log(" npx cohvu members manage your team");
|
|
232
|
-
console.log(" npx cohvu billing manage your subscription");
|
|
233
|
-
console.log(" npx cohvu switch switch between projects");
|
|
234
|
-
console.log(" npx cohvu clear clear all memories from a project");
|
|
235
|
-
console.log(" npx cohvu delete delete a project");
|
|
236
|
-
console.log();
|
|
237
|
-
}
|
|
238
|
-
async function setup() {
|
|
239
|
-
const results = await (0, setup_1.runSetup)();
|
|
240
|
-
(0, setup_1.printSetupResults)(results);
|
|
241
|
-
console.log("You're ready. Open any agent and start working.\n");
|
|
242
|
-
}
|
|
243
|
-
function logout() {
|
|
244
|
-
if ((0, fs_1.existsSync)(TOKENS_FILE)) {
|
|
245
|
-
(0, fs_1.unlinkSync)(TOKENS_FILE);
|
|
246
|
-
console.log("Logged out. Credentials removed.");
|
|
247
|
-
}
|
|
248
|
-
else {
|
|
249
|
-
console.log("No credentials found.");
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
async function doCreateProject(baseUrl, accessToken) {
|
|
253
|
-
const name = await prompt("Project name (e.g. My Project): ");
|
|
254
|
-
if (!name) {
|
|
255
|
-
console.log("Canceled.");
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
// Derive slug from name
|
|
259
|
-
const slug = name
|
|
52
|
+
function deriveSlug(name) {
|
|
53
|
+
return name
|
|
260
54
|
.toLowerCase()
|
|
261
55
|
.replace(/[^a-z0-9]+/g, "-")
|
|
262
56
|
.replace(/^-+|-+$/g, "");
|
|
263
|
-
// Create project via REST API
|
|
264
|
-
const createRes = await fetch(`${baseUrl}/v1/projects`, {
|
|
265
|
-
method: "POST",
|
|
266
|
-
headers: {
|
|
267
|
-
Authorization: `Bearer ${accessToken}`,
|
|
268
|
-
"Content-Type": "application/json",
|
|
269
|
-
},
|
|
270
|
-
body: JSON.stringify({ name, slug }),
|
|
271
|
-
});
|
|
272
|
-
if (!createRes.ok) {
|
|
273
|
-
const body = await createRes.text();
|
|
274
|
-
throw new Error(`Failed to create project: ${createRes.status} ${body}`);
|
|
275
|
-
}
|
|
276
|
-
const project = (await createRes.json());
|
|
277
|
-
console.log(`\nProject "${project.name}" created.`);
|
|
278
|
-
// Trial started — no checkout needed
|
|
279
|
-
if (project.trial_ends_at) {
|
|
280
|
-
const trialEnd = new Date(project.trial_ends_at);
|
|
281
|
-
console.log(`Free trial active until ${trialEnd.toLocaleDateString()}.`);
|
|
282
|
-
console.log("Your agents will connect automatically.\n");
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
// No trial (already used) — go to checkout
|
|
286
|
-
const checkoutRes = await fetch(`${baseUrl}/v1/projects/${project.id}/checkout`, {
|
|
287
|
-
method: "POST",
|
|
288
|
-
headers: {
|
|
289
|
-
Authorization: `Bearer ${accessToken}`,
|
|
290
|
-
"Content-Type": "application/json",
|
|
291
|
-
},
|
|
292
|
-
});
|
|
293
|
-
if (checkoutRes.ok) {
|
|
294
|
-
const checkout = (await checkoutRes.json());
|
|
295
|
-
if (checkout.checkout_url) {
|
|
296
|
-
console.log("Opening checkout...\n");
|
|
297
|
-
openBrowser(checkout.checkout_url);
|
|
298
|
-
console.log("Complete payment in your browser to activate the project.");
|
|
299
|
-
console.log("After payment, your agents will connect automatically.\n");
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
console.log("Your agents will connect automatically.\n");
|
|
304
|
-
}
|
|
305
|
-
async function createProject() {
|
|
306
|
-
const baseUrl = process.env.COHVU_API_URL ?? DEFAULT_BASE_URL;
|
|
307
|
-
const auth = await (0, auth_1.getAuthToken)(baseUrl);
|
|
308
|
-
await doCreateProject(baseUrl, auth.accessToken);
|
|
309
|
-
}
|
|
310
|
-
async function billing() {
|
|
311
|
-
const baseUrl = process.env.COHVU_API_URL ?? DEFAULT_BASE_URL;
|
|
312
|
-
const auth = await (0, auth_1.getAuthToken)(baseUrl);
|
|
313
|
-
const project = await resolveProject(baseUrl, auth.accessToken, "Which project?");
|
|
314
|
-
if (!project)
|
|
315
|
-
return;
|
|
316
|
-
// Fetch billing status
|
|
317
|
-
const billingRes = await fetch(`${baseUrl}/v1/projects/${project.project_id}/billing`, {
|
|
318
|
-
headers: { Authorization: `Bearer ${auth.accessToken}` },
|
|
319
|
-
});
|
|
320
|
-
if (!billingRes.ok) {
|
|
321
|
-
if (billingRes.status === 404) {
|
|
322
|
-
console.log("\nNo billing configured for this project.");
|
|
323
|
-
console.log("Run `npx cohvu create` to set up a new paid project.\n");
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
throw new Error(`Failed to fetch billing: ${billingRes.status}`);
|
|
327
|
-
}
|
|
328
|
-
const data = (await billingRes.json());
|
|
329
|
-
if (!data.subscription) {
|
|
330
|
-
console.log(`\n${project.name}: No active subscription.`);
|
|
331
|
-
console.log("Run `npx cohvu create` to set up billing.\n");
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
const sub = data.subscription;
|
|
335
|
-
console.log(`\n${project.name}`);
|
|
336
|
-
console.log(` Status: ${sub.status}`);
|
|
337
|
-
console.log(` Seats: ${sub.seat_count}`);
|
|
338
|
-
console.log(` Renews: ${new Date(sub.current_period_end).toLocaleDateString()}`);
|
|
339
|
-
if (sub.cancel_at_period_end) {
|
|
340
|
-
console.log(` Cancels at period end`);
|
|
341
|
-
}
|
|
342
|
-
// Offer to open portal
|
|
343
|
-
const answer = await prompt("\nOpen billing portal? (y/N): ");
|
|
344
|
-
if (answer.toLowerCase() === "y") {
|
|
345
|
-
const portalRes = await fetch(`${baseUrl}/v1/projects/${project.project_id}/billing/portal`, {
|
|
346
|
-
method: "POST",
|
|
347
|
-
headers: {
|
|
348
|
-
Authorization: `Bearer ${auth.accessToken}`,
|
|
349
|
-
"Content-Type": "application/json",
|
|
350
|
-
},
|
|
351
|
-
body: JSON.stringify({ return_url: "https://cohvu.com" }),
|
|
352
|
-
});
|
|
353
|
-
if (portalRes.ok) {
|
|
354
|
-
const portal = (await portalRes.json());
|
|
355
|
-
openBrowser(portal.url);
|
|
356
|
-
console.log("Opened billing portal in your browser.\n");
|
|
357
|
-
}
|
|
358
|
-
else {
|
|
359
|
-
console.log("Could not open billing portal.\n");
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
async function invite() {
|
|
364
|
-
const baseUrl = process.env.COHVU_API_URL ?? DEFAULT_BASE_URL;
|
|
365
|
-
const auth = await (0, auth_1.getAuthToken)(baseUrl);
|
|
366
|
-
const project = await resolveProject(baseUrl, auth.accessToken, "Which project?");
|
|
367
|
-
if (!project)
|
|
368
|
-
return;
|
|
369
|
-
console.log(`\n${project.name}\n`);
|
|
370
|
-
if (project.people_link) {
|
|
371
|
-
console.log(` Invite teammates: ${project.people_link}`);
|
|
372
|
-
}
|
|
373
|
-
if (project.agent_link) {
|
|
374
|
-
console.log(` Agent link: ${project.agent_link}`);
|
|
375
|
-
}
|
|
376
|
-
if (!project.people_link && !project.agent_link) {
|
|
377
|
-
console.log(" No invite links available.");
|
|
378
|
-
}
|
|
379
|
-
console.log();
|
|
380
|
-
console.log(" 1. Regenerate invite link");
|
|
381
|
-
console.log(" 2. Regenerate agent link");
|
|
382
|
-
console.log(" 3. Done");
|
|
383
|
-
const choice = await prompt("\n Choose (1-3): ");
|
|
384
|
-
if (choice === "1") {
|
|
385
|
-
const regenRes = await fetch(`${baseUrl}/v1/projects/${project.project_id}/regenerate-people-link`, {
|
|
386
|
-
method: "POST",
|
|
387
|
-
headers: { Authorization: `Bearer ${auth.accessToken}` },
|
|
388
|
-
});
|
|
389
|
-
if (regenRes.ok) {
|
|
390
|
-
const data = (await regenRes.json());
|
|
391
|
-
console.log(`\n New invite link: ${data.people_link}`);
|
|
392
|
-
console.log(" The old link no longer works.\n");
|
|
393
|
-
}
|
|
394
|
-
else {
|
|
395
|
-
const body = await regenRes.text();
|
|
396
|
-
console.log(` Failed to regenerate invite link. ${body}\n`);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
else if (choice === "2") {
|
|
400
|
-
const regenRes = await fetch(`${baseUrl}/v1/projects/${project.project_id}/members/regenerate-link`, {
|
|
401
|
-
method: "POST",
|
|
402
|
-
headers: { Authorization: `Bearer ${auth.accessToken}` },
|
|
403
|
-
});
|
|
404
|
-
if (regenRes.ok) {
|
|
405
|
-
const data = (await regenRes.json());
|
|
406
|
-
console.log(`\n New agent link: ${data.agent_link}`);
|
|
407
|
-
console.log(" The old link no longer works.\n");
|
|
408
|
-
}
|
|
409
|
-
else {
|
|
410
|
-
console.log(" Failed to regenerate agent link.\n");
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
async function members() {
|
|
415
|
-
const baseUrl = process.env.COHVU_API_URL ?? DEFAULT_BASE_URL;
|
|
416
|
-
const auth = await (0, auth_1.getAuthToken)(baseUrl);
|
|
417
|
-
const project = await resolveProject(baseUrl, auth.accessToken, "Which project?");
|
|
418
|
-
if (!project)
|
|
419
|
-
return;
|
|
420
|
-
const membersRes = await fetch(`${baseUrl}/v1/projects/${project.project_id}/members`, {
|
|
421
|
-
headers: { Authorization: `Bearer ${auth.accessToken}` },
|
|
422
|
-
});
|
|
423
|
-
if (!membersRes.ok) {
|
|
424
|
-
throw new Error(`Failed to fetch members: ${membersRes.status}`);
|
|
425
|
-
}
|
|
426
|
-
const memberList = (await membersRes.json());
|
|
427
|
-
if (memberList.length === 0) {
|
|
428
|
-
console.log("\nNo members.\n");
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
console.log(`\n${project.name} — ${memberList.length} member${memberList.length === 1 ? "" : "s"}\n`);
|
|
432
|
-
memberList.forEach((m, i) => {
|
|
433
|
-
console.log(` ${i + 1}. ${m.name ?? m.email ?? m.user_id} (${m.email ?? "no email"})`);
|
|
434
|
-
});
|
|
435
|
-
const choice = await prompt("\nRemove a member? Enter number (or press Enter to skip): ");
|
|
436
|
-
if (!choice)
|
|
437
|
-
return;
|
|
438
|
-
const index = parseInt(choice, 10) - 1;
|
|
439
|
-
if (isNaN(index) || index < 0 || index >= memberList.length) {
|
|
440
|
-
console.log("Canceled.");
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
const target = memberList[index];
|
|
444
|
-
const confirm = await prompt(`Remove ${target.name ?? target.email ?? target.user_id}? (y/N): `);
|
|
445
|
-
if (confirm.toLowerCase() !== "y") {
|
|
446
|
-
console.log("Canceled.");
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
const removeRes = await fetch(`${baseUrl}/v1/projects/${project.project_id}/members/${target.user_id}`, {
|
|
450
|
-
method: "DELETE",
|
|
451
|
-
headers: { Authorization: `Bearer ${auth.accessToken}` },
|
|
452
|
-
});
|
|
453
|
-
if (!removeRes.ok) {
|
|
454
|
-
const body = await removeRes.text();
|
|
455
|
-
throw new Error(`Remove failed: ${removeRes.status} ${body}`);
|
|
456
|
-
}
|
|
457
|
-
console.log(`\nRemoved ${target.name ?? target.email ?? target.user_id}.\n`);
|
|
458
|
-
}
|
|
459
|
-
async function deleteProject() {
|
|
460
|
-
const baseUrl = process.env.COHVU_API_URL ?? DEFAULT_BASE_URL;
|
|
461
|
-
const auth = await (0, auth_1.getAuthToken)(baseUrl);
|
|
462
|
-
const project = await resolveProject(baseUrl, auth.accessToken, "Which project do you want to delete?");
|
|
463
|
-
if (!project)
|
|
464
|
-
return;
|
|
465
|
-
console.log(`\nThis will permanently delete "${project.name}" and ALL its knowledge and memberships.`);
|
|
466
|
-
console.log("This cannot be undone.\n");
|
|
467
|
-
const confirm = await prompt(`Type "${project.slug}" to confirm: `);
|
|
468
|
-
if (confirm !== project.slug) {
|
|
469
|
-
console.log("Canceled.");
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
472
|
-
const deleteRes = await fetch(`${baseUrl}/v1/projects/${project.project_id}`, {
|
|
473
|
-
method: "DELETE",
|
|
474
|
-
headers: { Authorization: `Bearer ${auth.accessToken}` },
|
|
475
|
-
});
|
|
476
|
-
if (!deleteRes.ok) {
|
|
477
|
-
const body = await deleteRes.text();
|
|
478
|
-
throw new Error(`Delete failed: ${deleteRes.status} ${body}`);
|
|
479
|
-
}
|
|
480
|
-
console.log(`\nDeleted "${project.name}".\n`);
|
|
481
57
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const auth = await (0, auth_1.getAuthToken)(baseUrl);
|
|
485
|
-
const project = await resolveProject(baseUrl, auth.accessToken, "Which project do you want to clear?");
|
|
486
|
-
if (!project)
|
|
487
|
-
return;
|
|
488
|
-
console.log(`\nThis will remove ALL knowledge from "${project.name}".`);
|
|
489
|
-
console.log("The project itself, billing, and memberships are kept.\n");
|
|
490
|
-
const confirm = await prompt(`Type "${project.slug}" to confirm: `);
|
|
491
|
-
if (confirm !== project.slug) {
|
|
492
|
-
console.log("Canceled.");
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
const clearRes = await fetch(`${baseUrl}/v1/projects/${project.project_id}/knowledge`, {
|
|
496
|
-
method: "DELETE",
|
|
497
|
-
headers: { Authorization: `Bearer ${auth.accessToken}` },
|
|
498
|
-
});
|
|
499
|
-
if (!clearRes.ok) {
|
|
500
|
-
const body = await clearRes.text();
|
|
501
|
-
throw new Error(`Clear failed: ${clearRes.status} ${body}`);
|
|
502
|
-
}
|
|
503
|
-
const result = (await clearRes.json());
|
|
504
|
-
console.log(`\nCleared "${project.name}". Removed ${result.memories_removed} memories.\n`);
|
|
505
|
-
}
|
|
506
|
-
async function switchProject() {
|
|
507
|
-
const baseUrl = process.env.COHVU_API_URL ?? DEFAULT_BASE_URL;
|
|
508
|
-
const auth = await (0, auth_1.getAuthToken)(baseUrl);
|
|
509
|
-
const me = await fetchMe(baseUrl, auth.accessToken);
|
|
510
|
-
const rootProjects = me.projects.filter((s) => s.is_root);
|
|
511
|
-
if (rootProjects.length === 0) {
|
|
512
|
-
console.log("You have no projects. Run `npx cohvu create` first.");
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
if (rootProjects.length === 1) {
|
|
516
|
-
console.log(`You only have one project: ${rootProjects[0].name}. It's already active.`);
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
519
|
-
console.log(`\nYour projects:\n`);
|
|
520
|
-
rootProjects.forEach((s, i) => {
|
|
521
|
-
const active = s.project_id === me.user.active_project_id ? " ← active" : "";
|
|
522
|
-
console.log(` ${i + 1}. ${s.name} (${s.slug})${active}`);
|
|
523
|
-
});
|
|
524
|
-
const choice = await prompt(`\nSwitch to (1-${rootProjects.length}): `);
|
|
525
|
-
const index = parseInt(choice, 10) - 1;
|
|
526
|
-
if (isNaN(index) || index < 0 || index >= rootProjects.length) {
|
|
527
|
-
console.log("Canceled.");
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
const selected = rootProjects[index];
|
|
531
|
-
const res = await fetch(`${baseUrl}/v1/auth/active-project`, {
|
|
532
|
-
method: "POST",
|
|
533
|
-
headers: {
|
|
534
|
-
Authorization: `Bearer ${auth.accessToken}`,
|
|
535
|
-
"Content-Type": "application/json",
|
|
536
|
-
},
|
|
537
|
-
body: JSON.stringify({ project_id: selected.project_id }),
|
|
538
|
-
});
|
|
539
|
-
if (!res.ok) {
|
|
540
|
-
const body = await res.text();
|
|
541
|
-
throw new Error(`Failed to switch: ${res.status} ${body}`);
|
|
542
|
-
}
|
|
543
|
-
console.log(`\nSwitched to "${selected.name}". Your agents will use this project now.\n`);
|
|
544
|
-
}
|
|
545
|
-
function timeAgo(date) {
|
|
546
|
-
const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
|
|
547
|
-
if (seconds < 60)
|
|
548
|
-
return "just now";
|
|
549
|
-
const minutes = Math.floor(seconds / 60);
|
|
550
|
-
if (minutes < 60)
|
|
551
|
-
return `${minutes}m ago`;
|
|
552
|
-
const hours = Math.floor(minutes / 60);
|
|
553
|
-
if (hours < 24)
|
|
554
|
-
return `${hours}h ago`;
|
|
555
|
-
const days = Math.floor(hours / 24);
|
|
556
|
-
return `${days}d ago`;
|
|
557
|
-
}
|
|
558
|
-
async function browseMemories() {
|
|
559
|
-
const baseUrl = process.env.COHVU_API_URL ?? DEFAULT_BASE_URL;
|
|
560
|
-
const auth = await (0, auth_1.getAuthToken)(baseUrl);
|
|
561
|
-
const project = await resolveProject(baseUrl, auth.accessToken, "Which project?");
|
|
562
|
-
if (!project)
|
|
563
|
-
return;
|
|
564
|
-
console.log();
|
|
565
|
-
console.log(" 1. Browse recent");
|
|
566
|
-
console.log(" 2. Search");
|
|
567
|
-
const mode = await prompt("\n Choose (1-2): ");
|
|
568
|
-
if (mode === "1") {
|
|
569
|
-
let offset = 0;
|
|
570
|
-
const limit = 10;
|
|
571
|
-
while (true) {
|
|
572
|
-
const res = await fetch(`${baseUrl}/v1/projects/${project.project_id}/memories?limit=${limit}&offset=${offset}`, { headers: { Authorization: `Bearer ${auth.accessToken}` } });
|
|
573
|
-
if (!res.ok) {
|
|
574
|
-
throw new Error(`Failed to fetch memories: ${res.status}`);
|
|
575
|
-
}
|
|
576
|
-
const data = (await res.json());
|
|
577
|
-
if (offset === 0) {
|
|
578
|
-
console.log(`\n${project.name} — ${data.total} memories\n`);
|
|
579
|
-
}
|
|
580
|
-
if (data.memories.length === 0) {
|
|
581
|
-
if (offset === 0)
|
|
582
|
-
console.log(" No memories yet.\n");
|
|
583
|
-
break;
|
|
584
|
-
}
|
|
585
|
-
data.memories.forEach((m, i) => {
|
|
586
|
-
const preview = m.body.length > 120 ? m.body.slice(0, 120) + "..." : m.body;
|
|
587
|
-
console.log(` ${offset + i + 1}. ${preview} (${timeAgo(new Date(m.updated_at))})`);
|
|
588
|
-
});
|
|
589
|
-
offset += limit;
|
|
590
|
-
if (offset >= data.total) {
|
|
591
|
-
console.log();
|
|
592
|
-
break;
|
|
593
|
-
}
|
|
594
|
-
const more = await prompt("\n Show more? (y/N): ");
|
|
595
|
-
if (more.toLowerCase() !== "y") {
|
|
596
|
-
console.log();
|
|
597
|
-
break;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
else if (mode === "2") {
|
|
602
|
-
const query = await prompt("\n Search: ");
|
|
603
|
-
if (!query) {
|
|
604
|
-
console.log("Canceled.");
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
const res = await fetch(`${baseUrl}/v1/projects/${project.project_id}/memories/search?q=${encodeURIComponent(query)}`, { headers: { Authorization: `Bearer ${auth.accessToken}` } });
|
|
608
|
-
if (!res.ok) {
|
|
609
|
-
throw new Error(`Search failed: ${res.status}`);
|
|
610
|
-
}
|
|
611
|
-
const data = (await res.json());
|
|
612
|
-
if (data.memories.length === 0) {
|
|
613
|
-
console.log("\n No matching memories found.\n");
|
|
614
|
-
return;
|
|
615
|
-
}
|
|
616
|
-
console.log();
|
|
617
|
-
data.memories.forEach((m, i) => {
|
|
618
|
-
const preview = m.body.length > 120 ? m.body.slice(0, 120) + "..." : m.body;
|
|
619
|
-
console.log(` ${i + 1}. ${preview} (${timeAgo(new Date(m.updated_at))})`);
|
|
620
|
-
});
|
|
621
|
-
console.log();
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
async function forgetMemory() {
|
|
625
|
-
const baseUrl = process.env.COHVU_API_URL ?? DEFAULT_BASE_URL;
|
|
626
|
-
const auth = await (0, auth_1.getAuthToken)(baseUrl);
|
|
627
|
-
const project = await resolveProject(baseUrl, auth.accessToken, "Which project?");
|
|
628
|
-
if (!project)
|
|
629
|
-
return;
|
|
630
|
-
const query = await prompt("What do you want to forget? ");
|
|
631
|
-
if (!query) {
|
|
632
|
-
console.log("Canceled.");
|
|
633
|
-
return;
|
|
634
|
-
}
|
|
635
|
-
const searchRes = await fetch(`${baseUrl}/v1/projects/${project.project_id}/memories/search?q=${encodeURIComponent(query)}`, { headers: { Authorization: `Bearer ${auth.accessToken}` } });
|
|
636
|
-
if (!searchRes.ok) {
|
|
637
|
-
throw new Error(`Search failed: ${searchRes.status}`);
|
|
638
|
-
}
|
|
639
|
-
const data = (await searchRes.json());
|
|
640
|
-
if (data.memories.length === 0) {
|
|
641
|
-
console.log("\nNo matching memories found.\n");
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
console.log();
|
|
645
|
-
data.memories.forEach((m, i) => {
|
|
646
|
-
const preview = m.body.length > 120 ? m.body.slice(0, 120) + "..." : m.body;
|
|
647
|
-
const ago = timeAgo(new Date(m.updated_at));
|
|
648
|
-
console.log(` ${i + 1}. ${preview} (${ago})`);
|
|
649
|
-
});
|
|
650
|
-
const choice = await prompt("\nRemove which? (numbers separated by commas, or Enter to cancel): ");
|
|
651
|
-
if (!choice) {
|
|
652
|
-
console.log("Canceled.");
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
|
-
const indices = choice.split(",").map((s) => parseInt(s.trim(), 10) - 1);
|
|
656
|
-
const toDelete = indices
|
|
657
|
-
.filter((i) => i >= 0 && i < data.memories.length)
|
|
658
|
-
.map((i) => data.memories[i]);
|
|
659
|
-
if (toDelete.length === 0) {
|
|
660
|
-
console.log("Canceled.");
|
|
661
|
-
return;
|
|
662
|
-
}
|
|
663
|
-
const confirm = await prompt(`Remove ${toDelete.length} memory${toDelete.length === 1 ? "" : "s"}? (y/N): `);
|
|
664
|
-
if (confirm.toLowerCase() !== "y") {
|
|
665
|
-
console.log("Canceled.");
|
|
666
|
-
return;
|
|
667
|
-
}
|
|
668
|
-
console.log();
|
|
669
|
-
for (const m of toDelete) {
|
|
670
|
-
const delRes = await fetch(`${baseUrl}/v1/projects/${project.project_id}/memories/${m.id}`, {
|
|
671
|
-
method: "DELETE",
|
|
672
|
-
headers: { Authorization: `Bearer ${auth.accessToken}` },
|
|
673
|
-
});
|
|
674
|
-
if (delRes.ok) {
|
|
675
|
-
const preview = m.body.length > 120 ? m.body.slice(0, 120) + "..." : m.body;
|
|
676
|
-
console.log(` Removed: "${preview}"`);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
console.log();
|
|
680
|
-
}
|
|
681
|
-
async function status() {
|
|
682
|
-
const baseUrl = process.env.COHVU_API_URL ?? DEFAULT_BASE_URL;
|
|
683
|
-
const auth = await (0, auth_1.getAuthToken)(baseUrl);
|
|
684
|
-
const me = await fetchMe(baseUrl, auth.accessToken);
|
|
685
|
-
const rootProjects = me.projects.filter((s) => s.is_root);
|
|
686
|
-
console.log(`\n Signed in as ${me.user.email}`);
|
|
687
|
-
const active = me.user.active_project_id
|
|
688
|
-
? rootProjects.find((p) => p.project_id === me.user.active_project_id)
|
|
689
|
-
: rootProjects[0] ?? null;
|
|
690
|
-
if (active) {
|
|
691
|
-
const label = projectStatusLabel(active);
|
|
692
|
-
console.log(` Project: ${active.name}${label ? ` (${label})` : ""}`);
|
|
693
|
-
const membersRes = await fetch(`${baseUrl}/v1/projects/${active.project_id}/members`, {
|
|
694
|
-
headers: { Authorization: `Bearer ${auth.accessToken}` },
|
|
695
|
-
});
|
|
696
|
-
if (membersRes.ok) {
|
|
697
|
-
const memberList = (await membersRes.json());
|
|
698
|
-
console.log(` ${memberList.length} member${memberList.length === 1 ? "" : "s"}`);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
else {
|
|
702
|
-
console.log(" No active project. Run `npx cohvu create` to get started.");
|
|
703
|
-
}
|
|
704
|
-
console.log();
|
|
705
|
-
}
|
|
706
|
-
async function renameProject() {
|
|
707
|
-
const baseUrl = process.env.COHVU_API_URL ?? DEFAULT_BASE_URL;
|
|
708
|
-
const auth = await (0, auth_1.getAuthToken)(baseUrl);
|
|
709
|
-
const project = await resolveProject(baseUrl, auth.accessToken, "Which project do you want to rename?");
|
|
710
|
-
if (!project)
|
|
711
|
-
return;
|
|
712
|
-
const newName = await prompt(`New name for "${project.name}": `);
|
|
713
|
-
if (!newName) {
|
|
714
|
-
console.log("Canceled.");
|
|
715
|
-
return;
|
|
716
|
-
}
|
|
717
|
-
const newSlug = newName
|
|
718
|
-
.toLowerCase()
|
|
719
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
720
|
-
.replace(/^-+|-+$/g, "");
|
|
721
|
-
const res = await fetch(`${baseUrl}/v1/projects/${project.project_id}`, {
|
|
722
|
-
method: "PATCH",
|
|
723
|
-
headers: {
|
|
724
|
-
Authorization: `Bearer ${auth.accessToken}`,
|
|
725
|
-
"Content-Type": "application/json",
|
|
726
|
-
},
|
|
727
|
-
body: JSON.stringify({ name: newName, slug: newSlug }),
|
|
728
|
-
});
|
|
729
|
-
if (!res.ok) {
|
|
730
|
-
const body = await res.text();
|
|
731
|
-
throw new Error(`Rename failed: ${res.status} ${body}`);
|
|
732
|
-
}
|
|
733
|
-
console.log(`\nRenamed to "${newName}".\n`);
|
|
58
|
+
function write(text) {
|
|
59
|
+
process.stdout.write(text + "\n");
|
|
734
60
|
}
|
|
735
61
|
// ---------------------------------------------------------------------------
|
|
736
|
-
//
|
|
62
|
+
// Dashboard entry — handles auth + setup + project creation before TUI
|
|
737
63
|
// ---------------------------------------------------------------------------
|
|
738
|
-
async function
|
|
64
|
+
async function enterDashboard() {
|
|
739
65
|
const baseUrl = process.env.COHVU_API_URL ?? DEFAULT_BASE_URL;
|
|
740
|
-
|
|
66
|
+
// Try existing auth
|
|
67
|
+
let needsAuth = false;
|
|
741
68
|
try {
|
|
742
|
-
|
|
743
|
-
accessToken = auth.accessToken;
|
|
69
|
+
await (0, auth_1.getAuthToken)(baseUrl);
|
|
744
70
|
}
|
|
745
71
|
catch {
|
|
746
|
-
|
|
747
|
-
process.stderr.write("Not logged in. Waiting for login...\n");
|
|
748
|
-
const stdioTransport = new stdio_js_1.StdioServerTransport();
|
|
749
|
-
stdioTransport.onmessage = (message) => {
|
|
750
|
-
const msg = message;
|
|
751
|
-
if (msg.id !== undefined) {
|
|
752
|
-
stdioTransport.send({
|
|
753
|
-
jsonrpc: "2.0",
|
|
754
|
-
id: msg.id,
|
|
755
|
-
error: {
|
|
756
|
-
code: -32600,
|
|
757
|
-
message: "Cohvu is not logged in. Ask the user to run `npx cohvu login` in their terminal, then retry.",
|
|
758
|
-
},
|
|
759
|
-
}).catch(() => { });
|
|
760
|
-
}
|
|
761
|
-
};
|
|
762
|
-
await stdioTransport.start();
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
// Silently update instruction files if content has changed since last login
|
|
766
|
-
(0, setup_1.refreshInstructions)();
|
|
767
|
-
// Create the stdio transport (accepts MCP from Claude Desktop / Cursor / etc.)
|
|
768
|
-
const stdioTransport = new stdio_js_1.StdioServerTransport();
|
|
769
|
-
// Mutable headers — token is refreshed before expiry
|
|
770
|
-
const headers = {
|
|
771
|
-
Authorization: `Bearer ${accessToken}`,
|
|
772
|
-
};
|
|
773
|
-
// Custom fetch: 300s timeout on POSTs, auto-retry on 401 after token refresh.
|
|
774
|
-
// The agent should never see auth errors — they're resolved transparently.
|
|
775
|
-
let refreshing = null;
|
|
776
|
-
async function refreshTokenNow() {
|
|
777
|
-
if (refreshing)
|
|
778
|
-
return refreshing;
|
|
779
|
-
refreshing = (async () => {
|
|
780
|
-
try {
|
|
781
|
-
const auth = await (0, auth_1.getAuthToken)(baseUrl);
|
|
782
|
-
headers.Authorization = `Bearer ${auth.accessToken}`;
|
|
783
|
-
}
|
|
784
|
-
catch {
|
|
785
|
-
process.stderr.write("Token refresh failed.\n");
|
|
786
|
-
}
|
|
787
|
-
finally {
|
|
788
|
-
refreshing = null;
|
|
789
|
-
}
|
|
790
|
-
})();
|
|
791
|
-
return refreshing;
|
|
72
|
+
needsAuth = true;
|
|
792
73
|
}
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
74
|
+
if (needsAuth) {
|
|
75
|
+
// First-time or session expired
|
|
76
|
+
const hasTokensFile = (0, fs_1.existsSync)(TOKENS_FILE);
|
|
77
|
+
if (hasTokensFile) {
|
|
78
|
+
write("\n" + ansi_1.C.muted + " session expired. opening browser to sign in..." + ansi_1.C.reset);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
write("\n" + ansi_1.C.muted + " opening browser to sign in..." + ansi_1.C.reset);
|
|
82
|
+
}
|
|
83
|
+
await (0, auth_1.deviceAuthFlow)(baseUrl);
|
|
84
|
+
const api = new api_1.ApiClient(baseUrl);
|
|
85
|
+
const me = await api.me();
|
|
86
|
+
write(ansi_1.C.green + " signed in as " + ansi_1.C.reset + ansi_1.C.secondary + me.user.email + ansi_1.C.reset);
|
|
87
|
+
// Setup platforms
|
|
88
|
+
write(ansi_1.C.muted + " setting up your tools..." + ansi_1.C.reset);
|
|
89
|
+
await (0, setup_1.runSetup)();
|
|
90
|
+
write(ansi_1.C.muted + " done." + ansi_1.C.reset);
|
|
91
|
+
// Create project if needed
|
|
92
|
+
const rootProjects = me.projects.filter((s) => s.is_root);
|
|
93
|
+
if (rootProjects.length === 0) {
|
|
94
|
+
write("");
|
|
95
|
+
const name = await prompt(" " + ansi_1.C.muted + "project name \u203a " + ansi_1.C.reset);
|
|
96
|
+
if (name) {
|
|
97
|
+
const slug = deriveSlug(name);
|
|
810
98
|
try {
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
99
|
+
const project = await api.createProject(name, slug);
|
|
100
|
+
if (project.trial_ends_at) {
|
|
101
|
+
const trialDate = new Date(project.trial_ends_at);
|
|
102
|
+
const months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
|
|
103
|
+
const formatted = `${months[trialDate.getMonth()]} ${trialDate.getDate()}`;
|
|
104
|
+
write(ansi_1.C.green + " created " + ansi_1.C.reset + ansi_1.C.secondary + slug + ansi_1.C.reset +
|
|
105
|
+
ansi_1.C.muted + " \u00b7 trial ends " + ansi_1.C.reset + ansi_1.C.secondary + formatted + ansi_1.C.reset);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
write(ansi_1.C.green + " created " + ansi_1.C.reset + ansi_1.C.secondary + slug + ansi_1.C.reset);
|
|
109
|
+
try {
|
|
110
|
+
const checkout = await api.createCheckout(project.id);
|
|
111
|
+
if (checkout.checkout_url) {
|
|
112
|
+
write("");
|
|
113
|
+
write(ansi_1.C.muted + " opening checkout in browser..." + ansi_1.C.reset);
|
|
114
|
+
write(ansi_1.C.muted + " complete payment to activate" + ansi_1.C.reset);
|
|
115
|
+
openBrowser(checkout.checkout_url);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch { }
|
|
119
|
+
}
|
|
815
120
|
}
|
|
121
|
+
catch { }
|
|
816
122
|
}
|
|
123
|
+
write("");
|
|
817
124
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
// Refresh token every 50 minutes (tokens expire at 60)
|
|
829
|
-
setInterval(async () => {
|
|
830
|
-
try {
|
|
831
|
-
const auth = await (0, auth_1.getAuthToken)(baseUrl);
|
|
832
|
-
headers.Authorization = `Bearer ${auth.accessToken}`;
|
|
833
|
-
}
|
|
834
|
-
catch {
|
|
835
|
-
process.stderr.write("Token refresh failed. Session may expire.\n");
|
|
836
|
-
}
|
|
837
|
-
}, 50 * 60 * 1000);
|
|
838
|
-
// Bridge: stdio → HTTP → stdio
|
|
839
|
-
stdioTransport.onmessage = (message) => {
|
|
840
|
-
httpTransport.send(message).catch((error) => {
|
|
841
|
-
process.stderr.write(`Error forwarding to server: ${error}\n`);
|
|
842
|
-
const msg = message;
|
|
843
|
-
if (msg.id !== undefined) {
|
|
844
|
-
stdioTransport.send({
|
|
845
|
-
jsonrpc: "2.0",
|
|
846
|
-
id: msg.id,
|
|
847
|
-
error: {
|
|
848
|
-
code: -32603,
|
|
849
|
-
message: "Cohvu had a temporary connection issue. This is not a problem — just try your read or contribute call again.",
|
|
850
|
-
},
|
|
851
|
-
}).catch(() => { });
|
|
852
|
-
}
|
|
853
|
-
});
|
|
854
|
-
};
|
|
855
|
-
httpTransport.onmessage = (message) => {
|
|
856
|
-
stdioTransport.send(message).catch((error) => {
|
|
857
|
-
process.stderr.write(`Error forwarding to client: ${error}\n`);
|
|
858
|
-
});
|
|
859
|
-
};
|
|
860
|
-
httpTransport.onclose = () => {
|
|
861
|
-
stdioTransport.close().catch(() => { });
|
|
862
|
-
process.exit(0);
|
|
863
|
-
};
|
|
864
|
-
stdioTransport.onclose = () => {
|
|
865
|
-
httpTransport.close().catch(() => { });
|
|
866
|
-
process.exit(0);
|
|
867
|
-
};
|
|
868
|
-
httpTransport.onerror = (error) => {
|
|
869
|
-
process.stderr.write(`Remote transport error: ${error}\n`);
|
|
870
|
-
};
|
|
871
|
-
stdioTransport.onerror = (error) => {
|
|
872
|
-
process.stderr.write(`Stdio transport error: ${error}\n`);
|
|
873
|
-
};
|
|
874
|
-
// Start both transports
|
|
875
|
-
await stdioTransport.start();
|
|
876
|
-
await httpTransport.start();
|
|
125
|
+
write(ansi_1.C.muted + " opening cohvu..." + ansi_1.C.reset);
|
|
126
|
+
write("");
|
|
127
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Returning user
|
|
131
|
+
write("\n" + ansi_1.C.muted + " opening cohvu..." + ansi_1.C.reset);
|
|
132
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
133
|
+
}
|
|
134
|
+
await (0, dashboard_1.launchDashboard)();
|
|
877
135
|
}
|
|
878
136
|
//# sourceMappingURL=index.js.map
|