vibegroup 0.1.8 → 0.1.9
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/cli.js +355 -105
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -70,7 +70,7 @@ var package_default;
|
|
|
70
70
|
var init_package = __esm(() => {
|
|
71
71
|
package_default = {
|
|
72
72
|
name: "vibegroup",
|
|
73
|
-
version: "0.1.
|
|
73
|
+
version: "0.1.9",
|
|
74
74
|
description: "Talk to your teammates' Claude Code agents — agent-to-agent collaboration for Claude Code over a shared channel.",
|
|
75
75
|
type: "module",
|
|
76
76
|
bin: {
|
|
@@ -134,6 +134,209 @@ var init_package = __esm(() => {
|
|
|
134
134
|
};
|
|
135
135
|
});
|
|
136
136
|
|
|
137
|
+
// src/ui/runner.ts
|
|
138
|
+
import { render } from "ink";
|
|
139
|
+
function runInk(make) {
|
|
140
|
+
return new Promise((resolve) => {
|
|
141
|
+
let done = false;
|
|
142
|
+
let instance;
|
|
143
|
+
const finish = (code) => {
|
|
144
|
+
if (done)
|
|
145
|
+
return;
|
|
146
|
+
done = true;
|
|
147
|
+
instance?.unmount();
|
|
148
|
+
resolve(code);
|
|
149
|
+
};
|
|
150
|
+
instance = render(make(finish));
|
|
151
|
+
instance.waitUntilExit().then(() => finish(130));
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
var init_runner = () => {};
|
|
155
|
+
|
|
156
|
+
// src/ui/theme.ts
|
|
157
|
+
var color;
|
|
158
|
+
var init_theme = __esm(() => {
|
|
159
|
+
color = {
|
|
160
|
+
brand: "magenta",
|
|
161
|
+
accent: "cyan",
|
|
162
|
+
ok: "green",
|
|
163
|
+
warn: "yellow",
|
|
164
|
+
err: "red",
|
|
165
|
+
dim: "gray"
|
|
166
|
+
};
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// src/ui/Home.tsx
|
|
170
|
+
import { useState } from "react";
|
|
171
|
+
import { Box, Text, useInput } from "ink";
|
|
172
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
173
|
+
function Home({
|
|
174
|
+
identity,
|
|
175
|
+
version,
|
|
176
|
+
items = HOME_ITEMS,
|
|
177
|
+
onSelect
|
|
178
|
+
}) {
|
|
179
|
+
const [i, setI] = useState(0);
|
|
180
|
+
useInput((input, key) => {
|
|
181
|
+
if (input === "q" || key.escape || key.ctrl && input === "c")
|
|
182
|
+
return onSelect(null);
|
|
183
|
+
if (key.upArrow)
|
|
184
|
+
setI((s) => (s - 1 + items.length) % items.length);
|
|
185
|
+
if (key.downArrow)
|
|
186
|
+
setI((s) => (s + 1) % items.length);
|
|
187
|
+
if (key.return)
|
|
188
|
+
return onSelect(items[i].cmd);
|
|
189
|
+
});
|
|
190
|
+
const sel = items[i];
|
|
191
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
192
|
+
flexDirection: "column",
|
|
193
|
+
paddingX: 1,
|
|
194
|
+
paddingY: 1,
|
|
195
|
+
gap: 1,
|
|
196
|
+
children: [
|
|
197
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
198
|
+
borderStyle: "round",
|
|
199
|
+
borderColor: color.brand,
|
|
200
|
+
paddingX: 1,
|
|
201
|
+
flexDirection: "column",
|
|
202
|
+
children: [
|
|
203
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
204
|
+
children: [
|
|
205
|
+
/* @__PURE__ */ jsx(Text, {
|
|
206
|
+
color: color.brand,
|
|
207
|
+
bold: true,
|
|
208
|
+
children: "vibegroup"
|
|
209
|
+
}),
|
|
210
|
+
/* @__PURE__ */ jsx(Text, {
|
|
211
|
+
dimColor: true,
|
|
212
|
+
children: " talk to your teammates' Claude Code agents"
|
|
213
|
+
})
|
|
214
|
+
]
|
|
215
|
+
}),
|
|
216
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
217
|
+
dimColor: true,
|
|
218
|
+
children: [
|
|
219
|
+
identity ? `signed in as ${identity}` : "not signed in — start with init or login",
|
|
220
|
+
" · ",
|
|
221
|
+
"v",
|
|
222
|
+
version
|
|
223
|
+
]
|
|
224
|
+
})
|
|
225
|
+
]
|
|
226
|
+
}),
|
|
227
|
+
/* @__PURE__ */ jsx(Box, {
|
|
228
|
+
flexDirection: "column",
|
|
229
|
+
children: items.map((it, idx) => {
|
|
230
|
+
const active = idx === i;
|
|
231
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
232
|
+
children: [
|
|
233
|
+
/* @__PURE__ */ jsx(Text, {
|
|
234
|
+
color: active ? color.accent : undefined,
|
|
235
|
+
children: active ? "❯ " : " "
|
|
236
|
+
}),
|
|
237
|
+
/* @__PURE__ */ jsx(Box, {
|
|
238
|
+
width: 14,
|
|
239
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
240
|
+
bold: active,
|
|
241
|
+
color: active ? color.accent : undefined,
|
|
242
|
+
children: it.label
|
|
243
|
+
})
|
|
244
|
+
}),
|
|
245
|
+
/* @__PURE__ */ jsx(Text, {
|
|
246
|
+
dimColor: true,
|
|
247
|
+
children: it.desc
|
|
248
|
+
})
|
|
249
|
+
]
|
|
250
|
+
}, it.cmd);
|
|
251
|
+
})
|
|
252
|
+
}),
|
|
253
|
+
/* @__PURE__ */ jsx(Box, {
|
|
254
|
+
borderStyle: "round",
|
|
255
|
+
borderColor: color.dim,
|
|
256
|
+
paddingX: 1,
|
|
257
|
+
children: /* @__PURE__ */ jsxs(Text, {
|
|
258
|
+
children: [
|
|
259
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
260
|
+
dimColor: true,
|
|
261
|
+
children: [
|
|
262
|
+
sel.runnable ? "runs" : "type",
|
|
263
|
+
": "
|
|
264
|
+
]
|
|
265
|
+
}),
|
|
266
|
+
/* @__PURE__ */ jsx(Text, {
|
|
267
|
+
color: color.accent,
|
|
268
|
+
children: sel.usage
|
|
269
|
+
})
|
|
270
|
+
]
|
|
271
|
+
})
|
|
272
|
+
}),
|
|
273
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
274
|
+
dimColor: true,
|
|
275
|
+
children: [
|
|
276
|
+
"↑↓ navigate · ⏎ ",
|
|
277
|
+
sel.runnable ? "run" : "show command",
|
|
278
|
+
" · q quit"
|
|
279
|
+
]
|
|
280
|
+
})
|
|
281
|
+
]
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
var HOME_ITEMS;
|
|
285
|
+
var init_Home = __esm(() => {
|
|
286
|
+
init_theme();
|
|
287
|
+
HOME_ITEMS = [
|
|
288
|
+
{ cmd: "init", label: "init", desc: "One-time setup: install plugin, enable channel, sign in", usage: "vibegroup init [email]", runnable: true },
|
|
289
|
+
{ cmd: "who", label: "who", desc: "Live view of who's in a room (people + sessions)", usage: "vibegroup who --team <slug> [--room <name>]", runnable: false },
|
|
290
|
+
{ cmd: "claude", label: "claude", desc: "Launch Claude Code wired to a room", usage: "vibegroup claude --team <slug> [--room <name>] [--session <label>]", runnable: false },
|
|
291
|
+
{ cmd: "status", label: "status", desc: "Your auth + connection status", usage: "vibegroup status", runnable: true },
|
|
292
|
+
{ cmd: "team", label: "team create", desc: "Create a team (a WorkOS org + a general room)", usage: "vibegroup team create <slug> [--name <name>]", runnable: false },
|
|
293
|
+
{ cmd: "room", label: "room create", desc: "Add a room to a team", usage: "vibegroup room create <name> --team <slug>", runnable: false },
|
|
294
|
+
{ cmd: "rooms", label: "rooms", desc: "List a team's rooms", usage: "vibegroup rooms --team <slug>", runnable: false },
|
|
295
|
+
{ cmd: "invite", label: "invite", desc: "Invite someone to a team", usage: "vibegroup invite <email> --team <slug>", runnable: false },
|
|
296
|
+
{ cmd: "login", label: "login", desc: "Sign in / sign up (email code)", usage: "vibegroup login [email]", runnable: true },
|
|
297
|
+
{ cmd: "logout", label: "logout", desc: "Clear the cached session", usage: "vibegroup logout", runnable: true },
|
|
298
|
+
{ cmd: "install", label: "install", desc: "Register the vibegroup plugin in Claude Code", usage: "vibegroup install", runnable: true }
|
|
299
|
+
];
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// src/commands/home.ts
|
|
303
|
+
var exports_home = {};
|
|
304
|
+
__export(exports_home, {
|
|
305
|
+
homeCommand: () => homeCommand
|
|
306
|
+
});
|
|
307
|
+
import { createElement } from "react";
|
|
308
|
+
async function homeCommand(env, dispatch, version, help) {
|
|
309
|
+
if (!process.stdout.isTTY) {
|
|
310
|
+
console.log(help);
|
|
311
|
+
return 0;
|
|
312
|
+
}
|
|
313
|
+
const identity = readAuth(env)?.user?.email ?? null;
|
|
314
|
+
let chosen = null;
|
|
315
|
+
await runInk((onExit) => createElement(Home, {
|
|
316
|
+
identity,
|
|
317
|
+
version,
|
|
318
|
+
onSelect: (cmd) => {
|
|
319
|
+
chosen = cmd;
|
|
320
|
+
onExit(0);
|
|
321
|
+
}
|
|
322
|
+
}));
|
|
323
|
+
if (!chosen)
|
|
324
|
+
return 0;
|
|
325
|
+
const item = HOME_ITEMS.find((it) => it.cmd === chosen);
|
|
326
|
+
if (item && !item.runnable) {
|
|
327
|
+
console.log(`
|
|
328
|
+
Run:
|
|
329
|
+
${item.usage}`);
|
|
330
|
+
return 0;
|
|
331
|
+
}
|
|
332
|
+
return dispatch(chosen);
|
|
333
|
+
}
|
|
334
|
+
var init_home = __esm(() => {
|
|
335
|
+
init_runner();
|
|
336
|
+
init_Home();
|
|
337
|
+
init_auth();
|
|
338
|
+
});
|
|
339
|
+
|
|
137
340
|
// src/lib/allowlist.ts
|
|
138
341
|
function managedSettingsPath(plat = process.platform) {
|
|
139
342
|
if (plat === "darwin")
|
|
@@ -266,25 +469,6 @@ var init_channel = __esm(() => {
|
|
|
266
469
|
init_allowlist();
|
|
267
470
|
});
|
|
268
471
|
|
|
269
|
-
// src/ui/runner.ts
|
|
270
|
-
import { render } from "ink";
|
|
271
|
-
function runInk(make) {
|
|
272
|
-
return new Promise((resolve) => {
|
|
273
|
-
let done = false;
|
|
274
|
-
let instance;
|
|
275
|
-
const finish = (code) => {
|
|
276
|
-
if (done)
|
|
277
|
-
return;
|
|
278
|
-
done = true;
|
|
279
|
-
instance?.unmount();
|
|
280
|
-
resolve(code);
|
|
281
|
-
};
|
|
282
|
-
instance = render(make(finish));
|
|
283
|
-
instance.waitUntilExit().then(() => finish(130));
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
var init_runner = () => {};
|
|
287
|
-
|
|
288
472
|
// src/lib/emailAuth.ts
|
|
289
473
|
async function startEmailLogin(apiBase, email, f) {
|
|
290
474
|
const res = await f(`${apiBase}/auth/email/start`, {
|
|
@@ -328,30 +512,17 @@ function sessionFromTokens(tokens, email) {
|
|
|
328
512
|
};
|
|
329
513
|
}
|
|
330
514
|
|
|
331
|
-
// src/ui/theme.ts
|
|
332
|
-
var color;
|
|
333
|
-
var init_theme = __esm(() => {
|
|
334
|
-
color = {
|
|
335
|
-
brand: "magenta",
|
|
336
|
-
accent: "cyan",
|
|
337
|
-
ok: "green",
|
|
338
|
-
warn: "yellow",
|
|
339
|
-
err: "red",
|
|
340
|
-
dim: "gray"
|
|
341
|
-
};
|
|
342
|
-
});
|
|
343
|
-
|
|
344
515
|
// src/ui/Login.tsx
|
|
345
|
-
import { useEffect, useRef, useState } from "react";
|
|
346
|
-
import { Box, Text } from "ink";
|
|
516
|
+
import { useEffect, useRef, useState as useState2 } from "react";
|
|
517
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
347
518
|
import { Spinner, StatusMessage, TextInput, Alert } from "@inkjs/ui";
|
|
348
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
519
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
349
520
|
function Login({
|
|
350
521
|
apiBase,
|
|
351
522
|
initialEmail,
|
|
352
523
|
onExit
|
|
353
524
|
}) {
|
|
354
|
-
const [phase, setPhase] =
|
|
525
|
+
const [phase, setPhase] = useState2(initialEmail ? { t: "sending", email: initialEmail } : { t: "email" });
|
|
355
526
|
const started = useRef(false);
|
|
356
527
|
const send = async (email) => {
|
|
357
528
|
setPhase({ t: "sending", email });
|
|
@@ -380,33 +551,33 @@ function Login({
|
|
|
380
551
|
send(initialEmail);
|
|
381
552
|
}
|
|
382
553
|
}, []);
|
|
383
|
-
return /* @__PURE__ */
|
|
554
|
+
return /* @__PURE__ */ jsxs2(Box2, {
|
|
384
555
|
flexDirection: "column",
|
|
385
556
|
gap: 1,
|
|
386
557
|
paddingY: 1,
|
|
387
558
|
children: [
|
|
388
|
-
/* @__PURE__ */
|
|
559
|
+
/* @__PURE__ */ jsxs2(Text2, {
|
|
389
560
|
children: [
|
|
390
|
-
/* @__PURE__ */
|
|
561
|
+
/* @__PURE__ */ jsx2(Text2, {
|
|
391
562
|
color: color.brand,
|
|
392
563
|
bold: true,
|
|
393
564
|
children: "vibegroup"
|
|
394
565
|
}),
|
|
395
|
-
/* @__PURE__ */
|
|
566
|
+
/* @__PURE__ */ jsx2(Text2, {
|
|
396
567
|
dimColor: true,
|
|
397
568
|
children: " · sign in"
|
|
398
569
|
})
|
|
399
570
|
]
|
|
400
571
|
}),
|
|
401
|
-
phase.t === "email" && /* @__PURE__ */
|
|
572
|
+
phase.t === "email" && /* @__PURE__ */ jsxs2(Box2, {
|
|
402
573
|
flexDirection: "column",
|
|
403
574
|
children: [
|
|
404
|
-
/* @__PURE__ */
|
|
575
|
+
/* @__PURE__ */ jsx2(Text2, {
|
|
405
576
|
children: "Email to sign in with:"
|
|
406
577
|
}),
|
|
407
|
-
/* @__PURE__ */
|
|
578
|
+
/* @__PURE__ */ jsx2(Box2, {
|
|
408
579
|
marginTop: 1,
|
|
409
|
-
children: /* @__PURE__ */
|
|
580
|
+
children: /* @__PURE__ */ jsx2(TextInput, {
|
|
410
581
|
placeholder: "you@company.com",
|
|
411
582
|
onSubmit: (value) => {
|
|
412
583
|
const email = value.trim().toLowerCase();
|
|
@@ -419,25 +590,25 @@ function Login({
|
|
|
419
590
|
})
|
|
420
591
|
]
|
|
421
592
|
}),
|
|
422
|
-
phase.t === "sending" && /* @__PURE__ */
|
|
593
|
+
phase.t === "sending" && /* @__PURE__ */ jsx2(Spinner, {
|
|
423
594
|
label: `Sending a code to ${phase.email}…`
|
|
424
595
|
}),
|
|
425
|
-
phase.t === "code" && /* @__PURE__ */
|
|
596
|
+
phase.t === "code" && /* @__PURE__ */ jsxs2(Box2, {
|
|
426
597
|
flexDirection: "column",
|
|
427
598
|
children: [
|
|
428
|
-
/* @__PURE__ */
|
|
599
|
+
/* @__PURE__ */ jsxs2(Text2, {
|
|
429
600
|
children: [
|
|
430
601
|
"Enter the 6-digit code sent to ",
|
|
431
|
-
/* @__PURE__ */
|
|
602
|
+
/* @__PURE__ */ jsx2(Text2, {
|
|
432
603
|
color: color.accent,
|
|
433
604
|
children: phase.email
|
|
434
605
|
}),
|
|
435
606
|
":"
|
|
436
607
|
]
|
|
437
608
|
}),
|
|
438
|
-
/* @__PURE__ */
|
|
609
|
+
/* @__PURE__ */ jsx2(Box2, {
|
|
439
610
|
marginTop: 1,
|
|
440
|
-
children: /* @__PURE__ */
|
|
611
|
+
children: /* @__PURE__ */ jsx2(TextInput, {
|
|
441
612
|
placeholder: "123456",
|
|
442
613
|
onSubmit: (value) => {
|
|
443
614
|
const code = value.trim();
|
|
@@ -446,9 +617,9 @@ function Login({
|
|
|
446
617
|
}
|
|
447
618
|
})
|
|
448
619
|
}),
|
|
449
|
-
phase.error && /* @__PURE__ */
|
|
620
|
+
phase.error && /* @__PURE__ */ jsx2(Box2, {
|
|
450
621
|
marginTop: 1,
|
|
451
|
-
children: /* @__PURE__ */
|
|
622
|
+
children: /* @__PURE__ */ jsxs2(Text2, {
|
|
452
623
|
color: color.err,
|
|
453
624
|
children: [
|
|
454
625
|
phase.error,
|
|
@@ -458,10 +629,10 @@ function Login({
|
|
|
458
629
|
})
|
|
459
630
|
]
|
|
460
631
|
}),
|
|
461
|
-
phase.t === "verifying" && /* @__PURE__ */
|
|
632
|
+
phase.t === "verifying" && /* @__PURE__ */ jsx2(Spinner, {
|
|
462
633
|
label: "Verifying…"
|
|
463
634
|
}),
|
|
464
|
-
phase.t === "done" && /* @__PURE__ */
|
|
635
|
+
phase.t === "done" && /* @__PURE__ */ jsxs2(StatusMessage, {
|
|
465
636
|
variant: "success",
|
|
466
637
|
children: [
|
|
467
638
|
"Signed in as ",
|
|
@@ -469,7 +640,7 @@ function Login({
|
|
|
469
640
|
". You're good to go!"
|
|
470
641
|
]
|
|
471
642
|
}),
|
|
472
|
-
phase.t === "error" && /* @__PURE__ */
|
|
643
|
+
phase.t === "error" && /* @__PURE__ */ jsx2(Alert, {
|
|
473
644
|
variant: "error",
|
|
474
645
|
children: phase.message
|
|
475
646
|
})
|
|
@@ -486,10 +657,10 @@ var exports_login = {};
|
|
|
486
657
|
__export(exports_login, {
|
|
487
658
|
loginCommand: () => loginCommand
|
|
488
659
|
});
|
|
489
|
-
import { createElement } from "react";
|
|
660
|
+
import { createElement as createElement2 } from "react";
|
|
490
661
|
async function loginCommand(rest, env = process.env) {
|
|
491
662
|
const base = apiBase(env);
|
|
492
|
-
return runInk((onExit) =>
|
|
663
|
+
return runInk((onExit) => createElement2(Login, { apiBase: base, initialEmail: rest[0], onExit }));
|
|
493
664
|
}
|
|
494
665
|
var init_login = __esm(() => {
|
|
495
666
|
init_runner();
|
|
@@ -557,6 +728,60 @@ var init_init = __esm(() => {
|
|
|
557
728
|
init_login();
|
|
558
729
|
});
|
|
559
730
|
|
|
731
|
+
// src/lib/refresh.ts
|
|
732
|
+
function jwtExpMs(token) {
|
|
733
|
+
try {
|
|
734
|
+
const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64url").toString());
|
|
735
|
+
return typeof payload.exp === "number" ? payload.exp * 1000 : null;
|
|
736
|
+
} catch {
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
function tokenExpired(token, now = Date.now(), skewMs = 30000) {
|
|
741
|
+
const exp = jwtExpMs(token);
|
|
742
|
+
return exp !== null && now >= exp - skewMs;
|
|
743
|
+
}
|
|
744
|
+
async function refreshAccessToken(apiBase2, assertion, f = fetch) {
|
|
745
|
+
const res = await f(`${apiBase2.replace(/\/+$/, "")}/oauth2/token`, {
|
|
746
|
+
method: "POST",
|
|
747
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
748
|
+
body: new URLSearchParams({ grant_type: JWT_BEARER_GRANT, assertion }).toString()
|
|
749
|
+
});
|
|
750
|
+
if (!res.ok)
|
|
751
|
+
throw new Error(`token refresh failed (HTTP ${res.status})`);
|
|
752
|
+
const d = await res.json();
|
|
753
|
+
if (!d.access_token)
|
|
754
|
+
throw new Error("token refresh response missing access_token");
|
|
755
|
+
return { accessToken: d.access_token, expiresIn: d.expires_in ?? 3600, scope: d.scope };
|
|
756
|
+
}
|
|
757
|
+
async function getValidAccessToken(apiBase2, env = process.env, f = fetch, now = Date.now()) {
|
|
758
|
+
const auth = readAuth(env);
|
|
759
|
+
if (!auth?.accessToken)
|
|
760
|
+
return null;
|
|
761
|
+
if (!tokenExpired(auth.accessToken, now))
|
|
762
|
+
return auth.accessToken;
|
|
763
|
+
if (!auth.identityAssertion || tokenExpired(auth.identityAssertion, now, 0))
|
|
764
|
+
return null;
|
|
765
|
+
try {
|
|
766
|
+
const r = await refreshAccessToken(apiBase2, auth.identityAssertion, f);
|
|
767
|
+
const expMs = jwtExpMs(r.accessToken);
|
|
768
|
+
const next = {
|
|
769
|
+
...auth,
|
|
770
|
+
accessToken: r.accessToken,
|
|
771
|
+
scope: r.scope ?? auth.scope,
|
|
772
|
+
accessTokenExpiresAt: expMs ? new Date(expMs).toISOString() : auth.accessTokenExpiresAt
|
|
773
|
+
};
|
|
774
|
+
writeAuth(next, env);
|
|
775
|
+
return r.accessToken;
|
|
776
|
+
} catch {
|
|
777
|
+
return null;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
var JWT_BEARER_GRANT = "urn:ietf:params:oauth:grant-type:jwt-bearer";
|
|
781
|
+
var init_refresh = __esm(() => {
|
|
782
|
+
init_auth();
|
|
783
|
+
});
|
|
784
|
+
|
|
560
785
|
// src/lib/apiClient.ts
|
|
561
786
|
async function call(opts, method, path, body) {
|
|
562
787
|
const f = opts.fetchImpl ?? fetch;
|
|
@@ -607,20 +832,24 @@ __export(exports_team, {
|
|
|
607
832
|
roomCommand: () => roomCommand,
|
|
608
833
|
inviteCommand: () => inviteCommand
|
|
609
834
|
});
|
|
610
|
-
function client(env) {
|
|
611
|
-
|
|
612
|
-
if (!isLoggedIn(auth)) {
|
|
835
|
+
async function client(env) {
|
|
836
|
+
if (!isLoggedIn(readAuth(env))) {
|
|
613
837
|
console.error("Not logged in — run `vibegroup login` first.");
|
|
614
838
|
return null;
|
|
615
839
|
}
|
|
616
|
-
|
|
840
|
+
const token = await getValidAccessToken(apiBase(env), env);
|
|
841
|
+
if (!token) {
|
|
842
|
+
console.error("Session expired — run `vibegroup login` again.");
|
|
843
|
+
return null;
|
|
844
|
+
}
|
|
845
|
+
return { apiBase: apiBase(env), token };
|
|
617
846
|
}
|
|
618
847
|
async function teamCommand(rest, flags, env) {
|
|
619
848
|
if (rest[0] !== "create" || !rest[1]) {
|
|
620
849
|
console.error("usage: vibegroup team create <slug> [--name <name>]");
|
|
621
850
|
return 1;
|
|
622
851
|
}
|
|
623
|
-
const opts = client(env);
|
|
852
|
+
const opts = await client(env);
|
|
624
853
|
if (!opts)
|
|
625
854
|
return 1;
|
|
626
855
|
try {
|
|
@@ -639,7 +868,7 @@ async function roomCommand(rest, flags, env) {
|
|
|
639
868
|
console.error("usage: vibegroup room create <name> --team <slug>");
|
|
640
869
|
return 1;
|
|
641
870
|
}
|
|
642
|
-
const opts = client(env);
|
|
871
|
+
const opts = await client(env);
|
|
643
872
|
if (!opts)
|
|
644
873
|
return 1;
|
|
645
874
|
try {
|
|
@@ -658,7 +887,7 @@ async function roomsCommand(flags, env) {
|
|
|
658
887
|
console.error("usage: vibegroup rooms --team <slug>");
|
|
659
888
|
return 1;
|
|
660
889
|
}
|
|
661
|
-
const opts = client(env);
|
|
890
|
+
const opts = await client(env);
|
|
662
891
|
if (!opts)
|
|
663
892
|
return 1;
|
|
664
893
|
try {
|
|
@@ -678,7 +907,7 @@ async function inviteCommand(rest, flags, env) {
|
|
|
678
907
|
console.error("usage: vibegroup invite <email> --team <slug>");
|
|
679
908
|
return 1;
|
|
680
909
|
}
|
|
681
|
-
const opts = client(env);
|
|
910
|
+
const opts = await client(env);
|
|
682
911
|
if (!opts)
|
|
683
912
|
return 1;
|
|
684
913
|
try {
|
|
@@ -693,6 +922,7 @@ async function inviteCommand(rest, flags, env) {
|
|
|
693
922
|
var str = (v) => typeof v === "string" ? v : undefined;
|
|
694
923
|
var init_team = __esm(() => {
|
|
695
924
|
init_auth();
|
|
925
|
+
init_refresh();
|
|
696
926
|
init_apiClient();
|
|
697
927
|
init_cli();
|
|
698
928
|
});
|
|
@@ -729,10 +959,10 @@ function groupPeers(peers, selfMemberId) {
|
|
|
729
959
|
var DEFAULT_RELAY_HTTP = "https://relay.vibegroup.sh";
|
|
730
960
|
|
|
731
961
|
// src/ui/Who.tsx
|
|
732
|
-
import { useEffect as useEffect2, useRef as useRef2, useState as
|
|
733
|
-
import { Box as
|
|
962
|
+
import { useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
|
|
963
|
+
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
734
964
|
import { Spinner as Spinner2 } from "@inkjs/ui";
|
|
735
|
-
import { jsx as
|
|
965
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
736
966
|
function ago(ms) {
|
|
737
967
|
if (!ms)
|
|
738
968
|
return "—";
|
|
@@ -749,19 +979,24 @@ function Who({
|
|
|
749
979
|
relayHttp,
|
|
750
980
|
team,
|
|
751
981
|
room,
|
|
752
|
-
|
|
982
|
+
getToken,
|
|
753
983
|
selfMemberId,
|
|
754
984
|
onExit,
|
|
755
985
|
intervalMs = 3000
|
|
756
986
|
}) {
|
|
757
|
-
const [people, setPeople] =
|
|
758
|
-
const [selected, setSelected] =
|
|
759
|
-
const [error, setError] =
|
|
760
|
-
const [updated, setUpdated] =
|
|
987
|
+
const [people, setPeople] = useState3(null);
|
|
988
|
+
const [selected, setSelected] = useState3(0);
|
|
989
|
+
const [error, setError] = useState3("");
|
|
990
|
+
const [updated, setUpdated] = useState3(0);
|
|
761
991
|
const selRef = useRef2(0);
|
|
762
992
|
selRef.current = selected;
|
|
763
993
|
const refresh = async () => {
|
|
764
994
|
try {
|
|
995
|
+
const token = await getToken();
|
|
996
|
+
if (!token) {
|
|
997
|
+
setError("Session expired — run `vibegroup login` again.");
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
765
1000
|
const peers = await fetchPresence(relayHttp, team, room, token, fetch);
|
|
766
1001
|
const grouped = groupPeers(peers, selfMemberId);
|
|
767
1002
|
setPeople(grouped);
|
|
@@ -777,7 +1012,7 @@ function Who({
|
|
|
777
1012
|
const t = setInterval(() => void refresh(), intervalMs);
|
|
778
1013
|
return () => clearInterval(t);
|
|
779
1014
|
}, []);
|
|
780
|
-
|
|
1015
|
+
useInput2((input, key) => {
|
|
781
1016
|
if (input === "q" || key.escape || key.ctrl && input === "c")
|
|
782
1017
|
return onExit(0);
|
|
783
1018
|
if (input === "r")
|
|
@@ -791,30 +1026,30 @@ function Who({
|
|
|
791
1026
|
setSelected((s) => (s + 1) % n);
|
|
792
1027
|
});
|
|
793
1028
|
const sel = people && people.length > 0 ? people[Math.min(selected, people.length - 1)] : undefined;
|
|
794
|
-
return /* @__PURE__ */
|
|
1029
|
+
return /* @__PURE__ */ jsxs3(Box3, {
|
|
795
1030
|
flexDirection: "column",
|
|
796
1031
|
paddingX: 1,
|
|
797
1032
|
paddingY: 1,
|
|
798
1033
|
gap: 1,
|
|
799
1034
|
children: [
|
|
800
|
-
/* @__PURE__ */
|
|
1035
|
+
/* @__PURE__ */ jsxs3(Box3, {
|
|
801
1036
|
borderStyle: "round",
|
|
802
1037
|
borderColor: color.brand,
|
|
803
1038
|
paddingX: 1,
|
|
804
1039
|
justifyContent: "space-between",
|
|
805
1040
|
children: [
|
|
806
|
-
/* @__PURE__ */
|
|
1041
|
+
/* @__PURE__ */ jsxs3(Text3, {
|
|
807
1042
|
children: [
|
|
808
|
-
/* @__PURE__ */
|
|
1043
|
+
/* @__PURE__ */ jsx3(Text3, {
|
|
809
1044
|
color: color.brand,
|
|
810
1045
|
bold: true,
|
|
811
1046
|
children: "vibegroup"
|
|
812
1047
|
}),
|
|
813
|
-
/* @__PURE__ */
|
|
1048
|
+
/* @__PURE__ */ jsx3(Text3, {
|
|
814
1049
|
dimColor: true,
|
|
815
1050
|
children: " · "
|
|
816
1051
|
}),
|
|
817
|
-
/* @__PURE__ */
|
|
1052
|
+
/* @__PURE__ */ jsxs3(Text3, {
|
|
818
1053
|
color: color.accent,
|
|
819
1054
|
children: [
|
|
820
1055
|
team,
|
|
@@ -824,50 +1059,50 @@ function Who({
|
|
|
824
1059
|
})
|
|
825
1060
|
]
|
|
826
1061
|
}),
|
|
827
|
-
/* @__PURE__ */
|
|
1062
|
+
/* @__PURE__ */ jsx3(Text3, {
|
|
828
1063
|
dimColor: true,
|
|
829
1064
|
children: people ? `${people.length} ${people.length === 1 ? "person" : "people"}` : ""
|
|
830
1065
|
})
|
|
831
1066
|
]
|
|
832
1067
|
}),
|
|
833
|
-
!people && !error && /* @__PURE__ */
|
|
1068
|
+
!people && !error && /* @__PURE__ */ jsx3(Spinner2, {
|
|
834
1069
|
label: "Loading who's here…"
|
|
835
1070
|
}),
|
|
836
|
-
error && /* @__PURE__ */
|
|
1071
|
+
error && /* @__PURE__ */ jsxs3(Text3, {
|
|
837
1072
|
color: color.err,
|
|
838
1073
|
children: [
|
|
839
1074
|
"Couldn't read presence: ",
|
|
840
1075
|
error
|
|
841
1076
|
]
|
|
842
1077
|
}),
|
|
843
|
-
people && people.length === 0 && /* @__PURE__ */
|
|
1078
|
+
people && people.length === 0 && /* @__PURE__ */ jsx3(Text3, {
|
|
844
1079
|
dimColor: true,
|
|
845
1080
|
children: "No one's in this room yet."
|
|
846
1081
|
}),
|
|
847
|
-
people && people.length > 0 && /* @__PURE__ */
|
|
1082
|
+
people && people.length > 0 && /* @__PURE__ */ jsx3(Box3, {
|
|
848
1083
|
flexDirection: "column",
|
|
849
1084
|
children: people.map((p, i) => {
|
|
850
1085
|
const active = i === selected;
|
|
851
1086
|
const n = p.sessions.length;
|
|
852
|
-
return /* @__PURE__ */
|
|
1087
|
+
return /* @__PURE__ */ jsxs3(Box3, {
|
|
853
1088
|
children: [
|
|
854
|
-
/* @__PURE__ */
|
|
1089
|
+
/* @__PURE__ */ jsx3(Text3, {
|
|
855
1090
|
color: active ? color.accent : undefined,
|
|
856
1091
|
children: active ? "❯ " : " "
|
|
857
1092
|
}),
|
|
858
1093
|
dot(p.state),
|
|
859
|
-
/* @__PURE__ */
|
|
1094
|
+
/* @__PURE__ */ jsxs3(Text3, {
|
|
860
1095
|
bold: active,
|
|
861
1096
|
children: [
|
|
862
1097
|
" ",
|
|
863
1098
|
p.name || "unknown"
|
|
864
1099
|
]
|
|
865
1100
|
}),
|
|
866
|
-
p.isYou && /* @__PURE__ */
|
|
1101
|
+
p.isYou && /* @__PURE__ */ jsx3(Text3, {
|
|
867
1102
|
color: color.dim,
|
|
868
1103
|
children: " (you)"
|
|
869
1104
|
}),
|
|
870
|
-
/* @__PURE__ */
|
|
1105
|
+
/* @__PURE__ */ jsxs3(Text3, {
|
|
871
1106
|
dimColor: true,
|
|
872
1107
|
children: [
|
|
873
1108
|
" ",
|
|
@@ -882,13 +1117,13 @@ function Who({
|
|
|
882
1117
|
}, p.memberId || p.name);
|
|
883
1118
|
})
|
|
884
1119
|
}),
|
|
885
|
-
sel && /* @__PURE__ */
|
|
1120
|
+
sel && /* @__PURE__ */ jsxs3(Box3, {
|
|
886
1121
|
flexDirection: "column",
|
|
887
1122
|
borderStyle: "round",
|
|
888
1123
|
borderColor: color.dim,
|
|
889
1124
|
paddingX: 1,
|
|
890
1125
|
children: [
|
|
891
|
-
/* @__PURE__ */
|
|
1126
|
+
/* @__PURE__ */ jsxs3(Text3, {
|
|
892
1127
|
dimColor: true,
|
|
893
1128
|
children: [
|
|
894
1129
|
sel.name,
|
|
@@ -898,16 +1133,16 @@ function Who({
|
|
|
898
1133
|
sel.sessions.length === 1 ? "session" : "sessions"
|
|
899
1134
|
]
|
|
900
1135
|
}),
|
|
901
|
-
sel.sessions.slice().sort((a, b) => b.lastSeen - a.lastSeen).map((s) => /* @__PURE__ */
|
|
1136
|
+
sel.sessions.slice().sort((a, b) => b.lastSeen - a.lastSeen).map((s) => /* @__PURE__ */ jsxs3(Box3, {
|
|
902
1137
|
children: [
|
|
903
1138
|
dot(s.state),
|
|
904
|
-
/* @__PURE__ */
|
|
1139
|
+
/* @__PURE__ */ jsxs3(Text3, {
|
|
905
1140
|
children: [
|
|
906
1141
|
" ",
|
|
907
1142
|
s.session || shortId(s.peerId)
|
|
908
1143
|
]
|
|
909
1144
|
}),
|
|
910
|
-
/* @__PURE__ */
|
|
1145
|
+
/* @__PURE__ */ jsxs3(Text3, {
|
|
911
1146
|
dimColor: true,
|
|
912
1147
|
children: [
|
|
913
1148
|
" ",
|
|
@@ -922,7 +1157,7 @@ function Who({
|
|
|
922
1157
|
}, s.peerId))
|
|
923
1158
|
]
|
|
924
1159
|
}),
|
|
925
|
-
/* @__PURE__ */
|
|
1160
|
+
/* @__PURE__ */ jsxs3(Text3, {
|
|
926
1161
|
dimColor: true,
|
|
927
1162
|
children: [
|
|
928
1163
|
"↑↓ navigate · r refresh · q quit",
|
|
@@ -932,10 +1167,10 @@ function Who({
|
|
|
932
1167
|
]
|
|
933
1168
|
});
|
|
934
1169
|
}
|
|
935
|
-
var dot = (state) => state === "available" ? /* @__PURE__ */
|
|
1170
|
+
var dot = (state) => state === "available" ? /* @__PURE__ */ jsx3(Text3, {
|
|
936
1171
|
color: color.ok,
|
|
937
1172
|
children: "●"
|
|
938
|
-
}) : /* @__PURE__ */
|
|
1173
|
+
}) : /* @__PURE__ */ jsx3(Text3, {
|
|
939
1174
|
color: color.dim,
|
|
940
1175
|
children: "○"
|
|
941
1176
|
}), shortId = (peerId) => peerId.split("#")[1]?.slice(0, 8) ?? peerId.slice(0, 8);
|
|
@@ -948,7 +1183,7 @@ var exports_who = {};
|
|
|
948
1183
|
__export(exports_who, {
|
|
949
1184
|
whoCommand: () => whoCommand
|
|
950
1185
|
});
|
|
951
|
-
import { createElement as
|
|
1186
|
+
import { createElement as createElement3 } from "react";
|
|
952
1187
|
async function whoCommand(flags, env = process.env) {
|
|
953
1188
|
const auth = readAuth(env);
|
|
954
1189
|
if (!isLoggedIn(auth)) {
|
|
@@ -962,13 +1197,22 @@ async function whoCommand(flags, env = process.env) {
|
|
|
962
1197
|
}
|
|
963
1198
|
const room = str2(flags.room) ?? "general";
|
|
964
1199
|
const relayHttp = env.VIBEGROUP_RELAY_HTTP ?? DEFAULT_RELAY_HTTP;
|
|
965
|
-
|
|
1200
|
+
const apiBase2 = env.VIBEGROUP_API ?? "https://api.vibegroup.sh";
|
|
1201
|
+
return runInk((onExit) => createElement3(Who, {
|
|
1202
|
+
relayHttp,
|
|
1203
|
+
team,
|
|
1204
|
+
room,
|
|
1205
|
+
getToken: () => getValidAccessToken(apiBase2, env),
|
|
1206
|
+
selfMemberId: auth.user?.id,
|
|
1207
|
+
onExit
|
|
1208
|
+
}));
|
|
966
1209
|
}
|
|
967
1210
|
var str2 = (v) => typeof v === "string" ? v : undefined;
|
|
968
1211
|
var init_who = __esm(() => {
|
|
969
1212
|
init_runner();
|
|
970
1213
|
init_Who();
|
|
971
1214
|
init_auth();
|
|
1215
|
+
init_refresh();
|
|
972
1216
|
});
|
|
973
1217
|
|
|
974
1218
|
// src/lib/claudeLaunch.ts
|
|
@@ -1015,7 +1259,7 @@ function extractFlag(args, name) {
|
|
|
1015
1259
|
}
|
|
1016
1260
|
return { value, rest };
|
|
1017
1261
|
}
|
|
1018
|
-
function claudeCommand(args, env = process.env, launcher, channel = realChannelGate) {
|
|
1262
|
+
async function claudeCommand(args, env = process.env, launcher, channel = realChannelGate) {
|
|
1019
1263
|
if (!isLoggedIn(readAuth(env))) {
|
|
1020
1264
|
console.error("Not logged in — run `vibegroup login` first.");
|
|
1021
1265
|
return 1;
|
|
@@ -1028,6 +1272,10 @@ function claudeCommand(args, env = process.env, launcher, channel = realChannelG
|
|
|
1028
1272
|
console.error("No team selected — pass `--team <slug>` (the team whose room you want to join).");
|
|
1029
1273
|
return 1;
|
|
1030
1274
|
}
|
|
1275
|
+
if (!await getValidAccessToken(env.VIBEGROUP_API ?? DEFAULT_API_BASE, env)) {
|
|
1276
|
+
console.error("Session expired — run `vibegroup login` again.");
|
|
1277
|
+
return 1;
|
|
1278
|
+
}
|
|
1031
1279
|
const session = sessionFlag && sessionFlag.length > 0 ? sessionFlag : basename(process.cwd());
|
|
1032
1280
|
if (!dangerously && !channel.allowlisted()) {
|
|
1033
1281
|
console.log("Enabling the vibegroup channel — one-time, needs admin. Enter your password if prompted.");
|
|
@@ -1048,6 +1296,7 @@ var DEFAULT_API_BASE = "https://api.vibegroup.sh", realChannelGate;
|
|
|
1048
1296
|
var init_claudeCmd = __esm(() => {
|
|
1049
1297
|
init_claudeLaunch();
|
|
1050
1298
|
init_auth();
|
|
1299
|
+
init_refresh();
|
|
1051
1300
|
init_channel();
|
|
1052
1301
|
realChannelGate = { allowlisted: channelAllowlisted, enable: enableChannelWithSudo };
|
|
1053
1302
|
});
|
|
@@ -1104,9 +1353,10 @@ async function run(argv, env = process.env) {
|
|
|
1104
1353
|
}
|
|
1105
1354
|
switch (command) {
|
|
1106
1355
|
case "":
|
|
1107
|
-
case "help":
|
|
1108
|
-
|
|
1109
|
-
return
|
|
1356
|
+
case "help": {
|
|
1357
|
+
const { homeCommand: homeCommand2 } = await Promise.resolve().then(() => (init_home(), exports_home));
|
|
1358
|
+
return homeCommand2(env, (cmd) => run([cmd], env), VERSION, HELP);
|
|
1359
|
+
}
|
|
1110
1360
|
case "version":
|
|
1111
1361
|
console.log(VERSION);
|
|
1112
1362
|
return 0;
|