vibegroup 0.1.0 → 0.1.2
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/.claude-plugin/marketplace.json +18 -0
- package/dist/cli.js +405 -125
- package/package.json +35 -8
- package/plugin/.claude-plugin/plugin.json +13 -0
- package/plugin/.mcp.json +8 -0
- package/plugin/commands/init.md +57 -0
- package/plugin/commands/vibegroup.md +24 -0
- package/plugin/dist/channel.js +16960 -0
- package/plugin/hooks/hooks.json +7 -0
- package/plugin/hooks/session-start.sh +5 -0
- package/scripts/postinstall.mjs +38 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vibegroup",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "Terry Cruz Melo",
|
|
5
|
+
"email": "terry@cruz.pe"
|
|
6
|
+
},
|
|
7
|
+
"description": "vibegroup: let your Claude Code agents talk to each other across repos, machines, and networks.",
|
|
8
|
+
"plugins": [
|
|
9
|
+
{
|
|
10
|
+
"name": "vibegroup",
|
|
11
|
+
"source": "./plugin",
|
|
12
|
+
"description": "Ask peer Claude Code agents what they're working on, over an end-to-end-encrypted relay. Answers come from their live session via Claude Code Channels.",
|
|
13
|
+
"homepage": "https://vibegroup.sh",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"keywords": ["claude-code", "channel", "mcp", "agents", "collaboration", "relay"]
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -65,6 +65,67 @@ function isLoggedIn(tokens) {
|
|
|
65
65
|
}
|
|
66
66
|
var init_auth = () => {};
|
|
67
67
|
|
|
68
|
+
// package.json
|
|
69
|
+
var package_default;
|
|
70
|
+
var init_package = __esm(() => {
|
|
71
|
+
package_default = {
|
|
72
|
+
name: "vibegroup",
|
|
73
|
+
version: "0.1.2",
|
|
74
|
+
description: "Talk to your teammates' Claude Code agents — agent-to-agent collaboration for Claude Code over a shared channel.",
|
|
75
|
+
type: "module",
|
|
76
|
+
bin: {
|
|
77
|
+
vibegroup: "dist/cli.js"
|
|
78
|
+
},
|
|
79
|
+
files: [
|
|
80
|
+
"dist/cli.js",
|
|
81
|
+
"README.md",
|
|
82
|
+
"scripts/postinstall.mjs",
|
|
83
|
+
".claude-plugin/marketplace.json",
|
|
84
|
+
"plugin/.claude-plugin",
|
|
85
|
+
"plugin/.mcp.json",
|
|
86
|
+
"plugin/dist",
|
|
87
|
+
"plugin/commands",
|
|
88
|
+
"plugin/hooks"
|
|
89
|
+
],
|
|
90
|
+
engines: {
|
|
91
|
+
node: ">=18"
|
|
92
|
+
},
|
|
93
|
+
license: "UNLICENSED",
|
|
94
|
+
homepage: "https://vibegroup.sh",
|
|
95
|
+
bugs: {
|
|
96
|
+
url: "https://vibegroup.sh"
|
|
97
|
+
},
|
|
98
|
+
keywords: ["claude", "claude-code", "agents", "collaboration", "cli", "vibegroup"],
|
|
99
|
+
publishConfig: {
|
|
100
|
+
access: "public"
|
|
101
|
+
},
|
|
102
|
+
workspaces: ["packages/*", "plugin"],
|
|
103
|
+
scripts: {
|
|
104
|
+
"build:relay": "cd packages/relay && bun run build",
|
|
105
|
+
"build:plugin": "cd plugin && bun run build",
|
|
106
|
+
"build:cli": "bun run scripts/build-cli.ts",
|
|
107
|
+
"build:server": "cd packages/server && bun run build",
|
|
108
|
+
build: "bun run build:relay && bun run build:server && bun run build:plugin && bun run build:cli",
|
|
109
|
+
prepublishOnly: "bun run build:plugin && bun run build:cli",
|
|
110
|
+
postinstall: "node scripts/postinstall.mjs",
|
|
111
|
+
"test:cli": "bun test ./test/",
|
|
112
|
+
"test:protocol": "cd packages/protocol && bun test",
|
|
113
|
+
"test:relay": "cd packages/relay && bun test",
|
|
114
|
+
"test:server": "cd packages/server && bun test",
|
|
115
|
+
"test:plugin": "cd plugin && bun test",
|
|
116
|
+
test: "bun run test:cli && bun run test:protocol && bun run test:relay && bun run test:server && bun run test:plugin"
|
|
117
|
+
},
|
|
118
|
+
dependencies: {
|
|
119
|
+
"@inkjs/ui": "^2.0.0",
|
|
120
|
+
ink: "^7.1.0",
|
|
121
|
+
react: "^19.2.7"
|
|
122
|
+
},
|
|
123
|
+
devDependencies: {
|
|
124
|
+
"@types/react": "^19.2.17"
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
|
|
68
129
|
// src/lib/allowlist.ts
|
|
69
130
|
function managedSettingsPath(plat = process.platform) {
|
|
70
131
|
if (plat === "darwin")
|
|
@@ -81,6 +142,12 @@ function mergeManagedSettings(existing) {
|
|
|
81
142
|
next.allowedChannelPlugins = has ? current : [...current, VIBEGROUP_ENTRY];
|
|
82
143
|
return next;
|
|
83
144
|
}
|
|
145
|
+
function isAllowlisted(existing) {
|
|
146
|
+
if (!existing || existing.channelsEnabled !== true)
|
|
147
|
+
return false;
|
|
148
|
+
const current = Array.isArray(existing.allowedChannelPlugins) ? existing.allowedChannelPlugins : [];
|
|
149
|
+
return current.some((e) => e?.marketplace === "vibegroup" && e?.plugin === "vibegroup");
|
|
150
|
+
}
|
|
84
151
|
var VIBEGROUP_ENTRY;
|
|
85
152
|
var init_allowlist = __esm(() => {
|
|
86
153
|
VIBEGROUP_ENTRY = { marketplace: "vibegroup", plugin: "vibegroup" };
|
|
@@ -111,6 +178,25 @@ var init_allowlist2 = __esm(() => {
|
|
|
111
178
|
init_allowlist();
|
|
112
179
|
});
|
|
113
180
|
|
|
181
|
+
// src/ui/runner.ts
|
|
182
|
+
import { render } from "ink";
|
|
183
|
+
function runInk(make) {
|
|
184
|
+
return new Promise((resolve) => {
|
|
185
|
+
let done = false;
|
|
186
|
+
let instance;
|
|
187
|
+
const finish = (code) => {
|
|
188
|
+
if (done)
|
|
189
|
+
return;
|
|
190
|
+
done = true;
|
|
191
|
+
instance?.unmount();
|
|
192
|
+
resolve(code);
|
|
193
|
+
};
|
|
194
|
+
instance = render(make(finish));
|
|
195
|
+
instance.waitUntilExit().then(() => finish(130));
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
var init_runner = () => {};
|
|
199
|
+
|
|
114
200
|
// src/lib/workosAuth.ts
|
|
115
201
|
async function json(res) {
|
|
116
202
|
const text = await res.text();
|
|
@@ -191,18 +277,6 @@ async function register(ep, body, f) {
|
|
|
191
277
|
claim: parseCeremony(data.claim)
|
|
192
278
|
};
|
|
193
279
|
}
|
|
194
|
-
async function startClaim(ep, claimToken, email, f) {
|
|
195
|
-
const res = await f(ep.claimEndpoint, {
|
|
196
|
-
method: "POST",
|
|
197
|
-
headers: { "content-type": "application/json" },
|
|
198
|
-
body: JSON.stringify({ claim_token: claimToken, email })
|
|
199
|
-
});
|
|
200
|
-
const data = await json(res);
|
|
201
|
-
const ceremony = parseCeremony(data.claim_attempt) ?? parseCeremony(data.claim);
|
|
202
|
-
if (!ceremony)
|
|
203
|
-
throw new Error(`claim start failed: ${data.error ?? `HTTP ${res.status}`}`);
|
|
204
|
-
return ceremony;
|
|
205
|
-
}
|
|
206
280
|
async function pollClaim(tokenEndpoint, claimToken, f, nowMs = Date.now()) {
|
|
207
281
|
const res = await f(tokenEndpoint, {
|
|
208
282
|
method: "POST",
|
|
@@ -223,24 +297,7 @@ async function pollClaim(tokenEndpoint, claimToken, f, nowMs = Date.now()) {
|
|
|
223
297
|
throw new Error(`claim poll failed: ${data.error ?? `HTTP ${res.status}`}`);
|
|
224
298
|
}
|
|
225
299
|
}
|
|
226
|
-
|
|
227
|
-
const params = new URLSearchParams({ grant_type: JWT_BEARER_GRANT, assertion });
|
|
228
|
-
if (resource)
|
|
229
|
-
params.set("resource", resource);
|
|
230
|
-
const res = await f(tokenEndpoint, {
|
|
231
|
-
method: "POST",
|
|
232
|
-
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
233
|
-
body: params.toString()
|
|
234
|
-
});
|
|
235
|
-
const data = await json(res);
|
|
236
|
-
if (!res.ok || !data.access_token)
|
|
237
|
-
throw new Error(`assertion exchange failed: ${data.error ?? `HTTP ${res.status}`}`);
|
|
238
|
-
const tokens = tokensFromResponse(data, nowMs);
|
|
239
|
-
if (!tokens.identityAssertion)
|
|
240
|
-
tokens.identityAssertion = assertion;
|
|
241
|
-
return tokens;
|
|
242
|
-
}
|
|
243
|
-
var CLAIM_GRANT = "urn:workos:agent-auth:grant-type:claim", JWT_BEARER_GRANT = "urn:ietf:params:oauth:grant-type:jwt-bearer";
|
|
300
|
+
var CLAIM_GRANT = "urn:workos:agent-auth:grant-type:claim";
|
|
244
301
|
|
|
245
302
|
// src/lib/loginFlow.ts
|
|
246
303
|
async function login(apiBase, f, hooks) {
|
|
@@ -250,116 +307,332 @@ async function login(apiBase, f, hooks) {
|
|
|
250
307
|
emit({ type: "discovering", apiBase });
|
|
251
308
|
const ep = await discover(apiBase, f);
|
|
252
309
|
emit({ type: "discovered", endpoints: ep });
|
|
253
|
-
|
|
310
|
+
const email = (await hooks.email())?.trim();
|
|
311
|
+
if (!email)
|
|
254
312
|
return null;
|
|
255
|
-
emit({ type: "registering" });
|
|
256
|
-
const reg = await register(ep, { type: "
|
|
257
|
-
if (!reg.
|
|
258
|
-
throw new Error("
|
|
259
|
-
|
|
260
|
-
emit({ type: "
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
let interval = Math.max(1, ceremony.interval);
|
|
269
|
-
for (;; ) {
|
|
270
|
-
const r = await pollClaim(ep.tokenEndpoint, reg.claimToken, f, now());
|
|
271
|
-
if (r.status === "done") {
|
|
272
|
-
tokens = r.tokens;
|
|
273
|
-
emit({ type: "unlocked", scopes: reg.postClaimScopes });
|
|
274
|
-
return { tokens, claimed: true };
|
|
275
|
-
}
|
|
276
|
-
if (r.status === "expired")
|
|
277
|
-
break;
|
|
278
|
-
if (r.status === "slow_down")
|
|
279
|
-
interval += 5;
|
|
280
|
-
await sleep(interval * 1000);
|
|
313
|
+
emit({ type: "registering", email });
|
|
314
|
+
const reg = await register(ep, { type: "service_auth", login_hint: email }, f);
|
|
315
|
+
if (!reg.claim || !reg.claimToken)
|
|
316
|
+
throw new Error("service_auth did not return a claim ceremony");
|
|
317
|
+
emit({ type: "claim", ceremony: reg.claim });
|
|
318
|
+
emit({ type: "polling" });
|
|
319
|
+
let interval = Math.max(1, reg.claim.interval);
|
|
320
|
+
const deadline = now() + (hooks.timeoutMs ?? 10 * 60 * 1000);
|
|
321
|
+
for (;; ) {
|
|
322
|
+
const r = await pollClaim(ep.tokenEndpoint, reg.claimToken, f, now());
|
|
323
|
+
if (r.status === "done") {
|
|
324
|
+
emit({ type: "done" });
|
|
325
|
+
return { tokens: r.tokens, email };
|
|
281
326
|
}
|
|
327
|
+
if (r.status === "expired")
|
|
328
|
+
throw new Error("the code expired — run `vibegroup login` again");
|
|
329
|
+
if (r.status === "slow_down")
|
|
330
|
+
interval += 5;
|
|
331
|
+
if (now() > deadline)
|
|
332
|
+
throw new Error("login timed out");
|
|
333
|
+
await sleep(interval * 1000);
|
|
282
334
|
}
|
|
283
|
-
return { tokens, claimed: false };
|
|
284
335
|
}
|
|
285
336
|
var defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
286
337
|
var init_loginFlow = () => {};
|
|
287
338
|
|
|
339
|
+
// src/ui/session.ts
|
|
340
|
+
function jwtClaim(jwt, key) {
|
|
341
|
+
try {
|
|
342
|
+
return JSON.parse(Buffer.from(jwt.split(".")[1], "base64url").toString("utf8"))[key];
|
|
343
|
+
} catch {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function sessionFromTokens(tokens, email) {
|
|
348
|
+
const sub = jwtClaim(tokens.accessToken, "sub");
|
|
349
|
+
const exp = jwtClaim(tokens.accessToken, "exp");
|
|
350
|
+
return {
|
|
351
|
+
...tokens,
|
|
352
|
+
user: { id: typeof sub === "string" ? sub : email, email },
|
|
353
|
+
accessTokenExpiresAt: typeof exp === "number" ? new Date(exp * 1000).toISOString() : tokens.accessTokenExpiresAt
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// src/lib/openBrowser.ts
|
|
358
|
+
import { spawn } from "node:child_process";
|
|
359
|
+
function openBrowser(url) {
|
|
360
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
361
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
362
|
+
try {
|
|
363
|
+
spawn(cmd, args, { stdio: "ignore", detached: true }).unref();
|
|
364
|
+
} catch {}
|
|
365
|
+
}
|
|
366
|
+
var init_openBrowser = () => {};
|
|
367
|
+
|
|
368
|
+
// src/ui/theme.ts
|
|
369
|
+
var color;
|
|
370
|
+
var init_theme = __esm(() => {
|
|
371
|
+
color = {
|
|
372
|
+
brand: "magenta",
|
|
373
|
+
accent: "cyan",
|
|
374
|
+
ok: "green",
|
|
375
|
+
warn: "yellow",
|
|
376
|
+
err: "red",
|
|
377
|
+
dim: "gray"
|
|
378
|
+
};
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// src/ui/Login.tsx
|
|
382
|
+
import { useEffect, useRef, useState } from "react";
|
|
383
|
+
import { Box, Text } from "ink";
|
|
384
|
+
import { Spinner, StatusMessage, TextInput, Alert, Badge } from "@inkjs/ui";
|
|
385
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
386
|
+
function Login({
|
|
387
|
+
apiBase,
|
|
388
|
+
initialEmail,
|
|
389
|
+
onExit
|
|
390
|
+
}) {
|
|
391
|
+
const [phase, setPhase] = useState(initialEmail ? { t: "connecting" } : { t: "email" });
|
|
392
|
+
const opened = useRef(false);
|
|
393
|
+
const started = useRef(false);
|
|
394
|
+
const start = (email) => {
|
|
395
|
+
if (started.current)
|
|
396
|
+
return;
|
|
397
|
+
started.current = true;
|
|
398
|
+
setPhase({ t: "connecting" });
|
|
399
|
+
login(apiBase, fetch, {
|
|
400
|
+
email: async () => email,
|
|
401
|
+
onEvent: (e) => {
|
|
402
|
+
if (e.type === "registering")
|
|
403
|
+
setPhase({ t: "registering", email: e.email });
|
|
404
|
+
else if (e.type === "claim")
|
|
405
|
+
setPhase({ t: "waiting", code: e.ceremony.userCode, url: e.ceremony.verificationUri });
|
|
406
|
+
}
|
|
407
|
+
}).then((res) => {
|
|
408
|
+
if (!res) {
|
|
409
|
+
setPhase({ t: "error", message: "Login cancelled." });
|
|
410
|
+
setTimeout(() => onExit(1), 50);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
writeAuth(sessionFromTokens(res.tokens, res.email));
|
|
414
|
+
setPhase({ t: "done", email: res.email });
|
|
415
|
+
setTimeout(() => onExit(0), 500);
|
|
416
|
+
}).catch((err) => {
|
|
417
|
+
setPhase({ t: "error", message: err?.message ?? String(err) });
|
|
418
|
+
setTimeout(() => onExit(1), 800);
|
|
419
|
+
});
|
|
420
|
+
};
|
|
421
|
+
useEffect(() => {
|
|
422
|
+
if (initialEmail)
|
|
423
|
+
start(initialEmail);
|
|
424
|
+
}, []);
|
|
425
|
+
useEffect(() => {
|
|
426
|
+
if (phase.t === "waiting" && !opened.current) {
|
|
427
|
+
opened.current = true;
|
|
428
|
+
openBrowser(phase.url);
|
|
429
|
+
}
|
|
430
|
+
}, [phase]);
|
|
431
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
432
|
+
flexDirection: "column",
|
|
433
|
+
gap: 1,
|
|
434
|
+
paddingY: 1,
|
|
435
|
+
children: [
|
|
436
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
437
|
+
children: [
|
|
438
|
+
/* @__PURE__ */ jsx(Text, {
|
|
439
|
+
color: color.brand,
|
|
440
|
+
bold: true,
|
|
441
|
+
children: "vibegroup"
|
|
442
|
+
}),
|
|
443
|
+
/* @__PURE__ */ jsx(Text, {
|
|
444
|
+
dimColor: true,
|
|
445
|
+
children: " · sign in"
|
|
446
|
+
})
|
|
447
|
+
]
|
|
448
|
+
}),
|
|
449
|
+
phase.t === "email" && /* @__PURE__ */ jsxs(Box, {
|
|
450
|
+
flexDirection: "column",
|
|
451
|
+
children: [
|
|
452
|
+
/* @__PURE__ */ jsx(Text, {
|
|
453
|
+
children: "Email to sign in with:"
|
|
454
|
+
}),
|
|
455
|
+
/* @__PURE__ */ jsx(Box, {
|
|
456
|
+
marginTop: 1,
|
|
457
|
+
children: /* @__PURE__ */ jsx(TextInput, {
|
|
458
|
+
placeholder: "you@company.com",
|
|
459
|
+
onSubmit: (value) => {
|
|
460
|
+
const email = value.trim();
|
|
461
|
+
if (email)
|
|
462
|
+
start(email);
|
|
463
|
+
else
|
|
464
|
+
onExit(1);
|
|
465
|
+
}
|
|
466
|
+
})
|
|
467
|
+
})
|
|
468
|
+
]
|
|
469
|
+
}),
|
|
470
|
+
phase.t === "connecting" && /* @__PURE__ */ jsx(Spinner, {
|
|
471
|
+
label: "Connecting to vibegroup…"
|
|
472
|
+
}),
|
|
473
|
+
phase.t === "registering" && /* @__PURE__ */ jsx(Spinner, {
|
|
474
|
+
label: `Registering ${phase.email}…`
|
|
475
|
+
}),
|
|
476
|
+
phase.t === "waiting" && /* @__PURE__ */ jsxs(Box, {
|
|
477
|
+
flexDirection: "column",
|
|
478
|
+
gap: 1,
|
|
479
|
+
children: [
|
|
480
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
481
|
+
borderStyle: "round",
|
|
482
|
+
borderColor: color.accent,
|
|
483
|
+
paddingX: 1,
|
|
484
|
+
flexDirection: "column",
|
|
485
|
+
children: [
|
|
486
|
+
/* @__PURE__ */ jsx(Text, {
|
|
487
|
+
children: "Open this link, sign in, and enter the code:"
|
|
488
|
+
}),
|
|
489
|
+
/* @__PURE__ */ jsx(Box, {
|
|
490
|
+
marginY: 1,
|
|
491
|
+
children: /* @__PURE__ */ jsx(Badge, {
|
|
492
|
+
color: "cyan",
|
|
493
|
+
children: phase.code
|
|
494
|
+
})
|
|
495
|
+
}),
|
|
496
|
+
/* @__PURE__ */ jsx(Text, {
|
|
497
|
+
color: color.accent,
|
|
498
|
+
children: phase.url
|
|
499
|
+
}),
|
|
500
|
+
/* @__PURE__ */ jsx(Text, {
|
|
501
|
+
dimColor: true,
|
|
502
|
+
children: "(the code goes into that page — not back here)"
|
|
503
|
+
})
|
|
504
|
+
]
|
|
505
|
+
}),
|
|
506
|
+
/* @__PURE__ */ jsx(Spinner, {
|
|
507
|
+
label: "Waiting for you to confirm in the browser…"
|
|
508
|
+
})
|
|
509
|
+
]
|
|
510
|
+
}),
|
|
511
|
+
phase.t === "done" && /* @__PURE__ */ jsxs(StatusMessage, {
|
|
512
|
+
variant: "success",
|
|
513
|
+
children: [
|
|
514
|
+
"Signed in as ",
|
|
515
|
+
phase.email,
|
|
516
|
+
". You're good to go!"
|
|
517
|
+
]
|
|
518
|
+
}),
|
|
519
|
+
phase.t === "error" && /* @__PURE__ */ jsx(Alert, {
|
|
520
|
+
variant: "error",
|
|
521
|
+
children: phase.message
|
|
522
|
+
})
|
|
523
|
+
]
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
var init_Login = __esm(() => {
|
|
527
|
+
init_loginFlow();
|
|
528
|
+
init_auth();
|
|
529
|
+
init_openBrowser();
|
|
530
|
+
init_theme();
|
|
531
|
+
});
|
|
532
|
+
|
|
288
533
|
// src/commands/login.ts
|
|
289
534
|
var exports_login = {};
|
|
290
535
|
__export(exports_login, {
|
|
291
536
|
loginCommand: () => loginCommand
|
|
292
537
|
});
|
|
293
|
-
import {
|
|
294
|
-
async function
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
return (await rl.question(question)).trim();
|
|
298
|
-
} finally {
|
|
299
|
-
rl.close();
|
|
300
|
-
}
|
|
538
|
+
import { createElement } from "react";
|
|
539
|
+
async function loginCommand(rest, env = process.env) {
|
|
540
|
+
const base = apiBase(env);
|
|
541
|
+
return runInk((onExit) => createElement(Login, { apiBase: base, initialEmail: rest[0], onExit }));
|
|
301
542
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
break;
|
|
320
|
-
case "claim":
|
|
321
|
-
console.log(`
|
|
322
|
-
To unlock your account, sign in at vibegroup and enter this code: ${e.ceremony.userCode}`);
|
|
323
|
-
console.log(`${e.ceremony.verificationUri} — the code goes in there, not back to me.`);
|
|
324
|
-
break;
|
|
325
|
-
case "polling":
|
|
326
|
-
console.log(`
|
|
327
|
-
Waiting for you to confirm…`);
|
|
328
|
-
break;
|
|
329
|
-
case "unlocked":
|
|
330
|
-
console.log(" └ Unlocked · full access");
|
|
331
|
-
break;
|
|
332
|
-
}
|
|
543
|
+
var init_login = __esm(() => {
|
|
544
|
+
init_runner();
|
|
545
|
+
init_Login();
|
|
546
|
+
init_cli();
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
// src/commands/init.ts
|
|
550
|
+
var exports_init = {};
|
|
551
|
+
__export(exports_init, {
|
|
552
|
+
initCommand: () => initCommand
|
|
553
|
+
});
|
|
554
|
+
import { spawnSync } from "node:child_process";
|
|
555
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
556
|
+
import { fileURLToPath } from "node:url";
|
|
557
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
558
|
+
function packageRoot() {
|
|
559
|
+
return join2(dirname2(fileURLToPath(import.meta.url)), "..");
|
|
333
560
|
}
|
|
334
|
-
|
|
335
|
-
|
|
561
|
+
function commandExists(cmd) {
|
|
562
|
+
return spawnSync(cmd, ["--version"], { stdio: "ignore" }).status === 0;
|
|
563
|
+
}
|
|
564
|
+
function pluginInstalled() {
|
|
565
|
+
const r = spawnSync("claude", ["plugin", "list"], { encoding: "utf8" });
|
|
566
|
+
return r.status === 0 && /vibegroup@vibegroup/.test(r.stdout ?? "");
|
|
567
|
+
}
|
|
568
|
+
function readManagedSettings() {
|
|
336
569
|
try {
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
570
|
+
const p = managedSettingsPath();
|
|
571
|
+
if (existsSync3(p))
|
|
572
|
+
return JSON.parse(readFileSync3(p, "utf8"));
|
|
573
|
+
} catch {}
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
function writeAllowlistWithSudo() {
|
|
577
|
+
const path = managedSettingsPath();
|
|
578
|
+
const json2 = JSON.stringify(mergeManagedSettings(readManagedSettings()), null, 2);
|
|
579
|
+
const script = `mkdir -p "${dirname2(path)}" && cat > "${path}"`;
|
|
580
|
+
const r = spawnSync("sudo", ["sh", "-c", script], { input: json2, stdio: ["pipe", "inherit", "inherit"] });
|
|
581
|
+
return r.status === 0;
|
|
582
|
+
}
|
|
583
|
+
async function initCommand(rest, flags = {}, env = process.env) {
|
|
584
|
+
const dev = flags.dev === true;
|
|
585
|
+
console.log(`vibegroup setup
|
|
586
|
+
`);
|
|
587
|
+
if (!commandExists("claude")) {
|
|
588
|
+
console.error("Claude Code (`claude`) is not on your PATH. Install it first: https://docs.claude.com/claude-code");
|
|
589
|
+
return 1;
|
|
590
|
+
}
|
|
591
|
+
if (pluginInstalled()) {
|
|
592
|
+
console.log("✓ Plugin already installed.");
|
|
593
|
+
} else {
|
|
594
|
+
console.log("• Installing the vibegroup plugin into Claude Code…");
|
|
595
|
+
spawnSync("claude", ["plugin", "marketplace", "add", packageRoot()], { stdio: "inherit" });
|
|
596
|
+
spawnSync("claude", ["plugin", "install", "vibegroup@vibegroup"], { stdio: "inherit" });
|
|
597
|
+
if (!pluginInstalled()) {
|
|
598
|
+
console.error("Plugin install did not complete. Try manually: claude plugin install vibegroup@vibegroup");
|
|
347
599
|
return 1;
|
|
348
600
|
}
|
|
349
|
-
|
|
350
|
-
console.log(result.claimed ? `
|
|
351
|
-
Logged in. You're good to go!` : "\nRegistered with limited access. Run `vibegroup login` again anytime to link your account and unlock full access.");
|
|
352
|
-
return 0;
|
|
353
|
-
} catch (err) {
|
|
354
|
-
console.error(`
|
|
355
|
-
login failed: ${err.message}`);
|
|
356
|
-
return 1;
|
|
601
|
+
console.log("✓ Plugin installed.");
|
|
357
602
|
}
|
|
603
|
+
if (dev) {
|
|
604
|
+
console.log("• Dev mode: skipping the channel allowlist (launch with `vibegroup claude --dev`).");
|
|
605
|
+
} else if (isAllowlisted(readManagedSettings())) {
|
|
606
|
+
console.log("✓ Channel already enabled.");
|
|
607
|
+
} else {
|
|
608
|
+
console.log(`
|
|
609
|
+
• Enabling the vibegroup channel needs admin once (Claude Code gates channel plugins).`);
|
|
610
|
+
console.log(` Writing ${managedSettingsPath()} — enter your password if prompted.`);
|
|
611
|
+
if (!writeAllowlistWithSudo()) {
|
|
612
|
+
console.error(" Could not write managed settings. Retry, or run `vibegroup init --dev` to skip it and use `vibegroup claude --dev`.");
|
|
613
|
+
return 1;
|
|
614
|
+
}
|
|
615
|
+
console.log("✓ Channel enabled.");
|
|
616
|
+
}
|
|
617
|
+
if (isLoggedIn(readAuth(env))) {
|
|
618
|
+
console.log("✓ Already signed in.");
|
|
619
|
+
} else {
|
|
620
|
+
console.log(`
|
|
621
|
+
• Sign in to vibegroup:`);
|
|
622
|
+
const code = await loginCommand(rest, env);
|
|
623
|
+
if (code !== 0)
|
|
624
|
+
return code;
|
|
625
|
+
}
|
|
626
|
+
console.log(`
|
|
627
|
+
✓ Setup complete. Next:`);
|
|
628
|
+
console.log(' vibegroup team create <slug> --name "<name>" # if you need a team');
|
|
629
|
+
console.log(" vibegroup claude --team <slug> # launch a channel session");
|
|
630
|
+
return 0;
|
|
358
631
|
}
|
|
359
|
-
var
|
|
360
|
-
init_loginFlow();
|
|
632
|
+
var init_init = __esm(() => {
|
|
361
633
|
init_auth();
|
|
362
|
-
|
|
634
|
+
init_allowlist();
|
|
635
|
+
init_login();
|
|
363
636
|
});
|
|
364
637
|
|
|
365
638
|
// src/lib/apiClient.ts
|
|
@@ -480,7 +753,7 @@ var init_team = __esm(() => {
|
|
|
480
753
|
});
|
|
481
754
|
|
|
482
755
|
// src/lib/claudeLaunch.ts
|
|
483
|
-
import { spawnSync } from "node:child_process";
|
|
756
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
484
757
|
function buildClaudeArgs(opts = {}) {
|
|
485
758
|
const extra = opts.extraArgs ?? [];
|
|
486
759
|
return opts.dangerously ? ["--dangerously-load-development-channels", CHANNEL_SPEC, ...extra] : ["--channels", CHANNEL_SPEC, ...extra];
|
|
@@ -489,7 +762,7 @@ function launchClaude(opts = {}, launcher = defaultLauncher) {
|
|
|
489
762
|
return launcher("claude", buildClaudeArgs(opts), opts.env);
|
|
490
763
|
}
|
|
491
764
|
var CHANNEL_SPEC = "plugin:vibegroup@vibegroup", defaultLauncher = (cmd, args, env) => {
|
|
492
|
-
const r =
|
|
765
|
+
const r = spawnSync2(cmd, args, { stdio: "inherit", env: env ? { ...process.env, ...env } : process.env });
|
|
493
766
|
if (r.error)
|
|
494
767
|
throw r.error;
|
|
495
768
|
return r.status ?? 0;
|
|
@@ -620,9 +893,13 @@ async function run(argv, env = process.env) {
|
|
|
620
893
|
const { allowlistPathCommand: allowlistPathCommand2 } = await Promise.resolve().then(() => (init_allowlist2(), exports_allowlist));
|
|
621
894
|
return allowlistPathCommand2();
|
|
622
895
|
}
|
|
896
|
+
case "init": {
|
|
897
|
+
const { initCommand: initCommand2 } = await Promise.resolve().then(() => (init_init(), exports_init));
|
|
898
|
+
return initCommand2(rest, flags, env);
|
|
899
|
+
}
|
|
623
900
|
case "login": {
|
|
624
901
|
const { loginCommand: loginCommand2 } = await Promise.resolve().then(() => (init_login(), exports_login));
|
|
625
|
-
return loginCommand2(env);
|
|
902
|
+
return loginCommand2(rest, env);
|
|
626
903
|
}
|
|
627
904
|
case "team": {
|
|
628
905
|
const { teamCommand: teamCommand2 } = await Promise.resolve().then(() => (init_team(), exports_team));
|
|
@@ -647,12 +924,13 @@ async function run(argv, env = process.env) {
|
|
|
647
924
|
return 1;
|
|
648
925
|
}
|
|
649
926
|
}
|
|
650
|
-
var VERSION
|
|
927
|
+
var VERSION, DEFAULT_API_BASE2 = "https://api.vibegroup.sh", HELP = `vibegroup — talk to your teammates' Claude Code agents.
|
|
651
928
|
|
|
652
929
|
Usage: vibegroup <command> [options]
|
|
653
930
|
|
|
654
931
|
Commands:
|
|
655
|
-
|
|
932
|
+
init [email] One-time setup: install the plugin, enable the channel, sign in
|
|
933
|
+
login [email] Sign in / sign up (opens your browser, verifies email)
|
|
656
934
|
logout Clear the cached session
|
|
657
935
|
status Show your auth + connection status
|
|
658
936
|
team create <slug> [--name] Create a team (a WorkOS org + a general room)
|
|
@@ -665,6 +943,8 @@ Commands:
|
|
|
665
943
|
Run a command with --help for more.`;
|
|
666
944
|
var init_cli = __esm(() => {
|
|
667
945
|
init_auth();
|
|
946
|
+
init_package();
|
|
947
|
+
VERSION = package_default.version;
|
|
668
948
|
});
|
|
669
949
|
|
|
670
950
|
// src/bin.ts
|