svamp-cli 0.2.341 → 0.2.346
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/bin/skills/artifact/SKILL.md +1 -1
- package/bin/skills/crew/SKILL.md +21 -3
- package/dist/DefaultTransport-vfp78fug.mjs +47 -0
- package/dist/GeminiTransport-3OkhtFRO.mjs +130 -0
- package/dist/KimiTransport-D8n009Tr.mjs +141 -0
- package/dist/acpAgentConfig-CObOPlA8.mjs +48 -0
- package/dist/acpBackend-BZ1MJhQV.mjs +922 -0
- package/dist/{adminCommands-Btl3is3z.mjs → adminCommands-BvsYnRDA.mjs} +2 -18
- package/dist/{agentCommands-B7JaByb0.mjs → agentCommands-B2Nl7Fof.mjs} +6 -23
- package/dist/api-BHjyC0Ph.mjs +157 -0
- package/dist/{auth-pTUm-zt4.mjs → auth-CRz8TsEl.mjs} +1 -22
- package/dist/bootStatus-DqtNgJch.mjs +143 -0
- package/dist/claudeAuth-iAerdaMr.mjs +247 -0
- package/dist/cli.mjs +2939 -5
- package/dist/codexAppServerBackend-VGsYtbqg.mjs +786 -0
- package/dist/codexProvider-BtxYc5Rj.mjs +68 -0
- package/dist/{commands-BaxjRuxM.mjs → commands-BB1Kcyt-.mjs} +13 -19
- package/dist/{commands-BUkV04q6.mjs → commands-BOpoVBOM.mjs} +55 -25
- package/dist/{commands-C1L_R1H6.mjs → commands-C2aODeVc.mjs} +58 -33
- package/dist/{commands-znQSQbTX.mjs → commands-CiRCwjkJ.mjs} +10 -32
- package/dist/{commands-C4whLEwv.mjs → commands-D1pZq3d4.mjs} +103 -41
- package/dist/{commands-CMCSUI4A.mjs → commands-D69mnkYu.mjs} +20 -30
- package/dist/{commands-CfP6gPZF.mjs → commands-DW5RnHZw.mjs} +2 -19
- package/dist/{commands-EcIOSJHF.mjs → commands-H4PsTGn8.mjs} +58 -26
- package/dist/cron-hHGdb5kU.mjs +132 -0
- package/dist/detectIsolation-CzoDCEnh.mjs +319 -0
- package/dist/envFlag-COOuJ3AH.mjs +11 -0
- package/dist/fileLock-BMPwCrB6.mjs +80 -0
- package/dist/{fleet-U-WXVbW_.mjs → fleet-DM_Q7w69.mjs} +85 -24
- package/dist/flushExit-C-i6SGjj.mjs +37 -0
- package/dist/friendlyName-1UZcyDhu.mjs +586 -0
- package/dist/{headlessCli-CptF7_lZ.mjs → headlessCli-C8HzQvis.mjs} +3 -21
- package/dist/hookSettings-DYMTUgVA.mjs +10399 -0
- package/dist/{httpServer-Be1Hcddr.mjs → httpServer-BzEDbOGn.mjs} +14 -5
- package/dist/hyphaClient-ybivOjZo.mjs +49 -0
- package/dist/index.mjs +40 -8
- package/dist/instance-CeHz5C8v.mjs +203 -0
- package/dist/kimiProvider-DbxODebR.mjs +69 -0
- package/dist/kimiSetup-NGsoYB5h.mjs +48 -0
- package/dist/loopVerify-B47sSjxe.mjs +444 -0
- package/dist/machineTools-DygM1jXW.mjs +580 -0
- package/dist/model-CUHojegn.mjs +66 -0
- package/dist/{notifyCommands-DitnhzYz.mjs → notifyCommands-D-IKIxiq.mjs} +3 -18
- package/dist/package-DtuINqf5.mjs +64 -0
- package/dist/pinnedCodex-XW4qvOAe.mjs +11 -0
- package/dist/pinnedNono-5ZHb6328.mjs +15 -0
- package/dist/{rpc-zndUgvJ7.mjs → rpc-BB6RF4-F.mjs} +38 -6
- package/dist/{rpc-BPT9DJXL.mjs → rpc-Cf0grcru.mjs} +28 -20
- package/dist/{run-CrUTBmi4.mjs → run-ChdsgaSD.mjs} +17 -9
- package/dist/run-DkjwF-zA.mjs +9695 -0
- package/dist/sandboxDetect-Cdzsj5TC.mjs +19 -0
- package/dist/{scheduler-B0h2sfGn.mjs → scheduler-BY30ZrB9.mjs} +38 -6
- package/dist/securityContext-Cm5UTCuD.mjs +218 -0
- package/dist/serveAuth-DbVLRCRR.mjs +418 -0
- package/dist/{serveCommands-DpF0xKvz.mjs → serveCommands-CUHUiYnZ.mjs} +13 -34
- package/dist/sharingNotificationSync-qGIQ-3yf.mjs +359 -0
- package/dist/{sideband-B5n9fNxM.mjs → sideband-68HhGT0i.mjs} +1 -21
- package/dist/store-Bn0ByYev.mjs +347 -0
- package/dist/store-fi0f-ff1.mjs +184 -0
- package/dist/types-57J3Re0n.mjs +17 -0
- package/dist/wantsHelp-6bMOeNUz.mjs +5 -0
- package/dist/wrapCommand-CdNawSaa.mjs +117 -0
- package/package.json +3 -3
- package/dist/cli-pFCLXDrP.mjs +0 -2749
- package/dist/package-CigJ8nhu.mjs +0 -64
- package/dist/run-LHJh7jLy.mjs +0 -26384
- package/dist/sandboxDetect-DNTcbgWD.mjs +0 -12
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { randomUUID, createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
const SHARE_COLLECTION_ID = "public/svamp-shared-sessions";
|
|
4
|
+
const EVENT_COLLECTION_ID = "public/svamp-user-events";
|
|
5
|
+
const SYSTEM_COLLECTION_CONFIG = {
|
|
6
|
+
permissions: { "@": "lf+" },
|
|
7
|
+
private_children: true,
|
|
8
|
+
recipient_field: "recipientEmail",
|
|
9
|
+
recipient_can_delete: true
|
|
10
|
+
};
|
|
11
|
+
function computeCollectionConfigUpdate(existingConfig, extra) {
|
|
12
|
+
const cfg = existingConfig || {};
|
|
13
|
+
const changed = Object.entries(extra).some(([k, v]) => JSON.stringify(cfg[k]) !== JSON.stringify(v));
|
|
14
|
+
return { changed, merged: { ...cfg, ...extra } };
|
|
15
|
+
}
|
|
16
|
+
function emailHash(email) {
|
|
17
|
+
return createHash("sha256").update(email.toLowerCase()).digest("hex").slice(0, 12);
|
|
18
|
+
}
|
|
19
|
+
function sessionIdHash(sessionId) {
|
|
20
|
+
return createHash("sha256").update(sessionId).digest("hex").slice(0, 12);
|
|
21
|
+
}
|
|
22
|
+
function roleLabel(role) {
|
|
23
|
+
switch (role) {
|
|
24
|
+
case "admin":
|
|
25
|
+
return "full access";
|
|
26
|
+
case "interact":
|
|
27
|
+
return "can interact";
|
|
28
|
+
case "view":
|
|
29
|
+
return "view only";
|
|
30
|
+
default:
|
|
31
|
+
return String(role);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function shareAlias(sessionId, recipientEmail) {
|
|
35
|
+
return `share-${sessionIdHash(sessionId)}-${emailHash(recipientEmail)}`;
|
|
36
|
+
}
|
|
37
|
+
function eventAlias(eventId, recipientEmail) {
|
|
38
|
+
return `evt-${emailHash(recipientEmail)}-${eventId.slice(0, 8)}`;
|
|
39
|
+
}
|
|
40
|
+
class SharingNotificationSync {
|
|
41
|
+
server;
|
|
42
|
+
artifactManager = null;
|
|
43
|
+
amResolvedAt = 0;
|
|
44
|
+
shareCollectionId = null;
|
|
45
|
+
eventCollectionId = null;
|
|
46
|
+
initialized = false;
|
|
47
|
+
log;
|
|
48
|
+
// A resolved `public/artifact-manager` proxy is bound to a SPECIFIC backend client_id. When that
|
|
49
|
+
// backend pod is replaced (deploy/restart/idle-evict), the new instance registers under the same
|
|
50
|
+
// public alias but a fresh client_id — the cached proxy dangles and every call TIMES OUT (~30s).
|
|
51
|
+
// Before this fix that silently broke ALL zero-click share writes fleet-wide (empty
|
|
52
|
+
// svamp-shared-sessions → recipients never auto-discovered). So we re-resolve on a short TTL and,
|
|
53
|
+
// on any RPC error, force one fresh re-resolve + retry (a dead pod's proxy always fails first).
|
|
54
|
+
static AM_TTL_MS = 3e4;
|
|
55
|
+
constructor(server, log) {
|
|
56
|
+
this.server = server;
|
|
57
|
+
this.log = log;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Resolve `public/artifact-manager` on the daemon's live connection. hypha-rpc re-queries the
|
|
61
|
+
* server registry on each getService() call, so a fresh resolve yields the CURRENTLY-registered
|
|
62
|
+
* live instance (not the dangling cached one). Cached within a short TTL to avoid a lookup per
|
|
63
|
+
* write; `force` bypasses the cache after a failure.
|
|
64
|
+
*/
|
|
65
|
+
async getAM(force = false) {
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
if (!force && this.artifactManager && now - this.amResolvedAt < SharingNotificationSync.AM_TTL_MS) {
|
|
68
|
+
return this.artifactManager;
|
|
69
|
+
}
|
|
70
|
+
const am = await this.server.getService("public/artifact-manager");
|
|
71
|
+
this.artifactManager = am;
|
|
72
|
+
this.amResolvedAt = Date.now();
|
|
73
|
+
return am;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Run an artifact-manager operation with automatic recovery from a stale/dead proxy: try with the
|
|
77
|
+
* (possibly cached) instance; on ANY error, force a fresh re-resolve and retry ONCE. This is the
|
|
78
|
+
* heart of the stale-proxy fix — a replaced backend pod makes the first call time out, the retry
|
|
79
|
+
* re-binds to the live instance and succeeds. The caller still catches the final error.
|
|
80
|
+
*/
|
|
81
|
+
async withAM(op) {
|
|
82
|
+
try {
|
|
83
|
+
return await op(await this.getAM(false));
|
|
84
|
+
} catch (err) {
|
|
85
|
+
this.log(`[SHARING NOTIFY] artifact-manager call failed (${err?.message}); re-resolving + retrying`);
|
|
86
|
+
return await op(await this.getAM(true));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async init() {
|
|
90
|
+
try {
|
|
91
|
+
const am = await this.getAM(true);
|
|
92
|
+
if (!am) {
|
|
93
|
+
this.log("[SHARING NOTIFY] Artifact manager not available");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
this.shareCollectionId = SHARE_COLLECTION_ID;
|
|
97
|
+
this.eventCollectionId = EVENT_COLLECTION_ID;
|
|
98
|
+
this.initialized = true;
|
|
99
|
+
this.log(`[SHARING NOTIFY] Initialized (${EVENT_COLLECTION_ID} + ${SHARE_COLLECTION_ID})`);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
this.log(`[SHARING NOTIFY] Init failed: ${err.message}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// ── Share bookmark artifacts (svamp-shared-sessions) ──────────────
|
|
105
|
+
async notifyShare(params) {
|
|
106
|
+
if (!this.initialized || !this.shareCollectionId) return;
|
|
107
|
+
const alias = shareAlias(params.sessionId, params.recipientEmail);
|
|
108
|
+
const manifest = {
|
|
109
|
+
recipientEmail: params.recipientEmail.toLowerCase(),
|
|
110
|
+
sessionId: params.sessionId,
|
|
111
|
+
machineId: params.machineId,
|
|
112
|
+
machineServiceId: params.machineServiceId,
|
|
113
|
+
ownerWorkspace: params.ownerWorkspace,
|
|
114
|
+
ownerEmail: params.ownerEmail,
|
|
115
|
+
label: params.label || "",
|
|
116
|
+
role: params.role,
|
|
117
|
+
sharedAt: Date.now(),
|
|
118
|
+
shareType: params.shareType || "session"
|
|
119
|
+
};
|
|
120
|
+
try {
|
|
121
|
+
await this.withAM(async (am) => {
|
|
122
|
+
const artifact = await am.create({
|
|
123
|
+
alias,
|
|
124
|
+
parent_id: this.shareCollectionId,
|
|
125
|
+
type: "share-notification",
|
|
126
|
+
stage: true,
|
|
127
|
+
manifest,
|
|
128
|
+
_rkwargs: true
|
|
129
|
+
});
|
|
130
|
+
await am.commit({ artifact_id: artifact.id, _rkwargs: true }).catch(() => {
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
} catch (createErr) {
|
|
134
|
+
this.log(`[SHARING NOTIFY] share-notification attach skipped for ${params.recipientEmail}: ${createErr.message}`);
|
|
135
|
+
}
|
|
136
|
+
this.log(`[SHARING NOTIFY] Share notified ${params.recipientEmail} about ${params.shareType || "session"} ${params.sessionId.slice(0, 8)}`);
|
|
137
|
+
const shareType = params.shareType || "session";
|
|
138
|
+
const typeLabel = shareType === "machine" ? "Machine" : "Session";
|
|
139
|
+
await this.publishUserEvent({
|
|
140
|
+
id: randomUUID(),
|
|
141
|
+
recipientEmail: params.recipientEmail.toLowerCase(),
|
|
142
|
+
type: "sharing-invite",
|
|
143
|
+
source: {
|
|
144
|
+
service: "svamp-machine",
|
|
145
|
+
workspace: params.ownerWorkspace,
|
|
146
|
+
userEmail: params.ownerEmail
|
|
147
|
+
},
|
|
148
|
+
title: `${typeLabel} shared with you`,
|
|
149
|
+
// #0689: render the recipient's ACTUAL role (view/interact/admin) — authorize.ts genuinely
|
|
150
|
+
// enforces per-user roles, so the old hardcoded "(full access)" misstated a view/interact
|
|
151
|
+
// invitee's access.
|
|
152
|
+
body: `${params.ownerEmail} shared "${params.label || "Untitled"}" with you (${roleLabel(params.role)})`,
|
|
153
|
+
level: "info",
|
|
154
|
+
action: shareType === "session" ? {
|
|
155
|
+
type: "add-session-bookmark",
|
|
156
|
+
sessionId: params.sessionId,
|
|
157
|
+
workspace: params.ownerWorkspace,
|
|
158
|
+
machineServiceId: params.machineServiceId,
|
|
159
|
+
label: params.label || ""
|
|
160
|
+
} : {
|
|
161
|
+
type: "add-machine-bookmark",
|
|
162
|
+
machineId: params.machineId,
|
|
163
|
+
workspace: params.ownerWorkspace,
|
|
164
|
+
label: params.label || ""
|
|
165
|
+
},
|
|
166
|
+
createdAt: Date.now(),
|
|
167
|
+
expiresAt: Date.now() + 30 * 24 * 60 * 60 * 1e3
|
|
168
|
+
// 30 days
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
async removeNotification(sessionId, recipientEmail) {
|
|
172
|
+
if (!this.initialized || !this.shareCollectionId) return;
|
|
173
|
+
const alias = shareAlias(sessionId, recipientEmail);
|
|
174
|
+
try {
|
|
175
|
+
await this.withAM(async (am) => {
|
|
176
|
+
const existing = await am.read({
|
|
177
|
+
artifact_id: alias,
|
|
178
|
+
parent_id: this.shareCollectionId,
|
|
179
|
+
_rkwargs: true
|
|
180
|
+
});
|
|
181
|
+
await am.delete({
|
|
182
|
+
artifact_id: existing.id,
|
|
183
|
+
_rkwargs: true
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
this.log(`[SHARING NOTIFY] Removed share notification for ${recipientEmail} on ${sessionId.slice(0, 8)}`);
|
|
187
|
+
} catch {
|
|
188
|
+
}
|
|
189
|
+
await this.publishUserEvent({
|
|
190
|
+
id: randomUUID(),
|
|
191
|
+
recipientEmail: recipientEmail.toLowerCase(),
|
|
192
|
+
type: "sharing-revoked",
|
|
193
|
+
source: {
|
|
194
|
+
service: "svamp-machine",
|
|
195
|
+
workspace: this.server.config?.workspace || "",
|
|
196
|
+
userEmail: ""
|
|
197
|
+
},
|
|
198
|
+
title: "Session access removed",
|
|
199
|
+
body: `Your access to a shared session has been revoked`,
|
|
200
|
+
level: "warning",
|
|
201
|
+
createdAt: Date.now(),
|
|
202
|
+
expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1e3
|
|
203
|
+
// 7 days
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
// ── General user event artifacts (svamp-user-events) ─────────────
|
|
207
|
+
/**
|
|
208
|
+
* Publish a general user event to the event inbox collection.
|
|
209
|
+
* Any service can call this to deliver an event to any user's bell icon.
|
|
210
|
+
*/
|
|
211
|
+
async publishUserEvent(event) {
|
|
212
|
+
if (!this.initialized || !this.eventCollectionId) return;
|
|
213
|
+
const alias = eventAlias(event.id, event.recipientEmail);
|
|
214
|
+
try {
|
|
215
|
+
await this.withAM(async (am) => {
|
|
216
|
+
const artifact = await am.create({
|
|
217
|
+
alias,
|
|
218
|
+
parent_id: this.eventCollectionId,
|
|
219
|
+
type: "user-event",
|
|
220
|
+
stage: true,
|
|
221
|
+
manifest: event,
|
|
222
|
+
_rkwargs: true
|
|
223
|
+
});
|
|
224
|
+
await am.commit({
|
|
225
|
+
artifact_id: artifact.id,
|
|
226
|
+
_rkwargs: true
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
this.log(`[USER EVENT] Published ${event.type} to ${event.recipientEmail}`);
|
|
230
|
+
try {
|
|
231
|
+
this.server.emit?.({
|
|
232
|
+
type: "svamp:user-event",
|
|
233
|
+
data: event,
|
|
234
|
+
to: "*"
|
|
235
|
+
});
|
|
236
|
+
} catch {
|
|
237
|
+
}
|
|
238
|
+
} catch (err) {
|
|
239
|
+
this.log(`[USER EVENT] Failed to publish ${event.type} for ${event.recipientEmail}: ${err.message}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* List ALL children of a collection, paginating past hypha's default page cap (`limit=100`,
|
|
244
|
+
* server hypha/artifact.py list_children). The svamp-user-events / svamp-shared-sessions
|
|
245
|
+
* collections are GLOBAL across every deployment user, so a single unpaginated list() would
|
|
246
|
+
* return only the newest ~100 rows across ALL recipients — silently hiding a given user's
|
|
247
|
+
* events past that page. We page offset+=100 until a short/empty/duplicate page.
|
|
248
|
+
*/
|
|
249
|
+
async listAllChildren(parentId) {
|
|
250
|
+
const PAGE = 100;
|
|
251
|
+
const all = [];
|
|
252
|
+
const seen = /* @__PURE__ */ new Set();
|
|
253
|
+
for (let offset = 0; ; offset += PAGE) {
|
|
254
|
+
const page = await this.withAM((am) => am.list({ parent_id: parentId, offset, limit: PAGE, _rkwargs: true }));
|
|
255
|
+
if (!Array.isArray(page) || page.length === 0) break;
|
|
256
|
+
let fresh = 0;
|
|
257
|
+
for (const c of page) {
|
|
258
|
+
const k = String(c?.id ?? c?.manifest?.id ?? c?.alias ?? `${offset}:${fresh}`);
|
|
259
|
+
if (seen.has(k)) continue;
|
|
260
|
+
seen.add(k);
|
|
261
|
+
all.push(c);
|
|
262
|
+
fresh++;
|
|
263
|
+
}
|
|
264
|
+
if (page.length < PAGE || fresh === 0) break;
|
|
265
|
+
}
|
|
266
|
+
return all;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* List user events addressed to `recipientEmail` (the bell inbox), newest first, excluding
|
|
270
|
+
* expired ones. Reuses the global svamp-user-events collection — cross-workspace by design.
|
|
271
|
+
* Mirrors the webapp's fetchUserEvents so the CLI reads the same source of truth. Paginated
|
|
272
|
+
* so a recipient's events past the first 100 global rows are not silently dropped; expired
|
|
273
|
+
* events the caller owns are best-effort pruned (a no-op until the collection's
|
|
274
|
+
* recipient_can_delete is active — swallowed on PermissionError).
|
|
275
|
+
*/
|
|
276
|
+
async listUserEvents(recipientEmail) {
|
|
277
|
+
if (!this.initialized || !this.eventCollectionId) return [];
|
|
278
|
+
const normalized = recipientEmail.toLowerCase();
|
|
279
|
+
try {
|
|
280
|
+
const children = await this.listAllChildren(this.eventCollectionId);
|
|
281
|
+
if (!Array.isArray(children)) return [];
|
|
282
|
+
const now = Date.now();
|
|
283
|
+
const live = [];
|
|
284
|
+
const expiredOwnedIds = [];
|
|
285
|
+
for (const c of children) {
|
|
286
|
+
const m = c?.manifest;
|
|
287
|
+
if (!m || typeof m.recipientEmail !== "string" || m.recipientEmail.toLowerCase() !== normalized) continue;
|
|
288
|
+
if (m.expiresAt && m.expiresAt <= now) {
|
|
289
|
+
if (c?.id) expiredOwnedIds.push(c.id);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
live.push(m);
|
|
293
|
+
}
|
|
294
|
+
for (const id of expiredOwnedIds) {
|
|
295
|
+
this.withAM((am) => am.delete({ artifact_id: id, _rkwargs: true })).catch(() => {
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
return live.sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));
|
|
299
|
+
} catch (err) {
|
|
300
|
+
this.log(`[USER EVENT] listUserEvents failed for ${recipientEmail}: ${err.message}`);
|
|
301
|
+
return [];
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Acknowledge (dismiss) a user event by deleting its artifact. The alias is derived from the
|
|
306
|
+
* event id + recipient email, so only the recipient's own event is removed. Returns true if a
|
|
307
|
+
* matching event was deleted.
|
|
308
|
+
*/
|
|
309
|
+
async ackUserEvent(eventId, recipientEmail) {
|
|
310
|
+
if (!this.initialized || !this.eventCollectionId) return false;
|
|
311
|
+
const normalized = recipientEmail.toLowerCase();
|
|
312
|
+
try {
|
|
313
|
+
const children = await this.listAllChildren(this.eventCollectionId);
|
|
314
|
+
if (!Array.isArray(children)) return false;
|
|
315
|
+
const short = eventId.slice(0, 8);
|
|
316
|
+
const match = children.find((c) => {
|
|
317
|
+
const m = c?.manifest;
|
|
318
|
+
return m && typeof m.id === "string" && (m.id === eventId || m.id.slice(0, 8) === short) && typeof m.recipientEmail === "string" && m.recipientEmail.toLowerCase() === normalized;
|
|
319
|
+
});
|
|
320
|
+
if (!match) return false;
|
|
321
|
+
await this.withAM((am) => am.delete({ artifact_id: match.id, _rkwargs: true }));
|
|
322
|
+
this.log(`[USER EVENT] Acked ${eventId.slice(0, 8)} for ${recipientEmail}`);
|
|
323
|
+
return true;
|
|
324
|
+
} catch (err) {
|
|
325
|
+
this.log(`[USER EVENT] ackUserEvent failed for ${recipientEmail}: ${err.message}`);
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// ── Sharing config diff + sync ───────────────────────────────────
|
|
330
|
+
async syncSharing(sessionId, oldSharing, newSharing, context) {
|
|
331
|
+
if (!this.initialized) return;
|
|
332
|
+
const oldRoleByEmail = new Map(
|
|
333
|
+
(oldSharing?.allowedUsers || []).map((u) => [u.email.toLowerCase(), u.role])
|
|
334
|
+
);
|
|
335
|
+
const oldEmails = new Set(oldRoleByEmail.keys());
|
|
336
|
+
const newUsers = newSharing.allowedUsers || [];
|
|
337
|
+
const newEmails = new Set(newUsers.map((u) => u.email.toLowerCase()));
|
|
338
|
+
for (const user of newUsers) {
|
|
339
|
+
const prevRole = oldRoleByEmail.get(user.email.toLowerCase());
|
|
340
|
+
if (prevRole === void 0 || prevRole !== user.role) {
|
|
341
|
+
this.notifyShare({
|
|
342
|
+
recipientEmail: user.email,
|
|
343
|
+
sessionId,
|
|
344
|
+
role: user.role,
|
|
345
|
+
...context
|
|
346
|
+
}).catch(() => {
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
for (const email of oldEmails) {
|
|
351
|
+
if (!newEmails.has(email)) {
|
|
352
|
+
this.removeNotification(sessionId, email).catch(() => {
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export { SharingNotificationSync as S, SYSTEM_COLLECTION_CONFIG as a, computeCollectionConfigUpdate as c };
|
|
@@ -1,25 +1,5 @@
|
|
|
1
|
-
import { R as READ_ONLY_TOOLS,
|
|
1
|
+
import { R as READ_ONLY_TOOLS, l as loadMachineContext, b as buildMachineInstructions, m as machineToolsForRole, a as buildMachineTools } from './machineTools-DygM1jXW.mjs';
|
|
2
2
|
import 'node:child_process';
|
|
3
|
-
import 'os';
|
|
4
|
-
import 'fs/promises';
|
|
5
|
-
import 'fs';
|
|
6
|
-
import 'path';
|
|
7
|
-
import 'url';
|
|
8
|
-
import 'child_process';
|
|
9
|
-
import 'crypto';
|
|
10
|
-
import 'node:fs';
|
|
11
|
-
import 'node:path';
|
|
12
|
-
import 'node:crypto';
|
|
13
|
-
import 'util';
|
|
14
|
-
import 'http';
|
|
15
|
-
import 'net';
|
|
16
|
-
import 'node:os';
|
|
17
|
-
import 'node:events';
|
|
18
|
-
import '@agentclientprotocol/sdk';
|
|
19
|
-
import 'node:util';
|
|
20
|
-
import 'node:readline';
|
|
21
|
-
import 'node:fs/promises';
|
|
22
|
-
import 'yaml';
|
|
23
3
|
|
|
24
4
|
function toolsForRole(role) {
|
|
25
5
|
if (role === "admin") return [...READ_ONLY_TOOLS, "run_bash", "send_to_session"];
|