piclaw 0.0.4 → 0.0.6

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 (185) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/dist-D51xeTP2.js +12 -0
  3. package/.output/public/assets/index-B3x2_en6.css +1 -0
  4. package/.output/public/assets/index-SfjxJZSD.js +38 -0
  5. package/.output/public/assets/md4x-CNLJ57xO.wasm +0 -0
  6. package/.output/public/assets/md4x-CyfQToGJ.js +1 -0
  7. package/.output/public/assets/wasm-Cm7RZrwg.js +1 -0
  8. package/.output/public/icon.svg +25 -0
  9. package/.output/public/index.html +3 -2
  10. package/.output/public/manifest.json +1 -1
  11. package/.output/server/_chunks/bun.mjs +49 -0
  12. package/.output/server/_chunks/commands.mjs +280 -0
  13. package/.output/server/_chunks/config.mjs +2 -4
  14. package/.output/server/_chunks/db.mjs +11 -5
  15. package/.output/server/_chunks/logger.mjs +1 -1
  16. package/.output/server/_chunks/node.mjs +31 -0
  17. package/.output/server/_chunks/ntfy.mjs +45 -0
  18. package/.output/server/_chunks/pi.mjs +33 -1
  19. package/.output/server/_chunks/renderer-template.mjs +1 -1
  20. package/.output/server/_chunks/{bootstrap.mjs → server.mjs} +203 -205
  21. package/.output/server/_chunks/session.mjs +249 -40
  22. package/.output/server/_chunks/settings.mjs +1 -1
  23. package/.output/server/_chunks/terminal.mjs +35 -20
  24. package/.output/server/_chunks/virtual.mjs +129 -0
  25. package/.output/server/_jid_.delete.mjs +2 -2
  26. package/.output/server/_jid_.patch.mjs +30 -3
  27. package/.output/server/_jid_2.delete.mjs +2 -2
  28. package/.output/server/_libs/@aws-sdk/client-bedrock-runtime+[...].mjs +13148 -13149
  29. package/.output/server/_libs/@aws-sdk/credential-provider-http+[...].mjs +1 -1
  30. package/.output/server/_libs/@aws-sdk/credential-provider-ini+[...].mjs +2 -2
  31. package/.output/server/_libs/@aws-sdk/credential-provider-process+[...].mjs +1 -1
  32. package/.output/server/_libs/@aws-sdk/credential-provider-sso+[...].mjs +1 -1
  33. package/.output/server/_libs/@aws-sdk/credential-provider-web-identity+[...].mjs +1 -1
  34. package/.output/server/_libs/@google/genai.mjs +2 -2
  35. package/.output/server/_libs/@mariozechner/pi-agent-core+[...].mjs +40 -74
  36. package/.output/server/_libs/@mariozechner/pi-coding-agent+[...].mjs +1315 -1210
  37. package/.output/server/_libs/@smithy/credential-provider-imds+[...].mjs +3 -3
  38. package/.output/server/_libs/_.mjs +3 -2
  39. package/.output/server/_libs/_100.mjs +3 -0
  40. package/.output/server/_libs/_101.mjs +2 -0
  41. package/.output/server/_libs/_102.mjs +3 -0
  42. package/.output/server/_libs/_103.mjs +2 -0
  43. package/.output/server/_libs/_16.mjs +2 -5
  44. package/.output/server/_libs/_17.mjs +2 -3
  45. package/.output/server/_libs/_18.mjs +2 -3
  46. package/.output/server/_libs/_19.mjs +2 -4
  47. package/.output/server/_libs/_2.mjs +2 -3
  48. package/.output/server/_libs/_20.mjs +2 -2
  49. package/.output/server/_libs/_21.mjs +2 -0
  50. package/.output/server/_libs/_22.mjs +2 -0
  51. package/.output/server/_libs/_23.mjs +2 -0
  52. package/.output/server/_libs/_24.mjs +2 -0
  53. package/.output/server/_libs/_25.mjs +2 -0
  54. package/.output/server/_libs/_26.mjs +2 -0
  55. package/.output/server/_libs/_27.mjs +2 -0
  56. package/.output/server/_libs/_28.mjs +2 -0
  57. package/.output/server/_libs/_29.mjs +2 -0
  58. package/.output/server/_libs/_30.mjs +2 -0
  59. package/.output/server/_libs/_31.mjs +2 -0
  60. package/.output/server/_libs/_32.mjs +2 -0
  61. package/.output/server/_libs/_33.mjs +2 -0
  62. package/.output/server/_libs/_34.mjs +2 -0
  63. package/.output/server/_libs/_35.mjs +2 -0
  64. package/.output/server/_libs/_36.mjs +2 -0
  65. package/.output/server/_libs/_37.mjs +2 -0
  66. package/.output/server/_libs/_38.mjs +2 -0
  67. package/.output/server/_libs/_39.mjs +2 -0
  68. package/.output/server/_libs/_40.mjs +2 -0
  69. package/.output/server/_libs/_41.mjs +2 -0
  70. package/.output/server/_libs/_42.mjs +2 -0
  71. package/.output/server/_libs/_43.mjs +2 -0
  72. package/.output/server/_libs/_44.mjs +2 -0
  73. package/.output/server/_libs/_45.mjs +2 -0
  74. package/.output/server/_libs/_46.mjs +2 -0
  75. package/.output/server/_libs/_47.mjs +2 -0
  76. package/.output/server/_libs/_48.mjs +2 -0
  77. package/.output/server/_libs/_49.mjs +2 -0
  78. package/.output/server/_libs/_50.mjs +2 -0
  79. package/.output/server/_libs/_51.mjs +2 -0
  80. package/.output/server/_libs/_52.mjs +2 -0
  81. package/.output/server/_libs/_53.mjs +2 -0
  82. package/.output/server/_libs/_54.mjs +2 -0
  83. package/.output/server/_libs/_55.mjs +2 -0
  84. package/.output/server/_libs/_56.mjs +2 -0
  85. package/.output/server/_libs/_57.mjs +2 -0
  86. package/.output/server/_libs/_58.mjs +2 -0
  87. package/.output/server/_libs/_59.mjs +2 -0
  88. package/.output/server/_libs/_60.mjs +2 -0
  89. package/.output/server/_libs/_61.mjs +2 -0
  90. package/.output/server/_libs/_62.mjs +2 -0
  91. package/.output/server/_libs/_63.mjs +2 -0
  92. package/.output/server/_libs/_64.mjs +2 -0
  93. package/.output/server/_libs/_65.mjs +2 -0
  94. package/.output/server/_libs/_66.mjs +2 -0
  95. package/.output/server/_libs/_67.mjs +2 -0
  96. package/.output/server/_libs/_68.mjs +2 -0
  97. package/.output/server/_libs/_69.mjs +2 -0
  98. package/.output/server/_libs/_70.mjs +2 -0
  99. package/.output/server/_libs/_71.mjs +2 -0
  100. package/.output/server/_libs/_72.mjs +2 -0
  101. package/.output/server/_libs/_73.mjs +2 -0
  102. package/.output/server/_libs/_74.mjs +2 -0
  103. package/.output/server/_libs/_75.mjs +2 -0
  104. package/.output/server/_libs/_76.mjs +2 -0
  105. package/.output/server/_libs/_77.mjs +2 -0
  106. package/.output/server/_libs/_78.mjs +2 -0
  107. package/.output/server/_libs/_79.mjs +2 -0
  108. package/.output/server/_libs/_80.mjs +2 -0
  109. package/.output/server/_libs/_81.mjs +2 -0
  110. package/.output/server/_libs/_82.mjs +2 -0
  111. package/.output/server/_libs/_83.mjs +2 -0
  112. package/.output/server/_libs/_84.mjs +2 -0
  113. package/.output/server/_libs/_85.mjs +2 -0
  114. package/.output/server/_libs/_86.mjs +2 -0
  115. package/.output/server/_libs/_87.mjs +2 -0
  116. package/.output/server/_libs/_88.mjs +2 -0
  117. package/.output/server/_libs/_89.mjs +2 -0
  118. package/.output/server/_libs/_90.mjs +2 -0
  119. package/.output/server/_libs/_91.mjs +2 -0
  120. package/.output/server/_libs/_92.mjs +2 -0
  121. package/.output/server/_libs/_93.mjs +2 -0
  122. package/.output/server/_libs/_94.mjs +2 -0
  123. package/.output/server/_libs/_95.mjs +2 -0
  124. package/.output/server/_libs/_96.mjs +2 -0
  125. package/.output/server/_libs/_97.mjs +2 -0
  126. package/.output/server/_libs/_98.mjs +2 -0
  127. package/.output/server/_libs/_99.mjs +5 -0
  128. package/.output/server/_libs/amdefine.mjs +188 -0
  129. package/.output/server/_libs/aws-sdk__nested-clients.mjs +1 -1
  130. package/.output/server/_libs/compressjs.mjs +50 -0
  131. package/.output/server/_libs/diff.mjs +137 -0
  132. package/.output/server/_libs/get-uri.mjs +1 -1
  133. package/.output/server/_libs/http-proxy-agent.mjs +1 -1
  134. package/.output/server/_libs/https-proxy-agent.mjs +1 -1
  135. package/.output/server/_libs/just-bash+[...].mjs +80359 -0
  136. package/.output/server/_libs/md4x.mjs +73 -0
  137. package/.output/server/_libs/mixmark-io__domino.mjs +14801 -0
  138. package/.output/server/_libs/node-fetch.mjs +1 -1
  139. package/.output/server/_libs/node-liblzma.mjs +1107 -0
  140. package/.output/server/_libs/pac-proxy-agent+[...].mjs +1 -1
  141. package/.output/server/_libs/proxy-agent+proxy-from-env.mjs +1 -1
  142. package/.output/server/_libs/smithy__core.mjs +1 -1
  143. package/.output/server/_routes/api/files/delete.mjs +2 -2
  144. package/.output/server/_routes/api/files/groups.mjs +14 -0
  145. package/.output/server/_routes/api/files/raw.mjs +1 -1
  146. package/.output/server/_routes/api/files/read.mjs +1 -1
  147. package/.output/server/_routes/api/files/watch.mjs +1 -1
  148. package/.output/server/_routes/api/files/write.mjs +3 -2
  149. package/.output/server/_routes/api/files.mjs +1 -1
  150. package/.output/server/_routes/api/groups.mjs +2 -2
  151. package/.output/server/_routes/api/groups2.mjs +3 -2
  152. package/.output/server/_routes/api/health.mjs +2 -2
  153. package/.output/server/_routes/api/logs.mjs +4 -2
  154. package/.output/server/_routes/api/ntfy/setup.mjs +54 -0
  155. package/.output/server/_routes/api/ntfy/status.mjs +12 -0
  156. package/.output/server/_routes/api/pi/apikey.mjs +1 -1
  157. package/.output/server/_routes/api/pi/apikey_providers.mjs +1 -1
  158. package/.output/server/_routes/api/pi/commands.mjs +6 -0
  159. package/.output/server/_routes/api/pi/login/events.mjs +1 -1
  160. package/.output/server/_routes/api/pi/login/respond.mjs +1 -1
  161. package/.output/server/_routes/api/pi/login.mjs +1 -1
  162. package/.output/server/_routes/api/pi/logout.mjs +1 -1
  163. package/.output/server/_routes/api/pi/models.mjs +1 -1
  164. package/.output/server/_routes/api/pi/status.mjs +1 -1
  165. package/.output/server/_routes/api/send.mjs +22 -3
  166. package/.output/server/_routes/api/status.mjs +9 -7
  167. package/.output/server/_routes/api/tasks2.mjs +2 -2
  168. package/.output/server/_routes/api/telegram/setup.mjs +2 -2
  169. package/.output/server/_routes/api/telegram/status.mjs +2 -2
  170. package/.output/server/_routes/api/terminal2.mjs +3 -2
  171. package/.output/server/_utils.mjs +12 -4
  172. package/.output/server/build/md4x.wasm +0 -0
  173. package/.output/server/index.mjs +223 -149
  174. package/.output/server/node_modules/@mongodb-js/zstd/lib/index.js +55 -0
  175. package/.output/server/node_modules/@mongodb-js/zstd/package.json +51 -0
  176. package/.output/server/package.json +1 -0
  177. package/README.md +118 -24
  178. package/bin/piclaw.mjs +39 -1
  179. package/package.json +16 -13
  180. package/.output/public/assets/client-TIs-Ghqj.js +0 -9
  181. package/.output/public/assets/dist-BVjfG3ok.js +0 -12
  182. package/.output/public/assets/dist-DJh8l6yS.js +0 -1
  183. package/.output/public/assets/index-BNNEMkNV.js +0 -39
  184. package/.output/public/assets/index-CdWBxO5V.css +0 -1
  185. package/.output/public/assets/react-DFP7nCmh.js +0 -1
@@ -0,0 +1 @@
1
+ var e=`/assets/md4x-CNLJ57xO.wasm`;export{e as default};
@@ -0,0 +1 @@
1
+ import{t as e}from"./index-SfjxJZSD.js";var t;function n(){if(!t)throw Error("md4x: WASM not initialized. Call `await init()` first.");return t.exports}var r={fd_close:()=>0,fd_seek:()=>0,fd_write:()=>0,proc_exit:()=>{}};function i(e,t,n,...r){let{memory:i,md4x_alloc:a,md4x_free:o,md4x_result_ptr:s,md4x_result_size:c}=e,l=new TextEncoder().encode(n),u=a(l.length);new Uint8Array(i.buffer).set(l,u);let d=t(u,l.length,...r);if(o(u),d!==0)throw Error(`md4x: render failed`);let f=s(),p=c(),m=new TextDecoder().decode(new Uint8Array(i.buffer,f,p));return o(f),m}async function a(n){if(t)return;let i=n?.wasm,a;if(i instanceof ArrayBuffer||i instanceof Uint8Array)a=i;else if(i instanceof WebAssembly.Module){let{instance:e}=await WebAssembly.instantiate(i,{wasi_snapshot_preview1:r});t=e;return}else if(i instanceof Response||typeof i==`object`&&typeof i.then==`function`){let e=await i;a=e instanceof Response?await e.arrayBuffer():e}else{let t=globalThis.process?.getBuiltinModule?.(`fs/promises`);if(t){let e=new URL(`/assets/md4x-CNLJ57xO.wasm`,``+import.meta.url);a=await t.readFile(e)}else a=await fetch(await e(()=>import(`./md4x-CyfQToGJ.js`).then(e=>e.default),[])).then(e=>e.arrayBuffer())}let{instance:o}=await WebAssembly.instantiate(a,{wasi_snapshot_preview1:r});t=o}function o(e,t){let r=t?.full?8:0;return i(n(),n().md4x_to_html,e,r)}export{a as init,o as renderToHtml};
@@ -0,0 +1,25 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-label="PiClaw icon">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
4
+ <stop offset="0%" stop-color="#111820" />
5
+ <stop offset="100%" stop-color="#0a0e14" />
6
+ </linearGradient>
7
+ <linearGradient id="accent" x1="0" y1="0" x2="1" y2="1">
8
+ <stop offset="0%" stop-color="#39bae6" />
9
+ <stop offset="100%" stop-color="#0077cc" />
10
+ </linearGradient>
11
+ </defs>
12
+
13
+ <rect x="8" y="8" width="112" height="112" rx="24" fill="url(#bg)" />
14
+ <rect x="8" y="8" width="112" height="112" rx="24" fill="none" stroke="#1e2630" stroke-width="2" />
15
+
16
+ <path
17
+ d="M84 34c12 0 22 10 22 22 0 8-5 16-12 20l-12 6-10-8 8-11 10-4c3-1 4-3 4-5 0-4-3-7-7-7-3 0-6 2-7 5l-4 8-12-8 5-9c4-9 13-15 23-15z"
18
+ fill="url(#accent)"
19
+ />
20
+
21
+ <circle cx="44" cy="74" r="20" fill="#112a3a" stroke="#39bae6" stroke-width="2" />
22
+ <circle cx="44" cy="74" r="8" fill="#39bae6" />
23
+
24
+ <path d="M20 96h88" stroke="#1e2630" stroke-width="4" stroke-linecap="round" />
25
+ </svg>
@@ -11,9 +11,10 @@
11
11
  <meta name="apple-mobile-web-app-capable" content="yes" />
12
12
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
13
13
  <meta name="mobile-web-app-capable" content="yes" />
14
+ <link rel="icon" type="image/svg+xml" href="/icon.svg" />
14
15
  <link rel="manifest" href="/manifest.json" />
15
- <script type="module" crossorigin src="/assets/index-BNNEMkNV.js"></script>
16
- <link rel="stylesheet" crossorigin href="/assets/index-CdWBxO5V.css">
16
+ <script type="module" crossorigin src="/assets/index-SfjxJZSD.js"></script>
17
+ <link rel="stylesheet" crossorigin href="/assets/index-B3x2_en6.css">
17
18
  </head>
18
19
  <body style="background-color: #0a0e14">
19
20
  <div id="app"></div>
@@ -9,7 +9,7 @@
9
9
  "theme_color": "#0a0e14",
10
10
  "icons": [
11
11
  {
12
- "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='80' font-size='80'>🦀</text></svg>",
12
+ "src": "/icon.svg",
13
13
  "sizes": "any",
14
14
  "type": "image/svg+xml"
15
15
  }
@@ -0,0 +1,49 @@
1
+ function spawnBunPty(opts) {
2
+ const dataListeners = /* @__PURE__ */ new Set();
3
+ const exitListeners = /* @__PURE__ */ new Set();
4
+ const decoder = new TextDecoder();
5
+ let exited = false;
6
+ const proc = globalThis.Bun.spawn([opts.shell], {
7
+ cwd: opts.cwd,
8
+ env: opts.env,
9
+ terminal: {
10
+ cols: opts.cols,
11
+ rows: opts.rows,
12
+ data(_term, raw) {
13
+ const str = decoder.decode(raw, { stream: true });
14
+ for (const cb of dataListeners) cb(str);
15
+ },
16
+ exit() {
17
+ if (exited) return;
18
+ exited = true;
19
+ for (const cb of exitListeners) cb();
20
+ dataListeners.clear();
21
+ exitListeners.clear();
22
+ }
23
+ }
24
+ });
25
+ return {
26
+ write(data) {
27
+ proc.terminal.write(data);
28
+ },
29
+ resize(cols, rows) {
30
+ proc.terminal.resize(cols, rows);
31
+ },
32
+ kill() {
33
+ if (exited) return;
34
+ try {
35
+ proc.kill();
36
+ proc.terminal.close();
37
+ } catch {}
38
+ },
39
+ onData(cb) {
40
+ dataListeners.add(cb);
41
+ return () => dataListeners.delete(cb);
42
+ },
43
+ onExit(cb) {
44
+ exitListeners.add(cb);
45
+ return () => exitListeners.delete(cb);
46
+ }
47
+ };
48
+ }
49
+ export { spawnBunPty };
@@ -0,0 +1,280 @@
1
+ import { r as GROUPS_DIR } from "./config.mjs";
2
+ import { t as streamBus } from "./stream.mjs";
3
+ import { i as resolveGroupModel, o as sessions, r as modelRegistry } from "./session.mjs";
4
+ import { t as clearMessages, v as removeSession } from "./db.mjs";
5
+ import path from "node:path";
6
+ import { exec } from "node:child_process";
7
+ var commands = {
8
+ help: handleHelp,
9
+ info: handleInfo,
10
+ new: handleNew,
11
+ stop: handleStop,
12
+ model: handleModel,
13
+ thinking: handleThinking,
14
+ compact: handleCompact
15
+ };
16
+ /** Descriptions for registering commands with external channels (e.g. Telegram). */
17
+ const commandDescriptions = [
18
+ {
19
+ command: "help",
20
+ description: "Show available commands"
21
+ },
22
+ {
23
+ command: "info",
24
+ description: "Show session info"
25
+ },
26
+ {
27
+ command: "new",
28
+ description: "Reset session and start fresh"
29
+ },
30
+ {
31
+ command: "stop",
32
+ description: "Stop the current agent session"
33
+ },
34
+ {
35
+ command: "model",
36
+ description: "List or set model"
37
+ },
38
+ {
39
+ command: "thinking",
40
+ description: "Show or set thinking level"
41
+ },
42
+ {
43
+ command: "compact",
44
+ description: "Compact current session context"
45
+ }
46
+ ];
47
+ /** Commands that bypass the queue and execute immediately (even during streaming). */
48
+ var priorityCommands = {
49
+ stop: handleStop,
50
+ new: handleNew,
51
+ info: handleInfo
52
+ };
53
+ /**
54
+ * Try to handle a priority command that must bypass the queue.
55
+ * Called from the message loop and send API before enqueueing.
56
+ * Returns true if the message was a priority command (consumed).
57
+ */
58
+ function tryPriorityCommand(ctx, text) {
59
+ if (!text.startsWith("/")) return false;
60
+ const spaceIdx = text.indexOf(" ");
61
+ const name = (spaceIdx > 0 ? text.slice(1, spaceIdx) : text.slice(1)).toLowerCase();
62
+ const handler = priorityCommands[name];
63
+ if (!handler) return false;
64
+ Promise.resolve(handler(ctx, "")).catch((err) => {
65
+ ctx.server.sendBotMessage(ctx.chatJid, `Command /${name} failed: ${String(err)}`);
66
+ });
67
+ return true;
68
+ }
69
+ /**
70
+ * Try to handle a message as a `/command`. Returns true if handled.
71
+ */
72
+ function tryCommand(ctx, text) {
73
+ if (!text.startsWith("/")) return false;
74
+ const spaceIdx = text.indexOf(" ");
75
+ const name = (spaceIdx > 0 ? text.slice(1, spaceIdx) : text.slice(1)).toLowerCase();
76
+ const arg = spaceIdx > 0 ? text.slice(spaceIdx + 1).trim() : "";
77
+ const handler = commands[name];
78
+ if (handler) {
79
+ Promise.resolve(handler(ctx, arg)).catch((err) => {
80
+ ctx.server.sendBotMessage(ctx.chatJid, `Command /${name} failed: ${String(err)}`);
81
+ });
82
+ return true;
83
+ }
84
+ ctx.server.sendBotMessage(ctx.chatJid, `Unknown command \`/${name}\`. Use /help for available commands.`);
85
+ return true;
86
+ }
87
+ /**
88
+ * Try to handle a message as a `! <bash>` command. Returns true if handled.
89
+ * Runs the command directly in the group's working directory, bypassing the agent.
90
+ */
91
+ function tryBashCommand(ctx, text) {
92
+ if (!text.startsWith("!")) return false;
93
+ const cmd = text.slice(1).trim();
94
+ if (!cmd) return false;
95
+ const cwd = path.join(GROUPS_DIR, ctx.group.folder);
96
+ ctx.server.sendBotMessage(ctx.chatJid, `\`$ ${cmd}\``);
97
+ exec(cmd, {
98
+ cwd,
99
+ timeout: 5e3
100
+ }, (err, stdout, stderr) => {
101
+ const parts = [];
102
+ if (stdout) parts.push(stdout.trimEnd());
103
+ if (stderr) parts.push(stderr.trimEnd());
104
+ if (err && !stderr) parts.push(String(err.message || err));
105
+ const timedOut = err && "killed" in err && err.killed;
106
+ const output = parts.join("\n").replace(stripAnsiRe, "") || "(no output)";
107
+ const suffix = timedOut ? "\n(timed out)" : "";
108
+ ctx.server.sendBotMessage(ctx.chatJid, `\`\`\`\n${output}${suffix}\n\`\`\``);
109
+ });
110
+ return true;
111
+ }
112
+ function handleHelp(ctx) {
113
+ const lines = ["Available commands:", ...commandDescriptions.map((c) => ` /${c.command} — ${c.description}`)];
114
+ ctx.server.sendBotMessage(ctx.chatJid, lines.join("\n"));
115
+ }
116
+ function handleInfo(ctx) {
117
+ const resolved = resolveGroupModel(ctx.group);
118
+ const modelLabel = resolved ? `${resolved.provider}/${resolved.id}` : "none";
119
+ const modelSource = ctx.group.model ? `group (${ctx.group.model})` : "default";
120
+ const managed = sessions.get(ctx.chatJid);
121
+ const lines = [
122
+ `Group: ${ctx.group.name} (${ctx.group.folder})`,
123
+ `Model: ${modelLabel} [${modelSource}]`,
124
+ `Trigger: ${ctx.group.requiresTrigger ? "required" : "not required"}`
125
+ ];
126
+ if (managed) {
127
+ const s = managed.session;
128
+ const actualModel = s.model;
129
+ const actualLabel = actualModel ? `${actualModel.provider}/${actualModel.id}` : "none";
130
+ const ctx$ = s.getContextUsage();
131
+ lines.push("", `Session: ${s.sessionId}`, actualLabel !== modelLabel ? `Session model: ${actualLabel} (MISMATCH with resolved)` : `Session model: ${actualLabel}`, `Messages: ${s.messages.length}`, `Streaming: ${s.isStreaming ? "yes" : "no"}`, `Idle: ${formatDuration(Date.now() - managed.lastActivity)}`);
132
+ if (ctx$?.tokens != null && ctx$.percent != null) {
133
+ const pct = Math.round(ctx$.percent);
134
+ lines.push(`Context: ${ctx$.tokens.toLocaleString()} / ${ctx$.contextWindow.toLocaleString()} tokens (${pct}%)`);
135
+ }
136
+ } else lines.push("", "No active session");
137
+ ctx.server.sendBotMessage(ctx.chatJid, lines.join("\n"));
138
+ }
139
+ function formatDuration(ms) {
140
+ const s = Math.floor(ms / 1e3);
141
+ if (s < 60) return `${s}s`;
142
+ const m = Math.floor(s / 60);
143
+ if (m < 60) return `${m}m ${s % 60}s`;
144
+ return `${Math.floor(m / 60)}h ${m % 60}m`;
145
+ }
146
+ function handleNew(ctx) {
147
+ ctx.server.pi.kill(ctx.chatJid);
148
+ removeSession(ctx.group.folder);
149
+ clearMessages(ctx.chatJid);
150
+ ctx.server.sendBotMessage(ctx.chatJid, "Session reset. Next message starts a fresh conversation.");
151
+ }
152
+ function handleStop(ctx) {
153
+ if (!sessions.get(ctx.chatJid)) {
154
+ ctx.server.sendBotMessage(ctx.chatJid, "No active session to stop.");
155
+ return;
156
+ }
157
+ streamBus.emit({
158
+ type: "agent_end",
159
+ chatJid: ctx.chatJid
160
+ });
161
+ ctx.server.pi.kill(ctx.chatJid);
162
+ ctx.server.sendBotMessage(ctx.chatJid, "Session stopped.");
163
+ }
164
+ function handleModel(ctx, arg) {
165
+ if (!arg) return listModels(ctx);
166
+ if (arg === "default" || arg === "reset") return resetModel(ctx);
167
+ return setModel(ctx, arg);
168
+ }
169
+ function listModels(ctx) {
170
+ const available = modelRegistry.getAvailable();
171
+ const resolved = resolveGroupModel(ctx.group);
172
+ const lines = [
173
+ `Current: ${ctx.group.model || "default (global)"} → ${resolved ? `${resolved.provider}/${resolved.id}` : "none"}`,
174
+ "",
175
+ "Available models:"
176
+ ];
177
+ const byProvider = /* @__PURE__ */ new Map();
178
+ for (const m of available) {
179
+ let list = byProvider.get(m.provider);
180
+ if (!list) {
181
+ list = [];
182
+ byProvider.set(m.provider, list);
183
+ }
184
+ const marker = resolved && m.provider === resolved.provider && m.id === resolved.id ? " ←" : "";
185
+ list.push(` ${m.provider}/${m.id}${marker}`);
186
+ }
187
+ for (const [provider, models] of byProvider) {
188
+ lines.push(`[${provider}]`);
189
+ lines.push(...models);
190
+ }
191
+ lines.push("", "Usage: /model <query> | /model default");
192
+ ctx.server.sendBotMessage(ctx.chatJid, lines.join("\n"));
193
+ }
194
+ function resetModel(ctx) {
195
+ const had = ctx.group.model;
196
+ updateGroupModel(ctx, void 0);
197
+ ctx.server.sendBotMessage(ctx.chatJid, had ? `Model reset to default (was: ${had}).` : "Already using default model.");
198
+ }
199
+ function setModel(ctx, query) {
200
+ const match = findModel(query);
201
+ if (!match) {
202
+ ctx.server.sendBotMessage(ctx.chatJid, `No model matching "${query}". Use /model to list available models.`);
203
+ return;
204
+ }
205
+ const modelStr = `${match.provider}/${match.id}`;
206
+ updateGroupModel(ctx, modelStr);
207
+ ctx.server.sendBotMessage(ctx.chatJid, `Model set to: ${modelStr}`);
208
+ }
209
+ function updateGroupModel(ctx, model) {
210
+ const old = ctx.group.model;
211
+ ctx.group.model = model;
212
+ ctx.server.setRegisteredGroup(ctx.chatJid, ctx.group);
213
+ if (old !== model) ctx.server.pi.kill(ctx.chatJid);
214
+ }
215
+ var VALID_THINKING_LEVELS = [
216
+ "off",
217
+ "minimal",
218
+ "low",
219
+ "medium",
220
+ "high",
221
+ "xhigh"
222
+ ];
223
+ function handleThinking(ctx, arg) {
224
+ if (!arg) {
225
+ const current = ctx.group.thinkingLevel || "off";
226
+ ctx.server.sendBotMessage(ctx.chatJid, `Thinking level: ${current}\n\nUsage: /thinking <level>\nLevels: ${VALID_THINKING_LEVELS.join(", ")}`);
227
+ return;
228
+ }
229
+ const level = arg.toLowerCase();
230
+ if (!VALID_THINKING_LEVELS.includes(level)) {
231
+ ctx.server.sendBotMessage(ctx.chatJid, `Invalid thinking level "${arg}". Must be one of: ${VALID_THINKING_LEVELS.join(", ")}`);
232
+ return;
233
+ }
234
+ const old = ctx.group.thinkingLevel;
235
+ ctx.group.thinkingLevel = level === "off" ? void 0 : level;
236
+ ctx.server.setRegisteredGroup(ctx.chatJid, ctx.group);
237
+ if (old !== ctx.group.thinkingLevel) ctx.server.pi.kill(ctx.chatJid);
238
+ ctx.server.sendBotMessage(ctx.chatJid, `Thinking level set to: ${level}`);
239
+ }
240
+ async function handleCompact(ctx, arg) {
241
+ const managed = sessions.get(ctx.chatJid);
242
+ if (!managed) {
243
+ ctx.server.sendBotMessage(ctx.chatJid, "No active session to compact. Send a message first, then run /compact.");
244
+ return;
245
+ }
246
+ const session = managed.session;
247
+ if (session.isCompacting) {
248
+ ctx.server.sendBotMessage(ctx.chatJid, "Compaction is already in progress.");
249
+ return;
250
+ }
251
+ const before = session.getContextUsage();
252
+ ctx.server.sendBotMessage(ctx.chatJid, "Compacting session context…");
253
+ const result = await session.compact(arg || void 0);
254
+ const after = session.getContextUsage();
255
+ const lines = ["Compaction complete."];
256
+ if (typeof result?.tokensBefore === "number") lines.push(`Tokens before: ${result.tokensBefore.toLocaleString()}`);
257
+ if (before?.tokens != null) lines.push(`Estimated tokens before: ${before.tokens.toLocaleString()}`);
258
+ if (after?.tokens != null) lines.push(`Estimated tokens after: ${after.tokens.toLocaleString()}`);
259
+ ctx.server.sendBotMessage(ctx.chatJid, lines.join("\n"));
260
+ }
261
+ function findModel(query) {
262
+ const available = modelRegistry.getAvailable();
263
+ if (available.length === 0) return void 0;
264
+ const q = query.toLowerCase();
265
+ const exact = available.filter((m) => `${m.provider}/${m.id}`.toLowerCase() === q);
266
+ if (exact.length === 1) return exact[0];
267
+ const byId = available.filter((m) => m.id.toLowerCase() === q);
268
+ if (byId.length === 1) return byId[0];
269
+ const tokens = q.split(/[\s/]+/).filter(Boolean);
270
+ const fuzzy = available.filter((m) => {
271
+ const label = `${m.provider}/${m.id}`.toLowerCase();
272
+ return tokens.every((t) => label.includes(t));
273
+ });
274
+ const matches = exact.length > 1 ? exact : byId.length > 1 ? byId : fuzzy;
275
+ if (matches.length === 0) return void 0;
276
+ matches.sort((a, b) => a.id.length - b.id.length || b.id.localeCompare(a.id, void 0, { numeric: true }));
277
+ return matches[0];
278
+ }
279
+ var stripAnsiRe = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*[A-Za-z]`, "g");
280
+ export { tryPriorityCommand as i, tryBashCommand as n, tryCommand as r, commandDescriptions as t };
@@ -1,13 +1,11 @@
1
1
  import os from "os";
2
2
  import path from "path";
3
- process.cwd();
4
3
  const ASSISTANT_NAME = process.env.ASSISTANT_NAME || "pi";
5
- Number.parseInt(process.env.ADMIN_PORT || "3737", 10);
6
4
  const POLL_INTERVAL = 2e3;
7
5
  const SCHEDULER_POLL_INTERVAL = 6e4;
8
6
  function resolveDataRoot() {
9
- const xdgData = process.env.XDG_DATA_HOME || path.join(os.homedir(), ".local", "share");
10
- return path.resolve(xdgData, "piclaw");
7
+ if (process.env.PICLAW_DIR) return path.resolve(process.env.PICLAW_DIR);
8
+ return path.resolve(os.homedir(), ".pi", "claw");
11
9
  }
12
10
  const DATA_ROOT = resolveDataRoot();
13
11
  const STORE_DIR = path.resolve(DATA_ROOT, "store");
@@ -1,9 +1,14 @@
1
1
  import { d as STORE_DIR } from "./config.mjs";
2
2
  import { t as createLogger } from "./logger.mjs";
3
+ import { createRequire } from "node:module";
3
4
  import path from "path";
4
5
  import fs from "fs";
5
- import { DatabaseSync } from "node:sqlite";
6
6
  var logger = createLogger("db");
7
+ var _require = createRequire(import.meta.url);
8
+ function openDatabase(dbPath) {
9
+ if ("Bun" in globalThis) return new (_require("bun:sqlite")).Database(dbPath);
10
+ return new (_require("node:sqlite")).DatabaseSync(dbPath);
11
+ }
7
12
  var SCHEMA_VERSION = 2;
8
13
  var _db;
9
14
  function useDb() {
@@ -92,13 +97,14 @@ function initDatabase() {
92
97
  const existing = fs.existsSync(versionFile) ? Number.parseInt(fs.readFileSync(versionFile, "utf-8").trim(), 10) : 0;
93
98
  if (existing !== SCHEMA_VERSION) throw new Error(`Database schema version mismatch (have v${existing}, need v${SCHEMA_VERSION}). Delete ${STORE_DIR} to recreate.`);
94
99
  }
95
- _db = new DatabaseSync(dbPath);
96
- _db.exec("PRAGMA journal_mode = WAL");
97
- _db.exec("PRAGMA busy_timeout = 5000");
100
+ const db = openDatabase(dbPath);
101
+ db.exec("PRAGMA journal_mode = WAL");
102
+ db.exec("PRAGMA busy_timeout = 5000");
98
103
  if (!dbExists) {
99
- createSchema(_db);
104
+ createSchema(db);
100
105
  fs.writeFileSync(versionFile, String(SCHEMA_VERSION));
101
106
  }
107
+ _db = db;
102
108
  logger.info({ dbPath }, "Database initialized");
103
109
  }
104
110
  function storeMessage(msg) {
@@ -8,7 +8,7 @@ const LOG_LEVELS = {
8
8
  error: 40,
9
9
  fatal: 50
10
10
  };
11
- var currentLevel = LOG_LEVELS[process.env.LOG_LEVEL || "info"] ?? LOG_LEVELS.info;
11
+ var currentLevel = LOG_LEVELS[process.env.LOG_LEVEL || "debug"] ?? LOG_LEVELS.debug;
12
12
  var LOGS_FILE = path.join(STORE_DIR, "logs.jsonl");
13
13
  var MAX_LINES = 1e3;
14
14
  var TRIM_TO = 500;
@@ -0,0 +1,31 @@
1
+ async function spawnNodePty(opts) {
2
+ const term = (await import("node-pty")).spawn(opts.shell, [], {
3
+ name: "xterm-256color",
4
+ cols: opts.cols,
5
+ rows: opts.rows,
6
+ cwd: opts.cwd,
7
+ env: opts.env
8
+ });
9
+ return {
10
+ write(data) {
11
+ term.write(data);
12
+ },
13
+ resize(cols, rows) {
14
+ term.resize(cols, rows);
15
+ },
16
+ kill() {
17
+ try {
18
+ term.kill();
19
+ } catch {}
20
+ },
21
+ onData(cb) {
22
+ const d = term.onData(cb);
23
+ return () => d.dispose();
24
+ },
25
+ onExit(cb) {
26
+ const d = term.onExit(() => cb());
27
+ return () => d.dispose();
28
+ }
29
+ };
30
+ }
31
+ export { spawnNodePty };
@@ -0,0 +1,45 @@
1
+ import { t as createLogger } from "./logger.mjs";
2
+ import { b as setConfig, i as deleteConfig, l as getConfig } from "./db.mjs";
3
+ var logger = createLogger("ntfy");
4
+ var CONFIG_KEY = "ntfy";
5
+ function getNtfyConfig() {
6
+ return getConfig(CONFIG_KEY);
7
+ }
8
+ function setNtfyConfig(config) {
9
+ setConfig(CONFIG_KEY, config);
10
+ }
11
+ function clearNtfyConfig() {
12
+ deleteConfig(CONFIG_KEY);
13
+ }
14
+ async function sendNtfyNotification(config, opts) {
15
+ const url = `${config.serverUrl.replace(/\/$/, "")}/${encodeURIComponent(config.topic)}`;
16
+ const headers = {};
17
+ if (config.token) headers["Authorization"] = `Bearer ${config.token}`;
18
+ if (opts.title) headers["X-Title"] = opts.title;
19
+ if (opts.priority) headers["X-Priority"] = String(opts.priority);
20
+ if (opts.tags?.length) headers["X-Tags"] = opts.tags.join(",");
21
+ if (opts.click) headers["X-Click"] = opts.click;
22
+ if (opts.icon) headers["X-Icon"] = opts.icon;
23
+ if (opts.markdown) headers["X-Markdown"] = "true";
24
+ if (opts.delay) headers["X-Delay"] = opts.delay;
25
+ if (opts.actions?.length) headers["X-Actions"] = opts.actions.join("; ");
26
+ if (opts.attach) headers["X-Attach"] = opts.attach;
27
+ if (opts.filename) headers["X-Filename"] = opts.filename;
28
+ if (opts.email) headers["X-Email"] = opts.email;
29
+ const res = await fetch(url, {
30
+ method: "POST",
31
+ body: opts.message,
32
+ headers
33
+ });
34
+ if (!res.ok) {
35
+ const text = await res.text().catch(() => "");
36
+ logger.error({
37
+ status: res.status,
38
+ response: text,
39
+ url,
40
+ headers
41
+ }, "ntfy send failed");
42
+ }
43
+ logger.info({ ...opts }, "ntfy notification sent");
44
+ }
45
+ export { setNtfyConfig as i, getNtfyConfig as n, sendNtfyNotification as r, clearNtfyConfig as t };
@@ -116,7 +116,8 @@ var pi_exports = /* @__PURE__ */ __exportAll({
116
116
  modelRegistry: () => modelRegistry,
117
117
  run: () => run,
118
118
  sendMessage: () => sendMessage,
119
- shutdown: () => shutdown
119
+ shutdown: () => shutdown,
120
+ warmUp: () => warmUp
120
121
  });
121
122
  var logger = createLogger("pi");
122
123
  async function run(group, input, onOutput) {
@@ -147,6 +148,37 @@ function kill(groupJid) {
147
148
  sessions.delete(groupJid);
148
149
  }
149
150
  }
151
+ /**
152
+ * Eagerly restore in-memory sessions for groups that had persisted sessions.
153
+ * Called on server boot so sessions are warm before message recovery.
154
+ */
155
+ async function warmUp(groups, storedSessions, assistantName) {
156
+ const folderToJid = /* @__PURE__ */ new Map();
157
+ for (const [jid, group] of Object.entries(groups)) folderToJid.set(group.folder, {
158
+ jid,
159
+ group
160
+ });
161
+ const toRestore = Object.entries(storedSessions).filter(([folder]) => folderToJid.has(folder) && !sessions.has(folderToJid.get(folder).jid));
162
+ if (toRestore.length === 0) return;
163
+ logger.info({ count: toRestore.length }, "Warming up persisted sessions");
164
+ const results = await Promise.allSettled(toRestore.map(async ([folder, sessionId]) => {
165
+ const { jid, group } = folderToJid.get(folder);
166
+ await getOrCreateSession(group, {
167
+ prompt: "",
168
+ sessionId,
169
+ groupFolder: folder,
170
+ chatJid: jid,
171
+ isMain: folder === "main",
172
+ assistantName
173
+ });
174
+ return {
175
+ jid,
176
+ group: group.name
177
+ };
178
+ }));
179
+ for (const r of results) if (r.status === "fulfilled") logger.info({ group: r.value.group }, "Session restored");
180
+ else logger.warn({ err: r.reason }, "Failed to restore session");
181
+ }
150
182
  async function shutdown(timeoutMs) {
151
183
  for (const [jid, managed] of sessions) {
152
184
  try {
@@ -1,5 +1,5 @@
1
1
  import { r as HTTPResponse } from "../_libs/h3+rou3+srvx.mjs";
2
- const rendererTemplate = () => new HTTPResponse("<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta\n name=\"viewport\"\n content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover, interactive-widget=resizes-content\"\n />\n <title>PiClaw</title>\n <meta name=\"theme-color\" content=\"#0a0e14\" />\n <meta name=\"apple-mobile-web-app-capable\" content=\"yes\" />\n <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\" />\n <meta name=\"mobile-web-app-capable\" content=\"yes\" />\n <link rel=\"manifest\" href=\"/manifest.json\" />\n </head>\n <body style=\"background-color: #0a0e14\">\n <div id=\"app\"></div>\n <script type=\"module\" src=\"/src/app/index.ts\"><\/script>\n </body>\n</html>\n", { headers: { "content-type": "text/html; charset=utf-8" } });
2
+ const rendererTemplate = () => new HTTPResponse("<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta\n name=\"viewport\"\n content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover, interactive-widget=resizes-content\"\n />\n <title>PiClaw</title>\n <meta name=\"theme-color\" content=\"#0a0e14\" />\n <meta name=\"apple-mobile-web-app-capable\" content=\"yes\" />\n <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\" />\n <meta name=\"mobile-web-app-capable\" content=\"yes\" />\n <link rel=\"icon\" type=\"image/svg+xml\" href=\"/icon.svg\" />\n <link rel=\"manifest\" href=\"/manifest.json\" />\n </head>\n <body style=\"background-color: #0a0e14\">\n <div id=\"app\"></div>\n <script type=\"module\" src=\"/src/app/index.ts\"><\/script>\n </body>\n</html>\n", { headers: { "content-type": "text/html; charset=utf-8" } });
3
3
  function renderIndexHTML(event) {
4
4
  return rendererTemplate(event.req);
5
5
  }