@songsid/agend 2.2.0-beta.6 → 2.2.0-beta.8
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/dist/agent-cli-instructions.md +6 -2
- package/dist/agent-cli.js +9 -5
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-whats-new.js +9 -3
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/backend/kiro.d.ts +9 -0
- package/dist/backend/kiro.js +23 -0
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/muse.d.ts +26 -0
- package/dist/backend/muse.js +131 -0
- package/dist/backend/muse.js.map +1 -1
- package/dist/channel/adapters/discord.js +12 -1
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.js +6 -0
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-tools.js +3 -3
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/types.d.ts +3 -0
- package/dist/config-validator.js +3 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/cross-instance-envelope.js +2 -0
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +8 -0
- package/dist/daemon.js +126 -54
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +27 -0
- package/dist/delivery-outbox.js +175 -6
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/fleet-manager.d.ts +40 -1
- package/dist/fleet-manager.js +248 -40
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +16 -1
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +4 -0
- package/dist/human-delivery-receipts.d.ts +83 -0
- package/dist/human-delivery-receipts.js +194 -0
- package/dist/human-delivery-receipts.js.map +1 -0
- package/dist/instance-config-impact.js +1 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.js +1 -1
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +8 -0
- package/dist/locale.js.map +1 -1
- package/dist/log-service.d.ts +99 -0
- package/dist/log-service.js +235 -0
- package/dist/log-service.js.map +1 -0
- package/dist/log-steps.d.ts +143 -0
- package/dist/log-steps.js +646 -0
- package/dist/log-steps.js.map +1 -0
- package/dist/outbound-handlers.d.ts +3 -0
- package/dist/outbound-handlers.js +20 -4
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +15 -13
- package/dist/outbound-schemas.js +5 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-link-progress.d.ts +1 -1
- package/dist/public-link-progress.js +8 -2
- package/dist/public-link-progress.js.map +1 -1
- package/dist/public-preview.d.ts +2 -1
- package/dist/public-preview.js +8 -5
- package/dist/public-preview.js.map +1 -1
- package/dist/public-web-gateway.js +1 -1
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +4 -1
- package/dist/public-web-link.js +42 -13
- package/dist/public-web-link.js.map +1 -1
- package/dist/reply-buttons-controller.d.ts +3 -0
- package/dist/reply-buttons-controller.js +3 -2
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/settings-api.js +4 -0
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +2 -0
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +2 -0
- package/dist/settings-effect.js.map +1 -1
- package/dist/tool-progress.js +3 -2
- package/dist/tool-progress.js.map +1 -1
- package/dist/topic-commands.d.ts +3 -2
- package/dist/topic-commands.js +14 -4
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-jsonl.d.ts +80 -3
- package/dist/transcript-jsonl.js +147 -10
- package/dist/transcript-jsonl.js.map +1 -1
- package/dist/transcript-monitor.d.ts +11 -0
- package/dist/transcript-monitor.js +61 -13
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +24 -0
- package/dist/transcript-sources.js +205 -55
- package/dist/transcript-sources.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +7 -15
- package/dist/tunnel/cloudflared.js +36 -1
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/types.d.ts +5 -1
- package/dist/tunnel/types.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/ui/chat-strings.js +8 -2
- package/dist/ui/chat-thread.js +24 -4
- package/dist/ui/chat-time.js +80 -0
- package/dist/ui/panel-log.js +388 -0
- package/dist/ui/panel-settings.js +6 -1
- package/dist/ui/preview-panel.js +7 -1
- package/dist/ui/preview.js +5 -1
- package/dist/ui/settings-strings.js +2 -0
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app-route.js +6 -1
- package/dist/ui/shared/app-shell.js +3 -3
- package/dist/ui/shared/app-stream.js +2 -1
- package/dist/ui/shared/app.css +44 -6
- package/dist/ui/shared/app.js +5 -0
- package/dist/ui/shared/fonts/README.md +1 -1
- package/dist/ui/shared/instance-nav.js +9 -6
- package/dist/ui/shared/instance-switch.js +4 -4
- package/dist/ui/shared/tokens.css +1 -1
- package/dist/web-api.d.ts +5 -0
- package/dist/web-api.js +49 -1
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +2 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-commands.d.ts +2 -2
- package/dist/web-commands.js +3 -2
- package/dist/web-commands.js.map +1 -1
- package/dist/web-reply-files.d.ts +45 -0
- package/dist/web-reply-files.js +223 -0
- package/dist/web-reply-files.js.map +1 -0
- package/dist/web-settings-patch.d.ts +3 -1
- package/dist/web-settings-patch.js +4 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-shell-routes.d.ts +4 -0
- package/dist/web-shell-routes.js +9 -0
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/web-upload.d.ts +17 -4
- package/dist/web-upload.js +64 -13
- package/dist/web-upload.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #1770: the files an agent attaches to a reply, as the web chat shows them. When the reply is routed, each file is
|
|
3
|
+
* copied (a snapshot) into the instance's own `workspaces/<instance>/web-files/`, named by the id it is served under,
|
|
4
|
+
* and that copy is what the chat serves and what its history keeps. The agent's file may then be rewritten, moved or
|
|
5
|
+
* cleaned out of /tmp, and the fleet may restart: the message still shows what was sent. Telegram and Discord send
|
|
6
|
+
* the agent's own path, as before; nothing here touches that.
|
|
7
|
+
*
|
|
8
|
+
* Bounded: a snapshot leaves with its message (WebFileLedger.dropServed unlinks it), an instance's directory is held
|
|
9
|
+
* to REPLY_FILES_MAX_BYTES (the oldest go first), and a fleet start removes what no restored message holds.
|
|
10
|
+
*/
|
|
11
|
+
import { constants as fsConstants, closeSync, existsSync, fstatSync, lstatSync, openSync, readdirSync, realpathSync, unlinkSync } from "node:fs";
|
|
12
|
+
import { lstat, mkdir, open, realpath, rename, stat, unlink } from "node:fs/promises";
|
|
13
|
+
import { basename, dirname, extname, join, resolve, sep } from "node:path";
|
|
14
|
+
import { isSafeInstanceDirName } from "./web-chat-store.js";
|
|
15
|
+
import { MAX_SERVED_BYTES } from "./web-upload.js";
|
|
16
|
+
export const WEB_FILES_DIR = "web-files";
|
|
17
|
+
/** Per instance. One reply can carry 20 files of 50 MB: the newest reply always fits whole. */
|
|
18
|
+
export const REPLY_FILES_MAX_BYTES = 1024 ** 3;
|
|
19
|
+
/** A snapshot's name: its served id and the source's plain extension. Nothing else in the directory is ours. */
|
|
20
|
+
const SNAPSHOT_NAME = /^([0-9a-f]{32})(\.[a-z0-9]{1,10})?$/;
|
|
21
|
+
const TMP_NAME = /^\.tmp-[0-9a-f]{32}$/;
|
|
22
|
+
const CHUNK = 1 << 20;
|
|
23
|
+
const PROC_FD = process.platform === "linux" ? "/proc/self/fd" : null;
|
|
24
|
+
/** An instance's snapshot directory; null for a name that is not a safe directory name. */
|
|
25
|
+
export function webFilesDir(home, instance) {
|
|
26
|
+
return isSafeInstanceDirName(instance) ? join(home, "workspaces", instance, WEB_FILES_DIR) : null;
|
|
27
|
+
}
|
|
28
|
+
/** The extension a snapshot keeps (lower-cased, so the served type is the one the source's name gives); "" when not plain. */
|
|
29
|
+
export function snapshotExt(path) {
|
|
30
|
+
const ext = extname(path).toLowerCase();
|
|
31
|
+
return /^\.[a-z0-9]{1,10}$/.test(ext) ? ext : "";
|
|
32
|
+
}
|
|
33
|
+
/** The reply tool's own rule (tool-router assertSendable), applied again when copying: nothing under the state dir but inbox/. */
|
|
34
|
+
function isStateFile(real, stateDir) {
|
|
35
|
+
const roots = new Set([resolve(stateDir)]);
|
|
36
|
+
try {
|
|
37
|
+
roots.add(realpathSync(stateDir));
|
|
38
|
+
}
|
|
39
|
+
catch { /* not there: the resolved path is the rule */ }
|
|
40
|
+
return [...roots].some((root) => real.startsWith(root + sep) && !real.includes(sep + "inbox" + sep));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Copy the agent's file `src` into `dir` as `<id><ext>`. The path itself must not be a symlink; it is opened without
|
|
44
|
+
* following one, and what is copied is that open file — a regular file within maxFileBytes, the same file (device +
|
|
45
|
+
* inode) its real path names, and not a state file. Written to a temp name and renamed, so a snapshot is never partial.
|
|
46
|
+
*/
|
|
47
|
+
export async function snapshotReplyFile(src, dir, id, opts) {
|
|
48
|
+
const maxFile = opts.maxFileBytes ?? MAX_SERVED_BYTES;
|
|
49
|
+
const refused = (reason) => ({ ok: false, refused: true, reason });
|
|
50
|
+
let fh = null;
|
|
51
|
+
let tmp = null;
|
|
52
|
+
let evicted = [];
|
|
53
|
+
try {
|
|
54
|
+
let size, mtimeMs;
|
|
55
|
+
try {
|
|
56
|
+
const own = await lstat(src);
|
|
57
|
+
if (own.isSymbolicLink())
|
|
58
|
+
return refused("a symlink");
|
|
59
|
+
if (!own.isFile())
|
|
60
|
+
return refused("not a regular file");
|
|
61
|
+
fh = await open(src, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
|
|
62
|
+
const st = await fh.stat();
|
|
63
|
+
if (!st.isFile())
|
|
64
|
+
return refused("not a regular file");
|
|
65
|
+
if (st.size > maxFile)
|
|
66
|
+
return refused("over the size limit");
|
|
67
|
+
const real = await realpath(src);
|
|
68
|
+
const named = await stat(real);
|
|
69
|
+
if (named.dev !== st.dev || named.ino !== st.ino)
|
|
70
|
+
return refused("replaced while it was opened");
|
|
71
|
+
if (isStateFile(real, opts.stateDir))
|
|
72
|
+
return refused("inside the AgEnD state directory");
|
|
73
|
+
({ size, mtimeMs } = st);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return refused("cannot be read");
|
|
77
|
+
}
|
|
78
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
79
|
+
if (!inTree(dir))
|
|
80
|
+
return { ok: false, refused: false, reason: "the snapshot directory is not the instance's own", evicted };
|
|
81
|
+
evicted = makeRoom(dir, size, opts.maxDirBytes ?? REPLY_FILES_MAX_BYTES);
|
|
82
|
+
tmp = join(dir, `.tmp-${id}`);
|
|
83
|
+
const out = await open(tmp, "wx", 0o600);
|
|
84
|
+
let off = 0;
|
|
85
|
+
try {
|
|
86
|
+
const buf = Buffer.alloc(Math.min(CHUNK, Math.max(1, size)));
|
|
87
|
+
while (off < size) {
|
|
88
|
+
const { bytesRead } = await fh.read(buf, 0, Math.min(buf.length, size - off), off);
|
|
89
|
+
if (bytesRead <= 0)
|
|
90
|
+
break;
|
|
91
|
+
await out.write(buf, 0, bytesRead);
|
|
92
|
+
off += bytesRead;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
await out.close();
|
|
97
|
+
}
|
|
98
|
+
const after = await fh.stat();
|
|
99
|
+
// Rewritten while it was copied: the copy may be torn — not kept.
|
|
100
|
+
if (off !== size || after.size !== size || after.mtimeMs !== mtimeMs)
|
|
101
|
+
return { ok: false, refused: false, reason: "changed while it was copied", evicted };
|
|
102
|
+
const path = join(dir, id + snapshotExt(src));
|
|
103
|
+
await rename(tmp, path);
|
|
104
|
+
tmp = null;
|
|
105
|
+
return { ok: true, path, size, evicted };
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
return { ok: false, refused: false, reason: err.code ?? "copy failed", evicted };
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
if (fh)
|
|
112
|
+
await fh.close().catch(() => { });
|
|
113
|
+
if (tmp)
|
|
114
|
+
await unlink(tmp).catch(() => { });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** `dir` is a real directory at its own path (not a link elsewhere, nor under one). */
|
|
118
|
+
function inTree(dir) {
|
|
119
|
+
try {
|
|
120
|
+
return lstatSync(dir).isDirectory() && realpathSync(dir) === join(realpathSync(dirname(dir)), basename(dir));
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Run `fn` on `dir` opened as a directory and checked to be the in-tree one: removals go through the open directory
|
|
128
|
+
* where the platform allows it, so a parent swapped for a link afterwards cannot redirect them (as sweepOrphanedUploads).
|
|
129
|
+
*/
|
|
130
|
+
function withPinnedDir(dir, fallback, fn) {
|
|
131
|
+
let fd = null;
|
|
132
|
+
try {
|
|
133
|
+
if (!inTree(dir))
|
|
134
|
+
return fallback;
|
|
135
|
+
fd = openSync(dir, fsConstants.O_RDONLY | fsConstants.O_DIRECTORY | fsConstants.O_NOFOLLOW);
|
|
136
|
+
const pinned = fstatSync(fd);
|
|
137
|
+
const here = lstatSync(dir);
|
|
138
|
+
if (pinned.dev !== here.dev || pinned.ino !== here.ino)
|
|
139
|
+
return fallback;
|
|
140
|
+
const anchor = PROC_FD && existsSync(`${PROC_FD}/${fd}`) ? `${PROC_FD}/${fd}` : dir;
|
|
141
|
+
const stillPinned = () => {
|
|
142
|
+
if (anchor !== dir)
|
|
143
|
+
return true;
|
|
144
|
+
const now = lstatSync(dir);
|
|
145
|
+
return now.isDirectory() && now.dev === pinned.dev && now.ino === pinned.ino && inTree(dir);
|
|
146
|
+
};
|
|
147
|
+
return fn(anchor, stillPinned);
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return fallback;
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
if (fd !== null)
|
|
154
|
+
try {
|
|
155
|
+
closeSync(fd);
|
|
156
|
+
}
|
|
157
|
+
catch { /* closed */ }
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/** The snapshots in `dir` (regular files named as ours), oldest first. */
|
|
161
|
+
function snapshotsIn(anchor) {
|
|
162
|
+
const out = [];
|
|
163
|
+
for (const name of readdirSync(anchor)) {
|
|
164
|
+
const m = SNAPSHOT_NAME.exec(name);
|
|
165
|
+
if (!m)
|
|
166
|
+
continue;
|
|
167
|
+
try {
|
|
168
|
+
const st = lstatSync(join(anchor, name));
|
|
169
|
+
if (st.isFile())
|
|
170
|
+
out.push({ id: m[1], name, size: st.size, mtimeMs: st.mtimeMs });
|
|
171
|
+
}
|
|
172
|
+
catch { /* vanished */ }
|
|
173
|
+
}
|
|
174
|
+
return out.sort((a, b) => a.mtimeMs - b.mtimeMs || (a.name < b.name ? -1 : 1));
|
|
175
|
+
}
|
|
176
|
+
/** Remove the oldest snapshots until `incoming` more bytes fit under `maxBytes`; the ids removed. */
|
|
177
|
+
export function makeRoom(dir, incoming, maxBytes) {
|
|
178
|
+
return withPinnedDir(dir, [], (anchor, stillPinned) => {
|
|
179
|
+
const all = snapshotsIn(anchor);
|
|
180
|
+
let total = all.reduce((n, s) => n + s.size, 0);
|
|
181
|
+
const evicted = [];
|
|
182
|
+
for (const s of all) {
|
|
183
|
+
if (total + incoming <= maxBytes)
|
|
184
|
+
break;
|
|
185
|
+
if (!stillPinned())
|
|
186
|
+
break;
|
|
187
|
+
try {
|
|
188
|
+
unlinkSync(join(anchor, s.name));
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
total -= s.size;
|
|
194
|
+
evicted.push(s.id);
|
|
195
|
+
}
|
|
196
|
+
return evicted;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* At fleet start, after the web chat history is restored: remove every snapshot of `dir` whose id no restored message
|
|
201
|
+
* holds (`held`), and every temp file a stopped copy left. Nothing but our own names is touched. How many were removed.
|
|
202
|
+
*/
|
|
203
|
+
export function sweepReplySnapshots(dir, held) {
|
|
204
|
+
return withPinnedDir(dir, 0, (anchor, stillPinned) => {
|
|
205
|
+
let removed = 0;
|
|
206
|
+
for (const name of readdirSync(anchor)) {
|
|
207
|
+
const m = SNAPSHOT_NAME.exec(name);
|
|
208
|
+
if (!(m && !held.has(m[1])) && !TMP_NAME.test(name))
|
|
209
|
+
continue;
|
|
210
|
+
try {
|
|
211
|
+
if (!lstatSync(join(anchor, name)).isFile())
|
|
212
|
+
continue; // a link or a directory is not one of ours
|
|
213
|
+
if (!stillPinned())
|
|
214
|
+
break;
|
|
215
|
+
unlinkSync(join(anchor, name));
|
|
216
|
+
removed++;
|
|
217
|
+
}
|
|
218
|
+
catch { /* vanished */ }
|
|
219
|
+
}
|
|
220
|
+
return removed;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=web-reply-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-reply-files.js","sourceRoot":"","sources":["../src/web-reply-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,9 +4,11 @@ export declare const WEB_TERMINAL_TTL_MINUTES: {
|
|
|
4
4
|
readonly max: 20;
|
|
5
5
|
};
|
|
6
6
|
/** What Settings → Web may write; everything else under web: / web_terminal: is fleet.yaml's. */
|
|
7
|
-
export declare const WEB_SETTINGS_BODY_KEYS: readonly ["echo_to_channel", "public_link", "session", "web_terminal"];
|
|
7
|
+
export declare const WEB_SETTINGS_BODY_KEYS: readonly ["echo_to_channel", "log_tab", "public_link", "session", "web_terminal"];
|
|
8
8
|
export type WebSettingsBody = {
|
|
9
9
|
echo?: boolean;
|
|
10
|
+
/** #1218: the Log tab on (true) or off (false). */
|
|
11
|
+
logTab?: boolean;
|
|
10
12
|
publicLink?: NonNullable<WebConfig["public_link"]>;
|
|
11
13
|
session: Record<string, number | null> | null;
|
|
12
14
|
terminal: Record<string, boolean | number | null> | null;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { validPublicLinkPatch } from "./public-web-link.js";
|
|
10
10
|
export const WEB_TERMINAL_TTL_MINUTES = { min: 1, max: 20 };
|
|
11
11
|
/** What Settings → Web may write; everything else under web: / web_terminal: is fleet.yaml's. */
|
|
12
|
-
export const WEB_SETTINGS_BODY_KEYS = ["echo_to_channel", "public_link", "session", "web_terminal"];
|
|
12
|
+
export const WEB_SETTINGS_BODY_KEYS = ["echo_to_channel", "log_tab", "public_link", "session", "web_terminal"];
|
|
13
13
|
/** The whole body, or what is wrong with it (the handler's 400 message). */
|
|
14
14
|
export function parseWebSettingsBody(body) {
|
|
15
15
|
if (!body || typeof body !== "object" || Array.isArray(body)
|
|
@@ -18,6 +18,8 @@ export function parseWebSettingsBody(body) {
|
|
|
18
18
|
const patch = body;
|
|
19
19
|
if (patch.echo_to_channel !== undefined && typeof patch.echo_to_channel !== "boolean")
|
|
20
20
|
return { error: "echo_to_channel must be a boolean" };
|
|
21
|
+
if (patch.log_tab !== undefined && typeof patch.log_tab !== "boolean")
|
|
22
|
+
return { error: "log_tab must be a boolean" };
|
|
21
23
|
if (patch.public_link !== undefined && !validPublicLinkPatch(patch.public_link))
|
|
22
24
|
return { error: "invalid public_link settings" };
|
|
23
25
|
const session = webSessionPatch(patch.session);
|
|
@@ -28,6 +30,7 @@ export function parseWebSettingsBody(body) {
|
|
|
28
30
|
return { error: "web_terminal must be { enabled?: boolean|null, ttl_minutes?: 1..20|null }" };
|
|
29
31
|
return {
|
|
30
32
|
...(patch.echo_to_channel !== undefined ? { echo: patch.echo_to_channel } : {}),
|
|
33
|
+
...(patch.log_tab !== undefined ? { logTab: patch.log_tab } : {}),
|
|
31
34
|
...(patch.public_link !== undefined ? { publicLink: patch.public_link } : {}),
|
|
32
35
|
session, terminal,
|
|
33
36
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-settings-patch.js","sourceRoot":"","sources":["../src/web-settings-patch.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAG5D,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAW,CAAC;AACrE,iGAAiG;AACjG,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,iBAAiB,EAAE,aAAa,EAAE,SAAS,EAAE,cAAc,CAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"web-settings-patch.js","sourceRoot":"","sources":["../src/web-settings-patch.ts"],"names":[],"mappings":"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"}
|
|
@@ -31,6 +31,9 @@ export type ShellRoute = {
|
|
|
31
31
|
} | {
|
|
32
32
|
panel: "details";
|
|
33
33
|
instance: string;
|
|
34
|
+
} | {
|
|
35
|
+
panel: "log";
|
|
36
|
+
instance: string;
|
|
34
37
|
};
|
|
35
38
|
/** What a path is: the app shell (with its route), a malformed shell path (400), or not the shell at all (null). */
|
|
36
39
|
export type ShellMatch = {
|
|
@@ -60,4 +63,5 @@ export declare function isWebPageNavigation(path: string): boolean;
|
|
|
60
63
|
/** The app's client-side paths, for links the server builds (#1386 links, the tour). */
|
|
61
64
|
export declare function chatPath(instance: string): string;
|
|
62
65
|
export declare function viewPath(instance: string): string;
|
|
66
|
+
export declare function logPath(instance: string): string;
|
|
63
67
|
export declare function detailsPath(instance: string): string;
|
package/dist/web-shell-routes.js
CHANGED
|
@@ -54,6 +54,12 @@ export function shellRoute(method, path) {
|
|
|
54
54
|
const fleet = /^\/ui\/fleet\/([^/]+)$/.exec(path);
|
|
55
55
|
if (fleet)
|
|
56
56
|
return FLEET_TABS.includes(fleet[1]) ? { kind: "shell", route: { panel: "fleet", tab: fleet[1] } } : null;
|
|
57
|
+
// #1218: one instance's Log.
|
|
58
|
+
const log = /^\/ui\/log\/([^/]+)$/.exec(path);
|
|
59
|
+
if (log) {
|
|
60
|
+
const name = decodeSegment(log[1]);
|
|
61
|
+
return name !== null && isSafeInstanceName(name) ? { kind: "shell", route: { panel: "log", instance: name } } : { kind: "malformed" };
|
|
62
|
+
}
|
|
57
63
|
const chat = /^\/ui\/chat\/([^/]+)$/.exec(path);
|
|
58
64
|
if (chat) {
|
|
59
65
|
const name = decodeSegment(chat[1]);
|
|
@@ -100,6 +106,9 @@ export function chatPath(instance) {
|
|
|
100
106
|
export function viewPath(instance) {
|
|
101
107
|
return `/view/${encodeURIComponent(instance)}`;
|
|
102
108
|
}
|
|
109
|
+
export function logPath(instance) {
|
|
110
|
+
return `/ui/log/${encodeURIComponent(instance)}`;
|
|
111
|
+
}
|
|
103
112
|
export function detailsPath(instance) {
|
|
104
113
|
return `/ui/fleet/agent/${encodeURIComponent(instance)}`;
|
|
105
114
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-shell-routes.js","sourceRoot":"","sources":["../src/web-shell-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAU,CAAC;AAE7F,uHAAuH;AACvH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,CAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"web-shell-routes.js","sourceRoot":"","sources":["../src/web-shell-routes.ts"],"names":[],"mappings":"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"}
|
package/dist/web-upload.d.ts
CHANGED
|
@@ -57,6 +57,11 @@ export interface ServedFile {
|
|
|
57
57
|
/** The instance whose chat shows it. */
|
|
58
58
|
instance: string;
|
|
59
59
|
kind: UploadKind;
|
|
60
|
+
/**
|
|
61
|
+
* #1770: a reply file's snapshot (workspaces/<instance>/web-files): the fleet's own copy, kept as long as its message
|
|
62
|
+
* — outside the served-id cap, and removed from disk when the message leaves the history (dropServed).
|
|
63
|
+
*/
|
|
64
|
+
snapshot?: true;
|
|
60
65
|
}
|
|
61
66
|
export declare const isFileId: (id: string) => boolean;
|
|
62
67
|
/** Images the browser may show inline; everything else is served as a download. */
|
|
@@ -128,6 +133,7 @@ export declare class WebFileLedger {
|
|
|
128
133
|
mime?: string;
|
|
129
134
|
instance: string;
|
|
130
135
|
kind?: UploadKind;
|
|
136
|
+
snapshot?: boolean;
|
|
131
137
|
}): ServedFile | null;
|
|
132
138
|
/**
|
|
133
139
|
* The bytes of a served file, re-checked at read time: opened without following a symlink, still a
|
|
@@ -155,14 +161,21 @@ export declare class WebFileLedger {
|
|
|
155
161
|
* nothing is served under the id, or it is an upload no message has taken (web-pending-…): that is never kept.
|
|
156
162
|
*/
|
|
157
163
|
storablePath(id: string): string | null;
|
|
158
|
-
/**
|
|
164
|
+
/**
|
|
165
|
+
* #1565: these ids are no longer shown (their message left the history's window): nothing is served under them.
|
|
166
|
+
* #1770: a snapshot goes from disk with them — the file registered, still that file (device + inode), never a link.
|
|
167
|
+
*/
|
|
159
168
|
dropServed(ids: Iterable<string>): void;
|
|
169
|
+
/** #1770: the ids of an instance's snapshots served now — what a fleet start's sweep keeps. */
|
|
170
|
+
heldSnapshots(instance: string): Set<string>;
|
|
160
171
|
/**
|
|
161
172
|
* #1565: serve again, under the id it had, a file a message showed before the fleet restarted — only if it still
|
|
162
173
|
* passes every check: an id of ours in shape; never an upload no message took (web-pending-…); the path itself not a
|
|
163
174
|
* symlink, and a regular file within MAX_SERVED_BYTES; its real path inside this instance's inbox
|
|
164
|
-
* (`workspaces/<instance>/inbox`, itself not a link elsewhere)
|
|
165
|
-
*
|
|
175
|
+
* (`workspaces/<instance>/inbox`, itself not a link elsewhere) — or (#1770) inside its reply snapshots
|
|
176
|
+
* (`webFilesDir`, `workspaces/<instance>/web-files`, the same checks), then served as a snapshot again. Null when any
|
|
177
|
+
* check fails (the message then shows the name as unavailable). Fetched by id only, as ever: the stored path is the
|
|
178
|
+
* fleet's own file, never a request's.
|
|
166
179
|
*/
|
|
167
180
|
restoreServed(entry: {
|
|
168
181
|
id: unknown;
|
|
@@ -170,7 +183,7 @@ export declare class WebFileLedger {
|
|
|
170
183
|
name?: unknown;
|
|
171
184
|
mime?: unknown;
|
|
172
185
|
kind?: unknown;
|
|
173
|
-
}, instance: string, inboxDir: string): ServedFile | null;
|
|
186
|
+
}, instance: string, inboxDir: string, webFilesDir?: string): ServedFile | null;
|
|
174
187
|
/** Forget an instance's files (it was deleted). The files themselves are its workspace's to remove. */
|
|
175
188
|
forget(instance: string): void;
|
|
176
189
|
/**
|
package/dist/web-upload.js
CHANGED
|
@@ -284,10 +284,24 @@ export class WebFileLedger {
|
|
|
284
284
|
realPath, size, dev, ino, mime, instance: input.instance,
|
|
285
285
|
name: displayName(input.name ?? basename(realPath), "file"),
|
|
286
286
|
kind: input.kind ?? (INLINE_MIME.has(mime) ? "photo" : "document"),
|
|
287
|
+
...(input.snapshot ? { snapshot: true } : {}),
|
|
287
288
|
};
|
|
288
289
|
this.served.set(file.id, file);
|
|
289
|
-
|
|
290
|
-
|
|
290
|
+
// The cap bounds what this process registered by path. A snapshot is bounded by the history it belongs to (#1770):
|
|
291
|
+
// it leaves with its message, never here — dropping it here would lose a file its message still shows.
|
|
292
|
+
let over = 0;
|
|
293
|
+
for (const f of this.served.values())
|
|
294
|
+
if (!f.snapshot)
|
|
295
|
+
over++;
|
|
296
|
+
over -= this.maxServed;
|
|
297
|
+
for (const [id, f] of this.served) {
|
|
298
|
+
if (over <= 0)
|
|
299
|
+
break;
|
|
300
|
+
if (!f.snapshot) {
|
|
301
|
+
this.served.delete(id);
|
|
302
|
+
over--;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
291
305
|
return file;
|
|
292
306
|
}
|
|
293
307
|
/**
|
|
@@ -350,39 +364,76 @@ export class WebFileLedger {
|
|
|
350
364
|
const file = isFileId(id) ? this.served.get(id) : undefined;
|
|
351
365
|
return file && !basename(file.realPath).startsWith(PENDING_PREFIX) ? file.realPath : null;
|
|
352
366
|
}
|
|
353
|
-
/**
|
|
354
|
-
|
|
355
|
-
|
|
367
|
+
/**
|
|
368
|
+
* #1565: these ids are no longer shown (their message left the history's window): nothing is served under them.
|
|
369
|
+
* #1770: a snapshot goes from disk with them — the file registered, still that file (device + inode), never a link.
|
|
370
|
+
*/
|
|
371
|
+
dropServed(ids) {
|
|
372
|
+
for (const id of ids) {
|
|
373
|
+
const f = this.served.get(id);
|
|
374
|
+
this.served.delete(id);
|
|
375
|
+
if (!f?.snapshot)
|
|
376
|
+
continue;
|
|
377
|
+
try {
|
|
378
|
+
const st = lstatSync(f.realPath);
|
|
379
|
+
if (st.isFile() && st.dev === f.dev && st.ino === f.ino)
|
|
380
|
+
unlinkSync(f.realPath);
|
|
381
|
+
}
|
|
382
|
+
catch { /* already gone */ }
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/** #1770: the ids of an instance's snapshots served now — what a fleet start's sweep keeps. */
|
|
386
|
+
heldSnapshots(instance) {
|
|
387
|
+
const out = new Set();
|
|
388
|
+
for (const f of this.served.values())
|
|
389
|
+
if (f.snapshot && f.instance === instance)
|
|
390
|
+
out.add(f.id);
|
|
391
|
+
return out;
|
|
392
|
+
}
|
|
356
393
|
/**
|
|
357
394
|
* #1565: serve again, under the id it had, a file a message showed before the fleet restarted — only if it still
|
|
358
395
|
* passes every check: an id of ours in shape; never an upload no message took (web-pending-…); the path itself not a
|
|
359
396
|
* symlink, and a regular file within MAX_SERVED_BYTES; its real path inside this instance's inbox
|
|
360
|
-
* (`workspaces/<instance>/inbox`, itself not a link elsewhere)
|
|
361
|
-
*
|
|
397
|
+
* (`workspaces/<instance>/inbox`, itself not a link elsewhere) — or (#1770) inside its reply snapshots
|
|
398
|
+
* (`webFilesDir`, `workspaces/<instance>/web-files`, the same checks), then served as a snapshot again. Null when any
|
|
399
|
+
* check fails (the message then shows the name as unavailable). Fetched by id only, as ever: the stored path is the
|
|
400
|
+
* fleet's own file, never a request's.
|
|
362
401
|
*/
|
|
363
|
-
restoreServed(entry, instance, inboxDir) {
|
|
402
|
+
restoreServed(entry, instance, inboxDir, webFilesDir) {
|
|
364
403
|
if (typeof entry.id !== "string" || !isFileId(entry.id) || typeof entry.path !== "string" || !entry.path)
|
|
365
404
|
return null;
|
|
366
405
|
if (basename(entry.path).startsWith(PENDING_PREFIX))
|
|
367
406
|
return null;
|
|
368
407
|
let real;
|
|
369
408
|
let own;
|
|
409
|
+
let snapshot = false;
|
|
370
410
|
try {
|
|
371
411
|
own = lstatSync(entry.path);
|
|
372
412
|
if (own.isSymbolicLink() || !own.isFile() || own.size > MAX_SERVED_BYTES)
|
|
373
413
|
return null;
|
|
374
|
-
const inbox = realpathSync(inboxDir);
|
|
375
|
-
if (inbox !== join(realpathSync(dirname(inboxDir)), basename(inboxDir)))
|
|
376
|
-
return null;
|
|
377
414
|
real = realpathSync(entry.path);
|
|
378
|
-
|
|
415
|
+
// The directory, resolved, must be itself (not a link elsewhere); the file's real path directly under one of them.
|
|
416
|
+
const within = (dir) => {
|
|
417
|
+
if (!dir)
|
|
418
|
+
return false;
|
|
419
|
+
try {
|
|
420
|
+
const d = realpathSync(dir);
|
|
421
|
+
return d === join(realpathSync(dirname(dir)), basename(dir)) && real.startsWith(d + sep);
|
|
422
|
+
}
|
|
423
|
+
catch {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
if (within(webFilesDir))
|
|
428
|
+
snapshot = true;
|
|
429
|
+
else if (!within(inboxDir))
|
|
379
430
|
return null;
|
|
380
431
|
}
|
|
381
432
|
catch {
|
|
382
433
|
return null;
|
|
383
434
|
}
|
|
384
435
|
const kind = entry.kind === "photo" || entry.kind === "document" ? entry.kind : undefined;
|
|
385
|
-
const file = this.registerServed({ id: entry.id, path: real, instance,
|
|
436
|
+
const file = this.registerServed({ id: entry.id, path: real, instance, ...(snapshot ? { snapshot } : {}),
|
|
386
437
|
...(typeof entry.name === "string" ? { name: entry.name } : {}), ...(typeof entry.mime === "string" ? { mime: entry.mime } : {}), ...(kind ? { kind } : {}) });
|
|
387
438
|
// The file checked is the file registered (nothing swapped in between).
|
|
388
439
|
if (file && (file.realPath !== real || file.dev !== own.dev || file.ino !== own.ino)) {
|