@lightupai/polaris 0.0.102 → 0.0.104
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/Makefile +1 -1
- package/docker-compose.yml +5 -0
- package/package.json +1 -1
- package/skills/polaris/SKILL.md +9 -5
- package/src/cli/cli.ts +20 -12
- package/src/client/client.ts +32 -1
- package/src/daemon/daemon.ts +30 -0
- package/src/service/db.ts +42 -2
- package/src/service/server.ts +13 -0
package/Makefile
CHANGED
package/docker-compose.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightupai/polaris",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.104",
|
|
4
4
|
"description": "Record and stream Claude Code sessions to Slack. Like Gong for AI coding — capture every prompt, response, and tool call. Multiplayer collaboration across AI coding agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
package/skills/polaris/SKILL.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: polaris
|
|
3
3
|
description: Connect to a Polaris multiplayer collaboration session
|
|
4
|
-
allowed-tools: polaris_connect polaris_disconnect polaris_status polaris_reply polaris_context polaris_rename polaris_backfill polaris_team
|
|
5
|
-
argument-hint: [join #channel | tag [name] | backfill [duration] | rename <new-name> | disconnect | (no args for status)]
|
|
4
|
+
allowed-tools: polaris_connect polaris_disconnect polaris_status polaris_reply polaris_context polaris_rename polaris_rename_session polaris_backfill polaris_team
|
|
5
|
+
argument-hint: [join #channel [session-name] | tag [name] | backfill [duration] | rename <new-name> | rename-session <name> | disconnect | (no args for status)]
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
## Polaris — Multiplayer Collaboration
|
|
@@ -13,8 +13,8 @@ Manage your connection to a Polaris collaboration session.
|
|
|
13
13
|
|
|
14
14
|
Based on the arguments provided, do ONE of the following:
|
|
15
15
|
|
|
16
|
-
**`/polaris join #channel-name`** — Connect to an existing project, or create a new one:
|
|
17
|
-
1. Call `polaris_connect` with the given channel
|
|
16
|
+
**`/polaris join #channel-name [session-name]`** — Connect to an existing project, or create a new one:
|
|
17
|
+
1. Call `polaris_connect` with the given channel, user identity `user:manu.bansal`, and optional `session` name (e.g. `auth-feature`, `bugfix-154`)
|
|
18
18
|
2. If the response is `project_not_found`:
|
|
19
19
|
- Show the list of existing projects from the response
|
|
20
20
|
- Ask the user: "Project '#channel-name' doesn't exist. Create it? [y/N]"
|
|
@@ -22,10 +22,14 @@ Based on the arguments provided, do ONE of the following:
|
|
|
22
22
|
- If no: stop and show the existing project list so they can pick one
|
|
23
23
|
3. Report the connection status including the session name
|
|
24
24
|
|
|
25
|
-
**`/polaris rename <new-name>`** — Rename the current channel:
|
|
25
|
+
**`/polaris rename <new-name>`** — Rename the current **project** (and its Slack channel):
|
|
26
26
|
1. Call `polaris_rename` with the new name
|
|
27
27
|
2. Report the result
|
|
28
28
|
|
|
29
|
+
**`/polaris rename-session <new-name>`** — Rename this **session thread** only (not the project):
|
|
30
|
+
1. Call `polaris_rename_session` with the new name
|
|
31
|
+
2. Report the result
|
|
32
|
+
|
|
29
33
|
**`/polaris tag [name]`** — Tag a teammate on Slack:
|
|
30
34
|
1. Call `polaris_team` to get the team list with Slack identities
|
|
31
35
|
2. If a name was given, find the matching member. If no name, present a numbered list for the user to pick.
|
package/src/cli/cli.ts
CHANGED
|
@@ -428,8 +428,8 @@ async function install(participantId?: string) {
|
|
|
428
428
|
const skillContent = `---
|
|
429
429
|
name: polaris
|
|
430
430
|
description: Connect to a Polaris multiplayer collaboration session
|
|
431
|
-
allowed-tools: polaris_connect polaris_disconnect polaris_status polaris_reply polaris_context polaris_rename
|
|
432
|
-
argument-hint: [join #channel | rename <new-name> | disconnect | (no args for status)]
|
|
431
|
+
allowed-tools: polaris_connect polaris_disconnect polaris_status polaris_reply polaris_context polaris_rename polaris_rename_session
|
|
432
|
+
argument-hint: [join #channel [session-name] | rename <new-name> | rename-session <name> | disconnect | (no args for status)]
|
|
433
433
|
---
|
|
434
434
|
|
|
435
435
|
## Polaris — Multiplayer Collaboration
|
|
@@ -440,15 +440,19 @@ Manage your connection to a Polaris collaboration session.
|
|
|
440
440
|
|
|
441
441
|
Based on the arguments provided, do ONE of the following:
|
|
442
442
|
|
|
443
|
-
**\`/polaris join #channel-name\`** — Connect to a channel:
|
|
444
|
-
1. Call \`polaris_connect\` with the given project
|
|
445
|
-
2. A session name is auto-generated
|
|
443
|
+
**\`/polaris join #channel-name [session-name]\`** — Connect to a channel:
|
|
444
|
+
1. Call \`polaris_connect\` with the given project, user identity ${identity}, and optional \`session\` name (e.g. \`auth-feature\`, \`bugfix-154\`)
|
|
445
|
+
2. A session name is auto-generated if not provided
|
|
446
446
|
3. Report the connection status including the session name
|
|
447
447
|
|
|
448
|
-
**\`/polaris rename <new-name>\`** — Rename the current project:
|
|
448
|
+
**\`/polaris rename <new-name>\`** — Rename the current **project** (and its Slack channel):
|
|
449
449
|
1. Call \`polaris_rename\` with the new name
|
|
450
450
|
2. Report the result
|
|
451
451
|
|
|
452
|
+
**\`/polaris rename-session <new-name>\`** — Rename this **session thread** only (not the project):
|
|
453
|
+
1. Call \`polaris_rename_session\` with the new name
|
|
454
|
+
2. Report the result
|
|
455
|
+
|
|
452
456
|
**\`/polaris disconnect\`** — Disconnect:
|
|
453
457
|
1. Call \`polaris_disconnect\`
|
|
454
458
|
2. Confirm disconnection
|
|
@@ -567,8 +571,8 @@ async function login(appUrl: string, profileName?: string, loginHint?: string) {
|
|
|
567
571
|
const skillContent = `---
|
|
568
572
|
name: polaris
|
|
569
573
|
description: Connect to a Polaris multiplayer collaboration session
|
|
570
|
-
allowed-tools: polaris_connect polaris_disconnect polaris_status polaris_reply polaris_context polaris_rename
|
|
571
|
-
argument-hint: [join #channel | rename <new-name> | disconnect | (no args for status)]
|
|
574
|
+
allowed-tools: polaris_connect polaris_disconnect polaris_status polaris_reply polaris_context polaris_rename polaris_rename_session
|
|
575
|
+
argument-hint: [join #channel [session-name] | rename <new-name> | rename-session <name> | disconnect | (no args for status)]
|
|
572
576
|
---
|
|
573
577
|
|
|
574
578
|
## Polaris — Multiplayer Collaboration
|
|
@@ -579,15 +583,19 @@ Manage your connection to a Polaris collaboration session.
|
|
|
579
583
|
|
|
580
584
|
Based on the arguments provided, do ONE of the following:
|
|
581
585
|
|
|
582
|
-
**\`/polaris join #channel-name\`** — Connect to a channel:
|
|
583
|
-
1. Call \`polaris_connect\` with the given project
|
|
584
|
-
2. A session name is auto-generated
|
|
586
|
+
**\`/polaris join #channel-name [session-name]\`** — Connect to a channel:
|
|
587
|
+
1. Call \`polaris_connect\` with the given project, user identity ${identity}, and optional \`session\` name (e.g. \`auth-feature\`, \`bugfix-154\`)
|
|
588
|
+
2. A session name is auto-generated if not provided
|
|
585
589
|
3. Report the connection status including the session name
|
|
586
590
|
|
|
587
|
-
**\`/polaris rename <new-name>\`** — Rename the current project:
|
|
591
|
+
**\`/polaris rename <new-name>\`** — Rename the current **project** (and its Slack channel):
|
|
588
592
|
1. Call \`polaris_rename\` with the new name
|
|
589
593
|
2. Report the result
|
|
590
594
|
|
|
595
|
+
**\`/polaris rename-session <new-name>\`** — Rename this **session thread** only (not the project):
|
|
596
|
+
1. Call \`polaris_rename_session\` with the new name
|
|
597
|
+
2. Report the result
|
|
598
|
+
|
|
591
599
|
**\`/polaris disconnect\`** — Disconnect:
|
|
592
600
|
1. Call \`polaris_disconnect\`
|
|
593
601
|
2. Confirm disconnection
|
package/src/client/client.ts
CHANGED
|
@@ -156,7 +156,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
156
156
|
},
|
|
157
157
|
{
|
|
158
158
|
name: "polaris_rename",
|
|
159
|
-
description: "Rename the current
|
|
159
|
+
description: "Rename the current PROJECT (and its linked Slack channel). To rename just this session thread, use polaris_rename_session instead.",
|
|
160
160
|
inputSchema: {
|
|
161
161
|
type: "object" as const,
|
|
162
162
|
properties: {
|
|
@@ -165,6 +165,17 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
165
165
|
required: ["name"],
|
|
166
166
|
},
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
name: "polaris_rename_session",
|
|
170
|
+
description: "Rename this session thread (e.g. from 's-3f95' to 'auth-feature'). Does not affect the project or other sessions.",
|
|
171
|
+
inputSchema: {
|
|
172
|
+
type: "object" as const,
|
|
173
|
+
properties: {
|
|
174
|
+
name: { type: "string", description: "New session name (e.g. 'auth-feature', 'bugfix-154')" },
|
|
175
|
+
},
|
|
176
|
+
required: ["name"],
|
|
177
|
+
},
|
|
178
|
+
},
|
|
168
179
|
{
|
|
169
180
|
name: "polaris_context",
|
|
170
181
|
description: "Fetch activity from a sibling session in this project. Use this to see what other drivers have been doing.",
|
|
@@ -340,6 +351,26 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
340
351
|
}
|
|
341
352
|
}
|
|
342
353
|
|
|
354
|
+
if (name === "polaris_rename_session") {
|
|
355
|
+
if (!currentProject) {
|
|
356
|
+
return { content: [{ type: "text", text: "Not connected to a Polaris session. Use polaris_connect first." }] };
|
|
357
|
+
}
|
|
358
|
+
const newName = (args as { name: string }).name;
|
|
359
|
+
try {
|
|
360
|
+
const res = await daemonPost("/rename-session", { ccSessionId: CC_SESSION_ID, newName });
|
|
361
|
+
const body = await res.json() as { status?: string; oldName?: string; newName?: string; error?: string };
|
|
362
|
+
if (res.ok) {
|
|
363
|
+
const oldName = currentSession;
|
|
364
|
+
currentSession = newName;
|
|
365
|
+
await saveSessionState(currentProject, currentSession, currentUser, currentProfile);
|
|
366
|
+
return { content: [{ type: "text", text: `Renamed session "${oldName}" to "${newName}".` }] };
|
|
367
|
+
}
|
|
368
|
+
return { content: [{ type: "text", text: `Failed to rename session: ${body.error ?? "unknown error"}` }] };
|
|
369
|
+
} catch {
|
|
370
|
+
return { content: [{ type: "text", text: "Failed to rename session — is the Polaris daemon running?" }] };
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
343
374
|
if (name === "polaris_context") {
|
|
344
375
|
if (!currentProject) {
|
|
345
376
|
return { content: [{ type: "text", text: "Not connected to a Polaris session. Use polaris_connect first." }] };
|
package/src/daemon/daemon.ts
CHANGED
|
@@ -777,6 +777,36 @@ export function startDaemon(port = Number(process.env.POLARIS_DAEMON_PORT ?? 432
|
|
|
777
777
|
}
|
|
778
778
|
}
|
|
779
779
|
|
|
780
|
+
// POST /rename-session — rename the current session (not the project)
|
|
781
|
+
if (method === "POST" && pathname === "/rename-session") {
|
|
782
|
+
try {
|
|
783
|
+
const body = (await req.json()) as { ccSessionId: string; newName: string };
|
|
784
|
+
if (!body.ccSessionId || !body.newName) return error("ccSessionId and newName required", 400);
|
|
785
|
+
const mapping = sessions.get(body.ccSessionId);
|
|
786
|
+
if (!mapping || !mapping.project) return error("Not connected", 400);
|
|
787
|
+
|
|
788
|
+
const serviceUrl = getServiceUrlForProfile(mapping.profile);
|
|
789
|
+
const res = await fetch(
|
|
790
|
+
`${serviceUrl}/projects/${mapping.project}/sessions/${mapping.session}/rename`,
|
|
791
|
+
{
|
|
792
|
+
method: "POST",
|
|
793
|
+
headers: await authHeadersForProfile(mapping.profile),
|
|
794
|
+
body: JSON.stringify({ name: body.newName }),
|
|
795
|
+
}
|
|
796
|
+
);
|
|
797
|
+
if (!res.ok) {
|
|
798
|
+
const err = await res.text();
|
|
799
|
+
return new Response(err, { status: res.status });
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
const oldName = mapping.session;
|
|
803
|
+
mapping.session = body.newName;
|
|
804
|
+
return json({ status: "renamed", oldName, newName: body.newName });
|
|
805
|
+
} catch {
|
|
806
|
+
return error("Invalid JSON", 400);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
780
810
|
// POST /channel-update — bridge pushes channel rename notifications
|
|
781
811
|
if (method === "POST" && pathname === "/channel-update") {
|
|
782
812
|
try {
|
package/src/service/db.ts
CHANGED
|
@@ -34,8 +34,39 @@ export interface ProjectMember {
|
|
|
34
34
|
// --- Schema ---
|
|
35
35
|
|
|
36
36
|
export async function createDb(connectionString?: string): Promise<Sql> {
|
|
37
|
-
const
|
|
38
|
-
|
|
37
|
+
const connStr = connectionString ?? process.env.DATABASE_URL ?? "postgres://polaris:polaris@localhost:5432/polaris";
|
|
38
|
+
const sql = postgres(connStr);
|
|
39
|
+
// Retry on transient startup errors (ECONNREFUSED, "starting up" 57P03).
|
|
40
|
+
// Fail fast on auth/role/database errors — retrying won't help.
|
|
41
|
+
const TRANSIENT_CODES = new Set(["57P03", "ECONNREFUSED"]);
|
|
42
|
+
const FATAL_CODES = new Set(["28000", "28P01", "3D000"]);
|
|
43
|
+
const MAX_ATTEMPTS = 10;
|
|
44
|
+
const RETRY_DELAY_MS = 1000;
|
|
45
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
46
|
+
try {
|
|
47
|
+
await ensureSchema(sql);
|
|
48
|
+
return sql;
|
|
49
|
+
} catch (err: unknown) {
|
|
50
|
+
const code = (err as { code?: string }).code ?? "";
|
|
51
|
+
if (FATAL_CODES.has(code)) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Database connection failed (${code}): ${(err as Error).message}\n` +
|
|
54
|
+
`Hint: the role/password/database may not exist. ` +
|
|
55
|
+
`Try: docker compose down -v && docker compose up -d --wait`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (!TRANSIENT_CODES.has(code) && attempt === MAX_ATTEMPTS) {
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
if (attempt < MAX_ATTEMPTS) {
|
|
62
|
+
console.error(`[db] connection attempt ${attempt}/${MAX_ATTEMPTS} failed (${code}), retrying in ${RETRY_DELAY_MS}ms...`);
|
|
63
|
+
await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
|
|
64
|
+
} else {
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// unreachable
|
|
39
70
|
return sql;
|
|
40
71
|
}
|
|
41
72
|
|
|
@@ -311,6 +342,15 @@ export async function createProject(sql: Sql, orgId: string, name: string): Prom
|
|
|
311
342
|
return rowToProject(row);
|
|
312
343
|
}
|
|
313
344
|
|
|
345
|
+
export async function renameSession(sql: Sql, orgId: string, project: string, oldName: string, newName: string): Promise<void> {
|
|
346
|
+
const result = await sql`
|
|
347
|
+
UPDATE sessions SET name = ${newName}
|
|
348
|
+
WHERE org_id = ${orgId} AND name = ${oldName}
|
|
349
|
+
AND project_id = (SELECT id FROM projects WHERE org_id = ${orgId} AND name = ${project})
|
|
350
|
+
`;
|
|
351
|
+
if (result.count === 0) throw new Error(`Session not found: ${project}/${oldName}`);
|
|
352
|
+
}
|
|
353
|
+
|
|
314
354
|
export async function renameProject(sql: Sql, orgId: string, oldName: string, newName: string): Promise<void> {
|
|
315
355
|
await sql`
|
|
316
356
|
UPDATE projects SET name = ${newName}, slack_channel_name = ${newName}
|
package/src/service/server.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
listProjects,
|
|
10
10
|
getProject,
|
|
11
11
|
renameProject,
|
|
12
|
+
renameSession,
|
|
12
13
|
createSession,
|
|
13
14
|
getSession,
|
|
14
15
|
setDriver,
|
|
@@ -378,6 +379,18 @@ export async function startServer(opts: {
|
|
|
378
379
|
return json({ status: "renamed", oldName: params.proj, newName: body.name });
|
|
379
380
|
}
|
|
380
381
|
|
|
382
|
+
params = matchRoute(method, pathname, "/projects/:proj/sessions/:sess/rename", "POST");
|
|
383
|
+
if (params) {
|
|
384
|
+
const body = await req.json() as { name?: string };
|
|
385
|
+
if (!body.name) return error("name is required", 400);
|
|
386
|
+
try {
|
|
387
|
+
await renameSession(sql, orgId, params.proj, params.sess, body.name);
|
|
388
|
+
} catch (e) {
|
|
389
|
+
return error((e as Error).message, 404);
|
|
390
|
+
}
|
|
391
|
+
return json({ status: "renamed", oldName: params.sess, newName: body.name });
|
|
392
|
+
}
|
|
393
|
+
|
|
381
394
|
// --- Project ACL endpoints (visibility & members) ---
|
|
382
395
|
|
|
383
396
|
params = matchRoute(method, pathname, "/projects/:proj/visibility", "POST");
|