@lightupai/polaris 0.0.97 → 0.0.99

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.
@@ -43,32 +43,6 @@ jobs:
43
43
 
44
44
  - run: bun test
45
45
 
46
- publish:
47
- needs: test
48
- runs-on: ubuntu-latest
49
- if: github.ref == 'refs/heads/master' && github.event_name == 'push'
50
-
51
- steps:
52
- - uses: actions/checkout@v4
53
-
54
- - uses: oven-sh/setup-bun@v2
55
- with:
56
- bun-version: latest
57
-
58
- - run: bun install
59
-
60
- - name: Bump version from npm registry and publish
61
- run: |
62
- echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc
63
- PUBLISHED=$(npm view @lightupai/polaris version 2>/dev/null || echo "0.0.0")
64
- # Bump patch from the latest published version
65
- IFS='.' read -r MAJOR MINOR PATCH <<< "$PUBLISHED"
66
- NEW="$MAJOR.$MINOR.$((PATCH + 1))"
67
- echo "Published: $PUBLISHED → Publishing: $NEW"
68
- # Set version in package.json for this publish only (not committed)
69
- npm version "$NEW" --no-git-tag-version
70
- npm publish --access public
71
-
72
46
  deploy:
73
47
  needs: test
74
48
  runs-on: ubuntu-latest
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightupai/polaris",
3
- "version": "0.0.97",
3
+ "version": "0.0.99",
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",
@@ -47,6 +47,6 @@ Based on the arguments provided, do ONE of the following:
47
47
 
48
48
  ### Daemon
49
49
 
50
- The Polaris daemon runs locally and is managed automatically — do NOT suggest systemd, launchd configuration, or other process managers. If the user reports the daemon is down or not starting at login, tell them to run `polaris install` which sets up auto-restart (launchd on macOS).
50
+ The Polaris daemon runs locally and is managed automatically — do NOT manually configure launchd or systemd. If the user reports the daemon is down or not starting at login, tell them to run `polaris install` which sets up auto-restart (launchd on macOS, systemd user service on Linux).
51
51
 
52
52
  ### Arguments: $ARGUMENTS
package/src/cli/cli.ts CHANGED
@@ -107,16 +107,13 @@ function appToApi(appUrl: string): string {
107
107
  return appUrl.replace("app.", "api.");
108
108
  }
109
109
 
110
- // --- LaunchAgent (macOS daemon auto-restart) ---
110
+ // --- Daemon auto-restart (macOS: launchd, Linux: systemd user service) ---
111
111
 
112
+ // macOS: LaunchAgent
112
113
  const LAUNCH_AGENT_LABEL = "ai.withpolaris.daemon";
113
114
  const LAUNCH_AGENT_PLIST = join(homedir(), "Library", "LaunchAgents", `${LAUNCH_AGENT_LABEL}.plist`);
114
115
 
115
- // Writes a runner script + launchd plist so the daemon survives crashes and
116
- // starts automatically at login. No-op on non-macOS platforms.
117
116
  async function installLaunchAgent(daemonSecret?: string): Promise<void> {
118
- if (process.platform !== "darwin") return;
119
-
120
117
  const binDir = join(POLARIS_DIR, "bin");
121
118
  const runnerPath = join(binDir, "run-daemon.sh");
122
119
  const daemonTsPath = join(
@@ -175,11 +172,108 @@ async function installLaunchAgent(daemonSecret?: string): Promise<void> {
175
172
  }
176
173
 
177
174
  async function removeLaunchAgent(): Promise<void> {
178
- if (process.platform !== "darwin") return;
179
175
  Bun.spawnSync(["launchctl", "unload", "-w", LAUNCH_AGENT_PLIST], { stdout: "ignore", stderr: "ignore" });
180
176
  try { await rm(LAUNCH_AGENT_PLIST); } catch { /* already gone */ }
181
177
  }
182
178
 
179
+ // Linux: systemd user service
180
+ const SYSTEMD_SERVICE_NAME = "polaris-daemon";
181
+ const SYSTEMD_USER_DIR = join(homedir(), ".config", "systemd", "user");
182
+ const SYSTEMD_SERVICE_FILE = join(SYSTEMD_USER_DIR, `${SYSTEMD_SERVICE_NAME}.service`);
183
+
184
+ async function installSystemdService(daemonSecret?: string): Promise<void> {
185
+ // Resolve bun's absolute path at install time so systemd's minimal PATH
186
+ // doesn't matter. Bun.which() searches PATH; process.execPath is the
187
+ // interpreter we're already running under — use whichever looks like bun.
188
+ const bunPath = Bun.which("bun") ?? process.execPath;
189
+
190
+ const binDir = join(POLARIS_DIR, "bin");
191
+ const runnerPath = join(binDir, "run-daemon.sh");
192
+ const daemonTsPath = join(
193
+ POLARIS_DIR, "mcp", "node_modules", "@lightupai", "polaris", "src", "daemon", "daemon.ts"
194
+ );
195
+
196
+ await mkdir(binDir, { recursive: true });
197
+ // Bun path is baked in at install time — no runtime search needed
198
+ const runner = [
199
+ "#!/bin/sh",
200
+ `# Polaris daemon — managed by systemd (${SYSTEMD_SERVICE_NAME}.service)`,
201
+ `# bun path resolved at install time: ${bunPath}`,
202
+ `exec "${bunPath}" run "${daemonTsPath}"`,
203
+ ].join("\n") + "\n";
204
+ await writeFile(runnerPath, runner);
205
+ await chmod(runnerPath, 0o755);
206
+
207
+ const secretLine = daemonSecret ? `Environment=POLARIS_DAEMON_SECRET=${daemonSecret}\n` : "";
208
+ const unit = `[Unit]
209
+ Description=Polaris daemon
210
+ After=network.target
211
+
212
+ [Service]
213
+ Type=simple
214
+ ExecStart=${runnerPath}
215
+ Restart=always
216
+ RestartSec=5
217
+ ${secretLine}StandardOutput=append:/tmp/polaris-daemon.log
218
+ StandardError=append:/tmp/polaris-daemon.log
219
+
220
+ [Install]
221
+ WantedBy=default.target
222
+ `;
223
+
224
+ await mkdir(SYSTEMD_USER_DIR, { recursive: true });
225
+ await writeFile(SYSTEMD_SERVICE_FILE, unit);
226
+
227
+ // Stop any existing manually-started daemon so it releases port 4322
228
+ // before systemd takes over
229
+ await killExistingDaemon();
230
+
231
+ // Reload unit files, enable (start-at-login) and start/restart the service
232
+ Bun.spawnSync(["systemctl", "--user", "daemon-reload"], { stdout: "ignore", stderr: "ignore" });
233
+ Bun.spawnSync(["systemctl", "--user", "enable", SYSTEMD_SERVICE_NAME], { stdout: "ignore", stderr: "ignore" });
234
+ Bun.spawnSync(["systemctl", "--user", "restart", SYSTEMD_SERVICE_NAME], { stdout: "ignore", stderr: "ignore" });
235
+
236
+ // Verify the service came up
237
+ const check = Bun.spawnSync(["systemctl", "--user", "is-active", SYSTEMD_SERVICE_NAME], { stdout: "pipe", stderr: "ignore" });
238
+ if (check.stdout.toString().trim() !== "active") {
239
+ console.error(` Warning: daemon service did not start. Check logs:`);
240
+ const journal = Bun.spawnSync(
241
+ ["journalctl", "--user", "-u", SYSTEMD_SERVICE_NAME, "-n", "10", "--no-pager"],
242
+ { stdout: "pipe", stderr: "ignore" }
243
+ );
244
+ console.error(journal.stdout.toString().trim());
245
+ console.error(` Try manually: systemctl --user restart ${SYSTEMD_SERVICE_NAME}`);
246
+ }
247
+
248
+ // Enable lingering so the user service survives logout and starts at boot
249
+ // (best-effort — requires sudo-less loginctl or appropriate privileges)
250
+ Bun.spawnSync(["loginctl", "enable-linger"], { stdout: "ignore", stderr: "ignore" });
251
+ }
252
+
253
+ async function removeSystemdService(): Promise<void> {
254
+ Bun.spawnSync(["systemctl", "--user", "disable", "--now", SYSTEMD_SERVICE_NAME], { stdout: "ignore", stderr: "ignore" });
255
+ try { await rm(SYSTEMD_SERVICE_FILE); } catch { /* already gone */ }
256
+ Bun.spawnSync(["systemctl", "--user", "daemon-reload"], { stdout: "ignore", stderr: "ignore" });
257
+ }
258
+
259
+ // Platform dispatcher
260
+ async function installDaemonService(daemonSecret?: string): Promise<void> {
261
+ if (process.platform === "darwin") return installLaunchAgent(daemonSecret);
262
+ if (process.platform === "linux") return installSystemdService(daemonSecret);
263
+ // Windows and others: no managed service; daemon is started manually
264
+ }
265
+
266
+ async function removeDaemonService(): Promise<void> {
267
+ if (process.platform === "darwin") return removeLaunchAgent();
268
+ if (process.platform === "linux") return removeSystemdService();
269
+ }
270
+
271
+ function daemonServiceLabel(): string {
272
+ if (process.platform === "darwin") return "launchd (auto-restarts on crash, starts at login)";
273
+ if (process.platform === "linux") return "systemd user service (auto-restarts on crash, starts at login)";
274
+ return "background process";
275
+ }
276
+
183
277
  // --- Daemon secret ---
184
278
 
185
279
  // Shared local secret for daemon HTTP auth. Stored in ~/.polaris/config.json
@@ -347,7 +441,7 @@ Based on the arguments provided, do ONE of the following:
347
441
 
348
442
  ### Daemon
349
443
 
350
- The Polaris daemon runs locally and is managed automatically — do NOT suggest systemd, launchd configuration, or other process managers. If the user reports the daemon is down or not starting at login, tell them to run \`polaris install\` which sets up auto-restart (launchd on macOS).
444
+ The Polaris daemon runs locally and is managed automatically — do NOT manually configure launchd or systemd. If the user reports the daemon is down or not starting at login, tell them to run \`polaris install\` which sets up auto-restart (launchd on macOS, systemd user service on Linux).
351
445
 
352
446
  ### Arguments: $ARGUMENTS
353
447
  `;
@@ -859,7 +953,7 @@ async function recover() {
859
953
 
860
954
  async function logout(all = false) {
861
955
  if (all) {
862
- await removeLaunchAgent();
956
+ await removeDaemonService();
863
957
  try {
864
958
  await rm(POLARIS_DIR, { recursive: true });
865
959
  console.log("All profiles and credentials removed.");
@@ -962,8 +1056,8 @@ switch (command) {
962
1056
  case "install":
963
1057
  console.log("Polaris — installing local components\n");
964
1058
  await install();
965
- await installLaunchAgent(await ensureDaemonSecret());
966
- console.log(" ✓ Daemon registered with launchd (auto-restarts on crash, starts at login)");
1059
+ await installDaemonService(await ensureDaemonSecret());
1060
+ console.log(` ✓ Daemon registered with ${daemonServiceLabel()}`);
967
1061
  console.log("\nInstall complete.");
968
1062
  break;
969
1063
 
@@ -973,8 +1067,8 @@ switch (command) {
973
1067
  console.log("Polaris — authenticating\n");
974
1068
  await login(appUrl, profileName);
975
1069
  console.log("\n✓ Login complete!");
976
- await installLaunchAgent(await ensureDaemonSecret());
977
- console.log(" ✓ Daemon restarted");
1070
+ await installDaemonService(await ensureDaemonSecret());
1071
+ console.log(` ✓ Daemon registered with ${daemonServiceLabel()}`);
978
1072
 
979
1073
  console.log("\nNext: restart Claude Code, then run `/polaris join #channel-name` in your AI agent.");
980
1074
  break;
@@ -1027,8 +1121,8 @@ switch (command) {
1027
1121
  console.log("[2/2] Authenticating...\n");
1028
1122
  await login(DEFAULT_APP_URL);
1029
1123
  console.log("\n✓ Polaris is set up on this machine!");
1030
- await installLaunchAgent(await ensureDaemonSecret());
1031
- console.log(" ✓ Daemon registered with launchd (auto-restarts on crash, starts at login)");
1124
+ await installDaemonService(await ensureDaemonSecret());
1125
+ console.log(` ✓ Daemon registered with ${daemonServiceLabel()}`);
1032
1126
 
1033
1127
  console.log("\nNext: restart Claude Code, then run `/polaris join #channel-name` in your AI agent.");
1034
1128
  break;
@@ -102,7 +102,7 @@ const mcp = new Server(
102
102
  },
103
103
  instructions: `You are connected to Polaris — a multiplayer collaboration system. Messages from advisors and teammates may arrive as <channel source="polaris" from="..."> tags. Use /polaris commands to manage your session, or call the polaris tools directly.
104
104
 
105
- If the user reports the Polaris daemon is not running or keeps crashing, tell them to run \`polaris install\` — it registers the daemon with launchd (macOS) for auto-restart on crash and at login. Do NOT suggest systemd, manual launchd configuration, or other process managers.`,
105
+ If the user reports the Polaris daemon is not running or keeps crashing, tell them to run \`polaris install\` — it registers the daemon with launchd on macOS or a systemd user service on Linux for auto-restart on crash and at login. Do NOT suggest manually configuring launchd or systemd — \`polaris install\` handles it.`,
106
106
  }
107
107
  );
108
108