piclaw 0.0.19 → 0.0.21

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 (270) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/defult-D5RLDUrI.js +1 -0
  3. package/.output/public/assets/{dist-CMBqBOCp.js → dist-BH_oa-kv.js} +1 -1
  4. package/.output/public/assets/index-7JvURuHy.js +204 -0
  5. package/.output/public/assets/index-K43slwjJ.css +1 -0
  6. package/.output/public/index.html +11 -2
  7. package/.output/server/_...path_.get.mjs +16 -0
  8. package/.output/server/_chunks/app.mjs +261 -181
  9. package/.output/server/_chunks/browser.mjs +4 -1
  10. package/.output/server/_chunks/config.mjs +4 -0
  11. package/.output/server/_chunks/db.mjs +32 -28
  12. package/.output/server/_chunks/device-bus.mjs +123 -0
  13. package/.output/server/_chunks/dummy.mjs +1 -1
  14. package/.output/server/_chunks/logger.mjs +23 -0
  15. package/.output/server/_chunks/login.mjs +1 -1
  16. package/.output/server/_chunks/notes.mjs +1 -3
  17. package/.output/server/_chunks/renderer-template.mjs +1 -1
  18. package/.output/server/_chunks/sandbox.mjs +217 -0
  19. package/.output/server/_chunks/server.mjs +2302 -122
  20. package/.output/server/_chunks/terminal.mjs +63 -8
  21. package/.output/server/_chunks/uploads.mjs +60 -0
  22. package/.output/server/_chunks/virtual.mjs +192 -54
  23. package/.output/server/_id_.delete.mjs +5 -2
  24. package/.output/server/_id_.patch.mjs +2 -0
  25. package/.output/server/_id_2.delete.mjs +8 -0
  26. package/.output/server/_jid_.delete.mjs +5 -2
  27. package/.output/server/_jid_.patch.mjs +37 -4
  28. package/.output/server/_jid_2.delete.mjs +5 -2
  29. package/.output/server/_libs/@acemir/cssom+[...].mjs +2269 -1137
  30. package/.output/server/_libs/@google/genai.mjs +337 -273
  31. package/.output/server/_libs/@mariozechner/pi-agent-core+[...].mjs +381 -2073
  32. package/.output/server/_libs/@mariozechner/pi-coding-agent+[...].mjs +231 -131
  33. package/.output/server/_libs/_.mjs +3 -2
  34. package/.output/server/_libs/_10.mjs +2 -4
  35. package/.output/server/_libs/_11.mjs +2 -4
  36. package/.output/server/_libs/_12.mjs +2 -3
  37. package/.output/server/_libs/_13.mjs +2 -3
  38. package/.output/server/_libs/_14.mjs +2 -4
  39. package/.output/server/_libs/_15.mjs +2 -4
  40. package/.output/server/_libs/_16.mjs +2 -3
  41. package/.output/server/_libs/_17.mjs +2 -4
  42. package/.output/server/_libs/_18.mjs +2 -2
  43. package/.output/server/_libs/_19.mjs +2 -2
  44. package/.output/server/_libs/_2.mjs +3 -3
  45. package/.output/server/_libs/_20.mjs +2 -2
  46. package/.output/server/_libs/_21.mjs +2 -2
  47. package/.output/server/_libs/_22.mjs +2 -2
  48. package/.output/server/_libs/_23.mjs +2 -2
  49. package/.output/server/_libs/_24.mjs +2 -2
  50. package/.output/server/_libs/_25.mjs +2 -2
  51. package/.output/server/_libs/_26.mjs +2 -2
  52. package/.output/server/_libs/_27.mjs +2 -2
  53. package/.output/server/_libs/_28.mjs +2 -2
  54. package/.output/server/_libs/_29.mjs +2 -2
  55. package/.output/server/_libs/_3.mjs +3 -3
  56. package/.output/server/_libs/_30.mjs +2 -2
  57. package/.output/server/_libs/_31.mjs +2 -2
  58. package/.output/server/_libs/_32.mjs +2 -2
  59. package/.output/server/_libs/_33.mjs +2 -2
  60. package/.output/server/_libs/_34.mjs +2 -2
  61. package/.output/server/_libs/_35.mjs +2 -2
  62. package/.output/server/_libs/_36.mjs +2 -2
  63. package/.output/server/_libs/_37.mjs +2 -2
  64. package/.output/server/_libs/_38.mjs +2 -2
  65. package/.output/server/_libs/_39.mjs +2 -2
  66. package/.output/server/_libs/_4.mjs +4 -3
  67. package/.output/server/_libs/_40.mjs +2 -2
  68. package/.output/server/_libs/_41.mjs +2 -2
  69. package/.output/server/_libs/_42.mjs +2 -2
  70. package/.output/server/_libs/_43.mjs +2 -2
  71. package/.output/server/_libs/_44.mjs +2 -2
  72. package/.output/server/_libs/_45.mjs +2 -2
  73. package/.output/server/_libs/_46.mjs +2 -2
  74. package/.output/server/_libs/_47.mjs +2 -2
  75. package/.output/server/_libs/_48.mjs +2 -2
  76. package/.output/server/_libs/_49.mjs +2 -2
  77. package/.output/server/_libs/_5.mjs +2 -3
  78. package/.output/server/_libs/_50.mjs +2 -2
  79. package/.output/server/_libs/_51.mjs +2 -2
  80. package/.output/server/_libs/_52.mjs +2 -2
  81. package/.output/server/_libs/_53.mjs +2 -2
  82. package/.output/server/_libs/_54.mjs +2 -2
  83. package/.output/server/_libs/_55.mjs +2 -2
  84. package/.output/server/_libs/_56.mjs +2 -2
  85. package/.output/server/_libs/_57.mjs +2 -2
  86. package/.output/server/_libs/_58.mjs +2 -2
  87. package/.output/server/_libs/_59.mjs +2 -2
  88. package/.output/server/_libs/_6.mjs +2 -3
  89. package/.output/server/_libs/_60.mjs +2 -2
  90. package/.output/server/_libs/_61.mjs +2 -2
  91. package/.output/server/_libs/_62.mjs +2 -2
  92. package/.output/server/_libs/_63.mjs +2 -2
  93. package/.output/server/_libs/_64.mjs +2 -2
  94. package/.output/server/_libs/_65.mjs +2 -2
  95. package/.output/server/_libs/_66.mjs +2 -2
  96. package/.output/server/_libs/_67.mjs +2 -2
  97. package/.output/server/_libs/_68.mjs +2 -2
  98. package/.output/server/_libs/_69.mjs +2 -2
  99. package/.output/server/_libs/_7.mjs +2 -5
  100. package/.output/server/_libs/_70.mjs +2 -2
  101. package/.output/server/_libs/_71.mjs +2 -2
  102. package/.output/server/_libs/_72.mjs +2 -2
  103. package/.output/server/_libs/_73.mjs +2 -2
  104. package/.output/server/_libs/_74.mjs +2 -2
  105. package/.output/server/_libs/_75.mjs +2 -2
  106. package/.output/server/_libs/_76.mjs +2 -2
  107. package/.output/server/_libs/_77.mjs +2 -2
  108. package/.output/server/_libs/_78.mjs +2 -2
  109. package/.output/server/_libs/_79.mjs +2 -2
  110. package/.output/server/_libs/_8.mjs +2 -3
  111. package/.output/server/_libs/_80.mjs +2 -2
  112. package/.output/server/_libs/_81.mjs +2 -2
  113. package/.output/server/_libs/_82.mjs +2 -2
  114. package/.output/server/_libs/_83.mjs +2 -2
  115. package/.output/server/_libs/_84.mjs +2 -2
  116. package/.output/server/_libs/_85.mjs +2 -2
  117. package/.output/server/_libs/_86.mjs +2 -2
  118. package/.output/server/_libs/_87.mjs +2 -2
  119. package/.output/server/_libs/_88.mjs +2 -2
  120. package/.output/server/_libs/_89.mjs +2 -2
  121. package/.output/server/_libs/_9.mjs +2 -4
  122. package/.output/server/_libs/_90.mjs +5 -2
  123. package/.output/server/_libs/_91.mjs +3 -2
  124. package/.output/server/_libs/_92.mjs +2 -2
  125. package/.output/server/_libs/_93.mjs +2 -2
  126. package/.output/server/_libs/_94.mjs +2 -2
  127. package/.output/server/_libs/agent-base.mjs +1 -1
  128. package/.output/server/_libs/cheerio+[...].mjs +1 -1
  129. package/.output/server/_libs/data-uri-to-buffer.mjs +2 -67
  130. package/.output/server/_libs/data-urls+[...].mjs +1 -1
  131. package/.output/server/_libs/diff.mjs +1 -1
  132. package/.output/server/_libs/exodus__bytes.mjs +99 -81
  133. package/.output/server/_libs/fetch-blob+node-domexception.mjs +1 -1
  134. package/.output/server/_libs/h3+rou3+srvx.mjs +34 -4
  135. package/.output/server/_libs/html-encoding-sniffer.mjs +1 -1
  136. package/.output/server/_libs/https-proxy-agent.mjs +2 -2
  137. package/.output/server/_libs/jsdom.mjs +1 -1
  138. package/.output/server/_libs/just-bash+[...].mjs +4676 -3916
  139. package/.output/server/_libs/mariozechner__jiti.mjs +1 -1
  140. package/.output/server/_libs/mariozechner__pi-ai.mjs +1472 -0
  141. package/.output/server/_libs/md4x.mjs +1 -1
  142. package/.output/server/_libs/mime.mjs +838 -1
  143. package/.output/server/_libs/node-fetch.mjs +4 -4
  144. package/.output/server/_libs/node-liblzma.mjs +1 -1
  145. package/.output/server/_libs/silvia-odwyer__photon-node.mjs +1 -1
  146. package/.output/server/_routes/api/auth/approve.mjs +2 -0
  147. package/.output/server/_routes/api/auth/revoke.mjs +2 -0
  148. package/.output/server/_routes/api/auth/status.mjs +25 -6
  149. package/.output/server/_routes/api/browser2.mjs +1 -1
  150. package/.output/server/_routes/api/config2.mjs +2 -0
  151. package/.output/server/_routes/api/device_events.mjs +36 -0
  152. package/.output/server/_routes/api/files/groups.mjs +1 -2
  153. package/.output/server/_routes/api/files/raw.mjs +1 -1
  154. package/.output/server/_routes/api/groups.mjs +5 -3
  155. package/.output/server/_routes/api/groups2.mjs +18 -6
  156. package/.output/server/_routes/api/health.mjs +1 -2
  157. package/.output/server/_routes/api/messages.mjs +7 -1
  158. package/.output/server/_routes/api/notes/delete.mjs +4 -1
  159. package/.output/server/_routes/api/notes/write.mjs +2 -0
  160. package/.output/server/_routes/api/ntfy/setup.mjs +8 -0
  161. package/.output/server/_routes/api/pi/apikey.mjs +3 -2
  162. package/.output/server/_routes/api/pi/apikey_providers.mjs +1 -2
  163. package/.output/server/_routes/api/pi/commands.mjs +13 -3
  164. package/.output/server/_routes/api/pi/login/events.mjs +0 -1
  165. package/.output/server/_routes/api/pi/login/respond.mjs +2 -1
  166. package/.output/server/_routes/api/pi/login.mjs +1 -2
  167. package/.output/server/_routes/api/pi/logout.mjs +2 -1
  168. package/.output/server/_routes/api/pi/models.mjs +1 -2
  169. package/.output/server/_routes/api/pi/models_config2.mjs +2 -0
  170. package/.output/server/_routes/api/pi/settings2.mjs +2 -0
  171. package/.output/server/_routes/api/pi/status.mjs +1 -2
  172. package/.output/server/_routes/api/proxy.mjs +19 -1
  173. package/.output/server/_routes/api/sandbox.mjs +26 -0
  174. package/.output/server/_routes/api/sandbox2.mjs +17 -0
  175. package/.output/server/_routes/api/send.mjs +26 -18
  176. package/.output/server/_routes/api/status.mjs +1 -3
  177. package/.output/server/_routes/api/stop.mjs +11 -0
  178. package/.output/server/_routes/api/store/plugins.mjs +75 -0
  179. package/.output/server/_routes/api/store/skills.mjs +11 -0
  180. package/.output/server/_routes/api/tasks2.mjs +3 -2
  181. package/.output/server/_routes/api/telegram/setup.mjs +5 -2
  182. package/.output/server/_routes/api/telegram/status.mjs +1 -2
  183. package/.output/server/_routes/api/terminal2.mjs +2 -1
  184. package/.output/server/_routes/api/tunnel/setup.mjs +4 -2
  185. package/.output/server/_runtime.mjs +1 -2
  186. package/.output/server/_utils.mjs +10 -2
  187. package/.output/server/index.mjs +1 -1
  188. package/.output/server/node_modules/amdefine/amdefine.js +301 -0
  189. package/.output/server/node_modules/amdefine/package.json +16 -0
  190. package/.output/server/node_modules/compressjs/lib/BWT.js +420 -0
  191. package/.output/server/node_modules/compressjs/lib/BWTC.js +234 -0
  192. package/.output/server/node_modules/compressjs/lib/BitStream.js +108 -0
  193. package/.output/server/node_modules/compressjs/lib/Bzip2.js +936 -0
  194. package/.output/server/node_modules/compressjs/lib/CRC32.js +105 -0
  195. package/.output/server/node_modules/compressjs/lib/Context1Model.js +56 -0
  196. package/.output/server/node_modules/compressjs/lib/DefSumModel.js +152 -0
  197. package/.output/server/node_modules/compressjs/lib/DeflateDistanceModel.js +55 -0
  198. package/.output/server/node_modules/compressjs/lib/Dmc.js +197 -0
  199. package/.output/server/node_modules/compressjs/lib/DummyRangeCoder.js +81 -0
  200. package/.output/server/node_modules/compressjs/lib/FenwickModel.js +194 -0
  201. package/.output/server/node_modules/compressjs/lib/Huffman.js +514 -0
  202. package/.output/server/node_modules/compressjs/lib/HuffmanAllocator.js +227 -0
  203. package/.output/server/node_modules/compressjs/lib/LogDistanceModel.js +46 -0
  204. package/.output/server/node_modules/compressjs/lib/Lzjb.js +300 -0
  205. package/.output/server/node_modules/compressjs/lib/LzjbR.js +241 -0
  206. package/.output/server/node_modules/compressjs/lib/Lzp3.js +273 -0
  207. package/.output/server/node_modules/compressjs/lib/MTFModel.js +208 -0
  208. package/.output/server/node_modules/compressjs/lib/NoModel.js +46 -0
  209. package/.output/server/node_modules/compressjs/lib/PPM.js +343 -0
  210. package/.output/server/node_modules/compressjs/lib/RangeCoder.js +238 -0
  211. package/.output/server/node_modules/compressjs/lib/Simple.js +111 -0
  212. package/.output/server/node_modules/compressjs/lib/Stream.js +53 -0
  213. package/.output/server/node_modules/compressjs/lib/Util.js +324 -0
  214. package/.output/server/node_modules/compressjs/lib/freeze.js +14 -0
  215. package/.output/server/node_modules/compressjs/main.js +29 -0
  216. package/.output/server/node_modules/compressjs/package.json +35 -0
  217. package/.output/server/package.json +2 -1
  218. package/README.md +10 -1
  219. package/lib/index.d.mts +1 -0
  220. package/lib/index.mjs +1 -0
  221. package/lib/piclaw.mjs +100 -0
  222. package/lib/utils.mjs +96 -0
  223. package/package.json +16 -11
  224. package/.output/public/assets/defult-CMO6TZ5a.js +0 -1
  225. package/.output/public/assets/index-jdnbJw-M.js +0 -204
  226. package/.output/public/assets/index-ooXrRwgl.css +0 -1
  227. package/.output/server/_chunks/commands.mjs +0 -282
  228. package/.output/server/_chunks/pi.mjs +0 -202
  229. package/.output/server/_chunks/session.mjs +0 -1114
  230. package/.output/server/_libs/@aws-crypto/crc32+[...].mjs +0 -299
  231. package/.output/server/_libs/@aws-sdk/client-bedrock-runtime+[...].mjs +0 -17828
  232. package/.output/server/_libs/@aws-sdk/credential-provider-http+[...].mjs +0 -122
  233. package/.output/server/_libs/@aws-sdk/credential-provider-ini+[...].mjs +0 -417
  234. package/.output/server/_libs/@aws-sdk/credential-provider-process+[...].mjs +0 -54
  235. package/.output/server/_libs/@aws-sdk/credential-provider-sso+[...].mjs +0 -1151
  236. package/.output/server/_libs/@aws-sdk/credential-provider-web-identity+[...].mjs +0 -50
  237. package/.output/server/_libs/@smithy/credential-provider-imds+[...].mjs +0 -369
  238. package/.output/server/_libs/@tootallnate/quickjs-emscripten+[...].mjs +0 -3011
  239. package/.output/server/_libs/_100.mjs +0 -2
  240. package/.output/server/_libs/_101.mjs +0 -2
  241. package/.output/server/_libs/_102.mjs +0 -5
  242. package/.output/server/_libs/_103.mjs +0 -3
  243. package/.output/server/_libs/_104.mjs +0 -2
  244. package/.output/server/_libs/_105.mjs +0 -3
  245. package/.output/server/_libs/_106.mjs +0 -2
  246. package/.output/server/_libs/_107.mjs +0 -2
  247. package/.output/server/_libs/_95.mjs +0 -2
  248. package/.output/server/_libs/_96.mjs +0 -2
  249. package/.output/server/_libs/_97.mjs +0 -2
  250. package/.output/server/_libs/_98.mjs +0 -2
  251. package/.output/server/_libs/_99.mjs +0 -2
  252. package/.output/server/_libs/amdefine.mjs +0 -188
  253. package/.output/server/_libs/ast-types.mjs +0 -2270
  254. package/.output/server/_libs/aws-sdk__nested-clients.mjs +0 -3141
  255. package/.output/server/_libs/basic-ftp.mjs +0 -1906
  256. package/.output/server/_libs/compressjs.mjs +0 -50
  257. package/.output/server/_libs/degenerator+[...].mjs +0 -9964
  258. package/.output/server/_libs/get-uri.mjs +0 -413
  259. package/.output/server/_libs/http-proxy-agent.mjs +0 -123
  260. package/.output/server/_libs/ip-address.mjs +0 -1423
  261. package/.output/server/_libs/lru-cache.mjs +0 -732
  262. package/.output/server/_libs/netmask.mjs +0 -139
  263. package/.output/server/_libs/pac-proxy-agent+[...].mjs +0 -3104
  264. package/.output/server/_libs/proxy-agent+proxy-from-env.mjs +0 -204
  265. package/.output/server/_libs/smithy__core.mjs +0 -192
  266. package/.output/server/node_modules/tslib/modules/index.js +0 -70
  267. package/.output/server/node_modules/tslib/modules/package.json +0 -3
  268. package/.output/server/node_modules/tslib/package.json +0 -47
  269. package/.output/server/node_modules/tslib/tslib.js +0 -484
  270. package/bin/piclaw.mjs +0 -195
@@ -1,6 +1,8 @@
1
+ import { s as config } from "./config.mjs";
1
2
  import { t as createLogger } from "./logger.mjs";
3
+ import { t as deviceBus } from "./device-bus.mjs";
4
+ import { accessSync, constants } from "node:fs";
2
5
  import crypto from "node:crypto";
3
- import os from "node:os";
4
6
  var _isBun = typeof globalThis.Bun !== "undefined";
5
7
  var _isWebContainer = process.env.SHELL === "/bin/jsh" || !!process.versions?.webcontainer;
6
8
  async function spawnPty(opts) {
@@ -26,6 +28,7 @@ async function spawnVirtualPty(...args) {
26
28
  }
27
29
  var logger = createLogger("terminal");
28
30
  var MAX_SESSIONS = 20;
31
+ var _ttyId = 0;
29
32
  var SESSION_TIMEOUT = 1800 * 1e3;
30
33
  var TerminalManager = class {
31
34
  sessions = /* @__PURE__ */ new Map();
@@ -34,21 +37,44 @@ var TerminalManager = class {
34
37
  const id = crypto.randomUUID();
35
38
  const cols = opts?.cols ?? 80;
36
39
  const rows = opts?.rows ?? 24;
37
- const cwd = opts?.cwd?.startsWith("~") ? os.homedir() + opts.cwd.slice(1) : opts?.cwd ?? process.cwd();
40
+ const sandboxMode = config.get("sandbox");
41
+ const forceSandbox = sandboxMode === "enforced" || sandboxMode === "true";
38
42
  let handle;
39
- if (opts?.virtual) handle = await spawnVirtualPty({ cwd });
40
- else {
41
- const shell = process.env.SHELL || "/bin/bash";
43
+ let cwd;
44
+ if (opts?.sandboxId) {
45
+ handle = await spawnVirtualPty({ sandboxId: opts.sandboxId });
46
+ const { sandboxManager } = await import("../_libs/_89.mjs");
47
+ const sb = sandboxManager.get(opts.sandboxId);
48
+ cwd = sb.env.HOME || sb.cwd;
49
+ } else if (opts?.sandbox || forceSandbox) {
50
+ const sbId = `tty${++_ttyId}`;
51
+ cwd = `/home/${sbId}`;
52
+ handle = await spawnVirtualPty({ sandboxId: sbId });
53
+ } else {
54
+ cwd = opts?.cwd ?? process.cwd();
55
+ const shell = resolveShell();
42
56
  const env = { TERM: "xterm-256color" };
43
57
  for (const [k, v] of Object.entries(process.env)) if (v !== void 0) env[k] = v;
44
58
  env.TERM = "xterm-256color";
45
- handle = await spawnPty({
59
+ const spawnOpts = {
46
60
  shell,
47
61
  cols,
48
62
  rows,
49
63
  cwd,
50
64
  env
51
- });
65
+ };
66
+ try {
67
+ handle = await spawnPty(spawnOpts);
68
+ } catch (err) {
69
+ if (err?.code === "ENOENT") {
70
+ logger.warn({
71
+ shell,
72
+ err: err.message
73
+ }, "PTY spawn ENOENT, retrying");
74
+ await new Promise((r) => setTimeout(r, 500));
75
+ handle = await spawnPty(spawnOpts);
76
+ } else throw err;
77
+ }
52
78
  }
53
79
  const session = {
54
80
  id,
@@ -58,7 +84,7 @@ var TerminalManager = class {
58
84
  cols,
59
85
  rows,
60
86
  cwd,
61
- label: opts?.label ?? "shell",
87
+ label: opts?.label ?? (opts?.sandbox || opts?.sandboxId || forceSandbox ? "shell (Sandbox)" : "shell"),
62
88
  listeners: /* @__PURE__ */ new Set(),
63
89
  exitListeners: /* @__PURE__ */ new Set(),
64
90
  timeoutTimer: setTimeout(() => this.kill(id), SESSION_TIMEOUT)
@@ -77,6 +103,17 @@ var TerminalManager = class {
77
103
  rows,
78
104
  cwd
79
105
  }, "Terminal session created");
106
+ deviceBus.emitDeviceEvent("terminal", {
107
+ action: "created",
108
+ session: {
109
+ id,
110
+ cols,
111
+ rows,
112
+ cwd,
113
+ label: session.label,
114
+ createdAt: session.createdAt
115
+ }
116
+ });
80
117
  return session;
81
118
  }
82
119
  get(id) {
@@ -147,6 +184,10 @@ var TerminalManager = class {
147
184
  session.listeners.clear();
148
185
  this.sessions.delete(id);
149
186
  logger.info({ id }, "Terminal session destroyed");
187
+ deviceBus.emitDeviceEvent("terminal", {
188
+ action: "destroyed",
189
+ sessionId: id
190
+ });
150
191
  }
151
192
  resetTimeout(session) {
152
193
  clearTimeout(session.timeoutTimer);
@@ -154,4 +195,18 @@ var TerminalManager = class {
154
195
  }
155
196
  };
156
197
  const terminalManager = new TerminalManager();
198
+ /** Resolve a valid shell path with fallback chain. */
199
+ function resolveShell() {
200
+ const candidates = [
201
+ process.env.SHELL,
202
+ "/bin/zsh",
203
+ "/bin/bash",
204
+ "/bin/sh"
205
+ ];
206
+ for (const sh of candidates) if (sh) try {
207
+ accessSync(sh, constants.X_OK);
208
+ return sh;
209
+ } catch {}
210
+ return "/bin/sh";
211
+ }
157
212
  export { terminalManager as t };
@@ -0,0 +1,60 @@
1
+ import { n as GROUPS_DIR } from "./config.mjs";
2
+ import { n as src_default } from "../_libs/mime.mjs";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ function isImageMimeType(mimeType) {
6
+ return mimeType.startsWith("image/");
7
+ }
8
+ function getMimeType(filename) {
9
+ return src_default.getType(filename) || "application/octet-stream";
10
+ }
11
+ /** Resolve the downloads directory for a group */
12
+ function downloadsDir(groupFolder) {
13
+ return path.join(GROUPS_DIR, groupFolder, "downloads");
14
+ }
15
+ /** Assert that `resolved` is within `parent` to defeat `..` traversal */
16
+ function assertWithinDir(resolved, parent) {
17
+ fs.mkdirSync(parent, { recursive: true });
18
+ const realParent = fs.realpathSync(parent);
19
+ const real = fs.existsSync(resolved) ? fs.realpathSync(resolved) : path.resolve(resolved);
20
+ if (!real.startsWith(realParent + path.sep) && real !== realParent) throw new Error("Path traversal denied");
21
+ return real;
22
+ }
23
+ /** Save an attachment to disk under `<group_dir>/downloads/<msgId>/<name>` */
24
+ function saveAttachment(groupFolder, messageId, attachment) {
25
+ const groupDir = downloadsDir(groupFolder);
26
+ const dir = path.join(groupDir, messageId);
27
+ fs.mkdirSync(dir, { recursive: true });
28
+ const filePath = path.join(dir, path.basename(attachment.name));
29
+ assertWithinDir(filePath, groupDir);
30
+ fs.writeFileSync(filePath, Buffer.from(attachment.data, "base64"));
31
+ return {
32
+ path: `${groupFolder}/${messageId}/${path.basename(attachment.name)}`,
33
+ mimeType: attachment.mimeType,
34
+ name: path.basename(attachment.name)
35
+ };
36
+ }
37
+ /** Read an attachment file as base64 */
38
+ function readAttachmentBase64(groupFolder, attachment) {
39
+ const groupDir = downloadsDir(groupFolder);
40
+ const filePath = path.join(groupDir, attachment.path.slice(groupFolder.length + 1));
41
+ assertWithinDir(filePath, groupDir);
42
+ if (!fs.existsSync(filePath)) throw new Error(`Attachment not found: ${attachment.path}`);
43
+ return fs.readFileSync(filePath).toString("base64");
44
+ }
45
+ /** Get the absolute path for an attachment (for serving) */
46
+ function getAttachmentPath(relPath) {
47
+ const parts = relPath.split("/");
48
+ if (parts.length < 3) return void 0;
49
+ const groupFolder = parts[0];
50
+ const groupDir = path.join(GROUPS_DIR, groupFolder, "downloads");
51
+ const filePath = path.join(groupDir, parts.slice(1).join("/"));
52
+ if (!fs.existsSync(filePath)) return void 0;
53
+ try {
54
+ assertWithinDir(filePath, groupDir);
55
+ } catch {
56
+ return;
57
+ }
58
+ return filePath;
59
+ }
60
+ export { saveAttachment as a, readAttachmentBase64 as i, getMimeType as n, isImageMimeType as r, getAttachmentPath as t };
@@ -1,43 +1,123 @@
1
1
  async function spawnVirtualPty(opts) {
2
- const { Bash } = await import("../_libs/_99.mjs");
2
+ const { sandboxManager } = await import("../_libs/_89.mjs");
3
3
  const dataListeners = /* @__PURE__ */ new Set();
4
4
  const exitListeners = /* @__PURE__ */ new Set();
5
- let cwd = opts.cwd || "/";
6
- let env = {
7
- HOME: "/home/user",
8
- PATH: "/usr/bin:/bin",
9
- ...opts.env
10
- };
5
+ let sandbox;
6
+ if (opts.sandboxId) sandbox = sandboxManager.get(opts.sandboxId);
7
+ else {
8
+ const standaloneId = `tty${Date.now()}`;
9
+ sandbox = sandboxManager.get(standaloneId, {
10
+ cwd: opts.cwd || "/",
11
+ files: opts.files
12
+ });
13
+ if (opts.env) Object.assign(sandbox.env, opts.env);
14
+ }
11
15
  let line = "";
16
+ let cursor = 0;
12
17
  let alive = true;
13
18
  let busy = false;
14
19
  let pending = "";
15
- const bash = new Bash({
16
- cwd,
17
- env,
18
- files: opts.files
19
- });
20
20
  function emit(data) {
21
21
  for (const cb of dataListeners) cb(data);
22
22
  }
23
23
  function prompt() {
24
- const home = env.HOME || "";
24
+ const home = sandbox.env.HOME || "";
25
+ const cwd = sandbox.cwd;
25
26
  emit(`\x1b[1;32m${home && cwd.startsWith(home) ? "~" + cwd.slice(home.length) : cwd}\x1b[0m $ `);
26
27
  }
28
+ /** Redraw the line from the current cursor position (used after insertions/deletions). */
29
+ function redrawLine() {
30
+ const after = line.slice(cursor);
31
+ emit("\x1B[K" + after);
32
+ if (after.length > 0) emit(`\x1b[${after.length}D`);
33
+ }
34
+ /** Erase entire input line from terminal, reset cursor to prompt end. */
35
+ function clearLine() {
36
+ if (cursor < line.length) emit(`\x1b[${line.length - cursor}C`);
37
+ if (line.length > 0) emit("\b \b".repeat(line.length));
38
+ }
39
+ async function handleTab() {
40
+ const before = line.slice(0, cursor);
41
+ const wordStart = Math.max(before.lastIndexOf(" ") + 1, 0);
42
+ const word = before.slice(wordStart);
43
+ const cwd = sandbox.env.PWD || sandbox.cwd;
44
+ let dir;
45
+ let prefix;
46
+ if (word.includes("/")) {
47
+ const lastSlash = word.lastIndexOf("/");
48
+ const dirPart = word.slice(0, lastSlash) || "/";
49
+ dir = dirPart.startsWith("/") ? dirPart : sandbox.fs.resolvePath(cwd, dirPart);
50
+ prefix = word.slice(lastSlash + 1);
51
+ } else {
52
+ dir = cwd;
53
+ prefix = word;
54
+ }
55
+ let entries;
56
+ try {
57
+ if (sandbox.fs.readdirWithFileTypes) entries = await sandbox.fs.readdirWithFileTypes(dir);
58
+ else {
59
+ const names = await sandbox.fs.readdir(dir);
60
+ entries = await Promise.all(names.map(async (n) => {
61
+ return {
62
+ name: n,
63
+ isDirectory: (await sandbox.fs.stat(sandbox.fs.resolvePath(dir, n))).isDirectory
64
+ };
65
+ }));
66
+ }
67
+ } catch {
68
+ emit("\x07");
69
+ return;
70
+ }
71
+ const matches = entries.filter((e) => e.name.startsWith(prefix));
72
+ if (matches.length === 0) {
73
+ emit("\x07");
74
+ return;
75
+ }
76
+ if (matches.length === 1) {
77
+ const m = matches[0];
78
+ const suffix = m.name.slice(prefix.length) + (m.isDirectory ? "/" : " ");
79
+ line = line.slice(0, cursor) + suffix + line.slice(cursor);
80
+ cursor += suffix.length;
81
+ emit(suffix);
82
+ redrawLine();
83
+ return;
84
+ }
85
+ let common = matches[0].name;
86
+ for (let i = 1; i < matches.length; i++) while (!matches[i].name.startsWith(common)) common = common.slice(0, -1);
87
+ const extra = common.slice(prefix.length);
88
+ if (extra) {
89
+ line = line.slice(0, cursor) + extra + line.slice(cursor);
90
+ cursor += extra.length;
91
+ emit(extra);
92
+ redrawLine();
93
+ } else {
94
+ emit("\r\n" + matches.map((m) => m.name + (m.isDirectory ? "/" : "")).join(" ") + "\r\n");
95
+ prompt();
96
+ emit(line);
97
+ if (cursor < line.length) emit(`\x1b[${line.length - cursor}D`);
98
+ }
99
+ }
100
+ function exit() {
101
+ if (!alive) return;
102
+ alive = false;
103
+ emit("\r\nexit\r\n");
104
+ for (const cb of exitListeners) cb();
105
+ dataListeners.clear();
106
+ exitListeners.clear();
107
+ }
27
108
  async function exec(command) {
28
109
  const trimmed = command.trim();
29
110
  if (!trimmed) {
30
111
  prompt();
31
112
  return;
32
113
  }
114
+ if (trimmed === "exit" || trimmed.startsWith("exit ")) {
115
+ exit();
116
+ return;
117
+ }
33
118
  busy = true;
34
119
  try {
35
- const result = await bash.exec(trimmed, {
36
- cwd,
37
- env
38
- });
39
- if (result.env.PWD) cwd = result.env.PWD;
40
- for (const [k, v] of Object.entries(result.env)) env[k] = v;
120
+ const result = await sandbox.exec(trimmed);
41
121
  if (result.stdout) {
42
122
  const out = result.stdout.endsWith("\n") ? result.stdout.slice(0, -1) : result.stdout;
43
123
  if (out) emit(out.replaceAll("\n", "\r\n") + "\r\n");
@@ -66,45 +146,103 @@ async function spawnVirtualPty(opts) {
66
146
  pending += data;
67
147
  return;
68
148
  }
69
- for (const ch of data) switch (ch) {
70
- case "\r":
71
- case "\n": {
72
- emit("\r\n");
73
- const cmd = line;
74
- line = "";
75
- exec(cmd);
76
- break;
149
+ let i = 0;
150
+ while (i < data.length) {
151
+ const ch = data[i];
152
+ if (ch === "\x1B" && data[i + 1] === "[") {
153
+ const code = data[i + 2];
154
+ if (code === "C") {
155
+ if (cursor < line.length) {
156
+ cursor++;
157
+ emit("\x1B[C");
158
+ }
159
+ i += 3;
160
+ continue;
161
+ }
162
+ if (code === "D") {
163
+ if (cursor > 0) {
164
+ cursor--;
165
+ emit("\x1B[D");
166
+ }
167
+ i += 3;
168
+ continue;
169
+ }
170
+ i += 3;
171
+ continue;
77
172
  }
78
- case "":
79
- case "\b":
80
- if (line.length > 0) {
81
- line = line.slice(0, -1);
82
- emit("\b \b");
173
+ switch (ch) {
174
+ case "\r":
175
+ case "\n": {
176
+ emit("\r\n");
177
+ const cmd = line;
178
+ line = "";
179
+ cursor = 0;
180
+ exec(cmd);
181
+ break;
83
182
  }
84
- break;
85
- case "":
86
- line = "";
87
- emit("^C\r\n");
88
- prompt();
89
- break;
90
- case "":
91
- if (line.length === 0) {
92
- alive = false;
93
- emit("\r\nexit\r\n");
94
- for (const cb of exitListeners) cb();
95
- dataListeners.clear();
96
- exitListeners.clear();
183
+ case "":
184
+ case "\b":
185
+ if (cursor > 0) {
186
+ line = line.slice(0, cursor - 1) + line.slice(cursor);
187
+ cursor--;
188
+ emit("\b");
189
+ redrawLine();
190
+ }
191
+ break;
192
+ case " ":
193
+ handleTab();
194
+ break;
195
+ case "":
196
+ if (cursor > 0) {
197
+ emit(`\x1b[${cursor}D`);
198
+ cursor = 0;
199
+ }
200
+ break;
201
+ case "":
202
+ if (cursor < line.length) {
203
+ emit(`\x1b[${line.length - cursor}C`);
204
+ cursor = line.length;
205
+ }
206
+ break;
207
+ case "\v":
208
+ if (cursor < line.length) {
209
+ line = line.slice(0, cursor);
210
+ emit("\x1B[K");
211
+ }
212
+ break;
213
+ case "":
214
+ if (cursor > 0) {
215
+ clearLine();
216
+ line = line.slice(cursor);
217
+ cursor = 0;
218
+ prompt();
219
+ emit(line);
220
+ if (cursor < line.length) emit(`\x1b[${line.length - cursor}D`);
221
+ }
222
+ break;
223
+ case "":
224
+ line = "";
225
+ cursor = 0;
226
+ emit("^C\r\n");
227
+ prompt();
228
+ break;
229
+ case "":
230
+ if (line.length === 0) exit();
231
+ break;
232
+ case "\f":
233
+ emit("\x1B[2J\x1B[H");
234
+ prompt();
235
+ emit(line);
236
+ if (cursor < line.length) emit(`\x1b[${line.length - cursor}D`);
237
+ break;
238
+ default: if (ch >= " ") {
239
+ line = line.slice(0, cursor) + ch + line.slice(cursor);
240
+ cursor++;
241
+ emit(ch);
242
+ redrawLine();
97
243
  }
98
- break;
99
- case "\f":
100
- emit("\x1B[2J\x1B[H");
101
- prompt();
102
- emit(line);
103
- break;
104
- default: if (ch >= " " || ch === " ") {
105
- line += ch;
106
- emit(ch);
107
244
  }
245
+ i++;
108
246
  }
109
247
  },
110
248
  resize() {},
@@ -1,8 +1,11 @@
1
1
  import { a as defineHandler, l as getRouterParam } from "./_libs/h3+rou3+srvx.mjs";
2
- import { t as terminalManager } from "./_chunks/terminal.mjs";
2
+ import { t as deviceBus } from "./_chunks/device-bus.mjs";
3
+ import { t as sandboxManager } from "./_chunks/sandbox.mjs";
3
4
  var _id__delete_default = defineHandler((event) => {
4
5
  const id = getRouterParam(event, "id");
5
- terminalManager.kill(id);
6
+ if (!id) return { error: "missing id" };
7
+ if (!sandboxManager.destroy(id)) return { error: "sandbox not found" };
8
+ deviceBus.emitDeviceEvent("sandbox");
6
9
  return { ok: true };
7
10
  });
8
11
  export { _id__delete_default as default };
@@ -1,6 +1,7 @@
1
1
  import { t as createLogger } from "./_chunks/logger.mjs";
2
2
  import { E as updateTask } from "./_chunks/db.mjs";
3
3
  import { a as defineHandler, l as getRouterParam } from "./_libs/h3+rou3+srvx.mjs";
4
+ import { t as deviceBus } from "./_chunks/device-bus.mjs";
4
5
  var logger = createLogger("api:tasks");
5
6
  var _id__patch_default = defineHandler(async (event) => {
6
7
  const id = decodeURIComponent(getRouterParam(event, "id"));
@@ -15,6 +16,7 @@ var _id__patch_default = defineHandler(async (event) => {
15
16
  const fields = {};
16
17
  for (const key of allowed) if (key in body) fields[key] = body[key];
17
18
  await updateTask(id, fields);
19
+ deviceBus.emitDeviceEvent("tasks");
18
20
  logger.info({
19
21
  taskId: id,
20
22
  fields
@@ -0,0 +1,8 @@
1
+ import { a as defineHandler, l as getRouterParam } from "./_libs/h3+rou3+srvx.mjs";
2
+ import { t as terminalManager } from "./_chunks/terminal.mjs";
3
+ var _id__delete_default = defineHandler((event) => {
4
+ const id = getRouterParam(event, "id");
5
+ terminalManager.kill(id);
6
+ return { ok: true };
7
+ });
8
+ export { _id__delete_default as default };
@@ -1,10 +1,13 @@
1
- import "./_libs/@aws-sdk/client-bedrock-runtime+[...].mjs";
2
1
  import { a as defineHandler, l as getRouterParam } from "./_libs/h3+rou3+srvx.mjs";
3
- import "./_libs/@mariozechner/pi-agent-core+[...].mjs";
4
2
  import { t as server } from "./_chunks/server.mjs";
3
+ import "./_libs/@mariozechner/pi-agent-core+[...].mjs";
4
+ import { t as deviceBus } from "./_chunks/device-bus.mjs";
5
5
  var _jid__delete_default = defineHandler(async (event) => {
6
6
  const jid = decodeURIComponent(getRouterParam(event, "jid"));
7
7
  await server.cleanupChat(jid);
8
+ deviceBus.emitDeviceEvent("groups");
9
+ deviceBus.emitDeviceEvent("chats");
10
+ deviceBus.emitDeviceEvent("tasks");
8
11
  return { ok: true };
9
12
  });
10
13
  export { _jid__delete_default as default };
@@ -1,15 +1,18 @@
1
+ import { s as config } from "./_chunks/config.mjs";
1
2
  import { t as createLogger } from "./_chunks/logger.mjs";
2
- import "./_libs/@aws-sdk/client-bedrock-runtime+[...].mjs";
3
3
  import { a as defineHandler, l as getRouterParam, n as HTTPError } from "./_libs/h3+rou3+srvx.mjs";
4
- import "./_libs/@mariozechner/pi-agent-core+[...].mjs";
5
4
  import { t as server } from "./_chunks/server.mjs";
5
+ import "./_libs/@mariozechner/pi-agent-core+[...].mjs";
6
+ import { t as deviceBus } from "./_chunks/device-bus.mjs";
6
7
  var logger = createLogger("api:groups");
7
8
  var UPDATABLE_FIELDS = [
8
9
  "name",
9
10
  "trigger",
10
11
  "requiresTrigger",
11
12
  "model",
12
- "thinkingLevel"
13
+ "thinkingLevel",
14
+ "soul",
15
+ "sandbox"
13
16
  ];
14
17
  var VALID_THINKING_LEVELS = [
15
18
  "off",
@@ -27,6 +30,8 @@ var _jid__patch_default = defineHandler(async (event) => {
27
30
  for (const key of Object.keys(body)) if (!UPDATABLE_FIELDS.includes(key)) throw new HTTPError(`Unknown field: ${key}`, { status: 400 });
28
31
  const changes = [];
29
32
  const oldModel = group.model;
33
+ const oldSoul = group.soul;
34
+ const oldSandbox = group.sandbox;
30
35
  if ("name" in body) {
31
36
  const val = body.name;
32
37
  if (typeof val !== "string" || val.length < 1 || val.length > 64) throw new HTTPError("name must be a string (1-64 chars)", { status: 400 });
@@ -65,6 +70,18 @@ var _jid__patch_default = defineHandler(async (event) => {
65
70
  }
66
71
  group.thinkingLevel = val === null || val === "off" ? void 0 : val;
67
72
  }
73
+ if ("soul" in body) {
74
+ const val = body.soul;
75
+ if (val !== null && val !== void 0 && typeof val !== "string") throw new HTTPError("soul must be a string or null", { status: 400 });
76
+ if (typeof val === "string" && val.length > 1e3) throw new HTTPError("soul must be at most 1000 characters", { status: 400 });
77
+ group.soul = val ? String(val) : void 0;
78
+ }
79
+ if ("sandbox" in body) {
80
+ if (config.get("sandbox") === "enforced") throw new HTTPError("sandbox is globally enforced and cannot be changed per-group", { status: 400 });
81
+ const val = body.sandbox;
82
+ if (typeof val !== "boolean" && val !== void 0 && val !== null) throw new HTTPError("sandbox must be a boolean or null", { status: 400 });
83
+ group.sandbox = val === true ? true : void 0;
84
+ }
68
85
  await server.setRegisteredGroup(jid, group);
69
86
  if (oldModel !== group.model) {
70
87
  server.pi.kill(jid);
@@ -86,6 +103,19 @@ var _jid__patch_default = defineHandler(async (event) => {
86
103
  newThinkingLevel: group.thinkingLevel
87
104
  }, "Thinking level changed, session killed");
88
105
  }
106
+ if (oldSoul !== group.soul) {
107
+ server.pi.kill(jid);
108
+ changes.push(group.soul ? "soul updated" : "soul cleared");
109
+ logger.info({ jid }, "Soul changed, session killed");
110
+ }
111
+ if (oldSandbox !== group.sandbox) {
112
+ server.pi.kill(jid);
113
+ changes.push(`sandbox → ${group.sandbox ? "on" : "off"}`);
114
+ logger.info({
115
+ jid,
116
+ sandbox: !!group.sandbox
117
+ }, "Sandbox changed, session killed");
118
+ }
89
119
  if (changes.length > 0) {
90
120
  await server.sendBotMessage(jid, `Config updated: ${changes.join(", ")}`);
91
121
  logger.info({
@@ -93,11 +123,14 @@ var _jid__patch_default = defineHandler(async (event) => {
93
123
  changes
94
124
  }, "Group config updated");
95
125
  }
126
+ deviceBus.emitDeviceEvent("groups");
127
+ const enforced = config.get("sandbox") === "enforced";
96
128
  return {
97
129
  ok: true,
98
130
  group: {
99
131
  jid,
100
- ...group
132
+ ...group,
133
+ ...enforced ? { sandbox: "enforced" } : {}
101
134
  }
102
135
  };
103
136
  });
@@ -1,10 +1,13 @@
1
- import "./_libs/@aws-sdk/client-bedrock-runtime+[...].mjs";
2
1
  import { a as defineHandler, l as getRouterParam } from "./_libs/h3+rou3+srvx.mjs";
3
- import "./_libs/@mariozechner/pi-agent-core+[...].mjs";
4
2
  import { t as server } from "./_chunks/server.mjs";
3
+ import "./_libs/@mariozechner/pi-agent-core+[...].mjs";
4
+ import { t as deviceBus } from "./_chunks/device-bus.mjs";
5
5
  var _jid__delete_default = defineHandler(async (event) => {
6
6
  const jid = decodeURIComponent(getRouterParam(event, "jid"));
7
7
  await server.cleanupChat(jid);
8
+ deviceBus.emitDeviceEvent("groups");
9
+ deviceBus.emitDeviceEvent("chats");
10
+ deviceBus.emitDeviceEvent("tasks");
8
11
  return { ok: true };
9
12
  });
10
13
  export { _jid__delete_default as default };