clawcrib 0.1.0 → 0.1.2

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 (116) hide show
  1. package/bin/clawcrib.mjs +415 -34
  2. package/dist/.next/BUILD_ID +1 -1
  3. package/dist/.next/build-manifest.json +2 -2
  4. package/dist/.next/server/app/_global-error.html +2 -2
  5. package/dist/.next/server/app/_global-error.rsc +1 -1
  6. package/dist/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/dist/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/dist/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  9. package/dist/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  10. package/dist/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/dist/.next/server/app/_not-found.html +2 -2
  12. package/dist/.next/server/app/_not-found.rsc +1 -1
  13. package/dist/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  14. package/dist/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  15. package/dist/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  16. package/dist/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  17. package/dist/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  18. package/dist/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  19. package/dist/.next/server/app/browse.html +2 -2
  20. package/dist/.next/server/app/browse.rsc +1 -1
  21. package/dist/.next/server/app/browse.segments/_full.segment.rsc +1 -1
  22. package/dist/.next/server/app/browse.segments/_head.segment.rsc +1 -1
  23. package/dist/.next/server/app/browse.segments/_index.segment.rsc +1 -1
  24. package/dist/.next/server/app/browse.segments/_tree.segment.rsc +1 -1
  25. package/dist/.next/server/app/browse.segments/browse/__PAGE__.segment.rsc +1 -1
  26. package/dist/.next/server/app/browse.segments/browse.segment.rsc +1 -1
  27. package/dist/.next/server/app/builder.html +2 -2
  28. package/dist/.next/server/app/builder.rsc +1 -1
  29. package/dist/.next/server/app/builder.segments/_full.segment.rsc +1 -1
  30. package/dist/.next/server/app/builder.segments/_head.segment.rsc +1 -1
  31. package/dist/.next/server/app/builder.segments/_index.segment.rsc +1 -1
  32. package/dist/.next/server/app/builder.segments/_tree.segment.rsc +1 -1
  33. package/dist/.next/server/app/builder.segments/builder/__PAGE__.segment.rsc +1 -1
  34. package/dist/.next/server/app/builder.segments/builder.segment.rsc +1 -1
  35. package/dist/.next/server/app/crib.html +1 -1
  36. package/dist/.next/server/app/crib.rsc +1 -1
  37. package/dist/.next/server/app/crib.segments/_full.segment.rsc +1 -1
  38. package/dist/.next/server/app/crib.segments/_head.segment.rsc +1 -1
  39. package/dist/.next/server/app/crib.segments/_index.segment.rsc +1 -1
  40. package/dist/.next/server/app/crib.segments/_tree.segment.rsc +1 -1
  41. package/dist/.next/server/app/crib.segments/crib/__PAGE__.segment.rsc +1 -1
  42. package/dist/.next/server/app/crib.segments/crib.segment.rsc +1 -1
  43. package/dist/.next/server/app/demo.html +1 -1
  44. package/dist/.next/server/app/demo.rsc +1 -1
  45. package/dist/.next/server/app/demo.segments/_full.segment.rsc +1 -1
  46. package/dist/.next/server/app/demo.segments/_head.segment.rsc +1 -1
  47. package/dist/.next/server/app/demo.segments/_index.segment.rsc +1 -1
  48. package/dist/.next/server/app/demo.segments/_tree.segment.rsc +1 -1
  49. package/dist/.next/server/app/demo.segments/demo/__PAGE__.segment.rsc +1 -1
  50. package/dist/.next/server/app/demo.segments/demo.segment.rsc +1 -1
  51. package/dist/.next/server/app/feedback.html +2 -2
  52. package/dist/.next/server/app/feedback.rsc +1 -1
  53. package/dist/.next/server/app/feedback.segments/_full.segment.rsc +1 -1
  54. package/dist/.next/server/app/feedback.segments/_head.segment.rsc +1 -1
  55. package/dist/.next/server/app/feedback.segments/_index.segment.rsc +1 -1
  56. package/dist/.next/server/app/feedback.segments/_tree.segment.rsc +1 -1
  57. package/dist/.next/server/app/feedback.segments/feedback/__PAGE__.segment.rsc +1 -1
  58. package/dist/.next/server/app/feedback.segments/feedback.segment.rsc +1 -1
  59. package/dist/.next/server/app/gltest.html +1 -1
  60. package/dist/.next/server/app/gltest.rsc +1 -1
  61. package/dist/.next/server/app/gltest.segments/_full.segment.rsc +1 -1
  62. package/dist/.next/server/app/gltest.segments/_head.segment.rsc +1 -1
  63. package/dist/.next/server/app/gltest.segments/_index.segment.rsc +1 -1
  64. package/dist/.next/server/app/gltest.segments/_tree.segment.rsc +1 -1
  65. package/dist/.next/server/app/gltest.segments/gltest/__PAGE__.segment.rsc +1 -1
  66. package/dist/.next/server/app/gltest.segments/gltest.segment.rsc +1 -1
  67. package/dist/.next/server/app/help.html +2 -2
  68. package/dist/.next/server/app/help.rsc +1 -1
  69. package/dist/.next/server/app/help.segments/_full.segment.rsc +1 -1
  70. package/dist/.next/server/app/help.segments/_head.segment.rsc +1 -1
  71. package/dist/.next/server/app/help.segments/_index.segment.rsc +1 -1
  72. package/dist/.next/server/app/help.segments/_tree.segment.rsc +1 -1
  73. package/dist/.next/server/app/help.segments/help/__PAGE__.segment.rsc +1 -1
  74. package/dist/.next/server/app/help.segments/help.segment.rsc +1 -1
  75. package/dist/.next/server/app/index.html +1 -1
  76. package/dist/.next/server/app/index.rsc +2 -2
  77. package/dist/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  78. package/dist/.next/server/app/index.segments/_full.segment.rsc +2 -2
  79. package/dist/.next/server/app/index.segments/_head.segment.rsc +1 -1
  80. package/dist/.next/server/app/index.segments/_index.segment.rsc +1 -1
  81. package/dist/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  82. package/dist/.next/server/app/learn.html +2 -2
  83. package/dist/.next/server/app/learn.rsc +1 -1
  84. package/dist/.next/server/app/learn.segments/_full.segment.rsc +1 -1
  85. package/dist/.next/server/app/learn.segments/_head.segment.rsc +1 -1
  86. package/dist/.next/server/app/learn.segments/_index.segment.rsc +1 -1
  87. package/dist/.next/server/app/learn.segments/_tree.segment.rsc +1 -1
  88. package/dist/.next/server/app/learn.segments/learn/__PAGE__.segment.rsc +1 -1
  89. package/dist/.next/server/app/learn.segments/learn.segment.rsc +1 -1
  90. package/dist/.next/server/app/page_client-reference-manifest.js +1 -1
  91. package/dist/.next/server/app/privacy.html +2 -2
  92. package/dist/.next/server/app/privacy.rsc +1 -1
  93. package/dist/.next/server/app/privacy.segments/_full.segment.rsc +1 -1
  94. package/dist/.next/server/app/privacy.segments/_head.segment.rsc +1 -1
  95. package/dist/.next/server/app/privacy.segments/_index.segment.rsc +1 -1
  96. package/dist/.next/server/app/privacy.segments/_tree.segment.rsc +1 -1
  97. package/dist/.next/server/app/privacy.segments/privacy/__PAGE__.segment.rsc +1 -1
  98. package/dist/.next/server/app/privacy.segments/privacy.segment.rsc +1 -1
  99. package/dist/.next/server/app/terms.html +2 -2
  100. package/dist/.next/server/app/terms.rsc +1 -1
  101. package/dist/.next/server/app/terms.segments/_full.segment.rsc +1 -1
  102. package/dist/.next/server/app/terms.segments/_head.segment.rsc +1 -1
  103. package/dist/.next/server/app/terms.segments/_index.segment.rsc +1 -1
  104. package/dist/.next/server/app/terms.segments/_tree.segment.rsc +1 -1
  105. package/dist/.next/server/app/terms.segments/terms/__PAGE__.segment.rsc +1 -1
  106. package/dist/.next/server/app/terms.segments/terms.segment.rsc +1 -1
  107. package/dist/.next/server/chunks/ssr/_a46dee72._.js +1 -1
  108. package/dist/.next/server/middleware-manifest.json +1 -1
  109. package/dist/.next/server/pages/404.html +2 -2
  110. package/dist/.next/server/pages/500.html +2 -2
  111. package/dist/.next/static/chunks/d6211ae3ff8400ff.js +1 -0
  112. package/package.json +1 -1
  113. package/dist/.next/static/chunks/46b6f1723e3c2623.js +0 -1
  114. /package/dist/.next/static/{LDs2QlURjFsxe6doQgQaH → zYUlp2fcVlJKBhUQk1Pns}/_buildManifest.js +0 -0
  115. /package/dist/.next/static/{LDs2QlURjFsxe6doQgQaH → zYUlp2fcVlJKBhUQk1Pns}/_clientMiddlewareManifest.json +0 -0
  116. /package/dist/.next/static/{LDs2QlURjFsxe6doQgQaH → zYUlp2fcVlJKBhUQk1Pns}/_ssgManifest.js +0 -0
package/bin/clawcrib.mjs CHANGED
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { spawn } from "node:child_process";
4
- import { existsSync } from "node:fs";
4
+ import { existsSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs";
5
5
  import { createRequire } from "node:module";
6
+ import net from "node:net";
7
+ import os from "node:os";
6
8
  import path from "node:path";
7
9
  import { fileURLToPath } from "node:url";
8
10
 
@@ -14,24 +16,46 @@ const HELP = `
14
16
  ClawCrib — the live 3D office for your OpenClaw agents
15
17
 
16
18
  Usage
17
- npx clawcrib [options]
19
+ npx clawcrib [command] [options]
20
+
21
+ Commands
22
+ (none) Run in the foreground until you press Ctrl-C
23
+ start Run in the background; survives closing the terminal
24
+ stop Stop the background instance
25
+ restart Stop, then start
26
+ status Is it running, and is it responding?
27
+ install-service Keep it running across reboots (systemd / launchd)
28
+ uninstall-service Remove that service file
18
29
 
19
30
  Options
20
31
  -p, --port <port> Port to listen on (default: 18787)
21
32
  -g, --gateway <url> OpenClaw gateway WebSocket URL (e.g. ws://127.0.0.1:18789)
22
33
  -t, --token <token> Gateway auth token
23
- --host <host> Host to bind (default: 127.0.0.1)
34
+ --host <host> Host to bind (default: 127.0.0.1 — use 0.0.0.0 to
35
+ reach it from another machine over LAN/SSH)
24
36
  --no-open Don't open the browser
37
+ --demo Force the simulated team, even if a gateway is found
25
38
  -v, --version Print version
26
39
  -h, --help Show this help
27
40
 
28
- Without --gateway, ClawCrib runs in demo mode with a simulated team.
29
- Environment variables OPENCLAW_GATEWAY_URL / OPENCLAW_GATEWAY_TOKEN
30
- are honored when flags are not given.
41
+ A gateway on this machine (127.0.0.1:18789) is detected and connected
42
+ automatically you only need --gateway for a remote one. With no gateway
43
+ anywhere, ClawCrib shows a simulated team so the floor is never empty.
44
+ Over SSH the bind defaults to 0.0.0.0 so you can reach it from your laptop.
45
+ OPENCLAW_GATEWAY_URL / OPENCLAW_GATEWAY_TOKEN are honored too.
31
46
  `;
32
47
 
48
+ const COMMANDS = new Set(["start", "stop", "restart", "status", "install-service", "uninstall-service"]);
49
+
50
+ // Reached over SSH, a loopback bind is useless — the dashboard is only visible
51
+ // from inside the box you are already tunnelling into. Default to all
52
+ // interfaces there so `clawcrib start` just works; an explicit --host wins.
53
+ const OVER_SSH = Boolean(process.env.SSH_CONNECTION || process.env.SSH_TTY || process.env.SSH_CLIENT);
54
+
33
55
  function parseArgs(argv) {
34
- const opts = { port: 18787, host: "127.0.0.1", open: true, gateway: null, token: null };
56
+ const opts = { port: 18787, host: "127.0.0.1", open: true, gateway: null, token: null, cmd: "run", hostAuto: false, demo: false };
57
+ if (argv.length && COMMANDS.has(argv[0])) opts.cmd = argv.shift();
58
+ let hostGiven = false;
35
59
  for (let i = 0; i < argv.length; i++) {
36
60
  const a = argv[i];
37
61
  switch (a) {
@@ -49,10 +73,14 @@ function parseArgs(argv) {
49
73
  break;
50
74
  case "--host":
51
75
  opts.host = argv[++i];
76
+ hostGiven = true;
52
77
  break;
53
78
  case "--no-open":
54
79
  opts.open = false;
55
80
  break;
81
+ case "--demo":
82
+ opts.demo = true;
83
+ break;
56
84
  case "-v":
57
85
  case "--version":
58
86
  console.log(pkg.version);
@@ -72,6 +100,10 @@ function parseArgs(argv) {
72
100
  console.error("Invalid --port");
73
101
  process.exit(1);
74
102
  }
103
+ if (!hostGiven && OVER_SSH) {
104
+ opts.host = "0.0.0.0";
105
+ opts.hostAuto = true;
106
+ }
75
107
  return opts;
76
108
  }
77
109
 
@@ -112,38 +144,387 @@ const env = {
112
144
  // tells the app it is a self-hosted dashboard, not the marketing site:
113
145
  // the office is served at / and the website-only pages 404 (src/middleware.ts)
114
146
  CLAWCRIB_APP: "1",
147
+ // a transitive dep still calls util._extend; the DEP0060 notice reads like a
148
+ // crash to someone running this for the first time and it is not actionable
149
+ NODE_OPTIONS: [process.env.NODE_OPTIONS, "--no-deprecation"].filter(Boolean).join(" "),
115
150
  };
116
151
  if (opts.gateway) env.OPENCLAW_GATEWAY_URL = opts.gateway;
117
152
  if (opts.token) env.OPENCLAW_GATEWAY_TOKEN = opts.token;
118
153
 
119
- const child = spawn(process.execPath, [serverJs], {
120
- cwd: path.dirname(serverJs),
121
- env,
122
- stdio: ["ignore", "pipe", "inherit"],
123
- });
124
- child.stdout.on("data", () => {}); // keep Next's own listen log quiet; we print our own
125
-
126
- const url = `http://${opts.host === "0.0.0.0" ? "localhost" : opts.host}:${opts.port}`;
127
- const mode = env.OPENCLAW_GATEWAY_URL
128
- ? `live gateway: ${env.OPENCLAW_GATEWAY_URL}`
129
- : "demo mode (no gateway configured — pass --gateway ws://... for live data)";
130
-
131
- waitForServer(url).then((up) => {
132
- if (!up) {
133
- console.error("ClawCrib server did not come up within 30s.");
134
- child.kill();
154
+ const LOOPBACK = new Set(["127.0.0.1", "localhost", "::1"]);
155
+ const GATEWAY_PORT = 18789;
156
+
157
+ // The gateway almost always runs on the same box as the crib, so probe for a
158
+ // listener and say so rather than leaving someone in demo mode wondering why.
159
+ // TCP connect only: this never opens a session, so no device-pairing request
160
+ // is raised on the gateway.
161
+ function probePort(host, port, timeoutMs = 700) {
162
+ return new Promise((resolve) => {
163
+ const sock = net.connect({ host, port });
164
+ const finish = (ok) => {
165
+ sock.destroy();
166
+ resolve(ok);
167
+ };
168
+ sock.setTimeout(timeoutMs);
169
+ sock.once("connect", () => finish(true));
170
+ sock.once("timeout", () => finish(false));
171
+ sock.once("error", () => finish(false));
172
+ });
173
+ }
174
+
175
+ // first non-internal IPv4, so a remote box can print a URL you can actually open
176
+ function lanAddress() {
177
+ for (const ifaces of Object.values(os.networkInterfaces())) {
178
+ for (const i of ifaces ?? []) {
179
+ if (i.family === "IPv4" && !i.internal) return i.address;
180
+ }
181
+ }
182
+ return null;
183
+ }
184
+
185
+ // Must stay in step with device-identity.ts / api/crib/profiles: keyed off the
186
+ // user's home, never cwd (under npx the cwd is a throwaway cache directory).
187
+ const CONFIG_DIR = process.env.CLAWCRIB_CONFIG_DIR || path.join(os.homedir(), ".clawcrib");
188
+ const PID_FILE = path.join(CONFIG_DIR, "clawcrib.pid");
189
+ const LOG_FILE = path.join(CONFIG_DIR, "clawcrib.log");
190
+
191
+ // Health checks always go to loopback, but what we *print* when bound to all
192
+ // interfaces must be the address another machine can actually open. Printing
193
+ // "localhost" there is worse than useless: it is the one URL that cannot work
194
+ // from anywhere else.
195
+ const healthUrl = `http://${opts.host === "0.0.0.0" ? "127.0.0.1" : opts.host}:${opts.port}`;
196
+ const lanIp = lanAddress();
197
+ const url = opts.host === "0.0.0.0" && lanIp ? `http://${lanIp}:${opts.port}` : healthUrl;
198
+ let autoGateway = false;
199
+
200
+ // You installed this on the box your agents run on. Finding the gateway and
201
+ // then asking you to re-type its address is a step that exists for no reason —
202
+ // connect to it. `--demo` opts out, and an explicit --gateway always wins.
203
+ async function resolveGateway() {
204
+ if (env.OPENCLAW_GATEWAY_URL || opts.demo) return;
205
+ if (await probePort("127.0.0.1", GATEWAY_PORT)) {
206
+ env.OPENCLAW_GATEWAY_URL = `ws://127.0.0.1:${GATEWAY_PORT}`;
207
+ autoGateway = true;
208
+ }
209
+ }
210
+
211
+ function modeLine() {
212
+ if (!env.OPENCLAW_GATEWAY_URL) {
213
+ return opts.demo
214
+ ? "demo mode (--demo)"
215
+ : "demo mode — no gateway found on this machine. Pass --gateway ws://host:18789 for a remote one.";
216
+ }
217
+ return autoGateway
218
+ ? `live — auto-connected to the gateway on this machine (${env.OPENCLAW_GATEWAY_URL})`
219
+ : `live gateway: ${env.OPENCLAW_GATEWAY_URL}`;
220
+ }
221
+
222
+ const STATE_FILE = path.join(CONFIG_DIR, "clawcrib.json");
223
+
224
+ function readPid() {
225
+ try {
226
+ const pid = Number(readFileSync(PID_FILE, "utf8").trim());
227
+ // signal 0 tests for existence without touching the process
228
+ process.kill(pid, 0);
229
+ return pid;
230
+ } catch {
231
+ return null; // no pid file, or the process died without cleaning up
232
+ }
233
+ }
234
+
235
+ // What the running instance was actually started with. Without this, `start`
236
+ // can only ask "is something alive?" and would report success while silently
237
+ // ignoring the --host/--port/--gateway you just asked for.
238
+ function readState() {
239
+ try {
240
+ return JSON.parse(readFileSync(STATE_FILE, "utf8"));
241
+ } catch {
242
+ return null;
243
+ }
244
+ }
245
+
246
+ function settingsDiffer(prev) {
247
+ if (!prev) return null;
248
+ const now = { host: opts.host, port: opts.port, gateway: env.OPENCLAW_GATEWAY_URL ?? null };
249
+ const changed = [];
250
+ if (prev.host !== now.host) changed.push(`host ${prev.host} → ${now.host}`);
251
+ if (prev.port !== now.port) changed.push(`port ${prev.port} → ${now.port}`);
252
+ if ((prev.gateway ?? null) !== now.gateway) changed.push(`gateway ${prev.gateway ?? "none"} → ${now.gateway ?? "none"}`);
253
+ return changed.length ? changed : null;
254
+ }
255
+
256
+ async function printDetails() {
257
+ if (LOOPBACK.has(opts.host)) {
258
+ console.log(` Note: bound to ${opts.host} — reachable only from this machine.`);
259
+ console.log(` For LAN/SSH access, use --host 0.0.0.0`);
260
+ if (lanIp) console.log(` then open http://${lanIp}:${opts.port}`);
261
+ } else if (opts.host === "0.0.0.0") {
262
+ if (opts.hostAuto) console.log(` Bind: 0.0.0.0 (auto: this is an SSH session — pass --host to override)`);
263
+ console.log(` Open: ${url} from another machine on this network`);
264
+ console.log(` Not loading? The port is probably firewalled:`);
265
+ console.log(` sudo ufw allow ${opts.port}/tcp # ufw`);
266
+ console.log(` sudo firewall-cmd --add-port=${opts.port}/tcp --permanent && sudo firewall-cmd --reload`);
267
+ }
268
+ if (autoGateway) {
269
+ console.log(` If the gateway asks to pair, approve it there:`);
270
+ console.log(` openclaw devices approve <requestId>`);
271
+ }
272
+ }
273
+
274
+ function spawnServer(detached) {
275
+ mkdirSync(CONFIG_DIR, { recursive: true });
276
+ const out = detached ? openSync(LOG_FILE, "a") : "pipe";
277
+ return spawn(process.execPath, [serverJs], {
278
+ cwd: path.dirname(serverJs),
279
+ env,
280
+ detached,
281
+ stdio: detached ? ["ignore", out, out] : ["ignore", "pipe", "inherit"],
282
+ });
283
+ }
284
+
285
+ async function cmdStart() {
286
+ await resolveGateway();
287
+ const running = readPid();
288
+ if (running) {
289
+ const changed = settingsDiffer(readState());
290
+ if (!changed) {
291
+ console.log(`\n ClawCrib is already running (pid ${running}) at ${url}\n`);
292
+ return;
293
+ }
294
+ // You asked for different settings; honouring the old ones and calling it
295
+ // success is how "--host 0.0.0.0" silently did nothing.
296
+ console.log(`\n Restarting with new settings: ${changed.join(", ")}`);
297
+ await cmdStop();
298
+ }
299
+
300
+ // A clearer failure than Node's raw EADDRINUSE stack trace.
301
+ if (await probePort(opts.host === "0.0.0.0" ? "127.0.0.1" : opts.host, opts.port, 400)) {
302
+ console.error(
303
+ `\n Port ${opts.port} is already in use by something else.\n` +
304
+ ` Free it, or pick another: clawcrib start --port 18788\n`
305
+ );
135
306
  process.exit(1);
136
307
  }
137
- console.log(`\n ClawCrib v${pkg.version}`);
308
+ const child = spawnServer(true);
309
+ child.unref(); // survives this shell exiting
310
+ writeFileSync(PID_FILE, String(child.pid));
311
+ writeFileSync(
312
+ STATE_FILE,
313
+ JSON.stringify({ pid: child.pid, host: opts.host, port: opts.port, gateway: env.OPENCLAW_GATEWAY_URL ?? null })
314
+ );
315
+ if (!(await waitForServer(healthUrl))) {
316
+ console.error(`ClawCrib did not come up within 30s. Log: ${LOG_FILE}`);
317
+ try {
318
+ process.kill(child.pid);
319
+ } catch {
320
+ /* already gone */
321
+ }
322
+ rmSync(PID_FILE, { force: true });
323
+ process.exit(1);
324
+ }
325
+ console.log(`\n ClawCrib v${pkg.version} started in the background (pid ${child.pid})`);
138
326
  console.log(` Crib: ${url}`);
139
- console.log(` Mode: ${mode}\n`);
140
- if (opts.open) openBrowser(url);
141
- });
327
+ console.log(` Mode: ${modeLine()}`);
328
+ await printDetails();
329
+ console.log(`\n Log: ${LOG_FILE}`);
330
+ console.log(` Stop: clawcrib stop\n`);
331
+ }
142
332
 
143
- const shutdown = () => {
144
- child.kill("SIGTERM");
145
- process.exit(0);
146
- };
147
- process.on("SIGINT", shutdown);
148
- process.on("SIGTERM", shutdown);
149
- child.on("exit", (code) => process.exit(code ?? 0));
333
+ async function cmdStop() {
334
+ const pid = readPid();
335
+ if (!pid) {
336
+ console.log("\n ClawCrib is not running.\n");
337
+ rmSync(PID_FILE, { force: true });
338
+ return;
339
+ }
340
+ process.kill(pid, "SIGTERM");
341
+ for (let i = 0; i < 40; i++) {
342
+ await new Promise((r) => setTimeout(r, 250));
343
+ if (!readPid()) break;
344
+ }
345
+ if (readPid()) {
346
+ try {
347
+ process.kill(pid, "SIGKILL");
348
+ } catch {
349
+ /* raced with exit */
350
+ }
351
+ }
352
+ rmSync(PID_FILE, { force: true });
353
+ console.log(`\n ClawCrib stopped (pid ${pid}).\n`);
354
+ }
355
+
356
+ async function cmdStatus() {
357
+ const pid = readPid();
358
+ if (!pid) {
359
+ console.log("\n ClawCrib is not running.\n");
360
+ process.exitCode = 1;
361
+ return;
362
+ }
363
+ const responding = await waitForServer(healthUrl, 3000);
364
+ console.log(`\n ClawCrib is running (pid ${pid})`);
365
+ console.log(` Crib: ${url} — ${responding ? "responding" : "NOT responding"}`);
366
+ console.log(` Log: ${LOG_FILE}\n`);
367
+ if (!responding) process.exitCode = 1;
368
+ }
369
+
370
+ // Keeps it alive across reboots. Uses the resolved bin path, which is why a
371
+ // global install matters: an npx path points into a cache that gets evicted.
372
+ function serviceFiles() {
373
+ const binPath = process.argv[1];
374
+ // Deliberately the FOREGROUND run, not `start`: systemd and launchd want to
375
+ // supervise the process themselves. Pointing them at `start` — which forks
376
+ // and exits — makes launchd's KeepAlive treat every launch as a crash and
377
+ // respawn in a loop.
378
+ const args = [binPath, "--no-open"];
379
+ if (opts.port !== 18787) args.push("--port", String(opts.port));
380
+ if (opts.host !== "127.0.0.1") args.push("--host", opts.host);
381
+ if (opts.gateway) args.push("--gateway", opts.gateway);
382
+ if (opts.token) args.push("--token", opts.token);
383
+ return { binPath, args };
384
+ }
385
+
386
+ function cmdInstallService() {
387
+ const { binPath, args } = serviceFiles();
388
+ if (binPath.includes("_npx") || binPath.includes("npm/_cacache")) {
389
+ console.error(
390
+ "\n Refusing to install a service pointing at an npx cache path — it will be evicted.\n" +
391
+ " Install globally first: npm i -g clawcrib\n"
392
+ );
393
+ process.exit(1);
394
+ }
395
+ const exec = [process.execPath, ...args].map((a) => JSON.stringify(a)).join(" ");
396
+
397
+ if (process.platform === "linux") {
398
+ const unit = `[Unit]
399
+ Description=ClawCrib — live 3D office for OpenClaw agents
400
+ After=network-online.target
401
+
402
+ [Service]
403
+ Type=simple
404
+ ExecStart=${exec}
405
+ Restart=on-failure
406
+ RestartSec=5
407
+ Environment=CLAWCRIB_CONFIG_DIR=${CONFIG_DIR}
408
+
409
+ [Install]
410
+ WantedBy=default.target
411
+ `;
412
+ const dir = path.join(os.homedir(), ".config", "systemd", "user");
413
+ mkdirSync(dir, { recursive: true });
414
+ const file = path.join(dir, "clawcrib.service");
415
+ writeFileSync(file, unit);
416
+ console.log(`\n Wrote ${file}\n`);
417
+ console.log(" Enable it (starts now and on every boot):");
418
+ console.log(" systemctl --user daemon-reload");
419
+ console.log(" systemctl --user enable --now clawcrib");
420
+ console.log("\n So it keeps running after you log out of SSH:");
421
+ console.log(" sudo loginctl enable-linger $USER\n");
422
+ return;
423
+ }
424
+
425
+ if (process.platform === "darwin") {
426
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
427
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
428
+ <plist version="1.0">
429
+ <dict>
430
+ <key>Label</key><string>ai.clearmud.clawcrib</string>
431
+ <key>ProgramArguments</key>
432
+ <array>
433
+ ${[process.execPath, ...args].map((a) => ` <string>${a}</string>`).join("\n")}
434
+ </array>
435
+ <key>RunAtLoad</key><true/>
436
+ <key>KeepAlive</key><true/>
437
+ <key>StandardOutPath</key><string>${LOG_FILE}</string>
438
+ <key>StandardErrorPath</key><string>${LOG_FILE}</string>
439
+ </dict>
440
+ </plist>
441
+ `;
442
+ const dir = path.join(os.homedir(), "Library", "LaunchAgents");
443
+ mkdirSync(dir, { recursive: true });
444
+ const file = path.join(dir, "ai.clearmud.clawcrib.plist");
445
+ writeFileSync(file, plist);
446
+ console.log(`\n Wrote ${file}\n`);
447
+ console.log(" Enable it (starts now and on every login):");
448
+ console.log(` launchctl load -w ${file}\n`);
449
+ return;
450
+ }
451
+
452
+ console.error(`\n install-service supports linux (systemd) and macOS (launchd), not ${process.platform}.\n`);
453
+ process.exit(1);
454
+ }
455
+
456
+ function cmdUninstallService() {
457
+ const file =
458
+ process.platform === "linux"
459
+ ? path.join(os.homedir(), ".config", "systemd", "user", "clawcrib.service")
460
+ : path.join(os.homedir(), "Library", "LaunchAgents", "ai.clearmud.clawcrib.plist");
461
+ if (!existsSync(file)) {
462
+ console.log("\n No service file installed.\n");
463
+ return;
464
+ }
465
+ console.log(`\n Disable it first:`);
466
+ console.log(
467
+ process.platform === "linux"
468
+ ? " systemctl --user disable --now clawcrib"
469
+ : ` launchctl unload -w ${file}`
470
+ );
471
+ rmSync(file, { force: true });
472
+ console.log(`\n Removed ${file}\n`);
473
+ }
474
+
475
+ async function cmdRun() {
476
+ await resolveGateway();
477
+ const child = spawnServer(false);
478
+ child.stdout.on("data", () => {}); // keep Next's own listen log quiet; we print our own
479
+
480
+ waitForServer(healthUrl).then(async (up) => {
481
+ if (!up) {
482
+ console.error("ClawCrib server did not come up within 30s.");
483
+ child.kill();
484
+ process.exit(1);
485
+ }
486
+ console.log(`\n ClawCrib v${pkg.version}`);
487
+ console.log(` Crib: ${url}`);
488
+ console.log(` Mode: ${modeLine()}`);
489
+ await printDetails();
490
+ console.log(`\n Serving. Press Ctrl-C to stop.`);
491
+ console.log(` Prefer it always-on? clawcrib start (background)\n`);
492
+
493
+ // A headless box has no browser to open; spawning xdg-open just fails quietly.
494
+ const headless =
495
+ process.platform === "linux" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY;
496
+ if (opts.open && !headless) openBrowser(url);
497
+ });
498
+
499
+ const shutdown = () => {
500
+ child.kill("SIGTERM");
501
+ process.exit(0);
502
+ };
503
+ process.on("SIGINT", shutdown);
504
+ process.on("SIGTERM", shutdown);
505
+ child.on("exit", (code) => process.exit(code ?? 0));
506
+ }
507
+
508
+ switch (opts.cmd) {
509
+ case "start":
510
+ await cmdStart();
511
+ break;
512
+ case "stop":
513
+ await cmdStop();
514
+ break;
515
+ case "restart":
516
+ await cmdStop();
517
+ await cmdStart();
518
+ break;
519
+ case "status":
520
+ await cmdStatus();
521
+ break;
522
+ case "install-service":
523
+ cmdInstallService();
524
+ break;
525
+ case "uninstall-service":
526
+ cmdUninstallService();
527
+ break;
528
+ default:
529
+ await cmdRun();
530
+ }
@@ -1 +1 @@
1
- LDs2QlURjFsxe6doQgQaH
1
+ zYUlp2fcVlJKBhUQk1Pns
@@ -7,8 +7,8 @@
7
7
  "static/chunks/a6dad97d9634a72d.js"
8
8
  ],
9
9
  "lowPriorityFiles": [
10
- "static/LDs2QlURjFsxe6doQgQaH/_ssgManifest.js",
11
- "static/LDs2QlURjFsxe6doQgQaH/_buildManifest.js"
10
+ "static/zYUlp2fcVlJKBhUQk1Pns/_ssgManifest.js",
11
+ "static/zYUlp2fcVlJKBhUQk1Pns/_buildManifest.js"
12
12
  ],
13
13
  "rootMainFiles": [
14
14
  "static/chunks/251e16428085edaf.js",
@@ -1,2 +1,2 @@
1
- <!DOCTYPE html><!--LDs2QlURjFsxe6doQgQaH--><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/251e16428085edaf.js"/><script src="/_next/static/chunks/f188e57e58a39d0f.js" async=""></script><script src="/_next/static/chunks/045c83caa4d15373.js" async=""></script><script src="/_next/static/chunks/619d93506b5beb32.js" async=""></script><script src="/_next/static/chunks/82abf2d65f5428ae.js" async=""></script><script src="/_next/static/chunks/turbopack-aadb2fcebd6885bc.js" async=""></script><script src="/_next/static/chunks/ff1a16fafef87110.js" async=""></script><script src="/_next/static/chunks/0bd6498bda341889.js" async=""></script><meta name="next-size-adjust" content=""/><title>500: Internal Server Error.</title><script src="/_next/static/chunks/a6dad97d9634a72d.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}
2
- @media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">500</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">Internal Server Error.</h2></div></div></div><!--$--><!--/$--><script src="/_next/static/chunks/251e16428085edaf.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[39756,[\"/_next/static/chunks/ff1a16fafef87110.js\",\"/_next/static/chunks/0bd6498bda341889.js\"],\"default\"]\n3:I[37457,[\"/_next/static/chunks/ff1a16fafef87110.js\",\"/_next/static/chunks/0bd6498bda341889.js\"],\"default\"]\n4:I[97367,[\"/_next/static/chunks/ff1a16fafef87110.js\",\"/_next/static/chunks/0bd6498bda341889.js\"],\"OutletBoundary\"]\n5:\"$Sreact.suspense\"\n7:I[97367,[\"/_next/static/chunks/ff1a16fafef87110.js\",\"/_next/static/chunks/0bd6498bda341889.js\"],\"ViewportBoundary\"]\n9:I[97367,[\"/_next/static/chunks/ff1a16fafef87110.js\",\"/_next/static/chunks/0bd6498bda341889.js\"],\"MetadataBoundary\"]\nb:I[68027,[\"/_next/static/chunks/ff1a16fafef87110.js\",\"/_next/static/chunks/0bd6498bda341889.js\"],\"default\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"LDs2QlURjFsxe6doQgQaH\",\"c\":[\"\",\"_global-error\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"__PAGE__\",{}]}],[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"html\",null,{\"id\":\"__next_error__\",\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"title\",null,{\"children\":\"500: Internal Server Error.\"}]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"style\":{\"lineHeight\":\"48px\"},\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"paddingRight\":23,\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\"},\"children\":\"500\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"28px\"},\"children\":\"Internal Server Error.\"}]}]]}]}]}]]}],[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/ff1a16fafef87110.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0bd6498bda341889.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@6\"}]}]]}],{},null,false,false]},null,false,false],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L7\",null,{\"children\":\"$L8\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$L9\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.Metadata\",\"children\":\"$La\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$b\",\"$undefined\"],\"S\":true}\n"])</script><script>self.__next_f.push([1,"8:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"6:null\na:[]\n"])</script></body></html>
1
+ <!DOCTYPE html><!--zYUlp2fcVlJKBhUQk1Pns--><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/251e16428085edaf.js"/><script src="/_next/static/chunks/f188e57e58a39d0f.js" async=""></script><script src="/_next/static/chunks/045c83caa4d15373.js" async=""></script><script src="/_next/static/chunks/619d93506b5beb32.js" async=""></script><script src="/_next/static/chunks/82abf2d65f5428ae.js" async=""></script><script src="/_next/static/chunks/turbopack-aadb2fcebd6885bc.js" async=""></script><script src="/_next/static/chunks/ff1a16fafef87110.js" async=""></script><script src="/_next/static/chunks/0bd6498bda341889.js" async=""></script><meta name="next-size-adjust" content=""/><title>500: Internal Server Error.</title><script src="/_next/static/chunks/a6dad97d9634a72d.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}
2
+ @media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">500</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">Internal Server Error.</h2></div></div></div><!--$--><!--/$--><script src="/_next/static/chunks/251e16428085edaf.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[39756,[\"/_next/static/chunks/ff1a16fafef87110.js\",\"/_next/static/chunks/0bd6498bda341889.js\"],\"default\"]\n3:I[37457,[\"/_next/static/chunks/ff1a16fafef87110.js\",\"/_next/static/chunks/0bd6498bda341889.js\"],\"default\"]\n4:I[97367,[\"/_next/static/chunks/ff1a16fafef87110.js\",\"/_next/static/chunks/0bd6498bda341889.js\"],\"OutletBoundary\"]\n5:\"$Sreact.suspense\"\n7:I[97367,[\"/_next/static/chunks/ff1a16fafef87110.js\",\"/_next/static/chunks/0bd6498bda341889.js\"],\"ViewportBoundary\"]\n9:I[97367,[\"/_next/static/chunks/ff1a16fafef87110.js\",\"/_next/static/chunks/0bd6498bda341889.js\"],\"MetadataBoundary\"]\nb:I[68027,[\"/_next/static/chunks/ff1a16fafef87110.js\",\"/_next/static/chunks/0bd6498bda341889.js\"],\"default\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"zYUlp2fcVlJKBhUQk1Pns\",\"c\":[\"\",\"_global-error\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"__PAGE__\",{}]}],[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"html\",null,{\"id\":\"__next_error__\",\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"title\",null,{\"children\":\"500: Internal Server Error.\"}]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"style\":{\"lineHeight\":\"48px\"},\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"paddingRight\":23,\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\"},\"children\":\"500\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"28px\"},\"children\":\"Internal Server Error.\"}]}]]}]}]}]]}],[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/ff1a16fafef87110.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0bd6498bda341889.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@6\"}]}]]}],{},null,false,false]},null,false,false],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L7\",null,{\"children\":\"$L8\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$L9\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.Metadata\",\"children\":\"$La\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$b\",\"$undefined\"],\"S\":true}\n"])</script><script>self.__next_f.push([1,"8:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"6:null\na:[]\n"])</script></body></html>
@@ -6,7 +6,7 @@
6
6
  7:I[97367,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/0bd6498bda341889.js"],"ViewportBoundary"]
7
7
  9:I[97367,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/0bd6498bda341889.js"],"MetadataBoundary"]
8
8
  b:I[68027,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/0bd6498bda341889.js"],"default"]
9
- 0:{"P":null,"b":"LDs2QlURjFsxe6doQgQaH","c":["","_global-error"],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]}],[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","html",null,{"id":"__next_error__","children":[["$","head",null,{"children":["$","title",null,{"children":"500: Internal Server Error."}]}],["$","body",null,{"children":["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"style":{"lineHeight":"48px"},"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","paddingRight":23,"fontSize":24,"fontWeight":500,"verticalAlign":"top"},"children":"500"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"28px"},"children":"Internal Server Error."}]}]]}]}]}]]}],[["$","script","script-0",{"src":"/_next/static/chunks/ff1a16fafef87110.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0bd6498bda341889.js","async":true,"nonce":"$undefined"}]],["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$L7",null,{"children":"$L8"}],["$","div",null,{"hidden":true,"children":["$","$L9",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$La"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$b","$undefined"],"S":true}
9
+ 0:{"P":null,"b":"zYUlp2fcVlJKBhUQk1Pns","c":["","_global-error"],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]}],[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","html",null,{"id":"__next_error__","children":[["$","head",null,{"children":["$","title",null,{"children":"500: Internal Server Error."}]}],["$","body",null,{"children":["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"style":{"lineHeight":"48px"},"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","paddingRight":23,"fontSize":24,"fontWeight":500,"verticalAlign":"top"},"children":"500"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"28px"},"children":"Internal Server Error."}]}]]}]}]}]]}],[["$","script","script-0",{"src":"/_next/static/chunks/ff1a16fafef87110.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0bd6498bda341889.js","async":true,"nonce":"$undefined"}]],["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$L7",null,{"children":"$L8"}],["$","div",null,{"hidden":true,"children":["$","$L9",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$La"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$b","$undefined"],"S":true}
10
10
  8:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
11
11
  6:null
12
12
  a:[]
@@ -1,5 +1,5 @@
1
1
  1:"$Sreact.fragment"
2
2
  2:I[97367,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/0bd6498bda341889.js"],"OutletBoundary"]
3
3
  3:"$Sreact.suspense"
4
- 0:{"buildId":"LDs2QlURjFsxe6doQgQaH","rsc":["$","$1","c",{"children":[["$","html",null,{"id":"__next_error__","children":[["$","head",null,{"children":["$","title",null,{"children":"500: Internal Server Error."}]}],["$","body",null,{"children":["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"style":{"lineHeight":"48px"},"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","paddingRight":23,"fontSize":24,"fontWeight":500,"verticalAlign":"top"},"children":"500"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"28px"},"children":"Internal Server Error."}]}]]}]}]}]]}],[["$","script","script-0",{"src":"/_next/static/chunks/ff1a16fafef87110.js","async":true}],["$","script","script-1",{"src":"/_next/static/chunks/0bd6498bda341889.js","async":true}]],["$","$L2",null,{"children":["$","$3",null,{"name":"Next.MetadataOutlet","children":"$@4"}]}]]}],"loading":null,"isPartial":false}
4
+ 0:{"buildId":"zYUlp2fcVlJKBhUQk1Pns","rsc":["$","$1","c",{"children":[["$","html",null,{"id":"__next_error__","children":[["$","head",null,{"children":["$","title",null,{"children":"500: Internal Server Error."}]}],["$","body",null,{"children":["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"style":{"lineHeight":"48px"},"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","paddingRight":23,"fontSize":24,"fontWeight":500,"verticalAlign":"top"},"children":"500"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"28px"},"children":"Internal Server Error."}]}]]}]}]}]]}],[["$","script","script-0",{"src":"/_next/static/chunks/ff1a16fafef87110.js","async":true}],["$","script","script-1",{"src":"/_next/static/chunks/0bd6498bda341889.js","async":true}]],["$","$L2",null,{"children":["$","$3",null,{"name":"Next.MetadataOutlet","children":"$@4"}]}]]}],"loading":null,"isPartial":false}
5
5
  4:null
@@ -6,7 +6,7 @@
6
6
  7:I[97367,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/0bd6498bda341889.js"],"ViewportBoundary"]
7
7
  9:I[97367,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/0bd6498bda341889.js"],"MetadataBoundary"]
8
8
  b:I[68027,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/0bd6498bda341889.js"],"default"]
9
- 0:{"P":null,"b":"LDs2QlURjFsxe6doQgQaH","c":["","_global-error"],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]}],[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","html",null,{"id":"__next_error__","children":[["$","head",null,{"children":["$","title",null,{"children":"500: Internal Server Error."}]}],["$","body",null,{"children":["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"style":{"lineHeight":"48px"},"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","paddingRight":23,"fontSize":24,"fontWeight":500,"verticalAlign":"top"},"children":"500"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"28px"},"children":"Internal Server Error."}]}]]}]}]}]]}],[["$","script","script-0",{"src":"/_next/static/chunks/ff1a16fafef87110.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0bd6498bda341889.js","async":true,"nonce":"$undefined"}]],["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$L7",null,{"children":"$L8"}],["$","div",null,{"hidden":true,"children":["$","$L9",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$La"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$b","$undefined"],"S":true}
9
+ 0:{"P":null,"b":"zYUlp2fcVlJKBhUQk1Pns","c":["","_global-error"],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]}],[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","html",null,{"id":"__next_error__","children":[["$","head",null,{"children":["$","title",null,{"children":"500: Internal Server Error."}]}],["$","body",null,{"children":["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"style":{"lineHeight":"48px"},"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","paddingRight":23,"fontSize":24,"fontWeight":500,"verticalAlign":"top"},"children":"500"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"28px"},"children":"Internal Server Error."}]}]]}]}]}]]}],[["$","script","script-0",{"src":"/_next/static/chunks/ff1a16fafef87110.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0bd6498bda341889.js","async":true,"nonce":"$undefined"}]],["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$L7",null,{"children":"$L8"}],["$","div",null,{"hidden":true,"children":["$","$L9",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$La"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$b","$undefined"],"S":true}
10
10
  8:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
11
11
  6:null
12
12
  a:[]
@@ -2,4 +2,4 @@
2
2
  2:I[97367,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/0bd6498bda341889.js"],"ViewportBoundary"]
3
3
  3:I[97367,["/_next/static/chunks/ff1a16fafef87110.js","/_next/static/chunks/0bd6498bda341889.js"],"MetadataBoundary"]
4
4
  4:"$Sreact.suspense"
5
- 0:{"buildId":"LDs2QlURjFsxe6doQgQaH","rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"loading":null,"isPartial":false}
5
+ 0:{"buildId":"zYUlp2fcVlJKBhUQk1Pns","rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"loading":null,"isPartial":false}