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.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/defult-D5RLDUrI.js +1 -0
- package/.output/public/assets/{dist-CMBqBOCp.js → dist-BH_oa-kv.js} +1 -1
- package/.output/public/assets/index-7JvURuHy.js +204 -0
- package/.output/public/assets/index-K43slwjJ.css +1 -0
- package/.output/public/index.html +11 -2
- package/.output/server/_...path_.get.mjs +16 -0
- package/.output/server/_chunks/app.mjs +261 -181
- package/.output/server/_chunks/browser.mjs +4 -1
- package/.output/server/_chunks/config.mjs +4 -0
- package/.output/server/_chunks/db.mjs +32 -28
- package/.output/server/_chunks/device-bus.mjs +123 -0
- package/.output/server/_chunks/dummy.mjs +1 -1
- package/.output/server/_chunks/logger.mjs +23 -0
- package/.output/server/_chunks/login.mjs +1 -1
- package/.output/server/_chunks/notes.mjs +1 -3
- package/.output/server/_chunks/renderer-template.mjs +1 -1
- package/.output/server/_chunks/sandbox.mjs +217 -0
- package/.output/server/_chunks/server.mjs +2302 -122
- package/.output/server/_chunks/terminal.mjs +63 -8
- package/.output/server/_chunks/uploads.mjs +60 -0
- package/.output/server/_chunks/virtual.mjs +192 -54
- package/.output/server/_id_.delete.mjs +5 -2
- package/.output/server/_id_.patch.mjs +2 -0
- package/.output/server/_id_2.delete.mjs +8 -0
- package/.output/server/_jid_.delete.mjs +5 -2
- package/.output/server/_jid_.patch.mjs +37 -4
- package/.output/server/_jid_2.delete.mjs +5 -2
- package/.output/server/_libs/@acemir/cssom+[...].mjs +2269 -1137
- package/.output/server/_libs/@google/genai.mjs +337 -273
- package/.output/server/_libs/@mariozechner/pi-agent-core+[...].mjs +381 -2073
- package/.output/server/_libs/@mariozechner/pi-coding-agent+[...].mjs +231 -131
- package/.output/server/_libs/_.mjs +3 -2
- package/.output/server/_libs/_10.mjs +2 -4
- package/.output/server/_libs/_11.mjs +2 -4
- package/.output/server/_libs/_12.mjs +2 -3
- package/.output/server/_libs/_13.mjs +2 -3
- package/.output/server/_libs/_14.mjs +2 -4
- package/.output/server/_libs/_15.mjs +2 -4
- package/.output/server/_libs/_16.mjs +2 -3
- package/.output/server/_libs/_17.mjs +2 -4
- package/.output/server/_libs/_18.mjs +2 -2
- package/.output/server/_libs/_19.mjs +2 -2
- package/.output/server/_libs/_2.mjs +3 -3
- package/.output/server/_libs/_20.mjs +2 -2
- package/.output/server/_libs/_21.mjs +2 -2
- package/.output/server/_libs/_22.mjs +2 -2
- package/.output/server/_libs/_23.mjs +2 -2
- package/.output/server/_libs/_24.mjs +2 -2
- package/.output/server/_libs/_25.mjs +2 -2
- package/.output/server/_libs/_26.mjs +2 -2
- package/.output/server/_libs/_27.mjs +2 -2
- package/.output/server/_libs/_28.mjs +2 -2
- package/.output/server/_libs/_29.mjs +2 -2
- package/.output/server/_libs/_3.mjs +3 -3
- package/.output/server/_libs/_30.mjs +2 -2
- package/.output/server/_libs/_31.mjs +2 -2
- package/.output/server/_libs/_32.mjs +2 -2
- package/.output/server/_libs/_33.mjs +2 -2
- package/.output/server/_libs/_34.mjs +2 -2
- package/.output/server/_libs/_35.mjs +2 -2
- package/.output/server/_libs/_36.mjs +2 -2
- package/.output/server/_libs/_37.mjs +2 -2
- package/.output/server/_libs/_38.mjs +2 -2
- package/.output/server/_libs/_39.mjs +2 -2
- package/.output/server/_libs/_4.mjs +4 -3
- package/.output/server/_libs/_40.mjs +2 -2
- package/.output/server/_libs/_41.mjs +2 -2
- package/.output/server/_libs/_42.mjs +2 -2
- package/.output/server/_libs/_43.mjs +2 -2
- package/.output/server/_libs/_44.mjs +2 -2
- package/.output/server/_libs/_45.mjs +2 -2
- package/.output/server/_libs/_46.mjs +2 -2
- package/.output/server/_libs/_47.mjs +2 -2
- package/.output/server/_libs/_48.mjs +2 -2
- package/.output/server/_libs/_49.mjs +2 -2
- package/.output/server/_libs/_5.mjs +2 -3
- package/.output/server/_libs/_50.mjs +2 -2
- package/.output/server/_libs/_51.mjs +2 -2
- package/.output/server/_libs/_52.mjs +2 -2
- package/.output/server/_libs/_53.mjs +2 -2
- package/.output/server/_libs/_54.mjs +2 -2
- package/.output/server/_libs/_55.mjs +2 -2
- package/.output/server/_libs/_56.mjs +2 -2
- package/.output/server/_libs/_57.mjs +2 -2
- package/.output/server/_libs/_58.mjs +2 -2
- package/.output/server/_libs/_59.mjs +2 -2
- package/.output/server/_libs/_6.mjs +2 -3
- package/.output/server/_libs/_60.mjs +2 -2
- package/.output/server/_libs/_61.mjs +2 -2
- package/.output/server/_libs/_62.mjs +2 -2
- package/.output/server/_libs/_63.mjs +2 -2
- package/.output/server/_libs/_64.mjs +2 -2
- package/.output/server/_libs/_65.mjs +2 -2
- package/.output/server/_libs/_66.mjs +2 -2
- package/.output/server/_libs/_67.mjs +2 -2
- package/.output/server/_libs/_68.mjs +2 -2
- package/.output/server/_libs/_69.mjs +2 -2
- package/.output/server/_libs/_7.mjs +2 -5
- package/.output/server/_libs/_70.mjs +2 -2
- package/.output/server/_libs/_71.mjs +2 -2
- package/.output/server/_libs/_72.mjs +2 -2
- package/.output/server/_libs/_73.mjs +2 -2
- package/.output/server/_libs/_74.mjs +2 -2
- package/.output/server/_libs/_75.mjs +2 -2
- package/.output/server/_libs/_76.mjs +2 -2
- package/.output/server/_libs/_77.mjs +2 -2
- package/.output/server/_libs/_78.mjs +2 -2
- package/.output/server/_libs/_79.mjs +2 -2
- package/.output/server/_libs/_8.mjs +2 -3
- package/.output/server/_libs/_80.mjs +2 -2
- package/.output/server/_libs/_81.mjs +2 -2
- package/.output/server/_libs/_82.mjs +2 -2
- package/.output/server/_libs/_83.mjs +2 -2
- package/.output/server/_libs/_84.mjs +2 -2
- package/.output/server/_libs/_85.mjs +2 -2
- package/.output/server/_libs/_86.mjs +2 -2
- package/.output/server/_libs/_87.mjs +2 -2
- package/.output/server/_libs/_88.mjs +2 -2
- package/.output/server/_libs/_89.mjs +2 -2
- package/.output/server/_libs/_9.mjs +2 -4
- package/.output/server/_libs/_90.mjs +5 -2
- package/.output/server/_libs/_91.mjs +3 -2
- package/.output/server/_libs/_92.mjs +2 -2
- package/.output/server/_libs/_93.mjs +2 -2
- package/.output/server/_libs/_94.mjs +2 -2
- package/.output/server/_libs/agent-base.mjs +1 -1
- package/.output/server/_libs/cheerio+[...].mjs +1 -1
- package/.output/server/_libs/data-uri-to-buffer.mjs +2 -67
- package/.output/server/_libs/data-urls+[...].mjs +1 -1
- package/.output/server/_libs/diff.mjs +1 -1
- package/.output/server/_libs/exodus__bytes.mjs +99 -81
- package/.output/server/_libs/fetch-blob+node-domexception.mjs +1 -1
- package/.output/server/_libs/h3+rou3+srvx.mjs +34 -4
- package/.output/server/_libs/html-encoding-sniffer.mjs +1 -1
- package/.output/server/_libs/https-proxy-agent.mjs +2 -2
- package/.output/server/_libs/jsdom.mjs +1 -1
- package/.output/server/_libs/just-bash+[...].mjs +4676 -3916
- package/.output/server/_libs/mariozechner__jiti.mjs +1 -1
- package/.output/server/_libs/mariozechner__pi-ai.mjs +1472 -0
- package/.output/server/_libs/md4x.mjs +1 -1
- package/.output/server/_libs/mime.mjs +838 -1
- package/.output/server/_libs/node-fetch.mjs +4 -4
- package/.output/server/_libs/node-liblzma.mjs +1 -1
- package/.output/server/_libs/silvia-odwyer__photon-node.mjs +1 -1
- package/.output/server/_routes/api/auth/approve.mjs +2 -0
- package/.output/server/_routes/api/auth/revoke.mjs +2 -0
- package/.output/server/_routes/api/auth/status.mjs +25 -6
- package/.output/server/_routes/api/browser2.mjs +1 -1
- package/.output/server/_routes/api/config2.mjs +2 -0
- package/.output/server/_routes/api/device_events.mjs +36 -0
- package/.output/server/_routes/api/files/groups.mjs +1 -2
- package/.output/server/_routes/api/files/raw.mjs +1 -1
- package/.output/server/_routes/api/groups.mjs +5 -3
- package/.output/server/_routes/api/groups2.mjs +18 -6
- package/.output/server/_routes/api/health.mjs +1 -2
- package/.output/server/_routes/api/messages.mjs +7 -1
- package/.output/server/_routes/api/notes/delete.mjs +4 -1
- package/.output/server/_routes/api/notes/write.mjs +2 -0
- package/.output/server/_routes/api/ntfy/setup.mjs +8 -0
- package/.output/server/_routes/api/pi/apikey.mjs +3 -2
- package/.output/server/_routes/api/pi/apikey_providers.mjs +1 -2
- package/.output/server/_routes/api/pi/commands.mjs +13 -3
- package/.output/server/_routes/api/pi/login/events.mjs +0 -1
- package/.output/server/_routes/api/pi/login/respond.mjs +2 -1
- package/.output/server/_routes/api/pi/login.mjs +1 -2
- package/.output/server/_routes/api/pi/logout.mjs +2 -1
- package/.output/server/_routes/api/pi/models.mjs +1 -2
- package/.output/server/_routes/api/pi/models_config2.mjs +2 -0
- package/.output/server/_routes/api/pi/settings2.mjs +2 -0
- package/.output/server/_routes/api/pi/status.mjs +1 -2
- package/.output/server/_routes/api/proxy.mjs +19 -1
- package/.output/server/_routes/api/sandbox.mjs +26 -0
- package/.output/server/_routes/api/sandbox2.mjs +17 -0
- package/.output/server/_routes/api/send.mjs +26 -18
- package/.output/server/_routes/api/status.mjs +1 -3
- package/.output/server/_routes/api/stop.mjs +11 -0
- package/.output/server/_routes/api/store/plugins.mjs +75 -0
- package/.output/server/_routes/api/store/skills.mjs +11 -0
- package/.output/server/_routes/api/tasks2.mjs +3 -2
- package/.output/server/_routes/api/telegram/setup.mjs +5 -2
- package/.output/server/_routes/api/telegram/status.mjs +1 -2
- package/.output/server/_routes/api/terminal2.mjs +2 -1
- package/.output/server/_routes/api/tunnel/setup.mjs +4 -2
- package/.output/server/_runtime.mjs +1 -2
- package/.output/server/_utils.mjs +10 -2
- package/.output/server/index.mjs +1 -1
- package/.output/server/node_modules/amdefine/amdefine.js +301 -0
- package/.output/server/node_modules/amdefine/package.json +16 -0
- package/.output/server/node_modules/compressjs/lib/BWT.js +420 -0
- package/.output/server/node_modules/compressjs/lib/BWTC.js +234 -0
- package/.output/server/node_modules/compressjs/lib/BitStream.js +108 -0
- package/.output/server/node_modules/compressjs/lib/Bzip2.js +936 -0
- package/.output/server/node_modules/compressjs/lib/CRC32.js +105 -0
- package/.output/server/node_modules/compressjs/lib/Context1Model.js +56 -0
- package/.output/server/node_modules/compressjs/lib/DefSumModel.js +152 -0
- package/.output/server/node_modules/compressjs/lib/DeflateDistanceModel.js +55 -0
- package/.output/server/node_modules/compressjs/lib/Dmc.js +197 -0
- package/.output/server/node_modules/compressjs/lib/DummyRangeCoder.js +81 -0
- package/.output/server/node_modules/compressjs/lib/FenwickModel.js +194 -0
- package/.output/server/node_modules/compressjs/lib/Huffman.js +514 -0
- package/.output/server/node_modules/compressjs/lib/HuffmanAllocator.js +227 -0
- package/.output/server/node_modules/compressjs/lib/LogDistanceModel.js +46 -0
- package/.output/server/node_modules/compressjs/lib/Lzjb.js +300 -0
- package/.output/server/node_modules/compressjs/lib/LzjbR.js +241 -0
- package/.output/server/node_modules/compressjs/lib/Lzp3.js +273 -0
- package/.output/server/node_modules/compressjs/lib/MTFModel.js +208 -0
- package/.output/server/node_modules/compressjs/lib/NoModel.js +46 -0
- package/.output/server/node_modules/compressjs/lib/PPM.js +343 -0
- package/.output/server/node_modules/compressjs/lib/RangeCoder.js +238 -0
- package/.output/server/node_modules/compressjs/lib/Simple.js +111 -0
- package/.output/server/node_modules/compressjs/lib/Stream.js +53 -0
- package/.output/server/node_modules/compressjs/lib/Util.js +324 -0
- package/.output/server/node_modules/compressjs/lib/freeze.js +14 -0
- package/.output/server/node_modules/compressjs/main.js +29 -0
- package/.output/server/node_modules/compressjs/package.json +35 -0
- package/.output/server/package.json +2 -1
- package/README.md +10 -1
- package/lib/index.d.mts +1 -0
- package/lib/index.mjs +1 -0
- package/lib/piclaw.mjs +100 -0
- package/lib/utils.mjs +96 -0
- package/package.json +16 -11
- package/.output/public/assets/defult-CMO6TZ5a.js +0 -1
- package/.output/public/assets/index-jdnbJw-M.js +0 -204
- package/.output/public/assets/index-ooXrRwgl.css +0 -1
- package/.output/server/_chunks/commands.mjs +0 -282
- package/.output/server/_chunks/pi.mjs +0 -202
- package/.output/server/_chunks/session.mjs +0 -1114
- package/.output/server/_libs/@aws-crypto/crc32+[...].mjs +0 -299
- package/.output/server/_libs/@aws-sdk/client-bedrock-runtime+[...].mjs +0 -17828
- package/.output/server/_libs/@aws-sdk/credential-provider-http+[...].mjs +0 -122
- package/.output/server/_libs/@aws-sdk/credential-provider-ini+[...].mjs +0 -417
- package/.output/server/_libs/@aws-sdk/credential-provider-process+[...].mjs +0 -54
- package/.output/server/_libs/@aws-sdk/credential-provider-sso+[...].mjs +0 -1151
- package/.output/server/_libs/@aws-sdk/credential-provider-web-identity+[...].mjs +0 -50
- package/.output/server/_libs/@smithy/credential-provider-imds+[...].mjs +0 -369
- package/.output/server/_libs/@tootallnate/quickjs-emscripten+[...].mjs +0 -3011
- package/.output/server/_libs/_100.mjs +0 -2
- package/.output/server/_libs/_101.mjs +0 -2
- package/.output/server/_libs/_102.mjs +0 -5
- package/.output/server/_libs/_103.mjs +0 -3
- package/.output/server/_libs/_104.mjs +0 -2
- package/.output/server/_libs/_105.mjs +0 -3
- package/.output/server/_libs/_106.mjs +0 -2
- package/.output/server/_libs/_107.mjs +0 -2
- package/.output/server/_libs/_95.mjs +0 -2
- package/.output/server/_libs/_96.mjs +0 -2
- package/.output/server/_libs/_97.mjs +0 -2
- package/.output/server/_libs/_98.mjs +0 -2
- package/.output/server/_libs/_99.mjs +0 -2
- package/.output/server/_libs/amdefine.mjs +0 -188
- package/.output/server/_libs/ast-types.mjs +0 -2270
- package/.output/server/_libs/aws-sdk__nested-clients.mjs +0 -3141
- package/.output/server/_libs/basic-ftp.mjs +0 -1906
- package/.output/server/_libs/compressjs.mjs +0 -50
- package/.output/server/_libs/degenerator+[...].mjs +0 -9964
- package/.output/server/_libs/get-uri.mjs +0 -413
- package/.output/server/_libs/http-proxy-agent.mjs +0 -123
- package/.output/server/_libs/ip-address.mjs +0 -1423
- package/.output/server/_libs/lru-cache.mjs +0 -732
- package/.output/server/_libs/netmask.mjs +0 -139
- package/.output/server/_libs/pac-proxy-agent+[...].mjs +0 -3104
- package/.output/server/_libs/proxy-agent+proxy-from-env.mjs +0 -204
- package/.output/server/_libs/smithy__core.mjs +0 -192
- package/.output/server/node_modules/tslib/modules/index.js +0 -70
- package/.output/server/node_modules/tslib/modules/package.json +0 -3
- package/.output/server/node_modules/tslib/package.json +0 -47
- package/.output/server/node_modules/tslib/tslib.js +0 -484
- package/bin/piclaw.mjs +0 -195
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { t as deviceBus } from "./device-bus.mjs";
|
|
2
|
+
/** In-memory browser tab state, synced with admin UI */
|
|
1
3
|
var state = {
|
|
2
4
|
tabs: [],
|
|
3
5
|
activeTabId: null,
|
|
@@ -7,11 +9,12 @@ var listeners = /* @__PURE__ */ new Set();
|
|
|
7
9
|
function getBrowserState() {
|
|
8
10
|
return state;
|
|
9
11
|
}
|
|
10
|
-
function setBrowserState(incoming) {
|
|
12
|
+
function setBrowserState(incoming, opts) {
|
|
11
13
|
state.tabs = incoming.tabs;
|
|
12
14
|
state.activeTabId = incoming.activeTabId;
|
|
13
15
|
state.rev++;
|
|
14
16
|
for (const fn of listeners) fn(state);
|
|
17
|
+
if (!opts?.silent && state.tabs.length > 0) deviceBus.emitDeviceEvent("browser", { state: { ...state } });
|
|
15
18
|
}
|
|
16
19
|
function subscribeBrowser(fn) {
|
|
17
20
|
listeners.add(fn);
|
|
@@ -20,7 +20,8 @@ async function openDatabase(dbPath) {
|
|
|
20
20
|
return openSqlite3Database(dbPath);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
var SCHEMA_VERSION =
|
|
23
|
+
var SCHEMA_VERSION = 4;
|
|
24
|
+
var MINIMUM_VERSION = 4;
|
|
24
25
|
var _db;
|
|
25
26
|
function useDb() {
|
|
26
27
|
if (!_db) throw new Error("Database not initialized — call initDatabase() first");
|
|
@@ -33,7 +34,8 @@ async function createSchema(database) {
|
|
|
33
34
|
name TEXT,
|
|
34
35
|
last_message_time TEXT,
|
|
35
36
|
channel TEXT,
|
|
36
|
-
is_group INTEGER DEFAULT 0
|
|
37
|
+
is_group INTEGER DEFAULT 0,
|
|
38
|
+
downloads TEXT
|
|
37
39
|
);
|
|
38
40
|
CREATE TABLE IF NOT EXISTS messages (
|
|
39
41
|
id TEXT,
|
|
@@ -44,6 +46,7 @@ async function createSchema(database) {
|
|
|
44
46
|
timestamp TEXT,
|
|
45
47
|
is_from_me INTEGER,
|
|
46
48
|
is_bot_message INTEGER DEFAULT 0,
|
|
49
|
+
meta TEXT,
|
|
47
50
|
PRIMARY KEY (id, chat_jid),
|
|
48
51
|
FOREIGN KEY (chat_jid) REFERENCES chats(jid)
|
|
49
52
|
);
|
|
@@ -115,28 +118,18 @@ async function initDatabase() {
|
|
|
115
118
|
const versionFile = path.join(STORE_DIR, "db.version");
|
|
116
119
|
const dbExists = fs.existsSync(dbPath);
|
|
117
120
|
const existing = dbExists && fs.existsSync(versionFile) ? Number.parseInt(fs.readFileSync(versionFile, "utf-8").trim(), 10) : 0;
|
|
118
|
-
if (dbExists && existing
|
|
121
|
+
if (dbExists && existing < MINIMUM_VERSION) {
|
|
122
|
+
logger.warn(`Database v${existing} is below minimum v${MINIMUM_VERSION}, recreating`);
|
|
123
|
+
fs.unlinkSync(dbPath);
|
|
124
|
+
}
|
|
125
|
+
const needsCreate = !fs.existsSync(dbPath);
|
|
119
126
|
const db = await openDatabase(dbPath);
|
|
120
127
|
await db.exec("PRAGMA journal_mode = WAL");
|
|
121
128
|
await db.exec("PRAGMA busy_timeout = 5000");
|
|
122
|
-
if (
|
|
129
|
+
if (needsCreate) {
|
|
123
130
|
await createSchema(db);
|
|
124
131
|
fs.writeFileSync(versionFile, String(SCHEMA_VERSION));
|
|
125
|
-
} else if (existing
|
|
126
|
-
await db.exec(`
|
|
127
|
-
CREATE TABLE IF NOT EXISTS devices (
|
|
128
|
-
id TEXT PRIMARY KEY,
|
|
129
|
-
name TEXT NOT NULL,
|
|
130
|
-
status TEXT NOT NULL,
|
|
131
|
-
created_at TEXT NOT NULL,
|
|
132
|
-
approved_at TEXT,
|
|
133
|
-
last_seen TEXT NOT NULL,
|
|
134
|
-
ip TEXT
|
|
135
|
-
);
|
|
136
|
-
`);
|
|
137
|
-
fs.writeFileSync(versionFile, String(SCHEMA_VERSION));
|
|
138
|
-
logger.info("Migrated database v2 → v3 (added devices table)");
|
|
139
|
-
}
|
|
132
|
+
} else if (existing < SCHEMA_VERSION) fs.writeFileSync(versionFile, String(SCHEMA_VERSION));
|
|
140
133
|
_db = db;
|
|
141
134
|
await _setDbProvider({
|
|
142
135
|
getConfig,
|
|
@@ -147,9 +140,18 @@ async function initDatabase() {
|
|
|
147
140
|
}
|
|
148
141
|
async function storeMessage(msg) {
|
|
149
142
|
await useDb().prepare(`
|
|
150
|
-
INSERT OR REPLACE INTO messages (id, chat_jid, sender, sender_name, content, timestamp, is_from_me, is_bot_message)
|
|
151
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
152
|
-
`).run(msg.id, msg.chat_jid, msg.sender, msg.sender_name, msg.content, msg.timestamp, msg.is_from_me ? 1 : 0, msg.is_bot_message ? 1 : 0);
|
|
143
|
+
INSERT OR REPLACE INTO messages (id, chat_jid, sender, sender_name, content, timestamp, is_from_me, is_bot_message, meta)
|
|
144
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
145
|
+
`).run(msg.id, msg.chat_jid, msg.sender, msg.sender_name, msg.content, msg.timestamp, msg.is_from_me ? 1 : 0, msg.is_bot_message ? 1 : 0, msg.meta ? JSON.stringify(msg.meta) : null);
|
|
146
|
+
}
|
|
147
|
+
/** Parse meta JSON from DB rows */
|
|
148
|
+
function hydrateMeta(rows) {
|
|
149
|
+
for (const row of rows) if (typeof row.meta === "string") try {
|
|
150
|
+
row.meta = JSON.parse(row.meta);
|
|
151
|
+
} catch {
|
|
152
|
+
row.meta = void 0;
|
|
153
|
+
}
|
|
154
|
+
return rows;
|
|
153
155
|
}
|
|
154
156
|
async function getNewMessages(jids, sinceTimestamp, assistantName) {
|
|
155
157
|
if (jids.length === 0) return {
|
|
@@ -163,27 +165,29 @@ async function getNewMessages(jids, sinceTimestamp, assistantName) {
|
|
|
163
165
|
AND is_bot_message = 0
|
|
164
166
|
AND sender_name != ?
|
|
165
167
|
ORDER BY timestamp ASC`).all(...jids, sinceTimestamp, assistantName);
|
|
168
|
+
hydrateMeta(rows);
|
|
166
169
|
return {
|
|
167
170
|
messages: rows,
|
|
168
171
|
newTimestamp: rows.length > 0 ? rows[rows.length - 1].timestamp : sinceTimestamp
|
|
169
172
|
};
|
|
170
173
|
}
|
|
171
174
|
async function getMessagesSince(chatJid, sinceTimestamp, assistantName) {
|
|
172
|
-
return await useDb().prepare(`SELECT * FROM messages
|
|
175
|
+
return hydrateMeta(await useDb().prepare(`SELECT * FROM messages
|
|
173
176
|
WHERE chat_jid = ?
|
|
174
177
|
AND timestamp > ?
|
|
175
178
|
AND is_bot_message = 0
|
|
176
179
|
AND sender_name != ?
|
|
177
|
-
ORDER BY timestamp ASC`).all(chatJid, sinceTimestamp, assistantName);
|
|
180
|
+
ORDER BY timestamp ASC`).all(chatJid, sinceTimestamp, assistantName));
|
|
178
181
|
}
|
|
179
|
-
async function storeChatMetadata(jid, timestamp, name, channel, isGroup) {
|
|
180
|
-
await useDb().prepare(`INSERT INTO chats (jid, name, last_message_time, channel, is_group)
|
|
181
|
-
VALUES (?, ?, ?, ?, ?)
|
|
182
|
+
async function storeChatMetadata(jid, timestamp, name, channel, isGroup, downloads) {
|
|
183
|
+
await useDb().prepare(`INSERT INTO chats (jid, name, last_message_time, channel, is_group, downloads)
|
|
184
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
182
185
|
ON CONFLICT(jid) DO UPDATE SET
|
|
183
186
|
name = COALESCE(?, name),
|
|
184
187
|
last_message_time = ?,
|
|
185
188
|
channel = COALESCE(?, channel),
|
|
186
|
-
is_group = COALESCE(?, is_group)
|
|
189
|
+
is_group = COALESCE(?, is_group),
|
|
190
|
+
downloads = COALESCE(?, downloads)`).run(jid, name ?? null, timestamp, channel ?? null, isGroup ? 1 : 0, downloads ?? null, name ?? null, timestamp, channel ?? null, isGroup != null ? isGroup ? 1 : 0 : null, downloads ?? null);
|
|
187
191
|
}
|
|
188
192
|
async function getAllChats() {
|
|
189
193
|
return await useDb().prepare("SELECT * FROM chats ORDER BY last_message_time DESC").all();
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { a as getDevice } from "./auth.mjs";
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
/**
|
|
4
|
+
* DeviceBus — in-memory device presence tracking with event broadcasting.
|
|
5
|
+
*
|
|
6
|
+
* Tracks which devices have active SSE connections and broadcasts
|
|
7
|
+
* presence events (connect/disconnect) and custom events to all listeners.
|
|
8
|
+
*/
|
|
9
|
+
var DeviceBus = class {
|
|
10
|
+
connectionsByDevice = /* @__PURE__ */ new Map();
|
|
11
|
+
connectionIndex = /* @__PURE__ */ new Map();
|
|
12
|
+
deviceNames = /* @__PURE__ */ new Map();
|
|
13
|
+
listeners = /* @__PURE__ */ new Set();
|
|
14
|
+
async connect(deviceId, meta) {
|
|
15
|
+
const conn = {
|
|
16
|
+
id: crypto.randomUUID(),
|
|
17
|
+
deviceId,
|
|
18
|
+
connectedAt: Date.now(),
|
|
19
|
+
meta
|
|
20
|
+
};
|
|
21
|
+
if (!this.deviceNames.has(deviceId)) {
|
|
22
|
+
const device = await getDevice(deviceId);
|
|
23
|
+
if (device) this.deviceNames.set(deviceId, device.name);
|
|
24
|
+
}
|
|
25
|
+
const isFirstConnection = !this.connectionsByDevice.has(deviceId) || this.connectionsByDevice.get(deviceId).size === 0;
|
|
26
|
+
let set = this.connectionsByDevice.get(deviceId);
|
|
27
|
+
if (!set) {
|
|
28
|
+
set = /* @__PURE__ */ new Set();
|
|
29
|
+
this.connectionsByDevice.set(deviceId, set);
|
|
30
|
+
}
|
|
31
|
+
set.add(conn);
|
|
32
|
+
this.connectionIndex.set(conn.id, conn);
|
|
33
|
+
const name = this.deviceNames.get(deviceId) || "Unknown";
|
|
34
|
+
if (isFirstConnection) this.emit({
|
|
35
|
+
type: "device_connected",
|
|
36
|
+
deviceId,
|
|
37
|
+
deviceName: name,
|
|
38
|
+
connections: set.size
|
|
39
|
+
});
|
|
40
|
+
this.emit({
|
|
41
|
+
type: "connection_added",
|
|
42
|
+
deviceId,
|
|
43
|
+
connectionId: conn.id,
|
|
44
|
+
connections: set.size
|
|
45
|
+
});
|
|
46
|
+
const unsubscribe = () => this.disconnect(conn.id);
|
|
47
|
+
return {
|
|
48
|
+
connectionId: conn.id,
|
|
49
|
+
unsubscribe
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
disconnect(connectionId) {
|
|
53
|
+
const conn = this.connectionIndex.get(connectionId);
|
|
54
|
+
if (!conn) return;
|
|
55
|
+
this.connectionIndex.delete(connectionId);
|
|
56
|
+
const set = this.connectionsByDevice.get(conn.deviceId);
|
|
57
|
+
if (set) {
|
|
58
|
+
set.delete(conn);
|
|
59
|
+
const remaining = set.size;
|
|
60
|
+
const name = this.deviceNames.get(conn.deviceId) || "Unknown";
|
|
61
|
+
this.emit({
|
|
62
|
+
type: "connection_removed",
|
|
63
|
+
deviceId: conn.deviceId,
|
|
64
|
+
connectionId,
|
|
65
|
+
connections: remaining
|
|
66
|
+
});
|
|
67
|
+
if (remaining === 0) {
|
|
68
|
+
this.connectionsByDevice.delete(conn.deviceId);
|
|
69
|
+
this.deviceNames.delete(conn.deviceId);
|
|
70
|
+
this.emit({
|
|
71
|
+
type: "device_disconnected",
|
|
72
|
+
deviceId: conn.deviceId,
|
|
73
|
+
deviceName: name
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Emit a device event to all connected listeners */
|
|
79
|
+
emitDeviceEvent(topic, data) {
|
|
80
|
+
this.emit({
|
|
81
|
+
type: "device_event",
|
|
82
|
+
topic,
|
|
83
|
+
data
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/** Emit a device event to a specific device's connections */
|
|
87
|
+
emitDeviceEventTo(deviceId, topic, data) {
|
|
88
|
+
this.emit({
|
|
89
|
+
type: "device_event",
|
|
90
|
+
deviceId,
|
|
91
|
+
topic,
|
|
92
|
+
data
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** Send a notification to all connected devices */
|
|
96
|
+
notify(notification) {
|
|
97
|
+
this.emitDeviceEvent("notification", notification);
|
|
98
|
+
}
|
|
99
|
+
/** Send a notification to a specific device */
|
|
100
|
+
notifyDevice(deviceId, notification) {
|
|
101
|
+
this.emitDeviceEventTo(deviceId, "notification", notification);
|
|
102
|
+
}
|
|
103
|
+
/** Get snapshot of all connected devices */
|
|
104
|
+
getConnectedDevices() {
|
|
105
|
+
const result = [];
|
|
106
|
+
for (const [deviceId, set] of this.connectionsByDevice) if (set.size > 0) result.push({
|
|
107
|
+
deviceId,
|
|
108
|
+
name: this.deviceNames.get(deviceId) || "Unknown",
|
|
109
|
+
connections: set.size
|
|
110
|
+
});
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
/** Subscribe to all presence events */
|
|
114
|
+
subscribe(listener) {
|
|
115
|
+
this.listeners.add(listener);
|
|
116
|
+
return () => this.listeners.delete(listener);
|
|
117
|
+
}
|
|
118
|
+
emit(event) {
|
|
119
|
+
for (const listener of this.listeners) listener(event);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const deviceBus = new DeviceBus();
|
|
123
|
+
export { deviceBus as t };
|
|
@@ -64,6 +64,7 @@ function log(level, tag, dataOrMsg, msg) {
|
|
|
64
64
|
if (currentLevel > LOG_LEVELS[level]) return;
|
|
65
65
|
const [d, m] = typeof dataOrMsg === "string" ? [void 0, dataOrMsg] : [dataOrMsg, msg];
|
|
66
66
|
levelMap[level].fn(fmt(level, tag, m, d));
|
|
67
|
+
if (LOG_LEVELS[level] >= LOG_LEVELS.warn) notifyLog(level, tag, m);
|
|
67
68
|
try {
|
|
68
69
|
const entry = JSON.stringify({
|
|
69
70
|
level: LOG_LEVELS[level],
|
|
@@ -102,4 +103,26 @@ function getSystemLogs(opts) {
|
|
|
102
103
|
const limit = opts?.limit ?? 100;
|
|
103
104
|
return entries.slice(offset, offset + limit);
|
|
104
105
|
}
|
|
106
|
+
var levelToNotificationType = {
|
|
107
|
+
warn: "warning",
|
|
108
|
+
error: "alert",
|
|
109
|
+
fatal: "alert"
|
|
110
|
+
};
|
|
111
|
+
var _deviceBus;
|
|
112
|
+
function notifyLog(level, tag, message) {
|
|
113
|
+
if (!_deviceBus) {
|
|
114
|
+
import("../_libs/_88.mjs").then((m) => {
|
|
115
|
+
_deviceBus = m.deviceBus;
|
|
116
|
+
_deviceBus.notify({
|
|
117
|
+
title: `[${tag}] ${message}`.slice(0, 120),
|
|
118
|
+
type: levelToNotificationType[level] ?? "warning"
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
_deviceBus.notify({
|
|
124
|
+
title: `[${tag}] ${message}`.slice(0, 120),
|
|
125
|
+
type: levelToNotificationType[level] ?? "warning"
|
|
126
|
+
});
|
|
127
|
+
}
|
|
105
128
|
export { getSystemLogs as n, createLogger as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as createLogger } from "./logger.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { c as authStorage, u as modelRegistry } from "./server.mjs";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
var logger = createLogger("pi:login");
|
|
5
5
|
var LoginSession = class LoginSession {
|
|
@@ -24,10 +24,8 @@ function writeNote(ref, content) {
|
|
|
24
24
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
25
25
|
fs.writeFileSync(filePath, content);
|
|
26
26
|
}
|
|
27
|
-
/** Delete a note. Returns true if it existed.
|
|
27
|
+
/** Delete a note. Returns true if it existed. */
|
|
28
28
|
function deleteNote(ref) {
|
|
29
|
-
const { category } = parseRef(ref);
|
|
30
|
-
if (category === VIRTUAL_CATEGORY) return false;
|
|
31
29
|
const filePath = resolveNotePath(ref);
|
|
32
30
|
if (!fs.existsSync(filePath)) return false;
|
|
33
31
|
fs.unlinkSync(filePath);
|
|
@@ -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=\"icon\" type=\"image/svg+xml\" href=\"/icon.svg\" />\n <link rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/icon-192.png\" />\n <link rel=\"apple-touch-icon\" href=\"/icon-192.png\" />\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=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/icon-192.png\" />\n <link rel=\"apple-touch-icon\" href=\"/icon-192.png\" />\n <link rel=\"manifest\" href=\"/manifest.json\" />\n </head>\n <body style=\"background-color: #0a0e14\">\n <div id=\"app\"></div>\n <script>\n // Electrobun app-window: persist appToken as cookie for auth bypass\n var t = new URLSearchParams(location.search).get(\"appToken\");\n if (t) {\n var prev = document.cookie.match(/piclaw_app_token=([^;]*)/)?.[1];\n document.cookie = \"piclaw_app_token=\" + t + \";path=/;SameSite=Lax\";\n if (prev && prev !== t) location.reload();\n }\n <\/script>\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
|
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { t as DATA_ROOT } from "./config.mjs";
|
|
2
|
+
import { t as createLogger } from "./logger.mjs";
|
|
3
|
+
import { n as Ie, r as ft, t as At } from "../_libs/just-bash+[...].mjs";
|
|
4
|
+
import { t as deviceBus } from "./device-bus.mjs";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
/**
|
|
8
|
+
* Centralized sandbox manager — tracks in-memory filesystem sandboxes.
|
|
9
|
+
*
|
|
10
|
+
* A `Sandbox` owns a single `InMemoryFs` or `OverlayFs` + `Bash` instance.
|
|
11
|
+
* Both PI SDK coding tools (`src/lib/pi/tools/sandbox.ts`) and virtual PTY
|
|
12
|
+
* terminals (`src/lib/pty/virtual.ts`) can share the same sandbox when they
|
|
13
|
+
* belong to the same group, so files created by the agent are visible in the
|
|
14
|
+
* terminal and vice-versa.
|
|
15
|
+
*
|
|
16
|
+
* When `realCwd` is provided, uses `OverlayFs` (copy-on-write over the real
|
|
17
|
+
* group directory) so the agent can read real files while writes stay in memory.
|
|
18
|
+
*
|
|
19
|
+
* State is persisted to `<DATA_ROOT>/sandboxes/<id>/state.json` on destroy
|
|
20
|
+
* and hydrated automatically when a sandbox with the same id is created.
|
|
21
|
+
*/
|
|
22
|
+
var logger = createLogger("sandbox");
|
|
23
|
+
var SANDBOXES_DIR = path.resolve(DATA_ROOT, "sandboxes");
|
|
24
|
+
function defaultCwd(id) {
|
|
25
|
+
return `/home/${id}`;
|
|
26
|
+
}
|
|
27
|
+
function defaultEnv(id) {
|
|
28
|
+
return {
|
|
29
|
+
HOME: `/home/${id}`,
|
|
30
|
+
PATH: "/usr/bin:/bin"
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
var SandboxManager = class {
|
|
34
|
+
sandboxes = /* @__PURE__ */ new Map();
|
|
35
|
+
/** Get or create a sandbox for the given id (typically group folder). */
|
|
36
|
+
get(id, opts) {
|
|
37
|
+
let sb = this.sandboxes.get(id);
|
|
38
|
+
if (!sb) {
|
|
39
|
+
sb = this._create(id, opts);
|
|
40
|
+
this.sandboxes.set(id, sb);
|
|
41
|
+
logger.info({
|
|
42
|
+
id,
|
|
43
|
+
cwd: sb.cwd
|
|
44
|
+
}, "Sandbox created");
|
|
45
|
+
deviceBus.emitDeviceEvent("sandbox", {
|
|
46
|
+
action: "created",
|
|
47
|
+
id
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return sb;
|
|
51
|
+
}
|
|
52
|
+
/** Check if a sandbox exists. */
|
|
53
|
+
has(id) {
|
|
54
|
+
return this.sandboxes.has(id);
|
|
55
|
+
}
|
|
56
|
+
/** Destroy a sandbox, persisting its state to disk. */
|
|
57
|
+
destroy(id) {
|
|
58
|
+
const sb = this.sandboxes.get(id);
|
|
59
|
+
if (!sb) return false;
|
|
60
|
+
this._persist(sb).catch((err) => {
|
|
61
|
+
logger.warn({
|
|
62
|
+
id,
|
|
63
|
+
err: err.message
|
|
64
|
+
}, "Failed to persist sandbox state");
|
|
65
|
+
});
|
|
66
|
+
this.sandboxes.delete(id);
|
|
67
|
+
logger.info({ id }, "Sandbox destroyed (state persisted)");
|
|
68
|
+
deviceBus.emitDeviceEvent("sandbox", {
|
|
69
|
+
action: "destroyed",
|
|
70
|
+
id
|
|
71
|
+
});
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
/** List all active sandbox ids. */
|
|
75
|
+
list() {
|
|
76
|
+
return [...this.sandboxes.keys()];
|
|
77
|
+
}
|
|
78
|
+
/** Get info for all active sandboxes. */
|
|
79
|
+
getAll() {
|
|
80
|
+
return [...this.sandboxes.values()].map((sb) => ({
|
|
81
|
+
id: sb.id,
|
|
82
|
+
cwd: sb.env.PWD || sb.cwd,
|
|
83
|
+
fileCount: sb.fs.getAllPaths().length,
|
|
84
|
+
overlay: sb.fs instanceof ft
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
/** Destroy all sandboxes (persists each). */
|
|
88
|
+
shutdown() {
|
|
89
|
+
for (const id of this.sandboxes.keys()) this.destroy(id);
|
|
90
|
+
}
|
|
91
|
+
_create(id, opts) {
|
|
92
|
+
const persisted = this._hydrate(id);
|
|
93
|
+
const cwd = persisted?.cwd ?? opts?.cwd ?? defaultCwd(id);
|
|
94
|
+
const initialFiles = persisted?.files ?? opts?.files;
|
|
95
|
+
const useOverlay = !!opts?.realCwd;
|
|
96
|
+
let memFs;
|
|
97
|
+
if (useOverlay) {
|
|
98
|
+
memFs = new ft({
|
|
99
|
+
root: opts.realCwd,
|
|
100
|
+
mountPoint: cwd
|
|
101
|
+
});
|
|
102
|
+
if (initialFiles) for (const [p, content] of Object.entries(initialFiles)) memFs.writeFileSync(p, content);
|
|
103
|
+
} else memFs = new Ie(initialFiles);
|
|
104
|
+
const env = persisted?.env ?? {
|
|
105
|
+
...defaultEnv(id),
|
|
106
|
+
PWD: cwd
|
|
107
|
+
};
|
|
108
|
+
if (persisted) logger.info({ id }, "Sandbox hydrated from persisted state");
|
|
109
|
+
if (useOverlay) logger.info({
|
|
110
|
+
id,
|
|
111
|
+
root: opts.realCwd,
|
|
112
|
+
mountPoint: cwd
|
|
113
|
+
}, "Sandbox using OverlayFs");
|
|
114
|
+
const bash = new At({
|
|
115
|
+
cwd: env.PWD || cwd,
|
|
116
|
+
fs: memFs
|
|
117
|
+
});
|
|
118
|
+
const realCwd = opts?.realCwd;
|
|
119
|
+
const sandbox = {
|
|
120
|
+
id,
|
|
121
|
+
fs: memFs,
|
|
122
|
+
cwd,
|
|
123
|
+
env,
|
|
124
|
+
realCwd,
|
|
125
|
+
async exec(command, execOpts) {
|
|
126
|
+
const result = await bash.exec(command, {
|
|
127
|
+
cwd: execOpts?.cwd || env.PWD || sandbox.cwd,
|
|
128
|
+
env
|
|
129
|
+
});
|
|
130
|
+
if (result.env.PWD) sandbox.cwd = result.env.PWD;
|
|
131
|
+
for (const [k, v] of Object.entries(result.env)) env[k] = v;
|
|
132
|
+
return {
|
|
133
|
+
stdout: result.stdout || "",
|
|
134
|
+
stderr: result.stderr || "",
|
|
135
|
+
exitCode: result.exitCode ?? 0
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
async writeBack() {
|
|
139
|
+
if (!realCwd || !(memFs instanceof ft)) return {
|
|
140
|
+
written: 0,
|
|
141
|
+
deleted: 0
|
|
142
|
+
};
|
|
143
|
+
const canonicalRoot = fs.realpathSync(realCwd);
|
|
144
|
+
const mountPoint = memFs.getMountPoint();
|
|
145
|
+
const overlayPaths = new Set(memFs.getAllPaths());
|
|
146
|
+
const safeRealPath = (virtualPath) => {
|
|
147
|
+
const rel = virtualPath.slice(mountPoint.length);
|
|
148
|
+
if (!rel && virtualPath !== mountPoint) return null;
|
|
149
|
+
const real = path.resolve(canonicalRoot, "." + (rel || "/"));
|
|
150
|
+
if (!real.startsWith(canonicalRoot + path.sep) && real !== canonicalRoot) return null;
|
|
151
|
+
return real;
|
|
152
|
+
};
|
|
153
|
+
let written = 0;
|
|
154
|
+
let deleted = 0;
|
|
155
|
+
for (const vPath of overlayPaths) {
|
|
156
|
+
if (!vPath.startsWith(mountPoint)) continue;
|
|
157
|
+
const realP = safeRealPath(vPath);
|
|
158
|
+
if (!realP) continue;
|
|
159
|
+
const stat = await memFs.lstat(vPath);
|
|
160
|
+
if (stat.isFile) {
|
|
161
|
+
const content = await memFs.readFile(vPath);
|
|
162
|
+
fs.mkdirSync(path.dirname(realP), { recursive: true });
|
|
163
|
+
fs.writeFileSync(realP, content);
|
|
164
|
+
written++;
|
|
165
|
+
} else if (stat.isDirectory) fs.mkdirSync(realP, { recursive: true });
|
|
166
|
+
}
|
|
167
|
+
logger.info({
|
|
168
|
+
id,
|
|
169
|
+
written
|
|
170
|
+
}, "Sandbox writeBack complete");
|
|
171
|
+
return {
|
|
172
|
+
written,
|
|
173
|
+
deleted
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
return sandbox;
|
|
178
|
+
}
|
|
179
|
+
/** Serialize sandbox state to disk. */
|
|
180
|
+
async _persist(sb) {
|
|
181
|
+
try {
|
|
182
|
+
const files = {};
|
|
183
|
+
const allPaths = sb.fs.getAllPaths();
|
|
184
|
+
for (const p of allPaths) if ((await sb.fs.lstat(p)).isFile) files[p] = await sb.fs.readFile(p);
|
|
185
|
+
const state = {
|
|
186
|
+
cwd: sb.env.PWD || sb.cwd,
|
|
187
|
+
env: sb.env,
|
|
188
|
+
files
|
|
189
|
+
};
|
|
190
|
+
const dir = path.join(SANDBOXES_DIR, sb.id);
|
|
191
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
192
|
+
fs.writeFileSync(path.join(dir, "state.json"), JSON.stringify(state));
|
|
193
|
+
} catch (err) {
|
|
194
|
+
logger.warn({
|
|
195
|
+
id: sb.id,
|
|
196
|
+
err: err.message
|
|
197
|
+
}, "Failed to persist sandbox state");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/** Try to load persisted state from disk. */
|
|
201
|
+
_hydrate(id) {
|
|
202
|
+
try {
|
|
203
|
+
const stateFile = path.join(SANDBOXES_DIR, id, "state.json");
|
|
204
|
+
if (!fs.existsSync(stateFile)) return void 0;
|
|
205
|
+
const raw = fs.readFileSync(stateFile, "utf-8");
|
|
206
|
+
return JSON.parse(raw);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
logger.warn({
|
|
209
|
+
id,
|
|
210
|
+
err: err.message
|
|
211
|
+
}, "Failed to hydrate sandbox state");
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
const sandboxManager = new SandboxManager();
|
|
217
|
+
export { sandboxManager as t };
|