sliccy 6.105.0 → 6.105.2
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/node-server/_cloud_core/src/operations/list.d.ts.map +1 -1
- package/dist/node-server/_cloud_core/src/operations/list.js +149 -115
- package/dist/node-server/_shared/tray-signaling.d.ts +28 -1
- package/dist/node-server/_shared/tray-signaling.d.ts.map +1 -1
- package/dist/node-server/_shared/tray-signaling.js.map +1 -1
- package/dist/node-server/routes/lick-api.js +71 -4
- package/dist/ui/.vite/manifest.json +127 -127
- package/dist/ui/assets/SKILL-Boj7N1xe.js +1 -0
- package/dist/ui/assets/{account-store-DG6fHM_V.js → account-store-BaTmqQCH.js} +1 -1
- package/dist/ui/assets/{account-store-ChIwciCW.js → account-store-CHgSp6SB.js} +1 -1
- package/dist/ui/assets/{adobe-3WjIpRUT.js → adobe-CH2DsFrY.js} +1 -1
- package/dist/ui/assets/{adobe-DJkbFq9G.js → adobe-CpalWkIK.js} +1 -1
- package/dist/ui/assets/{agent-panels-tKYkUvdP.js → agent-panels-OnTGHQkG.js} +1 -1
- package/dist/ui/assets/{apply-layout-CM3LBbqI.js → apply-layout-B4zUEdwn.js} +1 -1
- package/dist/ui/assets/{apps-DjfwghFw.js → apps-KhIC51KH.js} +1 -1
- package/dist/ui/assets/{azure-openai-BdYS25f_.js → azure-openai-DzFu6LbW.js} +1 -1
- package/dist/ui/assets/{azure-openai-CtbbffyG.js → azure-openai-uxQv3w4B.js} +1 -1
- package/dist/ui/assets/cdp-ws-page-bridge-DI26JBMr.js +1 -0
- package/dist/ui/assets/{cerebras-cVRZgXZu.js → cerebras-r-in8Inh.js} +1 -1
- package/dist/ui/assets/{cerebras-cB1Un2e8.js → cerebras-uwqZLHdF.js} +1 -1
- package/dist/ui/assets/{connect-surface-DT_tKlEJ.js → connect-surface-COZ8qeD4.js} +1 -1
- package/dist/ui/assets/{dist-N1MMHRNk.js → dist-CIyvNQb4.js} +3 -3
- package/dist/ui/assets/{dist-BmTb9jmr.js → dist-CLbkZ_1_.js} +1 -1
- package/dist/ui/assets/{dist-BnDrtqYs.js → dist-ChNj9ewx.js} +1 -1
- package/dist/ui/assets/{dist-RekgwRC0.js → dist-DhiPJE96.js} +3 -3
- package/dist/ui/assets/{fs-C343c9le.js → fs-B7WAwF-q.js} +1 -1
- package/dist/ui/assets/{fs-CZt4jt7E.js → fs-CiufgGRG.js} +2 -2
- package/dist/ui/assets/{github-0N7JEs6o.js → github-C3CflEFq.js} +1 -1
- package/dist/ui/assets/{github-CbTrlTDc.js → github-CUPlgkJ4.js} +2 -2
- package/dist/ui/assets/{github-copilot-D8KEH9_8.js → github-copilot-BGah-ri-.js} +1 -1
- package/dist/ui/assets/{github-copilot-B_sSIHQ9.js → github-copilot-DnK9K-w0.js} +1 -1
- package/dist/ui/assets/{github-install-CORVkVS2.js → github-install-B3PxKIA9.js} +1 -1
- package/dist/ui/assets/{github-zip-Cx9nWjUO.js → github-zip-DlpU53sL.js} +1 -1
- package/dist/ui/assets/{js-realm-worker-5ergWUC8.js → js-realm-worker-zKU0v1vE.js} +1 -1
- package/dist/ui/assets/{kernel-worker-DAg1kk9T.js → kernel-worker-v5zxjG5E.js} +596 -596
- package/dist/ui/assets/lick-manager-DsQLFcKr.js +2 -0
- package/dist/ui/assets/{lick-manager-proxy-B953oJ2Q.js → lick-manager-proxy-DMaLHm_t.js} +1 -1
- package/dist/ui/assets/lick-ws-bridge-1UPwiSVX.js +1 -0
- package/dist/ui/assets/{local-llm-D7TUI-yw.js → local-llm-D-etSg11.js} +1 -1
- package/dist/ui/assets/{local-llm-BaGh_ZI-.js → local-llm-M_xiCmuW.js} +1 -1
- package/dist/ui/assets/{main-BcZbFpyc.js → main-BcfY0APV.js} +3 -3
- package/dist/ui/assets/{mount-3rB1M3LV.js → mount-Bx3D0gVr.js} +2 -2
- package/dist/ui/assets/{mount-ctgoIoWo.js → mount-CHtLYCJk.js} +1 -1
- package/dist/ui/assets/{mount-recovery-g_5aJYzL.js → mount-recovery-DL9GlfHA.js} +1 -1
- package/dist/ui/assets/new-session-COA3q4w7.js +2 -0
- package/dist/ui/assets/{oauth-bootstrap-yw-xe8Vv.js → oauth-bootstrap-BPR0IgmZ.js} +2 -2
- package/dist/ui/assets/{onboarding-orchestrator-B-FUyeGB.js → onboarding-orchestrator-5WhumpNJ.js} +1 -1
- package/dist/ui/assets/{openai-codex-BFok3oyS.js → openai-codex-CjwoH0Hy.js} +1 -1
- package/dist/ui/assets/{openai-codex-cLIR3T6l.js → openai-codex-pUiCqrNz.js} +1 -1
- package/dist/ui/assets/{openrouter-FWB-kSYo.js → openrouter-BCxd8O71.js} +1 -1
- package/dist/ui/assets/{openrouter-CDPyW_TQ.js → openrouter-CdL4vTyx.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-DAARzo5S.js → openrouter-oauth-CsUVNT2k.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-DZirIqg2.js → openrouter-oauth-Dj48fB3X.js} +1 -1
- package/dist/ui/assets/{panel-rpc-handlers-DrqsnYA3.js → panel-rpc-handlers-QwtLrbX7.js} +2 -2
- package/dist/ui/assets/{panelize-shell-giUuZ6dY.js → panelize-shell-CPME7tYP.js} +1 -1
- package/dist/ui/assets/provider-CKVeq844.js +2 -0
- package/dist/ui/assets/{provider-DUYOy2mD.js → provider-CR46uvW6.js} +1 -1
- package/dist/ui/assets/{provider-settings-Cw0rYGSz.js → provider-settings-e94aWkxT.js} +1 -1
- package/dist/ui/assets/provider-store-access-CamE9gn8.js +1 -0
- package/dist/ui/assets/provider-store-access-wSpMHw1M.js +1 -0
- package/dist/ui/assets/{providers-zHBaRRbi.js → providers-Bpgm6hcR.js} +1 -1
- package/dist/ui/assets/{providers-EvW22h9s.js → providers-N-GzEZuq.js} +2 -2
- package/dist/ui/assets/{quick-llm-CCC28hMC.js → quick-llm-Dx10uCk3.js} +1 -1
- package/dist/ui/assets/{recovery-screen-DRwDpcP6.js → recovery-screen-Xm3PO1gX.js} +1 -1
- package/dist/ui/assets/{redirect-uri-BUHYoStY.js → redirect-uri-BiQsjA10.js} +1 -1
- package/dist/ui/assets/{run-DL-Uxc18.js → run-Bu6cgQMI.js} +1 -1
- package/dist/ui/assets/{setup-feature-flags-remote-Czi_ny8P.js → setup-feature-flags-remote-Cf37xnQ1.js} +1 -1
- package/dist/ui/assets/sidecar-repair-6yH0jK3Q.js +1 -0
- package/dist/ui/assets/{skills-B9hTovVh.js → skills-Bm5AxgzO.js} +1 -1
- package/dist/ui/assets/slicc-version-rYLQXBjd.js +1 -0
- package/dist/ui/assets/{state-BKg54qnI.js → state-CJZU3MUJ.js} +1 -1
- package/dist/ui/assets/{store-Ckx5Qk8r.js → store-ByoR63_A.js} +1 -1
- package/dist/ui/assets/store-CKhg-xXm.js +1 -0
- package/dist/ui/assets/{sudo-BYKLWL8l.js → sudo-gXakxwpD.js} +1 -1
- package/dist/ui/assets/{sync-dialog-CaY6_dCO.js → sync-dialog-CtrrU_-X.js} +1 -1
- package/dist/ui/assets/{tray-sidecar-CHw8U7kW.js → tray-sidecar-DP-OKeYT.js} +1 -1
- package/dist/ui/assets/{upgrade-detection-BGI4k545.js → upgrade-detection-4KedpK0u.js} +1 -1
- package/dist/ui/assets/{voice-reply-BLB0WMGq.js → voice-reply-DJDzh5c_.js} +1 -1
- package/dist/ui/assets/{wc-attach-DfPSrixU.js → wc-attach-CHoh_npa.js} +2 -2
- package/dist/ui/assets/{wc-extension-DhuoWguy.js → wc-extension-DinzzE-u.js} +2 -2
- package/dist/ui/assets/{wc-float-label-cFzM9BsP.js → wc-float-label-_5Qu7XYI.js} +1 -1
- package/dist/ui/assets/{wc-floatbar-online-BKJPRq_0.js → wc-floatbar-online-DdJjsUiB.js} +1 -1
- package/dist/ui/assets/{wc-follower-oauth-CuUqosbc.js → wc-follower-oauth-CQymgqdo.js} +1 -1
- package/dist/ui/assets/{wc-follower-BiIsjqEt.js → wc-follower-orVLyho6.js} +3 -3
- package/dist/ui/assets/{wc-live-9ZO5QfgI.js → wc-live-Y30MNXP6.js} +7 -7
- package/dist/ui/assets/{wc-nav-DW3cMVIc.js → wc-nav-CObJ1-cm.js} +2 -2
- package/dist/ui/assets/{wc-onboarding-SeOTnz2o.js → wc-onboarding-13bz6Y1P.js} +2 -2
- package/dist/ui/assets/{wc-placeholder-BMJjSG0P.js → wc-placeholder-DEerShZk.js} +3 -3
- package/dist/ui/assets/{wc-settings-UD2VUoLt.js → wc-settings-S7sjuH3z.js} +3 -3
- package/dist/ui/assets/{wc-shell-hnMI7FOV.js → wc-shell-DHFdDKPB.js} +2 -2
- package/dist/ui/assets/{wc-shortcut-config-CqDAvdEe.js → wc-shortcut-config-CY9y5JD2.js} +1 -1
- package/dist/ui/assets/{wc-sprinkles-_BGRskZQ.js → wc-sprinkles-BX0o3VdV.js} +2 -2
- package/dist/ui/assets/{wc-transcript-export-DXo_nXRM.js → wc-transcript-export-BS7RBV-1.js} +2 -2
- package/dist/ui/assets/{wc-tray-Cy15l2Cc.js → wc-tray-CdT1k5xd.js} +2 -2
- package/dist/ui/assets/welcome-detection-DbHTpMYI.js +1 -0
- package/dist/ui/assets/{xai-grok-CKUQxsx-.js → xai-grok-DaoF-Hq_.js} +1 -1
- package/dist/ui/assets/{xai-grok-B6FjD17P.js → xai-grok-_z4Y3sUx.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/ui/llm-proxy-sw.js +1 -1
- package/dist/ui/packages/webapp/index.html +3 -3
- package/package.json +1 -1
- package/dist/ui/assets/SKILL-B-2ojpz4.js +0 -1
- package/dist/ui/assets/cdp-ws-page-bridge-D_PY6ku1.js +0 -1
- package/dist/ui/assets/lick-manager-CYlek4qq.js +0 -2
- package/dist/ui/assets/lick-ws-bridge-BJTVP6Z3.js +0 -1
- package/dist/ui/assets/new-session-D1hm84f7.js +0 -2
- package/dist/ui/assets/provider-DM7nUGRc.js +0 -2
- package/dist/ui/assets/provider-store-access-D4PsvZNr.js +0 -1
- package/dist/ui/assets/provider-store-access-DLlfWlp9.js +0 -1
- package/dist/ui/assets/sidecar-repair-FzgS788B.js +0 -1
- package/dist/ui/assets/slicc-version-mN6TT2sL.js +0 -1
- package/dist/ui/assets/store-DRjMIGJv.js +0 -1
- package/dist/ui/assets/welcome-detection-NLNTy3II.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/operations/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/operations/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,aAAa,CAAC;AAE7D,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAwLD;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,aAAa,EACnB,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,SAAS,EAAE,CAAC,CA8BtB"}
|
|
@@ -1,3 +1,146 @@
|
|
|
1
|
+
const STALE_RESERVATION_MS = 10 * 60 * 1000;
|
|
2
|
+
const DEFAULT_TTL_MS = 60 * 60 * 1000;
|
|
3
|
+
/** True when entry metadata matches every key/value in the filter (or no filter). */
|
|
4
|
+
function matchesMetadataFilter(entry, metadata) {
|
|
5
|
+
if (!metadata)
|
|
6
|
+
return true;
|
|
7
|
+
if (!entry.metadata)
|
|
8
|
+
return false;
|
|
9
|
+
for (const [k, v] of Object.entries(metadata)) {
|
|
10
|
+
if (entry.metadata[k] !== v)
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
function isStaleReservation(entry) {
|
|
16
|
+
// Reclaim if stale OR if reservedAt is missing (legacy/malformed entry
|
|
17
|
+
// from before commit e9011ba6 — could otherwise wedge cap forever).
|
|
18
|
+
if (!entry.reservedAt)
|
|
19
|
+
return true;
|
|
20
|
+
return Date.now() - new Date(entry.reservedAt).getTime() > STALE_RESERVATION_MS;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Handle a reserved registry entry: reclaim if stale, otherwise keep.
|
|
24
|
+
* Returns the entry to include in the reconciled list, or null if reclaimed.
|
|
25
|
+
*/
|
|
26
|
+
async function reconcileReserved(entry, registry) {
|
|
27
|
+
if (!isStaleReservation(entry))
|
|
28
|
+
return entry;
|
|
29
|
+
await registry.remove(entry.sandboxId);
|
|
30
|
+
console.warn('[cloud-core] reclaimed stale reservation', {
|
|
31
|
+
sandboxId: entry.sandboxId,
|
|
32
|
+
reservedAt: entry.reservedAt ?? '(missing)',
|
|
33
|
+
});
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
/** Mark a missing real sandbox dead (idempotent registry write). */
|
|
37
|
+
async function markDead(entry, registry) {
|
|
38
|
+
if (entry.state !== 'dead') {
|
|
39
|
+
await registry.update(entry.sandboxId, { state: 'dead' });
|
|
40
|
+
}
|
|
41
|
+
return { ...entry, state: 'dead' };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Reconcile one non-reserved registry entry against live substrate state.
|
|
45
|
+
* Mutates liveById (deletes matched sandbox ids).
|
|
46
|
+
*/
|
|
47
|
+
async function reconcileLiveEntry(entry, liveById, registry) {
|
|
48
|
+
const liveEntry = liveById.get(entry.sandboxId);
|
|
49
|
+
if (!liveEntry) {
|
|
50
|
+
// Substrate doesn't know about it — mark dead unless it's a placeholder.
|
|
51
|
+
// The 'pending-' prefix is a sentinel for "no real sandbox yet" (paired
|
|
52
|
+
// with state:'reserved' by reserveSlot before substrate.create).
|
|
53
|
+
if (entry.sandboxId.startsWith('pending-')) {
|
|
54
|
+
return entry;
|
|
55
|
+
}
|
|
56
|
+
return markDead(entry, registry);
|
|
57
|
+
}
|
|
58
|
+
if (entry.state !== liveEntry.state) {
|
|
59
|
+
await registry.update(entry.sandboxId, { state: liveEntry.state });
|
|
60
|
+
}
|
|
61
|
+
liveById.delete(entry.sandboxId);
|
|
62
|
+
return { ...entry, state: liveEntry.state };
|
|
63
|
+
}
|
|
64
|
+
async function reconcileRegistryEntry(entry, liveById, registry) {
|
|
65
|
+
// Reserved entries: check for stale reservations (TTL: 10 min) and reclaim.
|
|
66
|
+
// Stale reservations are from crashed operations that never completed or rolled back.
|
|
67
|
+
if (entry.state === 'reserved') {
|
|
68
|
+
return reconcileReserved(entry, registry);
|
|
69
|
+
}
|
|
70
|
+
return reconcileLiveEntry(entry, liveById, registry);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Recover joinUrl from /tmp/slicc-join.json ONLY when sandbox is running.
|
|
74
|
+
* Calling substrate.connect() on a paused sandbox would RESUME it, which:
|
|
75
|
+
* (a) burns sandbox runtime silently
|
|
76
|
+
* (b) destabilizes the leader chromium mid-list
|
|
77
|
+
* Paused orphans surface with joinUrl='' — UI hides the Open button.
|
|
78
|
+
*/
|
|
79
|
+
async function recoverJoinFromSandbox(summary, substrate, seed) {
|
|
80
|
+
if (seed.joinUrl || summary.state !== 'running')
|
|
81
|
+
return seed;
|
|
82
|
+
try {
|
|
83
|
+
const handle = await substrate.connect(summary.sandboxId);
|
|
84
|
+
const joinData = await handle.readFile('/tmp/slicc-join.json');
|
|
85
|
+
const parsed = JSON.parse(joinData);
|
|
86
|
+
return {
|
|
87
|
+
joinUrl: parsed.joinUrl ?? '',
|
|
88
|
+
trayId: seed.trayId ?? parsed.trayId,
|
|
89
|
+
lastJoinUpdatedAt: seed.lastJoinUpdatedAt ?? parsed.updatedAt,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
// File not readable (sandbox is transitioning, or file doesn't exist).
|
|
94
|
+
// Leave joinUrl empty — UI will handle gracefully.
|
|
95
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
96
|
+
console.warn('[cloud-core] orphan recovery readFile failed', {
|
|
97
|
+
sandboxId: summary.sandboxId,
|
|
98
|
+
err: msg,
|
|
99
|
+
});
|
|
100
|
+
return seed;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Rebuild a ConeEntry for a substrate sandbox missing from the registry. */
|
|
104
|
+
async function recoverOrphan(summary, substrate) {
|
|
105
|
+
const now = new Date().toISOString();
|
|
106
|
+
const recoveredJoin = await recoverJoinFromSandbox(summary, substrate, {
|
|
107
|
+
joinUrl: summary.metadata?.['joinUrl'] ?? '',
|
|
108
|
+
trayId: summary.metadata?.['trayId'],
|
|
109
|
+
lastJoinUpdatedAt: summary.metadata?.['lastJoinUpdatedAt'],
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
sandboxId: summary.sandboxId,
|
|
113
|
+
substrate: 'e2b',
|
|
114
|
+
name: summary.metadata?.['name'] ?? summary.name,
|
|
115
|
+
createdAt: summary.metadata?.['createdAt'] ?? now,
|
|
116
|
+
joinUrl: recoveredJoin.joinUrl,
|
|
117
|
+
lastSeen: now,
|
|
118
|
+
state: summary.state,
|
|
119
|
+
trayId: recoveredJoin.trayId,
|
|
120
|
+
lastJoinUpdatedAt: recoveredJoin.lastJoinUpdatedAt,
|
|
121
|
+
metadata: summary.metadata,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Refresh the timeout of every running cone so active users keep their cones
|
|
126
|
+
* alive past the 1h default. Failures are non-fatal — a sandbox that
|
|
127
|
+
* disappears between substrate.list() and extendTimeout() will be discovered
|
|
128
|
+
* dead on the NEXT list call.
|
|
129
|
+
*/
|
|
130
|
+
async function extendRunningTimeouts(cones, substrate) {
|
|
131
|
+
await Promise.all(cones
|
|
132
|
+
.filter((c) => c.state === 'running')
|
|
133
|
+
.map(async (c) => {
|
|
134
|
+
try {
|
|
135
|
+
await substrate.extendTimeout(c.sandboxId, DEFAULT_TTL_MS);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
// Sandbox may have died between list and extendTimeout; ignore.
|
|
139
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
140
|
+
console.warn('[cloud-core] extendTimeout failed', { sandboxId: c.sandboxId, err: msg });
|
|
141
|
+
}
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
1
144
|
/**
|
|
2
145
|
* List cones reconciling registry against substrate.
|
|
3
146
|
* - Substrate is source of truth for state (running/paused).
|
|
@@ -11,18 +154,6 @@ export async function listCones(deps, opts = {}) {
|
|
|
11
154
|
// Pass metadata filter to substrate.list for server-side filtering
|
|
12
155
|
const live = await deps.substrate.list(opts.metadata ? { metadata: opts.metadata } : undefined);
|
|
13
156
|
const liveById = new Map(live.map((s) => [s.sandboxId, s]));
|
|
14
|
-
// Helper to check if an entry matches the metadata filter
|
|
15
|
-
const matchesFilter = (entry) => {
|
|
16
|
-
if (!opts.metadata)
|
|
17
|
-
return true;
|
|
18
|
-
if (!entry.metadata)
|
|
19
|
-
return false;
|
|
20
|
-
for (const [k, v] of Object.entries(opts.metadata)) {
|
|
21
|
-
if (entry.metadata[k] !== v)
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
return true;
|
|
25
|
-
};
|
|
26
157
|
// Pass 1: walk registry; reconcile against live.
|
|
27
158
|
// Reconciliation runs for EVERY registry entry regardless of metadata filter
|
|
28
159
|
// — otherwise zombie entries (e.g. legacy entries without userId metadata)
|
|
@@ -31,115 +162,18 @@ export async function listCones(deps, opts = {}) {
|
|
|
31
162
|
// per-user view.
|
|
32
163
|
const reconciled = [];
|
|
33
164
|
for (const entry of registryEntries) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const STALE_MS = 10 * 60 * 1000;
|
|
38
|
-
// Reclaim if stale OR if reservedAt is missing (legacy/malformed entry
|
|
39
|
-
// from before commit e9011ba6 — could otherwise wedge cap forever).
|
|
40
|
-
const isStale = !entry.reservedAt || Date.now() - new Date(entry.reservedAt).getTime() > STALE_MS;
|
|
41
|
-
if (isStale) {
|
|
42
|
-
await deps.registry.remove(entry.sandboxId);
|
|
43
|
-
console.warn('[cloud-core] reclaimed stale reservation', {
|
|
44
|
-
sandboxId: entry.sandboxId,
|
|
45
|
-
reservedAt: entry.reservedAt ?? '(missing)',
|
|
46
|
-
});
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
// Active reservation — preserve as-is (in-flight operation holding a cap slot)
|
|
50
|
-
reconciled.push(entry);
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
const liveEntry = liveById.get(entry.sandboxId);
|
|
54
|
-
if (!liveEntry) {
|
|
55
|
-
// Substrate doesn't know about it — mark dead unless it's a placeholder.
|
|
56
|
-
// The 'pending-' prefix is a sentinel for "no real sandbox yet" (paired
|
|
57
|
-
// with state:'reserved' by reserveSlot before substrate.create).
|
|
58
|
-
if (entry.sandboxId.startsWith('pending-')) {
|
|
59
|
-
// Reservation placeholder — no real sandbox yet. Keep it alive.
|
|
60
|
-
reconciled.push(entry);
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
// Real sandbox is missing — mark dead
|
|
64
|
-
if (entry.state !== 'dead') {
|
|
65
|
-
await deps.registry.update(entry.sandboxId, { state: 'dead' });
|
|
66
|
-
}
|
|
67
|
-
reconciled.push({ ...entry, state: 'dead' });
|
|
68
|
-
}
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (entry.state !== liveEntry.state) {
|
|
72
|
-
await deps.registry.update(entry.sandboxId, { state: liveEntry.state });
|
|
73
|
-
}
|
|
74
|
-
reconciled.push({ ...entry, state: liveEntry.state });
|
|
75
|
-
liveById.delete(entry.sandboxId);
|
|
165
|
+
const next = await reconcileRegistryEntry(entry, liveById, deps.registry);
|
|
166
|
+
if (next)
|
|
167
|
+
reconciled.push(next);
|
|
76
168
|
}
|
|
77
169
|
// Pass 2: any substrate entries not in registry → recover.
|
|
78
170
|
for (const summary of liveById.values()) {
|
|
79
|
-
const
|
|
80
|
-
// Try to read the real joinUrl from the sandbox. Orphans created before this
|
|
81
|
-
// fix won't have it in metadata, but we can read /tmp/slicc-join.json directly.
|
|
82
|
-
let joinUrl = summary.metadata?.['joinUrl'] ?? '';
|
|
83
|
-
let trayId = summary.metadata?.['trayId'];
|
|
84
|
-
let lastJoinUpdatedAt = summary.metadata?.['lastJoinUpdatedAt'];
|
|
85
|
-
// Recover joinUrl from /tmp/slicc-join.json ONLY when sandbox is running.
|
|
86
|
-
// Calling substrate.connect() on a paused sandbox would RESUME it, which:
|
|
87
|
-
// (a) burns sandbox runtime silently
|
|
88
|
-
// (b) destabilizes the leader chromium mid-list
|
|
89
|
-
// Paused orphans surface with joinUrl='' — UI hides the Open button.
|
|
90
|
-
// User explicitly resumes via /api/cloud/resume which writes a fresh joinUrl.
|
|
91
|
-
if (!joinUrl && summary.state === 'running') {
|
|
92
|
-
try {
|
|
93
|
-
const handle = await deps.substrate.connect(summary.sandboxId);
|
|
94
|
-
const joinData = await handle.readFile('/tmp/slicc-join.json');
|
|
95
|
-
const parsed = JSON.parse(joinData);
|
|
96
|
-
joinUrl = parsed.joinUrl ?? '';
|
|
97
|
-
trayId = trayId ?? parsed.trayId;
|
|
98
|
-
lastJoinUpdatedAt = lastJoinUpdatedAt ?? parsed.updatedAt;
|
|
99
|
-
}
|
|
100
|
-
catch (err) {
|
|
101
|
-
// File not readable (sandbox is transitioning, or file doesn't exist).
|
|
102
|
-
// Leave joinUrl empty — UI will handle gracefully.
|
|
103
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
104
|
-
console.warn('[cloud-core] orphan recovery readFile failed', {
|
|
105
|
-
sandboxId: summary.sandboxId,
|
|
106
|
-
err: msg,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
const recovered = {
|
|
111
|
-
sandboxId: summary.sandboxId,
|
|
112
|
-
substrate: 'e2b',
|
|
113
|
-
name: summary.metadata?.['name'] ?? summary.name,
|
|
114
|
-
createdAt: summary.metadata?.['createdAt'] ?? now,
|
|
115
|
-
joinUrl,
|
|
116
|
-
lastSeen: now,
|
|
117
|
-
state: summary.state,
|
|
118
|
-
trayId,
|
|
119
|
-
lastJoinUpdatedAt,
|
|
120
|
-
metadata: summary.metadata,
|
|
121
|
-
};
|
|
171
|
+
const recovered = await recoverOrphan(summary, deps.substrate);
|
|
122
172
|
await deps.registry.append(recovered);
|
|
123
173
|
reconciled.push(recovered);
|
|
124
174
|
}
|
|
125
|
-
|
|
126
|
-
// alive past the 1h default. Failures are non-fatal — a sandbox that
|
|
127
|
-
// disappears between substrate.list() and extendTimeout() will be discovered
|
|
128
|
-
// dead on the NEXT list call.
|
|
129
|
-
const DEFAULT_TTL_MS = 60 * 60 * 1000;
|
|
130
|
-
await Promise.all(reconciled
|
|
131
|
-
.filter((c) => c.state === 'running')
|
|
132
|
-
.map(async (c) => {
|
|
133
|
-
try {
|
|
134
|
-
await deps.substrate.extendTimeout(c.sandboxId, DEFAULT_TTL_MS);
|
|
135
|
-
}
|
|
136
|
-
catch (err) {
|
|
137
|
-
// Sandbox may have died between list and extendTimeout; ignore.
|
|
138
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
139
|
-
console.warn('[cloud-core] extendTimeout failed', { sandboxId: c.sandboxId, err: msg });
|
|
140
|
-
}
|
|
141
|
-
}));
|
|
175
|
+
await extendRunningTimeouts(reconciled, deps.substrate);
|
|
142
176
|
// Apply the metadata filter to the RETURN value only — reconciliation
|
|
143
177
|
// above ran on all entries to keep zombies from accumulating.
|
|
144
|
-
return reconciled.filter(
|
|
178
|
+
return reconciled.filter((e) => matchesMetadataFilter(e, opts.metadata));
|
|
145
179
|
}
|
|
@@ -101,7 +101,22 @@ export interface WebhookEventMessage {
|
|
|
101
101
|
headers: Record<string, string>;
|
|
102
102
|
body: unknown;
|
|
103
103
|
timestamp: string;
|
|
104
|
+
/**
|
|
105
|
+
* Set when the sender is holding an HTTP request open for this delivery and
|
|
106
|
+
* wants the leader's disposition back as {@link LeaderWebhookDelivery}
|
|
107
|
+
* (issue #2524). Absent on fire-and-forget forwards (the preview-emit path),
|
|
108
|
+
* which the leader must not acknowledge.
|
|
109
|
+
*/
|
|
110
|
+
deliveryId?: string;
|
|
104
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* What became of one webhook delivery once it reached the leader's
|
|
114
|
+
* `LickManager`. Only the two failure values mean "no unit was woken":
|
|
115
|
+
* `filtered` is the webhook's own `--filter` doing its job, so it counts as
|
|
116
|
+
* accepted. Mirrored by `packages/webapp/src/scoops/lick-manager.ts`, which
|
|
117
|
+
* imports this type rather than restating it.
|
|
118
|
+
*/
|
|
119
|
+
export type WebhookDeliveryDisposition = 'delivered' | 'filtered' | 'unknown-webhook' | 'unresolved-target';
|
|
105
120
|
export interface WorkerPreviewRequest {
|
|
106
121
|
type: 'preview.request';
|
|
107
122
|
reqId: string;
|
|
@@ -238,9 +253,21 @@ export interface LeaderPushSend {
|
|
|
238
253
|
label: string;
|
|
239
254
|
requestId?: string;
|
|
240
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Leader → worker: the disposition of a `webhook.event` that carried a
|
|
258
|
+
* `deliveryId`. The worker is holding the webhook POST open on it, so a
|
|
259
|
+
* non-delivering disposition turns the HTTP receipt from `202 accepted` into an
|
|
260
|
+
* error the caller can act on (issue #2524). A leader too old to send this at
|
|
261
|
+
* all leaves the worker on its pre-#2524 `202` after a short budget.
|
|
262
|
+
*/
|
|
263
|
+
export interface LeaderWebhookDelivery {
|
|
264
|
+
type: 'webhook.delivery';
|
|
265
|
+
deliveryId: string;
|
|
266
|
+
disposition: WebhookDeliveryDisposition;
|
|
267
|
+
}
|
|
241
268
|
export type LeaderToWorkerControlMessage = {
|
|
242
269
|
type: 'ping';
|
|
243
|
-
} | LeaderPushRegister | LeaderPushSend | LeaderBootstrapOfferMessage | LeaderBootstrapIceCandidateMessage | LeaderBootstrapFailedMessage | LeaderPreviewResponseOk | LeaderPreviewResponseError | LeaderPreviewPurge | LeaderPreviewStateUpdate | LeaderBridgeCdpRequest | LeaderBridgeClose;
|
|
270
|
+
} | LeaderWebhookDelivery | LeaderPushRegister | LeaderPushSend | LeaderBootstrapOfferMessage | LeaderBootstrapIceCandidateMessage | LeaderBootstrapFailedMessage | LeaderPreviewResponseOk | LeaderPreviewResponseError | LeaderPreviewPurge | LeaderPreviewStateUpdate | LeaderBridgeCdpRequest | LeaderBridgeClose;
|
|
244
271
|
export interface BootstrapPollRequest {
|
|
245
272
|
action: 'poll';
|
|
246
273
|
controllerId?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tray-signaling.d.ts","sourceRoot":"","sources":["../src/tray-signaling.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"tray-signaling.d.ts","sourceRoot":"","sources":["../src/tray-signaling.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tray-signaling.js","sourceRoot":"","sources":["../src/tray-signaling.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAM,CAAC;AAChD,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAC5C,MAAM,CAAC,MAAM,6BAA6B,GAAG,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"tray-signaling.js","sourceRoot":"","sources":["../src/tray-signaling.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,53 @@
|
|
|
2
2
|
function respondBrowserUnavailable(res, err) {
|
|
3
3
|
res.status(503).json({ error: err instanceof Error ? err.message : 'Browser not connected' });
|
|
4
4
|
}
|
|
5
|
+
const WEBHOOK_OUTCOMES = [
|
|
6
|
+
'delivered',
|
|
7
|
+
'filtered',
|
|
8
|
+
'unknown-webhook',
|
|
9
|
+
'unresolved-target',
|
|
10
|
+
'malformed',
|
|
11
|
+
'failed',
|
|
12
|
+
];
|
|
13
|
+
function readDisposition(reply) {
|
|
14
|
+
const value = reply?.disposition;
|
|
15
|
+
return WEBHOOK_OUTCOMES.find((outcome) => outcome === value) ?? 'unknown';
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Answer the webhook POST. `delivered` / `filtered` keep the pre-#2524 receipt
|
|
19
|
+
* verbatim — a `--filter` dropping an event is the filter working, and the
|
|
20
|
+
* event WAS accepted. The remaining outcomes are the ones that used to lie.
|
|
21
|
+
*/
|
|
22
|
+
function respondWebhookDisposition(res, webhookId, outcome) {
|
|
23
|
+
if (outcome === 'delivered' || outcome === 'filtered' || outcome === 'unknown') {
|
|
24
|
+
res.json({ ok: true, received: true });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (outcome === 'unknown-webhook') {
|
|
28
|
+
res.status(404).json({
|
|
29
|
+
ok: false,
|
|
30
|
+
received: false,
|
|
31
|
+
error: `Webhook "${webhookId}" is not registered`,
|
|
32
|
+
code: 'WEBHOOK_NOT_REGISTERED',
|
|
33
|
+
});
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (outcome === 'unresolved-target') {
|
|
37
|
+
res.status(422).json({
|
|
38
|
+
ok: false,
|
|
39
|
+
received: false,
|
|
40
|
+
error: `Webhook "${webhookId}" targets a scoop or cone that no longer exists — the event was dropped`,
|
|
41
|
+
code: 'WEBHOOK_TARGET_UNRESOLVED',
|
|
42
|
+
});
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
res.status(500).json({
|
|
46
|
+
ok: false,
|
|
47
|
+
received: false,
|
|
48
|
+
error: `Webhook "${webhookId}" could not be dispatched (${outcome})`,
|
|
49
|
+
code: 'WEBHOOK_DISPATCH_FAILED',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
5
52
|
/**
|
|
6
53
|
* Routes that forward to the connected browser over the lick bridge:
|
|
7
54
|
* tray status, webhook management + receiver, and cron task management.
|
|
@@ -80,15 +127,35 @@ export function registerLickApiRoutes(app, bridge) {
|
|
|
80
127
|
body = { raw };
|
|
81
128
|
}
|
|
82
129
|
}
|
|
83
|
-
// Forward to browser for
|
|
84
|
-
|
|
130
|
+
// Forward to the browser and WAIT for the disposition. Before #2524 this
|
|
131
|
+
// broadcast and answered `{ ok: true, received: true }` unconditionally —
|
|
132
|
+
// so a delivery to a webhook nobody had registered, or one whose target
|
|
133
|
+
// scoop was gone, was reported to the caller as a success.
|
|
134
|
+
const event = {
|
|
85
135
|
type: 'webhook_event',
|
|
86
136
|
webhookId: id,
|
|
87
137
|
timestamp: new Date().toISOString(),
|
|
88
138
|
headers: req.headers,
|
|
89
139
|
body,
|
|
90
|
-
}
|
|
91
|
-
|
|
140
|
+
};
|
|
141
|
+
let disposition;
|
|
142
|
+
try {
|
|
143
|
+
disposition = readDisposition(await sendLickRequest('webhook_event', event));
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
147
|
+
// A browser older than #2524 does not know the request shape. Fall back
|
|
148
|
+
// to the legacy broadcast (and its legacy receipt) rather than failing a
|
|
149
|
+
// delivery the leader would have handled fine.
|
|
150
|
+
if (msg.includes('Unknown request type')) {
|
|
151
|
+
broadcastLickEvent(event);
|
|
152
|
+
res.json({ ok: true, received: true });
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
res.status(503).json({ ok: false, received: false, error: msg, code: 'BROWSER_UNAVAILABLE' });
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
respondWebhookDisposition(res, id, disposition);
|
|
92
159
|
});
|
|
93
160
|
// Cron task management API — forwards to browser
|
|
94
161
|
app.get('/api/crontasks', async (_req, res) => {
|