@roamcode.ai/server 1.0.0 → 1.0.1

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.
Files changed (99) hide show
  1. package/dist/index.d.ts +2406 -82
  2. package/dist/index.js +8314 -44
  3. package/dist/managed-update-helper.js +621 -26
  4. package/dist/mcp-send.js +76 -90
  5. package/dist/start.d.ts +591 -9
  6. package/dist/start.js +7629 -353
  7. package/package.json +3 -3
  8. package/dist/auth.d.ts +0 -63
  9. package/dist/auth.js +0 -133
  10. package/dist/claude-auth-service.d.ts +0 -76
  11. package/dist/claude-auth-service.js +0 -217
  12. package/dist/claude-latest-service.d.ts +0 -34
  13. package/dist/claude-latest-service.js +0 -61
  14. package/dist/config.d.ts +0 -78
  15. package/dist/config.js +0 -59
  16. package/dist/data-dir.d.ts +0 -42
  17. package/dist/data-dir.js +0 -70
  18. package/dist/diag.d.ts +0 -43
  19. package/dist/diag.js +0 -83
  20. package/dist/fs-service.d.ts +0 -90
  21. package/dist/fs-service.js +0 -290
  22. package/dist/managed-runtime.d.ts +0 -51
  23. package/dist/managed-runtime.js +0 -411
  24. package/dist/managed-update-helper.d.ts +0 -2
  25. package/dist/mcp-send.d.ts +0 -33
  26. package/dist/origin-check.d.ts +0 -37
  27. package/dist/origin-check.js +0 -101
  28. package/dist/pane-status.d.ts +0 -61
  29. package/dist/pane-status.js +0 -145
  30. package/dist/providers/claude-metadata-service.d.ts +0 -58
  31. package/dist/providers/claude-metadata-service.js +0 -352
  32. package/dist/providers/claude-provider.d.ts +0 -11
  33. package/dist/providers/claude-provider.js +0 -166
  34. package/dist/providers/codex-activity.d.ts +0 -21
  35. package/dist/providers/codex-activity.js +0 -122
  36. package/dist/providers/codex-app-server-client.d.ts +0 -90
  37. package/dist/providers/codex-app-server-client.js +0 -485
  38. package/dist/providers/codex-latest-service.d.ts +0 -50
  39. package/dist/providers/codex-latest-service.js +0 -174
  40. package/dist/providers/codex-metadata-service.d.ts +0 -161
  41. package/dist/providers/codex-metadata-service.js +0 -686
  42. package/dist/providers/codex-profile-client.d.ts +0 -16
  43. package/dist/providers/codex-profile-client.js +0 -52
  44. package/dist/providers/codex-profile-security.d.ts +0 -23
  45. package/dist/providers/codex-profile-security.js +0 -161
  46. package/dist/providers/codex-provider.d.ts +0 -15
  47. package/dist/providers/codex-provider.js +0 -174
  48. package/dist/providers/codex-thread-coordinator.d.ts +0 -18
  49. package/dist/providers/codex-thread-coordinator.js +0 -93
  50. package/dist/providers/codex-thread-persistence.d.ts +0 -9
  51. package/dist/providers/codex-thread-persistence.js +0 -45
  52. package/dist/providers/codex-thread-resolver.d.ts +0 -59
  53. package/dist/providers/codex-thread-resolver.js +0 -322
  54. package/dist/providers/options.d.ts +0 -7
  55. package/dist/providers/options.js +0 -155
  56. package/dist/providers/provider-artifacts.d.ts +0 -3
  57. package/dist/providers/provider-artifacts.js +0 -30
  58. package/dist/providers/registry.d.ts +0 -7
  59. package/dist/providers/registry.js +0 -23
  60. package/dist/providers/types.d.ts +0 -95
  61. package/dist/providers/types.js +0 -8
  62. package/dist/push-dispatch.d.ts +0 -81
  63. package/dist/push-dispatch.js +0 -100
  64. package/dist/push-store.d.ts +0 -25
  65. package/dist/push-store.js +0 -79
  66. package/dist/rate-limit.d.ts +0 -52
  67. package/dist/rate-limit.js +0 -72
  68. package/dist/server-config.d.ts +0 -60
  69. package/dist/server-config.js +0 -77
  70. package/dist/service-install.d.ts +0 -60
  71. package/dist/service-install.js +0 -221
  72. package/dist/session-defaults.d.ts +0 -26
  73. package/dist/session-defaults.js +0 -60
  74. package/dist/session-store.d.ts +0 -81
  75. package/dist/session-store.js +0 -654
  76. package/dist/static-routes.d.ts +0 -101
  77. package/dist/static-routes.js +0 -188
  78. package/dist/terminal-capability.d.ts +0 -5
  79. package/dist/terminal-capability.js +0 -27
  80. package/dist/terminal-manager.d.ts +0 -224
  81. package/dist/terminal-manager.js +0 -917
  82. package/dist/terminal-process.d.ts +0 -85
  83. package/dist/terminal-process.js +0 -238
  84. package/dist/terminal-shared.d.ts +0 -36
  85. package/dist/terminal-shared.js +0 -43
  86. package/dist/tmux-list.d.ts +0 -11
  87. package/dist/tmux-list.js +0 -39
  88. package/dist/transport.d.ts +0 -123
  89. package/dist/transport.js +0 -1559
  90. package/dist/updater.d.ts +0 -161
  91. package/dist/updater.js +0 -451
  92. package/dist/usage-service.d.ts +0 -118
  93. package/dist/usage-service.js +0 -173
  94. package/dist/vapid.d.ts +0 -17
  95. package/dist/vapid.js +0 -31
  96. package/dist/web-push-send.d.ts +0 -20
  97. package/dist/web-push-send.js +0 -21
  98. package/dist/ws-ticket.d.ts +0 -47
  99. package/dist/ws-ticket.js +0 -62
@@ -1,221 +0,0 @@
1
- import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { homedir, platform } from "node:os";
3
- import { dirname, join } from "node:path";
4
- import { spawnSync } from "node:child_process";
5
- function escapeXml(value) {
6
- return value
7
- .replace(/&/g, "&")
8
- .replace(/</g, "&lt;")
9
- .replace(/>/g, "&gt;")
10
- .replace(/\"/g, "&quot;")
11
- .replace(/'/g, "&apos;");
12
- }
13
- function quoteSystemd(value) {
14
- return /[\s\"\\]/.test(value) ? `"${value.replace(/\\/g, "\\\\").replace(/\"/g, '\\"')}"` : value;
15
- }
16
- export function buildServicePath(nodePath, home) {
17
- return [
18
- dirname(nodePath),
19
- "/opt/homebrew/bin",
20
- "/usr/local/bin",
21
- join(home, ".local", "bin"),
22
- join(home, ".local", "share", "pnpm"),
23
- join(home, "Library", "pnpm"),
24
- "/usr/bin",
25
- "/bin",
26
- ].join(":");
27
- }
28
- function programArguments(opts) {
29
- if (opts.executablePath)
30
- return [opts.executablePath];
31
- if (opts.nodePath && opts.cliPath)
32
- return [opts.nodePath, opts.cliPath];
33
- throw new Error("service requires executablePath or nodePath + cliPath");
34
- }
35
- export function renderLaunchdPlist(opts) {
36
- const args = programArguments(opts)
37
- .map((arg) => ` <string>${escapeXml(arg)}</string>`)
38
- .join("\n");
39
- const pathEntry = opts.servicePath
40
- ? `\n <key>PATH</key>\n <string>${escapeXml(opts.servicePath)}</string>`
41
- : "";
42
- const installEntry = opts.installRoot
43
- ? `\n <key>ROAMCODE_INSTALL_ROOT</key>\n <string>${escapeXml(opts.installRoot)}</string>`
44
- : "";
45
- return `<?xml version="1.0" encoding="UTF-8"?>
46
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
47
- <plist version="1.0">
48
- <dict>
49
- <key>Label</key>
50
- <string>${escapeXml(opts.label)}</string>
51
- <key>ProgramArguments</key>
52
- <array>
53
- ${args}
54
- </array>
55
- <key>EnvironmentVariables</key>
56
- <dict>
57
- <key>ROAMCODE_DATA_DIR</key>
58
- <string>${escapeXml(opts.dataDir)}</string>${installEntry}${pathEntry}
59
- </dict>
60
- <key>RunAtLoad</key>
61
- <true/>
62
- <key>KeepAlive</key>
63
- <true/>
64
- <key>StandardOutPath</key>
65
- <string>${escapeXml(join(opts.dataDir, "roamcode.log"))}</string>
66
- <key>StandardErrorPath</key>
67
- <string>${escapeXml(join(opts.dataDir, "roamcode.err.log"))}</string>
68
- </dict>
69
- </plist>
70
- `;
71
- }
72
- export function renderSystemdUnit(opts) {
73
- const command = programArguments(opts).map(quoteSystemd).join(" ");
74
- const pathLine = opts.servicePath ? `\nEnvironment=PATH=${opts.servicePath}` : "";
75
- const installLine = opts.installRoot ? `\nEnvironment=ROAMCODE_INSTALL_ROOT=${opts.installRoot}` : "";
76
- return `[Unit]
77
- Description=roamcode — operate Claude Code or Codex sessions remotely
78
- After=network-online.target
79
-
80
- [Service]
81
- ExecStart=${command}
82
- Environment=ROAMCODE_DATA_DIR=${opts.dataDir}${installLine}${pathLine}
83
- Restart=always
84
- RestartSec=3
85
-
86
- [Install]
87
- WantedBy=default.target
88
- `;
89
- }
90
- function persistRecord(dataDir, record) {
91
- mkdirSync(dataDir, { recursive: true, mode: 0o700 });
92
- const path = join(dataDir, "service.json");
93
- writeFileSync(path, JSON.stringify(record, null, 2) + "\n", { mode: 0o600 });
94
- chmodSync(path, 0o600);
95
- }
96
- export function installService(ctx) {
97
- const home = ctx.home ?? homedir();
98
- const os = ctx.os ?? platform();
99
- const servicePath = buildServicePath(ctx.nodePath, home);
100
- if (os === "darwin") {
101
- const label = ctx.label ?? "com.roamcode";
102
- const dir = join(home, "Library", "LaunchAgents");
103
- const path = join(dir, `${label}.plist`);
104
- mkdirSync(dir, { recursive: true });
105
- writeFileSync(path, renderLaunchdPlist({
106
- label,
107
- nodePath: ctx.nodePath,
108
- cliPath: ctx.cliPath,
109
- executablePath: ctx.executablePath,
110
- dataDir: ctx.dataDir,
111
- installRoot: ctx.installRoot,
112
- servicePath,
113
- }));
114
- chmodSync(path, 0o644);
115
- const record = {
116
- manager: "launchd",
117
- label,
118
- path,
119
- ...(ctx.executablePath ? { launcherPath: ctx.executablePath } : {}),
120
- ...(ctx.installRoot ? { installRoot: ctx.installRoot } : {}),
121
- };
122
- persistRecord(ctx.dataDir, record);
123
- return {
124
- path,
125
- record,
126
- instructions: `launchctl load -w "${path}" # start now + at login\nlaunchctl unload -w "${path}" # stop`,
127
- };
128
- }
129
- if (os === "linux") {
130
- const label = ctx.label ?? "roamcode";
131
- const dir = join(home, ".config", "systemd", "user");
132
- const path = join(dir, `${label}.service`);
133
- mkdirSync(dir, { recursive: true });
134
- writeFileSync(path, renderSystemdUnit({
135
- nodePath: ctx.nodePath,
136
- cliPath: ctx.cliPath,
137
- executablePath: ctx.executablePath,
138
- dataDir: ctx.dataDir,
139
- installRoot: ctx.installRoot,
140
- servicePath,
141
- }));
142
- chmodSync(path, 0o644);
143
- const record = {
144
- manager: "systemd",
145
- label,
146
- path,
147
- ...(ctx.executablePath ? { launcherPath: ctx.executablePath } : {}),
148
- ...(ctx.installRoot ? { installRoot: ctx.installRoot } : {}),
149
- };
150
- persistRecord(ctx.dataDir, record);
151
- return {
152
- path,
153
- record,
154
- instructions: [
155
- "systemctl --user daemon-reload",
156
- `systemctl --user enable --now ${label} # start now + at login`,
157
- `systemctl --user disable --now ${label} # stop`,
158
- "(run 'loginctl enable-linger $USER' so it runs without an active login session)",
159
- ].join("\n"),
160
- };
161
- }
162
- throw new Error(`unsupported platform: ${os} — run roamcode manually or under a user-level supervisor`);
163
- }
164
- export function readServiceRecord(dataDir) {
165
- try {
166
- const parsed = JSON.parse(readFileSync(join(dataDir, "service.json"), "utf8"));
167
- if ((parsed.manager !== "launchd" && parsed.manager !== "systemd") || typeof parsed.label !== "string") {
168
- return undefined;
169
- }
170
- const fallbackPath = parsed.manager === "launchd"
171
- ? join(homedir(), "Library", "LaunchAgents", `${parsed.label}.plist`)
172
- : join(homedir(), ".config", "systemd", "user", `${parsed.label}.service`);
173
- return { ...parsed, path: typeof parsed.path === "string" ? parsed.path : fallbackPath };
174
- }
175
- catch {
176
- return undefined;
177
- }
178
- }
179
- export function migrateServiceToLauncher(opts) {
180
- const current = readServiceRecord(opts.dataDir);
181
- if (!current)
182
- return undefined;
183
- return installService({
184
- nodePath: opts.nodePath,
185
- executablePath: opts.launcherPath,
186
- dataDir: opts.dataDir,
187
- installRoot: opts.installRoot,
188
- os: current.manager === "launchd" ? "darwin" : "linux",
189
- label: current.label,
190
- }).record;
191
- }
192
- export function restartService(record) {
193
- if (!existsSync(record.path))
194
- return { ok: false, error: `service file not found: ${record.path}` };
195
- if (record.manager === "launchd") {
196
- spawnSync("launchctl", ["unload", record.path], { stdio: "ignore" });
197
- const res = spawnSync("launchctl", ["load", "-w", record.path], { encoding: "utf8" });
198
- return res.status === 0 ? { ok: true } : { ok: false, error: res.stderr || "launchctl load failed" };
199
- }
200
- const reload = spawnSync("systemctl", ["--user", "daemon-reload"], { encoding: "utf8" });
201
- if (reload.status !== 0)
202
- return { ok: false, error: reload.stderr || "systemctl daemon-reload failed" };
203
- const restart = spawnSync("systemctl", ["--user", "restart", record.label], { encoding: "utf8" });
204
- return restart.status === 0 ? { ok: true } : { ok: false, error: restart.stderr || "systemctl restart failed" };
205
- }
206
- /** Enable the per-user service and start it immediately. This is only called by the explicit
207
- * `roamcode install` command; OTA updates use restartService and never change enablement. */
208
- export function enableService(record) {
209
- if (!existsSync(record.path))
210
- return { ok: false, error: `service file not found: ${record.path}` };
211
- if (record.manager === "launchd") {
212
- spawnSync("launchctl", ["unload", record.path], { stdio: "ignore" });
213
- const result = spawnSync("launchctl", ["load", "-w", record.path], { encoding: "utf8" });
214
- return result.status === 0 ? { ok: true } : { ok: false, error: result.stderr || "launchctl load failed" };
215
- }
216
- const reload = spawnSync("systemctl", ["--user", "daemon-reload"], { encoding: "utf8" });
217
- if (reload.status !== 0)
218
- return { ok: false, error: reload.stderr || "systemctl daemon-reload failed" };
219
- const result = spawnSync("systemctl", ["--user", "enable", "--now", record.label], { encoding: "utf8" });
220
- return result.status === 0 ? { ok: true } : { ok: false, error: result.stderr || "systemctl enable failed" };
221
- }
@@ -1,26 +0,0 @@
1
- export interface SessionDefaults {
2
- effort: string;
3
- model?: string;
4
- dangerouslySkip: boolean;
5
- permissionMode?: string;
6
- codex?: {
7
- model?: string;
8
- reasoningEffort?: string;
9
- sandbox?: "read-only" | "workspace-write" | "danger-full-access";
10
- approvalPolicy?: "untrusted" | "on-request" | "never";
11
- profile?: string;
12
- webSearch?: boolean;
13
- addDirs?: string[];
14
- dangerouslyBypassApprovalsAndSandbox?: boolean;
15
- };
16
- }
17
- export interface StoredSessionDefaults {
18
- defaults: SessionDefaults;
19
- revision: number;
20
- updatedAt: number;
21
- }
22
- export declare function normalizeSessionDefaults(value: unknown): SessionDefaults;
23
- export declare class SessionDefaultsConflictError extends Error {
24
- readonly current: StoredSessionDefaults | undefined;
25
- constructor(current: StoredSessionDefaults | undefined);
26
- }
@@ -1,60 +0,0 @@
1
- import { z } from "zod";
2
- const MAX_MODEL = 128;
3
- const MAX_PROFILE = 128;
4
- const MAX_PATH = 4096;
5
- const MAX_ADD_DIRS = 32;
6
- const SAFE_MODEL = /^[A-Za-z0-9][A-Za-z0-9._:/\u005b\u005d-]*$/;
7
- const SAFE_EFFORT = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
8
- const SAFE_PROFILE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
9
- const SAFE_PATH = /^\/[^\x00-\x1f\x7f]*$/;
10
- const modelToken = z.string().min(1).max(MAX_MODEL).regex(SAFE_MODEL, "must be a safe model token");
11
- const effortToken = z.string().min(1).max(128).regex(SAFE_EFFORT, "must be a safe effort token");
12
- const profileToken = z.string().min(1).max(MAX_PROFILE).regex(SAFE_PROFILE, "must be a safe profile token");
13
- const pathToken = z.string().min(1).max(MAX_PATH).regex(SAFE_PATH, "must be a safe absolute path");
14
- const codexDefaultsSchema = z
15
- .object({
16
- model: modelToken.optional(),
17
- reasoningEffort: effortToken.optional(),
18
- sandbox: z.enum(["read-only", "workspace-write", "danger-full-access"]).optional(),
19
- approvalPolicy: z.enum(["untrusted", "on-request", "never"]).optional(),
20
- profile: profileToken.optional(),
21
- webSearch: z.boolean().optional(),
22
- addDirs: z.array(pathToken).max(MAX_ADD_DIRS).optional(),
23
- dangerouslyBypassApprovalsAndSandbox: z.boolean().optional(),
24
- })
25
- .strict()
26
- .transform((codex) => {
27
- if (codex.dangerouslyBypassApprovalsAndSandbox !== true)
28
- return codex;
29
- const dangerousCodex = { ...codex };
30
- delete dangerousCodex.sandbox;
31
- delete dangerousCodex.approvalPolicy;
32
- return dangerousCodex;
33
- });
34
- const sessionDefaultsSchema = z
35
- .object({
36
- effort: effortToken.default("medium"),
37
- model: modelToken.optional(),
38
- dangerouslySkip: z.boolean().default(false),
39
- permissionMode: z.enum(["default", "acceptEdits", "plan", "bypassPermissions"]).optional(),
40
- codex: codexDefaultsSchema.optional(),
41
- })
42
- .strict()
43
- .transform((defaults) => {
44
- if (!defaults.dangerouslySkip)
45
- return defaults;
46
- const dangerousDefaults = { ...defaults };
47
- delete dangerousDefaults.permissionMode;
48
- return dangerousDefaults;
49
- });
50
- export function normalizeSessionDefaults(value) {
51
- return sessionDefaultsSchema.parse(value === undefined ? {} : value);
52
- }
53
- export class SessionDefaultsConflictError extends Error {
54
- current;
55
- constructor(current) {
56
- super("Session defaults revision conflict");
57
- this.current = current;
58
- this.name = "SessionDefaultsConflictError";
59
- }
60
- }
@@ -1,81 +0,0 @@
1
- import type { CodexSessionOptions } from "./providers/types.js";
2
- import { type SessionDefaults, type StoredSessionDefaults } from "./session-defaults.js";
3
- export type StoredStatus = "running" | "dormant" | "errored" | "stopped";
4
- interface StoredSessionBase {
5
- id: string;
6
- cwd: string;
7
- status: StoredStatus;
8
- createdAt: number;
9
- lastActivityAt: number;
10
- /** Always "terminal" — the only session kind. Kept so rehydrate can filter/guard on it. */
11
- mode: "terminal";
12
- /** Optional user-set display name (PATCH /sessions/:id). Absent = unnamed (the UI shows the cwd). */
13
- name?: string;
14
- }
15
- export interface StoredClaudeSession extends StoredSessionBase {
16
- provider: "claude";
17
- dangerouslySkip: boolean;
18
- /** The USER-chosen spawn flags the session was created with (`--model`/`--effort`/`--permission-mode`/
19
- * `--dangerously-skip-permissions`/`--add-dir …`) — everything EXCEPT the ephemeral per-session
20
- * `--mcp-config`/`--settings` paths (those are regenerated per spawn). Persisted so a RESTART can
21
- * respawn the ended session with the same settings instead of a bare claude. Absent = no extra flags. */
22
- spawnArgs?: string[];
23
- providerSessionId?: never;
24
- launchOptions?: never;
25
- integrationStatus?: never;
26
- }
27
- export interface StoredIntegrationStatus {
28
- attachments: "ready" | "degraded";
29
- activity: "ready" | "degraded";
30
- detail?: string;
31
- }
32
- export interface StoredCodexSession extends StoredSessionBase {
33
- provider: "codex";
34
- launchOptions: CodexSessionOptions;
35
- providerSessionId?: string;
36
- integrationStatus?: StoredIntegrationStatus;
37
- dangerouslySkip?: never;
38
- spawnArgs?: never;
39
- }
40
- export type StoredSession = StoredClaudeSession | StoredCodexSession;
41
- /** How a store is actually backed: "sqlite" (durable) or "memory-fallback" (the native module failed to
42
- * load — NOT durable across restarts). Surfaced so start.ts can warn loudly + /diag can report it. */
43
- export type StoreMode = "sqlite" | "memory-fallback";
44
- export interface SessionStore {
45
- /** Atomically claim a brand-new physical id. Rejects any existing owner without changing its data. */
46
- claimNew(session: StoredSession): void;
47
- upsert(session: StoredSession): void;
48
- /** True when any physical provider table owns the id, including corrupt or cross-table ambiguous rows. */
49
- has(id: string): boolean;
50
- get(id: string): StoredSession | undefined;
51
- list(): StoredSession[];
52
- setStatus(id: string, status: StoredStatus): void;
53
- touch(id: string, at: number): void;
54
- /** Set/clear a session's display name (undefined clears — the row's `name` goes back to NULL). */
55
- setName(id: string, name: string | undefined): void;
56
- /** Persist/clear the exact resumable identity for Codex sessions only. Claude rows are never touched. */
57
- setProviderSessionId(id: string, value: string | undefined): void;
58
- /** Claim a hidden, non-resumable identity pending exact-thread cross-check. */
59
- markProvisionalProviderSessionId(id: string, value: string): void;
60
- /** Remove only the matching hidden provisional identity. */
61
- clearProvisionalProviderSessionId(id: string, value: string): void;
62
- /** Atomically promote only the matching hidden provisional identity to resumable state. */
63
- commitProvisionalProviderSessionId(id: string, value: string): void;
64
- getSessionDefaults(): StoredSessionDefaults | undefined;
65
- putSessionDefaults(defaults: SessionDefaults, expectedRevision: number, updatedAt: number): StoredSessionDefaults;
66
- delete(id: string): void;
67
- close(): void;
68
- /** "sqlite" when better-sqlite3 loaded; "memory-fallback" when it didn't (non-durable). */
69
- readonly mode: StoreMode;
70
- }
71
- /** Internal trust check used by security-sensitive capabilities; only stores opened by this module are accepted. */
72
- export declare function isConcreteSessionStore(store: SessionStore): boolean;
73
- export interface OpenSessionStoreOptions {
74
- /** Path to the SQLite file. ":memory:" uses an in-process DB. */
75
- dbPath: string;
76
- /** Injectable better-sqlite3 loader (the seam tests use to FORCE the in-memory fallback by throwing).
77
- * Defaults to `require("better-sqlite3")`. */
78
- loadDatabase?: () => typeof import("better-sqlite3");
79
- }
80
- export declare function openSessionStore(opts: OpenSessionStoreOptions): SessionStore;
81
- export {};