@lightupai/polaris 0.0.109 → 0.0.113
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/hooks/statusline.sh +20 -5
- package/package.json +1 -1
- package/src/cli/cli.ts +45 -0
- package/src/client/client.ts +13 -5
- package/src/daemon/daemon.ts +8 -0
package/hooks/statusline.sh
CHANGED
|
@@ -41,7 +41,7 @@ if [ -f "$INSTALLED_PKG" ]; then
|
|
|
41
41
|
NOW=$(date +%s 2>/dev/null || echo "0")
|
|
42
42
|
CACHE_AGE=$(( NOW - CACHE_TS )) 2>/dev/null || CACHE_AGE=9999
|
|
43
43
|
STALE=false
|
|
44
|
-
[ "$CACHE_AGE" -gt
|
|
44
|
+
[ "$CACHE_AGE" -gt 300 ] 2>/dev/null && STALE=true
|
|
45
45
|
else
|
|
46
46
|
STALE=true
|
|
47
47
|
fi
|
|
@@ -72,11 +72,26 @@ if [ "$CONNECTED" = "true" ]; then
|
|
|
72
72
|
if [ -n "$ACCOUNT" ]; then
|
|
73
73
|
ACCOUNT_TAG="[${ACCOUNT}] "
|
|
74
74
|
fi
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
|
|
76
|
+
# Liveness indicator: seconds since last delivered event
|
|
77
|
+
RECENCY=""
|
|
78
|
+
LAST_AGE=$(echo "$STATUS" | jq -r '.lastEventAge // ""' 2>/dev/null || echo "")
|
|
79
|
+
if [ -n "$LAST_AGE" ] && [ "$LAST_AGE" != "null" ]; then
|
|
80
|
+
if [ "$LAST_AGE" -lt 60 ] 2>/dev/null; then
|
|
81
|
+
RECENCY=" · just now"
|
|
82
|
+
elif [ "$LAST_AGE" -lt 3600 ] 2>/dev/null; then
|
|
83
|
+
RECENCY=" · $(( LAST_AGE / 60 ))m ago"
|
|
84
|
+
else
|
|
85
|
+
RECENCY=" · $(( LAST_AGE / 3600 ))h ago"
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Show Slack channel only when it differs from the project name (adds info, avoids duplication)
|
|
90
|
+
SLACK_TAG=""
|
|
91
|
+
if [ -n "$SLACK" ] && [ "$SLACK" != "$PROJECT" ]; then
|
|
92
|
+
SLACK_TAG=" → #${SLACK}"
|
|
79
93
|
fi
|
|
94
|
+
echo "polaris: ${ACCOUNT_TAG}#${PROJECT}/${SESSION} (${USER})${SLACK_TAG}${RECENCY}${UPDATE_MSG}"
|
|
80
95
|
else
|
|
81
96
|
echo "polaris: not connected${UPDATE_MSG}"
|
|
82
97
|
fi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightupai/polaris",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.113",
|
|
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/src/cli/cli.ts
CHANGED
|
@@ -275,7 +275,52 @@ async function removeSystemdService(): Promise<void> {
|
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
// Platform dispatcher
|
|
278
|
+
// --- Session state migration ---
|
|
279
|
+
//
|
|
280
|
+
// When upgrading from a pre-0.0.109 daemon (which never wrote daemon-state.json),
|
|
281
|
+
// the new daemon would start with an empty session map and drop every connected
|
|
282
|
+
// session. This function fetches the running daemon's session list via GET /status
|
|
283
|
+
// and writes daemon-state.json before the daemon is stopped, so the new daemon
|
|
284
|
+
// can restore the map and reconnect cloud WebSockets without any user action.
|
|
285
|
+
// Best-effort: failures are silent (daemon may not be running, or already migrated).
|
|
286
|
+
async function migrateSessionState(): Promise<void> {
|
|
287
|
+
const stateFile = join(POLARIS_DIR, "sessions", "daemon-state.json");
|
|
288
|
+
// If a 0.0.109+ daemon already wrote the file, its state is more complete
|
|
289
|
+
// (includes learned hook aliases) — don't overwrite it.
|
|
290
|
+
try { await readFile(stateFile); return; } catch { /* doesn't exist — proceed */ }
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
const config = await loadConfig();
|
|
294
|
+
const secret = resolveDaemonSecret(config);
|
|
295
|
+
const headers: Record<string, string> = {};
|
|
296
|
+
if (secret) headers["x-polaris-daemon-secret"] = secret;
|
|
297
|
+
const res = await fetch("http://127.0.0.1:4322/status", {
|
|
298
|
+
headers,
|
|
299
|
+
signal: AbortSignal.timeout(2000),
|
|
300
|
+
});
|
|
301
|
+
if (!res.ok) return;
|
|
302
|
+
const data = await res.json() as { sessions?: Array<{ ccSessionId: string; project: string; session: string; user: string }> };
|
|
303
|
+
if (!data.sessions || data.sessions.length === 0) return;
|
|
304
|
+
|
|
305
|
+
const activeProfile = config?.active || "";
|
|
306
|
+
const sessions = data.sessions.map((s) => ({
|
|
307
|
+
ccSessionId: s.ccSessionId,
|
|
308
|
+
project: s.project,
|
|
309
|
+
session: s.session,
|
|
310
|
+
user: s.user,
|
|
311
|
+
agent: "agent:claude",
|
|
312
|
+
profile: activeProfile,
|
|
313
|
+
}));
|
|
314
|
+
|
|
315
|
+
await mkdir(join(POLARIS_DIR, "sessions"), { recursive: true });
|
|
316
|
+
await writeFile(stateFile, JSON.stringify({ sessions }));
|
|
317
|
+
console.log(` ✓ Migrated ${sessions.length} active session(s) to persistent state`);
|
|
318
|
+
} catch { /* best-effort — daemon not running or unreachable */ }
|
|
319
|
+
}
|
|
320
|
+
|
|
278
321
|
async function installDaemonService(daemonSecret?: string): Promise<void> {
|
|
322
|
+
// Migrate session state from older daemon before it is stopped
|
|
323
|
+
await migrateSessionState();
|
|
279
324
|
if (process.platform === "darwin") return installLaunchAgent(daemonSecret);
|
|
280
325
|
if (process.platform === "linux") return installSystemdService(daemonSecret);
|
|
281
326
|
// Windows and others: no managed service; daemon is started manually
|
package/src/client/client.ts
CHANGED
|
@@ -495,17 +495,25 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
495
495
|
return { content: [{ type: "text", text: `Polaris is already up to date (v${currentVersion}).` }] };
|
|
496
496
|
}
|
|
497
497
|
|
|
498
|
-
// Update
|
|
499
|
-
|
|
498
|
+
// Update the copy that's actually in use: ~/.polaris/mcp (no sudo required).
|
|
499
|
+
// This is the same location the version was read from, and where the daemon
|
|
500
|
+
// and MCP server binaries are resolved by run-daemon.sh and claude mcp add.
|
|
501
|
+
const mcpDir = `${homedir()}/.polaris/mcp`;
|
|
502
|
+
const npmResult = Bun.spawnSync(
|
|
503
|
+
["npm", "install", "--prefix", mcpDir, `@lightupai/polaris@${latestVersion}`],
|
|
504
|
+
{ stdout: "pipe", stderr: "pipe" }
|
|
505
|
+
);
|
|
500
506
|
if (npmResult.exitCode !== 0) {
|
|
501
507
|
return { content: [{ type: "text", text: `Update failed:\n${npmResult.stderr.toString().trim()}` }] };
|
|
502
508
|
}
|
|
503
509
|
|
|
504
|
-
// Refresh hooks,
|
|
505
|
-
|
|
510
|
+
// Refresh hooks, migrate session state, and restart the daemon using the
|
|
511
|
+
// local polaris binary (not relying on PATH, which may not include it).
|
|
512
|
+
const polarisBin = `${mcpDir}/node_modules/.bin/polaris`;
|
|
513
|
+
const installResult = Bun.spawnSync([polarisBin, "install"], { stdout: "pipe", stderr: "pipe" });
|
|
506
514
|
const msg = installResult.exitCode === 0
|
|
507
515
|
? `Updated Polaris from v${currentVersion} to v${latestVersion}.\n\nRestart Claude Code for the new MCP server to take effect.`
|
|
508
|
-
: `Updated
|
|
516
|
+
: `Updated package to v${latestVersion}, but \`polaris install\` failed — run it manually.\n${installResult.stderr.toString().trim()}`;
|
|
509
517
|
return { content: [{ type: "text", text: msg }] };
|
|
510
518
|
}
|
|
511
519
|
|
package/src/daemon/daemon.ts
CHANGED
|
@@ -1042,6 +1042,13 @@ export function startDaemon(port = Number(process.env.POLARIS_DAEMON_PORT ?? 432
|
|
|
1042
1042
|
}
|
|
1043
1043
|
}
|
|
1044
1044
|
}
|
|
1045
|
+
// Include seconds since last delivered event so the status line can show
|
|
1046
|
+
// a liveness indicator (e.g. "· 2m ago") without a second request.
|
|
1047
|
+
let lastEventAge: number | null = null;
|
|
1048
|
+
const cp = await readCheckpoint(ccSessionId);
|
|
1049
|
+
if (cp && cp.project === mapping.project && cp.session === mapping.session) {
|
|
1050
|
+
lastEventAge = Math.floor((Date.now() - new Date(cp.lastEventTimestamp).getTime()) / 1000);
|
|
1051
|
+
}
|
|
1045
1052
|
return json({
|
|
1046
1053
|
connected: true,
|
|
1047
1054
|
project: mapping.project,
|
|
@@ -1049,6 +1056,7 @@ export function startDaemon(port = Number(process.env.POLARIS_DAEMON_PORT ?? 432
|
|
|
1049
1056
|
user: mapping.user,
|
|
1050
1057
|
slackChannel,
|
|
1051
1058
|
account: emailFromToken(await getTokenForProfile(mapping.profile)),
|
|
1059
|
+
lastEventAge,
|
|
1052
1060
|
});
|
|
1053
1061
|
}
|
|
1054
1062
|
|