@ravefox/space 0.2.1 → 0.4.0

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 (167) hide show
  1. package/LICENSE +21 -205
  2. package/README.md +104 -31
  3. package/bin/space.js +113 -0
  4. package/lib/agents.js +181 -0
  5. package/lib/boardTools.js +89 -0
  6. package/lib/credentials.js +95 -0
  7. package/lib/daemon.js +294 -0
  8. package/lib/harness.js +166 -0
  9. package/lib/http.js +101 -0
  10. package/lib/login.js +68 -0
  11. package/lib/mcp.js +39 -0
  12. package/lib/runtime.js +253 -0
  13. package/lib/store.js +96 -0
  14. package/lib/tools.js +93 -0
  15. package/lib/workspaceSandbox.js +77 -0
  16. package/lib/workspaceTools.js +160 -0
  17. package/lib/workspaceWorker.js +17 -0
  18. package/package.json +27 -46
  19. package/NOTICE +0 -55
  20. package/assets/webhook-sdk/python/bot.py +0 -63
  21. package/assets/webhook-sdk/python/commonly.py +0 -232
  22. package/node_modules/@commonlyai/seats/README.md +0 -25
  23. package/node_modules/@commonlyai/seats/package.json +0 -22
  24. package/node_modules/@commonlyai/seats/src/adapters/hermes.js +0 -264
  25. package/node_modules/@commonlyai/seats/src/adapters/stub.js +0 -24
  26. package/node_modules/@commonlyai/seats/src/contract.js +0 -48
  27. package/node_modules/@commonlyai/seats/src/failures.js +0 -44
  28. package/node_modules/@commonlyai/seats/src/harness-plan.js +0 -329
  29. package/node_modules/@commonlyai/seats/src/index.js +0 -22
  30. package/node_modules/@commonlyai/seats/src/process-group.js +0 -269
  31. package/node_modules/@commonlyai/seats/src/record/format.js +0 -737
  32. package/node_modules/@commonlyai/seats/src/record/otel.js +0 -238
  33. package/node_modules/@commonlyai/seats/src/runtime-observation.js +0 -29
  34. package/node_modules/@commonlyai/seats/src/seat-model.js +0 -17
  35. package/node_modules/@commonlyai/seats/src/supervisor.js +0 -308
  36. package/node_modules/@commonlyai/seats/src/trace/events.js +0 -46
  37. package/node_modules/@commonlyai/seats/src/trace/store.js +0 -276
  38. package/node_modules/@commonlyai/seats/src/turn-processes.js +0 -226
  39. package/node_modules/@commonlyai/seats/src/upstream-refusal.js +0 -418
  40. package/node_modules/@commonlyai/seats/src/valet/env.js +0 -201
  41. package/node_modules/@commonlyai/seats/src/valet/grant.js +0 -123
  42. package/node_modules/@commonlyai/seats/src/valet/permissions.js +0 -213
  43. package/node_modules/@commonlyai/seats/src/wire/provider-proxy.js +0 -529
  44. package/skills/commonly/SKILL.md +0 -233
  45. package/src/commands/agent.js +0 -6334
  46. package/src/commands/daemon.js +0 -933
  47. package/src/commands/dev.js +0 -153
  48. package/src/commands/doctor.js +0 -141
  49. package/src/commands/inbox.js +0 -330
  50. package/src/commands/login.js +0 -194
  51. package/src/commands/mcp.js +0 -56
  52. package/src/commands/pod.js +0 -120
  53. package/src/commands/task.js +0 -279
  54. package/src/commands/verify.js +0 -48
  55. package/src/index.js +0 -99
  56. package/src/lib/adapter-login.js +0 -76
  57. package/src/lib/adapters/broker-bridge-entry.js +0 -76
  58. package/src/lib/adapters/claude.js +0 -2207
  59. package/src/lib/adapters/codex-broker-bridge.mjs +0 -298
  60. package/src/lib/adapters/codex.js +0 -1898
  61. package/src/lib/adapters/dsh.js +0 -513
  62. package/src/lib/adapters/goose.js +0 -510
  63. package/src/lib/adapters/hermes.js +0 -18
  64. package/src/lib/adapters/index.js +0 -68
  65. package/src/lib/adapters/opencode.js +0 -1681
  66. package/src/lib/adapters/pi-commonly-mcp.mjs +0 -71
  67. package/src/lib/adapters/pi-mcp-client.mjs +0 -617
  68. package/src/lib/adapters/pi-shell-env.mjs +0 -41
  69. package/src/lib/adapters/pi.js +0 -1025
  70. package/src/lib/adapters/qwen.js +0 -523
  71. package/src/lib/adapters/seat-model.js +0 -8
  72. package/src/lib/adapters/stub.js +0 -1
  73. package/src/lib/adapters/tested-version.js +0 -106
  74. package/src/lib/api.js +0 -205
  75. package/src/lib/claim-outcome.js +0 -128
  76. package/src/lib/claimed-work-gate.js +0 -73
  77. package/src/lib/cli-hint.js +0 -33
  78. package/src/lib/commit-attribution.js +0 -391
  79. package/src/lib/config.js +0 -155
  80. package/src/lib/content-refusal.js +0 -94
  81. package/src/lib/credential-file.js +0 -208
  82. package/src/lib/daemon-agent-states.js +0 -95
  83. package/src/lib/daemon-hint.js +0 -18
  84. package/src/lib/daemon-logs.js +0 -43
  85. package/src/lib/daemon-service-windows.js +0 -263
  86. package/src/lib/daemon-service.js +0 -486
  87. package/src/lib/daemon-state.js +0 -84
  88. package/src/lib/daemon-store.js +0 -119
  89. package/src/lib/daemon-supervisor.js +0 -629
  90. package/src/lib/daemon-watch.js +0 -115
  91. package/src/lib/declared-mcp-guard.js +0 -284
  92. package/src/lib/default-environment.js +0 -207
  93. package/src/lib/device-login.js +0 -126
  94. package/src/lib/digest-frame.js +0 -151
  95. package/src/lib/doctor.js +0 -291
  96. package/src/lib/egress-proxy.js +0 -160
  97. package/src/lib/enforcement.js +0 -946
  98. package/src/lib/environment.js +0 -681
  99. package/src/lib/first-output-watchdog.js +0 -191
  100. package/src/lib/git-readonly-server.js +0 -138
  101. package/src/lib/git-readonly.js +0 -463
  102. package/src/lib/git-remote-commonly.mjs +0 -100
  103. package/src/lib/grant-broker-guard.js +0 -345
  104. package/src/lib/hooks-config.js +0 -238
  105. package/src/lib/host-gh-broker.js +0 -152
  106. package/src/lib/host-gh-shim.mjs +0 -98
  107. package/src/lib/inbox.js +0 -483
  108. package/src/lib/instance-proxy.js +0 -76
  109. package/src/lib/machine-host.js +0 -29
  110. package/src/lib/mcp-credential-delivery.js +0 -121
  111. package/src/lib/mcp-first-run.js +0 -50
  112. package/src/lib/mcp-home.js +0 -644
  113. package/src/lib/mcp-ready.js +0 -68
  114. package/src/lib/mcp-server-version.js +0 -123
  115. package/src/lib/mcp-token.js +0 -36
  116. package/src/lib/mcp-warm-child.mjs +0 -412
  117. package/src/lib/memory-bridge.js +0 -191
  118. package/src/lib/memory-import.js +0 -156
  119. package/src/lib/missing-harness.js +0 -31
  120. package/src/lib/model-unavailable.js +0 -154
  121. package/src/lib/native-runtime-inventory.js +0 -40
  122. package/src/lib/outsider-grant-mcp.js +0 -82
  123. package/src/lib/own-key-launch.js +0 -42
  124. package/src/lib/pod-catch-up.js +0 -55
  125. package/src/lib/pod-focus.js +0 -224
  126. package/src/lib/pod-tail.js +0 -68
  127. package/src/lib/poll-liveness.js +0 -103
  128. package/src/lib/poll-retry.js +0 -81
  129. package/src/lib/poller.js +0 -200
  130. package/src/lib/provider-proxy.js +0 -42
  131. package/src/lib/public-seat-mcp-flag.js +0 -36
  132. package/src/lib/public-tool-error.js +0 -60
  133. package/src/lib/public-withheld-tools.js +0 -48
  134. package/src/lib/reply-receipt.js +0 -55
  135. package/src/lib/resolve-on-path.js +0 -22
  136. package/src/lib/runtime-inventory.js +0 -56
  137. package/src/lib/runtime-observation.js +0 -37
  138. package/src/lib/sandbox/bwrap.js +0 -275
  139. package/src/lib/sandbox/codex-perimeter.js +0 -16
  140. package/src/lib/sandbox/host-check.js +0 -268
  141. package/src/lib/sandbox/mode.js +0 -49
  142. package/src/lib/sandbox/opencode-loopback.mjs +0 -68
  143. package/src/lib/sandbox/seatbelt.js +0 -684
  144. package/src/lib/sandbox/turn-dir.js +0 -56
  145. package/src/lib/sandbox/windows-unsandboxed.js +0 -183
  146. package/src/lib/sandbox/workspace-git-config.js +0 -243
  147. package/src/lib/sandbox/workspace-pin.js +0 -158
  148. package/src/lib/seat-runtime-controls.js +0 -61
  149. package/src/lib/seat-stop.js +0 -97
  150. package/src/lib/server-skills.js +0 -32
  151. package/src/lib/session-recycle.js +0 -219
  152. package/src/lib/session-store.js +0 -215
  153. package/src/lib/skill-prompt.js +0 -74
  154. package/src/lib/skills-import.js +0 -113
  155. package/src/lib/spawn-credential.js +0 -402
  156. package/src/lib/spawn-retry.js +0 -311
  157. package/src/lib/stable-install.js +0 -176
  158. package/src/lib/supervised-turn.js +0 -352
  159. package/src/lib/task.js +0 -235
  160. package/src/lib/turn-failure-notice.js +0 -177
  161. package/src/lib/unconfined-seat-guard.js +0 -94
  162. package/src/lib/update-check.js +0 -192
  163. package/src/lib/upstream-refusal.js +0 -1
  164. package/src/lib/webhook-server.js +0 -109
  165. package/src/lib/windows-command.js +0 -393
  166. package/src/lib/workspace-git.js +0 -87
  167. package/src/lib/workspace-trust.js +0 -162
@@ -1,191 +0,0 @@
1
- /**
2
- * First-output watchdog for a seat's model CLI spawn.
3
- *
4
- * On 2026-10-30 every launchd seat's `claude -p` turn parked while the Mac's
5
- * console was locked: no transcript row, no stdout, no stderr, for the full
6
- * 45-minute run timeout. Each one held its delivery the whole time, the retry
7
- * respawned it into the same hang, and the log said only "claude timed out
8
- * after 2700000ms", which names nothing.
9
- *
10
- * The signal is not stdout. A seat runs `claude -p --output-format text`,
11
- * which writes stdout once, at the end, so a healthy 20-minute turn has zero
12
- * stdout bytes for 20 minutes. What a healthy turn does write is its session
13
- * transcript: measured on the dart seat, the first row landed 0.7 s after the
14
- * spawn, and the hung spawns that night wrote no row at all. So a spawn counts
15
- * as started when it writes any stdout or stderr byte OR its transcript grows.
16
- * A spawn that does neither inside the window is killed with a reason. When
17
- * the transcript is missing altogether, the path may simply be wrong, so the
18
- * spawn is killed only if the console is also locked.
19
- *
20
- * That is the rule for a plain-text spawn. A stream-json spawn (TASK-710) counts
21
- * only model events on stdout; see `modelStream` on watchFirstOutput.
22
- */
23
- import { realpathSync, statSync } from 'fs';
24
- import { readProcess } from '@commonlyai/seats/process-group';
25
-
26
- export const DEFAULT_FIRST_OUTPUT_WINDOW_MS = 180_000;
27
-
28
- // 0 turns the watchdog off; anything unparseable keeps the default.
29
- export const firstOutputWindowMs = (env = process.env) => {
30
- const raw = env?.COMMONLY_FIRST_OUTPUT_WINDOW_MS;
31
- if (raw === undefined || raw === '') return DEFAULT_FIRST_OUTPUT_WINDOW_MS;
32
- const parsed = Number(raw);
33
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_FIRST_OUTPUT_WINDOW_MS;
34
- };
35
-
36
- export const SPAWN_STALLED_CODE = 'spawn_stalled';
37
-
38
- // The message carries the class word spawn-retry.js matches, so the stall gets
39
- // its own backoff rather than RUNTIME's 5-second retry.
40
- export const spawnStalledError = ({ label, windowMs, reason }) => {
41
- const error = new Error(
42
- `${label} produced no output in ${Math.round(windowMs / 1000)}s (${reason}); killed before the run timeout`,
43
- );
44
- error.code = SPAWN_STALLED_CODE;
45
- error.stallReason = reason;
46
- return error;
47
- };
48
-
49
- // `ioreg -n Root -d1` prints `"IOConsoleLocked" = Yes` while the screen is
50
- // locked. True, false, or null when it cannot tell (not darwin, ioreg failed).
51
- // child_process is loaded on demand: suites that mock it with only `spawn`
52
- // would otherwise fail to import every adapter that uses this module.
53
- export const consoleLocked = async ({ platform = process.platform, execFileImpl } = {}) => {
54
- if (platform !== 'darwin') return null;
55
- const run = execFileImpl || (await import('child_process')).execFile;
56
- if (typeof run !== 'function') return null;
57
- return new Promise((resolve) => {
58
- run('/usr/sbin/ioreg', ['-n', 'Root', '-d1'], { timeout: 5000 }, (error, stdout) => {
59
- if (error) return resolve(null);
60
- const match = String(stdout).match(/"IOConsoleLocked"\s*=\s*(Yes|No)/);
61
- return resolve(match ? match[1] === 'Yes' : null);
62
- });
63
- });
64
- };
65
-
66
- // Claude names its project folder after process.cwd(), which on macOS is the
67
- // physical path (/tmp is /private/tmp), so the transcript is looked for there.
68
- export const physicalPath = (dir) => {
69
- if (!dir) return dir;
70
- try {
71
- return realpathSync(dir);
72
- } catch {
73
- return dir;
74
- }
75
- };
76
-
77
- // How long a stalled spawn gets to exit after SIGTERM before SIGKILL.
78
- export const STALL_KILL_GRACE_MS = 10_000;
79
-
80
- // The boot line for a claude seat. This watchdog is the stall guard a claude
81
- // turn runs: the seats supervisor's stall window is off for claude
82
- // (STALL_DEFAULTS_MS in supervised-turn.js), and its package default of 120 s
83
- // was once read as this one's (the G6 walk, 2026-11-01).
84
- export const describeFirstOutputWatch = (env = process.env) => {
85
- const windowMs = firstOutputWindowMs(env);
86
- if (!windowMs) return 'first-output watchdog OFF for claude (COMMONLY_FIRST_OUTPUT_WINDOW_MS=0)';
87
- return `first-output watchdog ON for claude: a turn with no model output in ${windowMs}ms is killed `
88
- + `(SIGTERM, then SIGKILL ${STALL_KILL_GRACE_MS}ms later if it is still alive)`;
89
- };
90
-
91
- /**
92
- * SIGKILL each snapshotted descendant of a stalled spawn that is still alive
93
- * once the spawn itself has exited. Its parent may have exited, so it may now
94
- * belong to launchd: only the pid and start time are matched, which still
95
- * rules out a reused pid. Returns the pids signalled.
96
- */
97
- export const killStallSurvivors = (snapshot, { read = readProcess, kill = process.kill.bind(process) } = {}) => {
98
- const killed = [];
99
- for (const row of snapshot || []) {
100
- const now = read(row.pid);
101
- if (!now || now.started !== row.started) continue;
102
- try { kill(row.pid, 'SIGKILL'); killed.push(row.pid); } catch { /* gone */ }
103
- }
104
- return killed;
105
- };
106
-
107
- const sizeOf = (file) => {
108
- if (!file) return -1;
109
- try {
110
- return statSync(file).size;
111
- } catch {
112
- return -1;
113
- }
114
- };
115
-
116
- // A stream-json line is model progress when it is an event other than `system`.
117
- // The `system` events are `init` (written before any model call) and
118
- // `api_retry` (a provider failing and retrying). Counting either would keep a
119
- // hung provider looking alive until the run timeout.
120
- export const isModelProgressLine = (line) => {
121
- try {
122
- const event = JSON.parse(line);
123
- return !!event && typeof event === 'object' && event.type !== 'system';
124
- } catch {
125
- return false;
126
- }
127
- };
128
-
129
- /**
130
- * Watch `proc` for its first sign of life. Returns `{ stop }`; call it when the
131
- * process closes. `onStall(reason)` fires at most once, after the caller should
132
- * kill the process.
133
- *
134
- * With `modelStream`, the spawn writes stream-json events. Only model output
135
- * counts as life there. A transcript row does not, because claude writes its
136
- * first row about a second after spawn, before any model reply. Stderr does
137
- * not either: claude prints warnings at spawn (an unrecognised model, a
138
- * connector notice) whatever the provider does next.
139
- */
140
- export const watchFirstOutput = ({
141
- proc,
142
- windowMs,
143
- transcriptPath = null,
144
- modelStream = false,
145
- onStall,
146
- lockProbe = consoleLocked,
147
- setTimeoutImpl = setTimeout,
148
- clearTimeoutImpl = clearTimeout,
149
- }) => {
150
- if (!windowMs) return { stop: () => {} };
151
- let started = false;
152
- let stopped = false;
153
- let partial = '';
154
- const markStarted = () => { started = true; };
155
- if (modelStream) {
156
- proc.stdout?.on('data', (chunk) => {
157
- const lines = (partial + chunk.toString()).split('\n');
158
- partial = lines.pop();
159
- if (lines.some(isModelProgressLine)) markStarted();
160
- });
161
- } else {
162
- proc.stdout?.on('data', markStarted);
163
- proc.stderr?.on('data', markStarted);
164
- }
165
- // A missing file counts as empty, so creating an empty one is not growth.
166
- const baseline = Math.max(0, sizeOf(transcriptPath));
167
- const timer = setTimeoutImpl(async () => {
168
- const size = sizeOf(transcriptPath);
169
- if (started || (!modelStream && size > baseline)) return;
170
- const locked = await lockProbe().catch(() => null);
171
- // Output can arrive while the probe runs (ps or ioreg may take a second).
172
- if (stopped || started) return;
173
- if (modelStream) {
174
- // The model signal does not read the transcript, so a missing path cannot
175
- // hide a hung provider here.
176
- onStall(locked ? 'console locked' : 'stuck: no model output inside the window');
177
- return;
178
- }
179
- // No transcript where one was expected may only mean the path is wrong, so
180
- // that alone never kills a turn: it takes a locked console as well.
181
- if (size < 0 && locked !== true) return;
182
- onStall(locked ? 'console locked' : 'no transcript, stdout or stderr');
183
- }, windowMs);
184
- timer?.unref?.();
185
- return {
186
- stop: () => {
187
- stopped = true;
188
- clearTimeoutImpl(timer);
189
- },
190
- };
191
- };
@@ -1,138 +0,0 @@
1
- /**
2
- * The `workspace_git` MCP server: one tool, `git`, for a workspace-trust seat
3
- * (TASK-294). The claude adapter spawns it as
4
- *
5
- * node git-readonly-server.js --root <workspace> --git <binary>
6
- *
7
- * and the model sees it as `mcp__workspace_git__git`. It speaks the MCP stdio
8
- * transport (newline-delimited JSON-RPC 2.0) and hands every call to
9
- * runReadonlyGit, which refuses anything that is not read-only and runs the
10
- * rest with execFile in the workspace.
11
- */
12
- import { realpathSync, statSync } from 'fs';
13
- import { isAbsolute } from 'path';
14
- import { createInterface } from 'readline';
15
- import { fileURLToPath } from 'url';
16
- import { READONLY_GIT_SUBCOMMANDS, runReadonlyGit } from './git-readonly.js';
17
-
18
- export const SERVER_NAME = 'workspace_git';
19
- export const TOOL_NAME = 'git';
20
- const SERVER_VERSION = '1.0.0';
21
-
22
- // The tool surface is the same in each of these, so the server answers in the
23
- // version the client asked for when it is one of them.
24
- const SUPPORTED_PROTOCOL_VERSIONS = Object.freeze(['2024-11-05', '2025-03-26', '2025-06-18']);
25
-
26
- export const GIT_TOOL = Object.freeze({
27
- name: TOOL_NAME,
28
- description: `Run read-only git in your workspace: ${READONLY_GIT_SUBCOMMANDS.join(', ')}. `
29
- + 'Pass the subcommand and its arguments separately, one argument per array entry, '
30
- + "e.g. { subcommand: 'log', args: ['-n', '5', '--oneline'] }. "
31
- + 'Options that write a file, run a program, or read outside the workspace are refused with the reason, '
32
- + 'and paths must stay inside the workspace. For log, diff and show, put paths after \'--\' '
33
- + "(e.g. ['-p', '--', 'src/app.js']). .env files are left out of every result. "
34
- + 'This is the only git available to you.',
35
- inputSchema: {
36
- type: 'object',
37
- properties: {
38
- subcommand: { type: 'string', enum: [...READONLY_GIT_SUBCOMMANDS] },
39
- args: {
40
- type: 'array',
41
- items: { type: 'string' },
42
- description: 'Arguments after the subcommand, one per entry.',
43
- },
44
- },
45
- required: ['subcommand'],
46
- additionalProperties: false,
47
- },
48
- });
49
-
50
- const reply = (id, result) => ({ jsonrpc: '2.0', id, result });
51
- const fail = (id, code, message) => ({ jsonrpc: '2.0', id, error: { code, message } });
52
-
53
- /**
54
- * A handler for one parsed JSON-RPC message. Resolves the response, or null
55
- * for a notification (a message with no id), which gets none.
56
- */
57
- export const createGitServerHandler = ({
58
- root, git = 'git', baseEnv = process.env, run = runReadonlyGit,
59
- }) => async (message) => {
60
- if (!message || typeof message !== 'object' || Array.isArray(message) || typeof message.method !== 'string') {
61
- return fail(message?.id ?? null, -32600, 'Invalid Request');
62
- }
63
- const { id, method, params } = message;
64
- if (id === undefined) return null;
65
- if (method === 'initialize') {
66
- const asked = params?.protocolVersion;
67
- return reply(id, {
68
- protocolVersion: SUPPORTED_PROTOCOL_VERSIONS.includes(asked) ? asked : SUPPORTED_PROTOCOL_VERSIONS[0],
69
- capabilities: { tools: {} },
70
- serverInfo: { name: SERVER_NAME, version: SERVER_VERSION },
71
- });
72
- }
73
- if (method === 'ping') return reply(id, {});
74
- if (method === 'tools/list') return reply(id, { tools: [GIT_TOOL] });
75
- if (method === 'tools/call') {
76
- if (params?.name !== TOOL_NAME) return fail(id, -32602, `Unknown tool: ${params?.name}`);
77
- const { subcommand, args = [] } = params.arguments || {};
78
- const { ok, text } = await run({
79
- subcommand, args, root, git, baseEnv,
80
- });
81
- return reply(id, { content: [{ type: 'text', text }], isError: !ok });
82
- }
83
- return fail(id, -32601, `Method not found: ${method}`);
84
- };
85
-
86
- /** `--root <dir> --git <binary>`; the root must be an existing directory. */
87
- export const parseServerArgs = (argv) => {
88
- const value = (flag) => {
89
- const at = argv.indexOf(flag);
90
- return at === -1 ? undefined : argv[at + 1];
91
- };
92
- const root = value('--root');
93
- if (typeof root !== 'string' || !isAbsolute(root)) throw new Error('--root <absolute workspace path> is required');
94
- const real = realpathSync(root);
95
- if (!statSync(real).isDirectory()) throw new Error(`--root ${root} is not a directory`);
96
- return { root: real, git: value('--git') || 'git' };
97
- };
98
-
99
- export const serve = ({
100
- root, git, input = process.stdin, output = process.stdout, baseEnv = process.env,
101
- }) => {
102
- const handle = createGitServerHandler({ root, git, baseEnv });
103
- // A closed pipe on the client side is the client leaving, not an error.
104
- output.on('error', () => {});
105
- const lines = createInterface({ input, crlfDelay: Infinity });
106
- lines.on('line', async (line) => {
107
- if (!line.trim()) return;
108
- let message;
109
- try {
110
- message = JSON.parse(line);
111
- } catch {
112
- output.write(`${JSON.stringify(fail(null, -32700, 'Parse error'))}\n`);
113
- return;
114
- }
115
- const response = await handle(message);
116
- if (response) output.write(`${JSON.stringify(response)}\n`);
117
- });
118
- return lines;
119
- };
120
-
121
- const runAsMain = () => {
122
- try {
123
- return Boolean(process.argv[1]) && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
124
- } catch {
125
- return false;
126
- }
127
- };
128
-
129
- if (runAsMain()) {
130
- let options;
131
- try {
132
- options = parseServerArgs(process.argv.slice(2));
133
- } catch (err) {
134
- process.stderr.write(`${SERVER_NAME}: ${err.message}\n`);
135
- process.exit(2);
136
- }
137
- serve(options);
138
- }