sliccy 6.154.1 → 6.154.3
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/browser-shutdown.d.ts +6 -1
- package/dist/node-server/browser-shutdown.js +3 -3
- package/dist/node-server/chrome-launch.d.ts +3 -0
- package/dist/node-server/chrome-launch.js +2 -1
- package/dist/node-server/electron-controller.d.ts +63 -2
- package/dist/node-server/electron-controller.js +86 -48
- package/dist/node-server/electron-runtime.d.ts +15 -1
- package/dist/node-server/electron-runtime.js +10 -10
- package/dist/node-server/fetch-proxy-gzip.js +5 -7
- package/dist/node-server/hosted-bootstrap.d.ts +1 -0
- package/dist/node-server/hosted-bootstrap.js +12 -14
- package/dist/node-server/hostfs.d.ts +37 -1
- package/dist/node-server/hostfs.js +5 -5
- package/dist/node-server/install-cli.d.ts +1 -0
- package/dist/node-server/install-cli.js +1 -1
- package/dist/node-server/launch-url.js +2 -6
- package/dist/node-server/leader-restart.d.ts +9 -0
- package/dist/node-server/leader-restart.js +1 -1
- package/dist/node-server/publish-chrome-web-store-main.d.ts +1 -0
- package/dist/node-server/publish-chrome-web-store-main.js +13 -0
- package/dist/node-server/publish-chrome-web-store.d.ts +2 -0
- package/dist/node-server/publish-chrome-web-store.js +2 -15
- package/dist/node-server/release-package-main.d.ts +1 -0
- package/dist/node-server/release-package-main.js +17 -0
- package/dist/node-server/release-package.d.ts +38 -3
- package/dist/node-server/release-package.js +46 -50
- package/dist/node-server/routes/fetch-proxy.d.ts +40 -1
- package/dist/node-server/routes/fetch-proxy.js +20 -12
- package/dist/node-server/routes/secrets.d.ts +23 -1
- package/dist/node-server/routes/secrets.js +34 -30
- package/dist/node-server/secrets/proxy-manager.d.ts +2 -1
- package/dist/node-server/secrets/proxy-manager.js +27 -26
- package/dist/node-server/secrets/session-id-file.d.ts +5 -1
- package/dist/node-server/secrets/session-id-file.js +4 -4
- package/dist/node-server/secrets/sign-and-forward.d.ts +8 -0
- package/dist/node-server/secrets/sign-and-forward.js +1 -1
- package/dist/node-server/sudo/select.d.ts +2 -0
- package/dist/node-server/sudo/select.js +1 -1
- package/dist/node-server/sudo/tty-backend.d.ts +1 -0
- package/dist/node-server/sudo/tty-backend.js +1 -1
- package/dist/ui/.vite/manifest.json +171 -171
- package/dist/ui/assets/SKILL-BGww2dFz.js +1 -0
- package/dist/ui/assets/{SKILL-cqUV8C7U.js → SKILL-Cl9PVarg.js} +1 -1
- package/dist/ui/assets/{account-store-BqdqUcgS.js → account-store-BYDUpeqo.js} +1 -1
- package/dist/ui/assets/{account-store-BgFuxoIt.js → account-store-BvEgWXDi.js} +1 -1
- package/dist/ui/assets/{adobe-BWEqvpfE.js → adobe-Bf44Mdem.js} +1 -1
- package/dist/ui/assets/{adobe-De7VHe-t.js → adobe-DIeHS2Yx.js} +1 -1
- package/dist/ui/assets/{agent-bridge-EXV0qNBB.js → agent-bridge-pmiSNwoc.js} +1 -1
- package/dist/ui/assets/{agent-panels-dcldnH3V.js → agent-panels-CWBbnFvW.js} +1 -1
- package/dist/ui/assets/api-endpoint-3qEcn7BY.js +1 -0
- package/dist/ui/assets/{apply-layout-DVgK-NlB.js → apply-layout-Eft8a3mq.js} +1 -1
- package/dist/ui/assets/{apps-BqrASr5C.js → apps-BklKPyC6.js} +1 -1
- package/dist/ui/assets/{auto-mount-table-0jMTi9rV.js → auto-mount-table-BDyGIOIb.js} +1 -1
- package/dist/ui/assets/{azure-openai-vXGFZfPE.js → azure-openai-7VfJn11R.js} +1 -1
- package/dist/ui/assets/{azure-openai-94kdulWT.js → azure-openai-o6cG2UDZ.js} +1 -1
- package/dist/ui/assets/{backend-hostfs-BxfjZHzO.js → backend-hostfs-BJU2QL9q.js} +1 -1
- package/dist/ui/assets/{budget-usage-source-BNEsxDHX.js → budget-usage-source-B5GSAynq.js} +1 -1
- package/dist/ui/assets/{cdp-D0GOzfX9.js → cdp-Di9nU0k7.js} +1 -1
- package/dist/ui/assets/{cerebras-eckgXNEU.js → cerebras-BRpdCI1J.js} +1 -1
- package/dist/ui/assets/{cerebras-v41u2mpJ.js → cerebras-DuUgRhfz.js} +1 -1
- package/dist/ui/assets/{client-BOSdqiLb.js → client-B6iKb_ye.js} +1 -1
- package/dist/ui/assets/{composer-speech-ButVgF9_.js → composer-speech-Chlrvbhg.js} +1 -1
- package/dist/ui/assets/{connect-surface-C0WXwJHl.js → connect-surface-DBXtjfUP.js} +1 -1
- package/dist/ui/assets/{core-D59qaXEb.js → core-C9qgaWgE.js} +2 -2
- package/dist/ui/assets/{directed-approval-D1CLB6cG.js → directed-approval-C2OTs8pv.js} +1 -1
- package/dist/ui/assets/dist-BPPG9gF6.js +12 -0
- package/dist/ui/assets/dist-BnmWpUNI.js +18 -0
- package/dist/ui/assets/{dist-DCrgApJQ.js → dist-CFI3XeIO.js} +1 -1
- package/dist/ui/assets/{dist-UJah3hVh.js → dist-Ciikigd5.js} +1 -1
- package/dist/ui/assets/{ensure-speech-assets-uGHvkmNC.js → ensure-speech-assets-DOu9EHnU.js} +1 -1
- package/dist/ui/assets/{extension-ops-B9_1VB7W.js → extension-ops-B05mAXsq.js} +1 -1
- package/dist/ui/assets/{fs-DK3k_wQh.js → fs-Dqh17T5J.js} +2 -2
- package/dist/ui/assets/{fs-mC_RRTZZ.js → fs-T4T9WqhX.js} +1 -1
- package/dist/ui/assets/{github-CT-Oyj_0.js → github-CL_7yoHL.js} +1 -1
- package/dist/ui/assets/github-auth-A6Mopmg8.js +1 -0
- package/dist/ui/assets/{github-copilot-CACLYChL.js → github-copilot-B2ugBoRh.js} +1 -1
- package/dist/ui/assets/{github-copilot-D2H-jvYu.js → github-copilot-_5qoxGCT.js} +1 -1
- package/dist/ui/assets/github-install-8yCqacop.js +1 -0
- package/dist/ui/assets/{github-BLOM522m.js → github-koUNqi7Y.js} +2 -2
- package/dist/ui/assets/github-zip-CV41OIqx.js +1 -0
- package/dist/ui/assets/{guest-tool-gate-6uzbOlEb.js → guest-tool-gate-_NuvhpkB.js} +1 -1
- package/dist/ui/assets/{hear-BXcOoxVl.js → hear-DScAQlnK.js} +1 -1
- package/dist/ui/assets/{help-BAqixBa0.js → help-BvOxNE_5.js} +1 -1
- package/dist/ui/assets/{js-realm-worker-CRY5Fv-C.js → js-realm-worker-fE5Mi5F2.js} +1 -1
- package/dist/ui/assets/{kernel-worker-C1DLWYBM.js → kernel-worker-DURzH7I0.js} +586 -585
- package/dist/ui/assets/{lick-urls-HYeTtmbf.js → lick-urls-BZVq_P3x.js} +1 -1
- package/dist/ui/assets/{lick-ws-bridge-DqmithfF.js → lick-ws-bridge-CDToX4F0.js} +1 -1
- package/dist/ui/assets/{local-llm-D723hPLf.js → local-llm-D_HQ1XG8.js} +1 -1
- package/dist/ui/assets/{local-llm-Cv98D1SL.js → local-llm-QN3Afs2P.js} +1 -1
- package/dist/ui/assets/{main-C-pVQBFE.js → main-DyPgoSsm.js} +3 -3
- package/dist/ui/assets/{main-cherry-BdH-nz0p.js → main-cherry-v6ksBa29.js} +1 -1
- package/dist/ui/assets/{memory-curation-seam-eUVjWT6W.js → memory-curation-seam-DXGqoWuF.js} +1 -1
- package/dist/ui/assets/{mount-3VFe3Pa7.js → mount-CdTXGHvm.js} +3 -3
- package/dist/ui/assets/{mount-Dh4tJ6XQ.js → mount-Drg9K5Dc.js} +1 -1
- package/dist/ui/assets/{mount-bridge-client-B5qLR9Xe.js → mount-bridge-client-DWY67yip.js} +1 -1
- package/dist/ui/assets/{mount-recovery-Cp7KYqTq.js → mount-recovery-CUhmg6UT.js} +1 -1
- package/dist/ui/assets/{new-session-hwwKqrO5.js → new-session-BBCvIUGf.js} +2 -2
- package/dist/ui/assets/{oauth-bootstrap-BP7bAgwJ.js → oauth-bootstrap-37qdAFNO.js} +2 -2
- package/dist/ui/assets/{oauth-service-BZl-9EoE.js → oauth-service-IlBYPvwr.js} +1 -1
- package/dist/ui/assets/{onboarding-orchestrator-DOOTJ9OG.js → onboarding-orchestrator-CgzN2Wut.js} +1 -1
- package/dist/ui/assets/{openai-codex--3foibzj.js → openai-codex-CByg7ExV.js} +1 -1
- package/dist/ui/assets/{openai-codex-vGpH6Ldz.js → openai-codex-yA937LOi.js} +1 -1
- package/dist/ui/assets/{openrouter-D17vOiK5.js → openrouter-Bq9TiHqe.js} +1 -1
- package/dist/ui/assets/{openrouter-B3v5OQPe.js → openrouter-D1YZxJcz.js} +1 -1
- package/dist/ui/assets/{openrouter-free-Mha-ykI6.js → openrouter-free-BwEyNzqx.js} +1 -1
- package/dist/ui/assets/{openrouter-free-DndyeHH8.js → openrouter-free-Db1fart9.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-C4aGXNeF.js → openrouter-oauth-CFlNuCUA.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-ov7dnUnh.js → openrouter-oauth-Cj61jTQa.js} +1 -1
- package/dist/ui/assets/{panel-rpc-handlers-CDsWA3s8.js → panel-rpc-handlers-CLsTGXPT.js} +2 -2
- package/dist/ui/assets/{panelize-shell-CXpZWzgn.js → panelize-shell-Bf9yr7Qf.js} +1 -1
- package/dist/ui/assets/{port-bridge-client-CzOd_VUf.js → port-bridge-client-Ds3xwapn.js} +1 -1
- package/dist/ui/assets/provenance-DdmsQ3Nr.js +1 -0
- package/dist/ui/assets/{provider-BtT9_ZsY.js → provider-BD-0pPN8.js} +1 -1
- package/dist/ui/assets/{provider-2fDgJArv.js → provider-C66yENxs.js} +2 -2
- package/dist/ui/assets/provider-settings-DysstkNf.js +1 -0
- package/dist/ui/assets/provider-store-access-BeX0OGEs.js +1 -0
- package/dist/ui/assets/provider-store-access-Cc45yVjV.js +1 -0
- package/dist/ui/assets/{providers-BWmPj6ob.js → providers-BtR_Oc-m.js} +2 -2
- package/dist/ui/assets/{providers-D_mm-oKv.js → providers-C2bj3jKv.js} +1 -1
- package/dist/ui/assets/proxied-fetch-DEK8DT-z.js +1 -0
- package/dist/ui/assets/{quick-llm-Ch_X9AH9.js → quick-llm-D-nM71kO.js} +1 -1
- package/dist/ui/assets/{recovery-screen-Ldb8xS5S.js → recovery-screen-YuMG99R0.js} +1 -1
- package/dist/ui/assets/{redirect-uri-C6BjkDE0.js → redirect-uri-CgKdq7-K.js} +1 -1
- package/dist/ui/assets/rest-ops-Bps3Ay-k.js +1 -0
- package/dist/ui/assets/{run-DXUToOLa.js → run-CDicdmnK.js} +1 -1
- package/dist/ui/assets/{run-Bmb6g7U2.js → run-CdzvBpS0.js} +3 -3
- package/dist/ui/assets/{run-xG3-BOwR.js → run-D5LoTYwZ.js} +1 -1
- package/dist/ui/assets/{secrets-bridge-client-DBeju3mP.js → secrets-bridge-client-ZpBL1Zhl.js} +1 -1
- package/dist/ui/assets/{session-jsonl-DlNZajGM.js → session-jsonl-By2mMQqb.js} +1 -1
- package/dist/ui/assets/{setup-feature-flags-remote-D-IPtvuE.js → setup-feature-flags-remote-BuBwyibG.js} +1 -1
- package/dist/ui/assets/sidecar-repair-DRkuMyU_.js +1 -0
- package/dist/ui/assets/{silent-renew-backoff-BocHwLdw.js → silent-renew-backoff-CS_htRXY.js} +1 -1
- package/dist/ui/assets/{skills-CTDnQTUW.js → skills-Cg34jB8I.js} +1 -1
- package/dist/ui/assets/slicc-version-DbT116fI.js +1 -0
- package/dist/ui/assets/{snapshot-BZ-_n6uU.js → snapshot-B6SF0dpr.js} +1 -1
- package/dist/ui/assets/{snapshot-features-BWzc1ERL.js → snapshot-features-A-jmNbR9.js} +1 -1
- package/dist/ui/assets/{soundscape-CowYlYSr.js → soundscape-smt9sQsm.js} +1 -1
- package/dist/ui/assets/{speak-rCiisrK7.js → speak-DeYQY3R8.js} +1 -1
- package/dist/ui/assets/{sprinkle-manager-DaVvW4Pl.js → sprinkle-manager-q3Ku7Zvz.js} +1 -1
- package/dist/ui/assets/{sprinkle-renderer-CDcbztf1.js → sprinkle-renderer-B9Re-_rl.js} +1 -1
- package/dist/ui/assets/{state-DHftNsPN.js → state-NPdZC8rd.js} +1 -1
- package/dist/ui/assets/{store-CTM4679Z.js → store-BbRPb7Di.js} +1 -1
- package/dist/ui/assets/{store-MZJeSOYF.js → store-BfdKxUK3.js} +1 -1
- package/dist/ui/assets/{sudo-DLRz2gLx.js → sudo-CtUlj7iw.js} +1 -1
- package/dist/ui/assets/{sync-dialog-BUFCYPxf.js → sync-dialog-CXgM1Uvh.js} +1 -1
- package/dist/ui/assets/{transformers-env-BfjOXz9f.js → transformers-env-C7dJtjmn.js} +1 -1
- package/dist/ui/assets/{tray-runtime-config--rtR6Gxd.js → tray-runtime-config-CRZxBChd.js} +1 -1
- package/dist/ui/assets/{tray-sidecar-BP70hrzZ.js → tray-sidecar-BtFzGslJ.js} +1 -1
- package/dist/ui/assets/types-Dzr0qYl3.js +1 -0
- package/dist/ui/assets/{upgrade-detection-Be8A7nY7.js → upgrade-detection-BWpOvoVx.js} +1 -1
- package/dist/ui/assets/upskill-lick-skip-ZGNBIEXS.js +1 -0
- package/dist/ui/assets/{voice-reply-BtqQr2DX.js → voice-reply-CCkglCEp.js} +1 -1
- package/dist/ui/assets/{wc-attach-zW7DuRo7.js → wc-attach-C8CgBet1.js} +2 -2
- package/dist/ui/assets/{wc-extension-DGYx9jMG.js → wc-extension-CHb4P34s.js} +2 -2
- package/dist/ui/assets/{wc-follower-Bc6Mmmda.js → wc-follower-DmIZM7ao.js} +3 -3
- package/dist/ui/assets/{wc-follower-oauth-DOaUEVw8.js → wc-follower-oauth-CQgoqG9v.js} +1 -1
- package/dist/ui/assets/{wc-gelatiere-CdqOnO6V.js → wc-gelatiere-B8JhDjog.js} +1 -1
- package/dist/ui/assets/{wc-live-BrKyGs-7.js → wc-live-gCevk2Vh.js} +5 -5
- package/dist/ui/assets/{wc-nav-dSAbBsA5.js → wc-nav-CpHEdfwe.js} +2 -2
- package/dist/ui/assets/{wc-onboarding-C1uyya9g.js → wc-onboarding-iwpabkC3.js} +2 -2
- package/dist/ui/assets/{wc-placeholder-Cw7uGVuJ.js → wc-placeholder-Cnl3OQfj.js} +3 -3
- package/dist/ui/assets/{wc-secret-request-CwA6DiWC.js → wc-secret-request-B9oB6kAt.js} +1 -1
- package/dist/ui/assets/{wc-settings-DJEhDr23.js → wc-settings-_0roTDdf.js} +3 -3
- package/dist/ui/assets/{wc-shell-C-DwVQCG.js → wc-shell-DkJPUxGT.js} +3 -3
- package/dist/ui/assets/{wc-shortcut-config-D9HBEfqv.js → wc-shortcut-config-CIup4nE8.js} +1 -1
- package/dist/ui/assets/{wc-shortcuts-7i4KncH2.js → wc-shortcuts-C9SNe-vm.js} +1 -1
- package/dist/ui/assets/{wc-sprinkles-DsRJYYRI.js → wc-sprinkles-C7_z9Yfm.js} +2 -2
- package/dist/ui/assets/{wc-transcript-export-Dq9HNkpA.js → wc-transcript-export-DnNvjokD.js} +2 -2
- package/dist/ui/assets/{wc-tray-f6xi76Bq.js → wc-tray-DhWe0-Ie.js} +4 -4
- package/dist/ui/assets/welcome-detection-BbyyaOna.js +1 -0
- package/dist/ui/assets/{whisper-session-B6knWfce.js → whisper-session-CMXLPRdV.js} +2 -2
- package/dist/ui/assets/{xai-grok-B0KRInrx.js → xai-grok-BNRMntfw.js} +1 -1
- package/dist/ui/assets/{xai-grok-BIfBT2BL.js → xai-grok-BiF6I0r6.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/ui/packages/webapp/index.html +3 -3
- package/package.json +3 -3
- package/dist/ui/assets/SKILL-CuHGVKei.js +0 -1
- package/dist/ui/assets/api-endpoint-Lx_FU5cP.js +0 -1
- package/dist/ui/assets/dist-B3W7EgGh.js +0 -12
- package/dist/ui/assets/dist-CmI2JIly.js +0 -18
- package/dist/ui/assets/github-install-Bk-KTJD-.js +0 -1
- package/dist/ui/assets/github-zip-Dvb4zHOl.js +0 -1
- package/dist/ui/assets/provider-settings-D2AYwHOa.js +0 -1
- package/dist/ui/assets/provider-store-access-BdvWm9Kj.js +0 -1
- package/dist/ui/assets/provider-store-access-C5A5Se3D.js +0 -1
- package/dist/ui/assets/proxied-fetch-B57RqgUb.js +0 -1
- package/dist/ui/assets/rest-ops-CB2up_K0.js +0 -1
- package/dist/ui/assets/sidecar-repair-DRUvDMSe.js +0 -1
- package/dist/ui/assets/slicc-version-CWl4uhV3.js +0 -1
- package/dist/ui/assets/welcome-detection-DmqQ-hO4.js +0 -1
|
@@ -56,7 +56,8 @@
|
|
|
56
56
|
* otherwise express's default handler answers malformed JSON with code-less
|
|
57
57
|
* HTML and the webapp cannot rethrow a faithful `FsError`.
|
|
58
58
|
*/
|
|
59
|
-
import type { Express } from 'express';
|
|
59
|
+
import type { ErrorRequestHandler, Express, Response } from 'express';
|
|
60
|
+
import { createReadStream } from 'fs';
|
|
60
61
|
import type { HostMountMapping } from './runtime-flags.js';
|
|
61
62
|
/**
|
|
62
63
|
* Cap on a WHOLE-FILE `GET /api/hostfs/read` (and on `PUT /write`). It exists
|
|
@@ -68,6 +69,14 @@ import type { HostMountMapping } from './runtime-flags.js';
|
|
|
68
69
|
* Matches the webapp's hostfs body cap (backend-hostfs.ts).
|
|
69
70
|
*/
|
|
70
71
|
export declare const HOSTFS_MAX_BODY_BYTES: number;
|
|
72
|
+
interface FsCodeError {
|
|
73
|
+
status: number;
|
|
74
|
+
code: string;
|
|
75
|
+
message: string;
|
|
76
|
+
}
|
|
77
|
+
/** Map an fs errno (or traversal rejection) to an HTTP status + FsError code. */
|
|
78
|
+
export declare function toFsCodeError(err: unknown): FsCodeError;
|
|
79
|
+
export declare function sendFsError(res: Response, err: unknown): void;
|
|
71
80
|
/**
|
|
72
81
|
* Resolve `relPath` against `root`, rejecting anything that leaves the root
|
|
73
82
|
* either lexically (`..`) or via symlinks. Symlinks *inside* the root that
|
|
@@ -114,6 +123,22 @@ export type ParsedByteRange = {
|
|
|
114
123
|
* A zero-length file has no satisfiable range at all.
|
|
115
124
|
*/
|
|
116
125
|
export declare function parseByteRange(header: string | undefined, size: number): ParsedByteRange;
|
|
126
|
+
/**
|
|
127
|
+
* Stream a file (or a window of it) to the response instead of buffering it.
|
|
128
|
+
*
|
|
129
|
+
* `readFile` + `res.send` held the whole body in the server process — 92 MB
|
|
130
|
+
* per pack read in the benchmark of issue #2707 — for no benefit: express is
|
|
131
|
+
* already writing to a socket. Headers are committed on the stream's `open`,
|
|
132
|
+
* so a failure to open (a raced unlink, EACCES) still rejects BEFORE anything
|
|
133
|
+
* is on the wire and is mapped to the usual errno JSON by the caller. After
|
|
134
|
+
* that point the status line is spent, so a mid-body failure can only be
|
|
135
|
+
* reported by destroying the connection — a truncated body the client's
|
|
136
|
+
* `fetch` rejects on, which its retry path already handles.
|
|
137
|
+
*/
|
|
138
|
+
export declare function streamFileBody(res: Response, target: string, status: number, headers: Record<string, string>, window?: {
|
|
139
|
+
start: number;
|
|
140
|
+
end: number;
|
|
141
|
+
}, createStream?: typeof createReadStream): Promise<void>;
|
|
117
142
|
/**
|
|
118
143
|
* Resolve the configured mappings to realpath'd roots. Mappings whose OS
|
|
119
144
|
* path does not exist (or is not a directory) are dropped with a warning —
|
|
@@ -139,6 +164,16 @@ export declare function isHostFsStableBodyRequest(req: {
|
|
|
139
164
|
method?: string;
|
|
140
165
|
url?: string;
|
|
141
166
|
}): boolean;
|
|
167
|
+
/**
|
|
168
|
+
* Map body-parser failures on the hostfs surface to `{ code, message }`.
|
|
169
|
+
* Registered after the routes (express only runs an error handler for
|
|
170
|
+
* middleware mounted before it), so it catches the stable dispatcher's JSON
|
|
171
|
+
* parser AND `PUT /write`'s raw parser. Mirrors swift-server, which answers
|
|
172
|
+
* 400 `EINVAL` for an unparseable body and 413 `EFBIG` for an oversized one.
|
|
173
|
+
* Anything that is not a body-parser error, and every non-hostfs path, is
|
|
174
|
+
* passed through untouched.
|
|
175
|
+
*/
|
|
176
|
+
export declare const hostFsBodyErrorHandler: ErrorRequestHandler;
|
|
142
177
|
/**
|
|
143
178
|
* Register the /api/hostfs routes for the resolved mount roots. The route
|
|
144
179
|
* surface mirrors `MountBackend` 1:1 (webapp `backend-hostfs.ts`):
|
|
@@ -167,3 +202,4 @@ export declare function isHostFsStableBodyRequest(req: {
|
|
|
167
202
|
* per-op routes — so every error this route emits MUST carry a `code`.
|
|
168
203
|
*/
|
|
169
204
|
export declare function registerHostFsRoutes(app: Express, roots: readonly HostMountRoot[]): void;
|
|
205
|
+
export {};
|
|
@@ -75,7 +75,7 @@ function errnoCode(err) {
|
|
|
75
75
|
return typeof code === 'string' ? code : null;
|
|
76
76
|
}
|
|
77
77
|
/** Map an fs errno (or traversal rejection) to an HTTP status + FsError code. */
|
|
78
|
-
function toFsCodeError(err) {
|
|
78
|
+
export function toFsCodeError(err) {
|
|
79
79
|
const code = errnoCode(err) ?? 'EIO';
|
|
80
80
|
const message = err instanceof Error ? err.message : String(err);
|
|
81
81
|
switch (code) {
|
|
@@ -93,7 +93,7 @@ function toFsCodeError(err) {
|
|
|
93
93
|
return { status: 500, code, message };
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
-
function sendFsError(res, err) {
|
|
96
|
+
export function sendFsError(res, err) {
|
|
97
97
|
// A streamed body may already have committed the status line; the only
|
|
98
98
|
// honest signal left is a broken connection.
|
|
99
99
|
if (res.headersSent) {
|
|
@@ -200,8 +200,8 @@ export function parseByteRange(header, size) {
|
|
|
200
200
|
* reported by destroying the connection — a truncated body the client's
|
|
201
201
|
* `fetch` rejects on, which its retry path already handles.
|
|
202
202
|
*/
|
|
203
|
-
async function streamFileBody(res, target, status, headers, window) {
|
|
204
|
-
const stream =
|
|
203
|
+
export async function streamFileBody(res, target, status, headers, window, createStream = createReadStream) {
|
|
204
|
+
const stream = createStream(target, window);
|
|
205
205
|
await new Promise((resolveStream, rejectStream) => {
|
|
206
206
|
let committed = false;
|
|
207
207
|
stream.once('open', () => {
|
|
@@ -382,7 +382,7 @@ export function isHostFsStableBodyRequest(req) {
|
|
|
382
382
|
* Anything that is not a body-parser error, and every non-hostfs path, is
|
|
383
383
|
* passed through untouched.
|
|
384
384
|
*/
|
|
385
|
-
const hostFsBodyErrorHandler = (err, req, res, next) => {
|
|
385
|
+
export const hostFsBodyErrorHandler = (err, req, res, next) => {
|
|
386
386
|
const type = err?.type;
|
|
387
387
|
if (typeof type !== 'string' || res.headersSent || !isHostFsPath(req.path)) {
|
|
388
388
|
next(err);
|
|
@@ -4,6 +4,7 @@ export interface ResolvedCliAsset {
|
|
|
4
4
|
downloadUrl: string;
|
|
5
5
|
}
|
|
6
6
|
export declare function cliAssetName(platform: string, arch: string): string | null;
|
|
7
|
+
export declare function defaultIsWritableDir(dir: string): boolean;
|
|
7
8
|
/**
|
|
8
9
|
* OS-idiomatic install dir, in preference order:
|
|
9
10
|
*
|
|
@@ -30,7 +30,7 @@ export function cliAssetName(platform, arch) {
|
|
|
30
30
|
}
|
|
31
31
|
return `slicc-${os}-${goArch}${os === 'windows' ? '.exe' : ''}`;
|
|
32
32
|
}
|
|
33
|
-
function defaultIsWritableDir(dir) {
|
|
33
|
+
export function defaultIsWritableDir(dir) {
|
|
34
34
|
try {
|
|
35
35
|
accessSync(dir, constants.W_OK);
|
|
36
36
|
return true;
|
|
@@ -15,12 +15,8 @@ function buildTrayJoinLaunchUrl(locationHref, joinUrl) {
|
|
|
15
15
|
}
|
|
16
16
|
return buildCanonicalTrayLaunchUrl(locationHref, parsedJoinUrl.joinUrl);
|
|
17
17
|
}
|
|
18
|
-
function buildTrayLeadLaunchUrl(locationHref,
|
|
19
|
-
|
|
20
|
-
if (!normalizedBase) {
|
|
21
|
-
throw new Error(`Invalid tray worker base URL: ${workerBaseUrl}`);
|
|
22
|
-
}
|
|
23
|
-
return buildCanonicalTrayLaunchUrl(locationHref, normalizedBase);
|
|
18
|
+
function buildTrayLeadLaunchUrl(locationHref, normalizedWorkerBaseUrl) {
|
|
19
|
+
return buildCanonicalTrayLaunchUrl(locationHref, normalizedWorkerBaseUrl);
|
|
24
20
|
}
|
|
25
21
|
export function resolveCliBrowserLaunchUrl(options) {
|
|
26
22
|
if (options.lead && options.join) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Express } from 'express';
|
|
2
|
+
import { WebSocket } from 'ws';
|
|
2
3
|
export interface CdpTargetInfo {
|
|
3
4
|
id?: string;
|
|
4
5
|
targetId?: string;
|
|
@@ -18,6 +19,14 @@ export type RestartResult = {
|
|
|
18
19
|
message: string;
|
|
19
20
|
};
|
|
20
21
|
export declare function restartLeader(cdp: CdpLike, pageUrlPrefix: string): Promise<RestartResult>;
|
|
22
|
+
export declare class CdpClient {
|
|
23
|
+
private readonly socket;
|
|
24
|
+
private nextId;
|
|
25
|
+
private readonly pending;
|
|
26
|
+
constructor(socket: WebSocket);
|
|
27
|
+
send(method: string, params?: unknown, sessionId?: string): Promise<unknown>;
|
|
28
|
+
close(): void;
|
|
29
|
+
}
|
|
21
30
|
/**
|
|
22
31
|
* HTTP-backed CdpLike. Implements Target.getTargets via the CDP HTTP /json
|
|
23
32
|
* endpoint. Target.attachToTarget + Page.reload are sent over WebSocket.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { publishChromeWebStoreRelease } from './publish-chrome-web-store.js';
|
|
4
|
+
const dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
5
|
+
const projectRoot = resolve(dirname, '..', '..');
|
|
6
|
+
const manifestPath = process.argv[2]
|
|
7
|
+
? resolve(projectRoot, process.argv[2])
|
|
8
|
+
: resolve(projectRoot, 'artifacts', 'release', 'release-artifacts.json');
|
|
9
|
+
void publishChromeWebStoreRelease({ manifestPath }).catch((error) => {
|
|
10
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11
|
+
console.error(`[publish-chrome-web-store] ${message}`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
});
|
|
@@ -46,6 +46,8 @@ export interface PublishChromeWebStoreOptions {
|
|
|
46
46
|
export declare function parseServiceAccountCredentials(jsonValue: string | undefined, base64Value: string | undefined): ServiceAccountCredentials | undefined;
|
|
47
47
|
export declare function readChromeWebStoreConfig(env?: NodeJS.ProcessEnv): ChromeWebStoreConfig | null;
|
|
48
48
|
export declare function createServiceAccountAssertion(serviceAccount: ServiceAccountCredentials, nowSeconds: number): string;
|
|
49
|
+
export declare function cancelPendingSubmission(config: ChromeWebStoreConfig, accessToken: string, fetchImpl: typeof fetch): Promise<void>;
|
|
50
|
+
export declare function waitForPendingReviewCancellation(config: ChromeWebStoreConfig, accessToken: string, fetchImpl: typeof fetch, waitMs: (ms: number) => Promise<void>, pollIntervalMs: number, maxAttempts: number): Promise<FetchStatusResponse>;
|
|
49
51
|
export declare function waitForUploadCompletion(config: ChromeWebStoreConfig, accessToken: string, fetchImpl: typeof fetch, waitMs: (ms: number) => Promise<void>, pollIntervalMs: number, maxAttempts: number): Promise<FetchStatusResponse>;
|
|
50
52
|
export declare function publishChromeWebStoreRelease(options?: PublishChromeWebStoreOptions): Promise<ChromeWebStorePublishResult | null>;
|
|
51
53
|
export {};
|
|
@@ -201,7 +201,7 @@ async function fetchUploadStatus(config, accessToken, fetchImpl) {
|
|
|
201
201
|
});
|
|
202
202
|
return expectJsonResponse(response, 'Chrome Web Store upload status');
|
|
203
203
|
}
|
|
204
|
-
async function cancelPendingSubmission(config, accessToken, fetchImpl) {
|
|
204
|
+
export async function cancelPendingSubmission(config, accessToken, fetchImpl) {
|
|
205
205
|
const itemName = createPublisherItemName(config);
|
|
206
206
|
const response = await fetchImpl(`https://chromewebstore.googleapis.com/v2/${itemName}:cancelSubmission`, {
|
|
207
207
|
method: 'POST',
|
|
@@ -214,7 +214,7 @@ async function cancelPendingSubmission(config, accessToken, fetchImpl) {
|
|
|
214
214
|
throw new Error(`Chrome Web Store cancel submission failed with ${response.status} ${response.statusText}.${text ? ` ${text}` : ''}`);
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
|
-
async function waitForPendingReviewCancellation(config, accessToken, fetchImpl, waitMs, pollIntervalMs, maxAttempts) {
|
|
217
|
+
export async function waitForPendingReviewCancellation(config, accessToken, fetchImpl, waitMs, pollIntervalMs, maxAttempts) {
|
|
218
218
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
219
219
|
const status = await fetchUploadStatus(config, accessToken, fetchImpl);
|
|
220
220
|
if (status.submittedItemRevisionStatus?.state !== 'PENDING_REVIEW') {
|
|
@@ -358,16 +358,3 @@ export async function publishChromeWebStoreRelease(options = {}) {
|
|
|
358
358
|
publishState: publishResponse.state,
|
|
359
359
|
};
|
|
360
360
|
}
|
|
361
|
-
async function main() {
|
|
362
|
-
const manifestPath = process.argv[2]
|
|
363
|
-
? resolve(PROJECT_ROOT, process.argv[2])
|
|
364
|
-
: DEFAULT_RELEASE_MANIFEST_PATH;
|
|
365
|
-
await publishChromeWebStoreRelease({ manifestPath });
|
|
366
|
-
}
|
|
367
|
-
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
|
|
368
|
-
void main().catch((error) => {
|
|
369
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
370
|
-
console.error(`[publish-chrome-web-store] ${message}`);
|
|
371
|
-
process.exit(1);
|
|
372
|
-
});
|
|
373
|
-
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { packageReleaseArtifacts, toProjectRelative } from './release-package.js';
|
|
4
|
+
const dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
5
|
+
const projectRoot = resolve(dirname, '..', '..');
|
|
6
|
+
const releaseDir = resolve(projectRoot, 'artifacts', 'release');
|
|
7
|
+
try {
|
|
8
|
+
const manifest = packageReleaseArtifacts({ projectRoot, releaseDir });
|
|
9
|
+
console.log(`Created extension archive: ${manifest.extensionArchive}`);
|
|
10
|
+
console.log(`Created npm package tarball: ${manifest.npmPackageTarball}`);
|
|
11
|
+
console.log(`Created release manifest: ${toProjectRelative(projectRoot, resolve(releaseDir, 'release-artifacts.json'))}`);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15
|
+
console.error(`[package:release] ${message}`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
@@ -1,16 +1,51 @@
|
|
|
1
|
-
interface
|
|
1
|
+
export interface PackageMetadata {
|
|
2
|
+
name: string;
|
|
3
|
+
version: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ZipEntry {
|
|
2
6
|
path: string;
|
|
3
7
|
data: Buffer;
|
|
4
8
|
mode: number;
|
|
5
9
|
}
|
|
6
|
-
interface ReleaseManifest {
|
|
10
|
+
export interface ReleaseManifest {
|
|
7
11
|
version: string;
|
|
8
12
|
extensionArchive: string;
|
|
9
13
|
npmPackageTarball: string;
|
|
10
14
|
}
|
|
15
|
+
export declare function comparePaths(left: string, right: string): number;
|
|
11
16
|
export declare function sanitizeArtifactName(value: string): string;
|
|
12
17
|
export declare function collectZipEntries(rootDir: string): ZipEntry[];
|
|
13
18
|
export declare function createDeterministicZip(entries: readonly ZipEntry[]): Buffer;
|
|
19
|
+
export declare function requirePath(path: string, description: string): void;
|
|
20
|
+
export declare function toProjectRelative(projectRoot: string, path: string): string;
|
|
21
|
+
export declare function resolveNpmCommand(options?: {
|
|
22
|
+
npmExecPath?: string;
|
|
23
|
+
execPath?: string;
|
|
24
|
+
platform?: NodeJS.Platform;
|
|
25
|
+
}): {
|
|
26
|
+
command: string;
|
|
27
|
+
argsPrefix: string[];
|
|
28
|
+
};
|
|
14
29
|
export declare function parseNpmPackFilename(output: string): string;
|
|
15
|
-
export declare function
|
|
30
|
+
export declare function createExtensionArchive(metadata: PackageMetadata, projectRoot: string, releaseDir: string): string;
|
|
31
|
+
interface NpmPackResult {
|
|
32
|
+
status: number | null;
|
|
33
|
+
stdout: string;
|
|
34
|
+
stderr: string;
|
|
35
|
+
}
|
|
36
|
+
export declare function createNpmPackageTarball(options: {
|
|
37
|
+
projectRoot: string;
|
|
38
|
+
releaseDir: string;
|
|
39
|
+
npm?: {
|
|
40
|
+
command: string;
|
|
41
|
+
argsPrefix: string[];
|
|
42
|
+
};
|
|
43
|
+
spawn?: (command: string, args: string[], options: object) => NpmPackResult;
|
|
44
|
+
}): string;
|
|
45
|
+
export declare function writeReleaseManifest(manifest: ReleaseManifest, releaseDir: string): string;
|
|
46
|
+
export declare function packageReleaseArtifacts(options: {
|
|
47
|
+
projectRoot: string;
|
|
48
|
+
releaseDir?: string;
|
|
49
|
+
spawn?: (command: string, args: string[], options: object) => NpmPackResult;
|
|
50
|
+
}): ReleaseManifest;
|
|
16
51
|
export {};
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
import { spawnSync } from 'child_process';
|
|
2
2
|
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, } from 'fs';
|
|
3
3
|
import { join, relative, resolve } from 'path';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
4
|
import { deflateRawSync } from 'zlib';
|
|
6
|
-
const Dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
7
|
-
const PROJECT_ROOT = resolve(Dirname, '..', '..');
|
|
8
|
-
const RELEASE_DIR = resolve(PROJECT_ROOT, 'artifacts', 'release');
|
|
9
5
|
const FIXED_ZIP_DATE = new Date(Date.UTC(1980, 0, 1, 0, 0, 0));
|
|
10
6
|
const ZIP_VERSION = 20;
|
|
11
7
|
const ZIP_UTF8_FLAG = 0x0800;
|
|
12
8
|
const ZIP_METHOD_DEFLATE = 8;
|
|
13
9
|
const ZIP_FILE_MODE = 0o100644;
|
|
14
10
|
const CRC32_TABLE = buildCrc32Table();
|
|
15
|
-
function comparePaths(left, right) {
|
|
11
|
+
export function comparePaths(left, right) {
|
|
16
12
|
if (left < right)
|
|
17
13
|
return -1;
|
|
18
14
|
if (left > right)
|
|
@@ -138,21 +134,21 @@ function encodeDosDateTime(value) {
|
|
|
138
134
|
function readJsonFile(path) {
|
|
139
135
|
return JSON.parse(readFileSync(path, 'utf8'));
|
|
140
136
|
}
|
|
141
|
-
function requirePath(path, description) {
|
|
137
|
+
export function requirePath(path, description) {
|
|
142
138
|
if (!existsSync(path)) {
|
|
143
139
|
throw new Error(`${description} was not found at ${path}. Run the required build command(s) first.`);
|
|
144
140
|
}
|
|
145
141
|
}
|
|
146
|
-
function toProjectRelative(path) {
|
|
147
|
-
return relative(
|
|
142
|
+
export function toProjectRelative(projectRoot, path) {
|
|
143
|
+
return relative(projectRoot, path).split('\\').join('/');
|
|
148
144
|
}
|
|
149
|
-
function resolveNpmCommand() {
|
|
150
|
-
const npmExecPath = process.env['npm_execpath'];
|
|
145
|
+
export function resolveNpmCommand(options = {}) {
|
|
146
|
+
const npmExecPath = options.npmExecPath ?? process.env['npm_execpath'];
|
|
151
147
|
if (npmExecPath) {
|
|
152
|
-
return { command: process.execPath, argsPrefix: [npmExecPath] };
|
|
148
|
+
return { command: options.execPath ?? process.execPath, argsPrefix: [npmExecPath] };
|
|
153
149
|
}
|
|
154
150
|
return {
|
|
155
|
-
command: process.platform === 'win32' ? 'npm.cmd' : 'npm',
|
|
151
|
+
command: (options.platform ?? process.platform) === 'win32' ? 'npm.cmd' : 'npm',
|
|
156
152
|
argsPrefix: [],
|
|
157
153
|
};
|
|
158
154
|
}
|
|
@@ -164,60 +160,60 @@ export function parseNpmPackFilename(output) {
|
|
|
164
160
|
}
|
|
165
161
|
return filename;
|
|
166
162
|
}
|
|
167
|
-
function createExtensionArchive(metadata) {
|
|
168
|
-
const extensionDir = resolve(
|
|
163
|
+
export function createExtensionArchive(metadata, projectRoot, releaseDir) {
|
|
164
|
+
const extensionDir = resolve(projectRoot, 'dist', 'extension');
|
|
169
165
|
requirePath(extensionDir, 'Extension build output');
|
|
170
|
-
const zipPath = resolve(
|
|
166
|
+
const zipPath = resolve(releaseDir, `${sanitizeArtifactName(metadata.name)}-extension-v${metadata.version}.zip`);
|
|
171
167
|
const zipBuffer = createDeterministicZip(collectZipEntries(extensionDir));
|
|
172
168
|
writeFileSync(zipPath, zipBuffer);
|
|
173
169
|
return zipPath;
|
|
174
170
|
}
|
|
175
|
-
function createNpmPackageTarball() {
|
|
176
|
-
requirePath(resolve(
|
|
177
|
-
requirePath(resolve(
|
|
178
|
-
const npm = resolveNpmCommand();
|
|
179
|
-
const
|
|
180
|
-
|
|
171
|
+
export function createNpmPackageTarball(options) {
|
|
172
|
+
requirePath(resolve(options.projectRoot, 'dist', 'node-server'), 'CLI build output');
|
|
173
|
+
requirePath(resolve(options.projectRoot, 'dist', 'ui'), 'UI build output');
|
|
174
|
+
const npm = options.npm ?? resolveNpmCommand();
|
|
175
|
+
const args = [
|
|
176
|
+
...npm.argsPrefix,
|
|
177
|
+
'pack',
|
|
178
|
+
'--json',
|
|
179
|
+
'--ignore-scripts',
|
|
180
|
+
'--pack-destination',
|
|
181
|
+
options.releaseDir,
|
|
182
|
+
];
|
|
183
|
+
const spawnOptions = {
|
|
184
|
+
cwd: options.projectRoot,
|
|
181
185
|
encoding: 'utf8',
|
|
182
186
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
183
|
-
}
|
|
187
|
+
};
|
|
188
|
+
const result = options.spawn
|
|
189
|
+
? options.spawn(npm.command, args, spawnOptions)
|
|
190
|
+
: spawnSync(npm.command, args, spawnOptions);
|
|
184
191
|
if (result.status !== 0) {
|
|
185
192
|
throw new Error((result.stderr || result.stdout || 'npm pack failed').trim());
|
|
186
193
|
}
|
|
187
|
-
return resolve(
|
|
194
|
+
return resolve(options.releaseDir, parseNpmPackFilename(result.stdout));
|
|
188
195
|
}
|
|
189
|
-
function writeReleaseManifest(manifest) {
|
|
190
|
-
const manifestPath = resolve(
|
|
196
|
+
export function writeReleaseManifest(manifest, releaseDir) {
|
|
197
|
+
const manifestPath = resolve(releaseDir, 'release-artifacts.json');
|
|
191
198
|
writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
192
199
|
return manifestPath;
|
|
193
200
|
}
|
|
194
|
-
export function packageReleaseArtifacts() {
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const
|
|
201
|
+
export function packageReleaseArtifacts(options) {
|
|
202
|
+
const releaseDir = options.releaseDir ?? resolve(options.projectRoot, 'artifacts', 'release');
|
|
203
|
+
const packageJson = readJsonFile(resolve(options.projectRoot, 'package.json'));
|
|
204
|
+
rmSync(releaseDir, { recursive: true, force: true });
|
|
205
|
+
mkdirSync(releaseDir, { recursive: true });
|
|
206
|
+
const extensionArchive = createExtensionArchive(packageJson, options.projectRoot, releaseDir);
|
|
207
|
+
const npmPackageTarball = createNpmPackageTarball({
|
|
208
|
+
projectRoot: options.projectRoot,
|
|
209
|
+
releaseDir,
|
|
210
|
+
spawn: options.spawn,
|
|
211
|
+
});
|
|
200
212
|
const manifest = {
|
|
201
213
|
version: packageJson.version,
|
|
202
|
-
extensionArchive: toProjectRelative(extensionArchive),
|
|
203
|
-
npmPackageTarball: toProjectRelative(npmPackageTarball),
|
|
214
|
+
extensionArchive: toProjectRelative(options.projectRoot, extensionArchive),
|
|
215
|
+
npmPackageTarball: toProjectRelative(options.projectRoot, npmPackageTarball),
|
|
204
216
|
};
|
|
205
|
-
writeReleaseManifest(manifest);
|
|
217
|
+
writeReleaseManifest(manifest, releaseDir);
|
|
206
218
|
return manifest;
|
|
207
219
|
}
|
|
208
|
-
function main() {
|
|
209
|
-
const manifest = packageReleaseArtifacts();
|
|
210
|
-
console.log(`Created extension archive: ${manifest.extensionArchive}`);
|
|
211
|
-
console.log(`Created npm package tarball: ${manifest.npmPackageTarball}`);
|
|
212
|
-
console.log(`Created release manifest: ${toProjectRelative(resolve(RELEASE_DIR, 'release-artifacts.json'))}`);
|
|
213
|
-
}
|
|
214
|
-
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
|
|
215
|
-
try {
|
|
216
|
-
main();
|
|
217
|
-
}
|
|
218
|
-
catch (error) {
|
|
219
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
220
|
-
console.error(`[package:release] ${message}`);
|
|
221
|
-
process.exit(1);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Transform } from 'node:stream';
|
|
2
|
+
import type { Express, Request, Response } from 'express';
|
|
2
3
|
import type { SecretProxyManager } from '../secrets/proxy-manager.js';
|
|
3
4
|
import { AgentActivityTracker } from './agent-activity.js';
|
|
4
5
|
export interface FetchProxyDeps {
|
|
@@ -13,6 +14,43 @@ export interface FetchProxyDeps {
|
|
|
13
14
|
*/
|
|
14
15
|
logger?: Pick<Console, 'log' | 'warn' | 'error'>;
|
|
15
16
|
}
|
|
17
|
+
export declare function attachUpstreamAbort(res: Response): {
|
|
18
|
+
controller: AbortController;
|
|
19
|
+
detach: () => void;
|
|
20
|
+
};
|
|
21
|
+
/** Get the body — either from express.json()'s parsed body or raw chunks. */
|
|
22
|
+
export declare function collectRawBody(req: Request): Promise<Buffer>;
|
|
23
|
+
/**
|
|
24
|
+
* Build the forwarded header set: copy non-hop-by-hop headers, then restore
|
|
25
|
+
* the forbidden-header transports (Cookie/Origin/Referer/Proxy-*) the browser
|
|
26
|
+
* could not send via fetch(). `accept-encoding` is skipped so undici
|
|
27
|
+
* negotiates gzip/br and decompresses transparently (#3037).
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildForwardHeaders(req: Request, targetUrl: string): Record<string, string>;
|
|
30
|
+
interface ForbiddenSecret {
|
|
31
|
+
secretName: string;
|
|
32
|
+
hostname: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Apply request-side secret injection: unmask headers and URL-embedded
|
|
36
|
+
* credentials. Mutates `headers` to attach a synthetic Authorization when the
|
|
37
|
+
* URL carried credentials. Returns the forbidden descriptor when a masked
|
|
38
|
+
* secret is used against a domain it is not scoped to, else the cleaned URL.
|
|
39
|
+
*/
|
|
40
|
+
export declare function injectRequestSecrets(secretProxy: SecretProxyManager, headers: Record<string, string>, targetUrl: string, targetHostname: string): {
|
|
41
|
+
forbidden: ForbiddenSecret;
|
|
42
|
+
} | {
|
|
43
|
+
cleanedUrl: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Buffer-aware UTF-8 secret scrubber. A `StringDecoder` keeps trailing partial
|
|
47
|
+
* multi-byte sequences out of the scrub so codepoints straddling a chunk
|
|
48
|
+
* boundary aren't corrupted (fatal for CJK/emoji model output). Pass-through
|
|
49
|
+
* when the body is non-text or no secrets are configured.
|
|
50
|
+
*/
|
|
51
|
+
export declare function createScrubStream(secretProxy: SecretProxyManager, isText: boolean): Transform;
|
|
52
|
+
/** Stream the upstream body to the client through the secret-scrub transform. */
|
|
53
|
+
export declare function streamUpstreamBody(res: Response, upstream: globalThis.Response, secretProxy: SecretProxyManager, detachClientClose: () => void): void;
|
|
16
54
|
/**
|
|
17
55
|
* Fetch proxy — forwards cross-origin requests from the browser to bypass
|
|
18
56
|
* CORS (used by just-bash's curl, which calls the browser's fetch() API).
|
|
@@ -20,3 +58,4 @@ export interface FetchProxyDeps {
|
|
|
20
58
|
* checks req.body first.
|
|
21
59
|
*/
|
|
22
60
|
export declare function registerFetchProxyRoute(app: Express, deps: FetchProxyDeps): void;
|
|
61
|
+
export {};
|
|
@@ -4,6 +4,18 @@ import { HMAC_SIGN_HEADER, isFormContentType, isLoopbackOrigin, isTextContentTyp
|
|
|
4
4
|
import { createMaybeGunzipStream } from '../fetch-proxy-gzip.js';
|
|
5
5
|
import { buildFetchProxyExposeHeaders, FETCH_PROXY_CONTENT_LENGTH_HEADER, FETCH_PROXY_SKIP_HEADERS, FETCH_PROXY_SKIP_RESPONSE_HEADERS, FETCH_PROXY_SKIP_RESPONSE_PREFIXES, } from '../fetch-proxy-headers.js';
|
|
6
6
|
import { AgentActivityTracker, registerAgentActivityRoute } from './agent-activity.js';
|
|
7
|
+
export function attachUpstreamAbort(res) {
|
|
8
|
+
const controller = new AbortController();
|
|
9
|
+
const onClose = () => {
|
|
10
|
+
if (!res.writableEnded)
|
|
11
|
+
controller.abort();
|
|
12
|
+
};
|
|
13
|
+
res.on('close', onClose);
|
|
14
|
+
return {
|
|
15
|
+
controller,
|
|
16
|
+
detach: () => res.off('close', onClose),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
7
19
|
/** Pick the first value of a possibly-multi-valued request header. */
|
|
8
20
|
function firstHeaderValue(value) {
|
|
9
21
|
if (value === undefined)
|
|
@@ -11,7 +23,7 @@ function firstHeaderValue(value) {
|
|
|
11
23
|
return Array.isArray(value) ? value[0] : value;
|
|
12
24
|
}
|
|
13
25
|
/** Get the body — either from express.json()'s parsed body or raw chunks. */
|
|
14
|
-
async function collectRawBody(req) {
|
|
26
|
+
export async function collectRawBody(req) {
|
|
15
27
|
if (req.body && typeof req.body === 'object' && Object.keys(req.body).length > 0) {
|
|
16
28
|
// Body was already parsed by express.json() — re-serialize it.
|
|
17
29
|
return Buffer.from(JSON.stringify(req.body), 'utf-8');
|
|
@@ -28,7 +40,7 @@ async function collectRawBody(req) {
|
|
|
28
40
|
* could not send via fetch(). `accept-encoding` is skipped so undici
|
|
29
41
|
* negotiates gzip/br and decompresses transparently (#3037).
|
|
30
42
|
*/
|
|
31
|
-
function buildForwardHeaders(req, targetUrl) {
|
|
43
|
+
export function buildForwardHeaders(req, targetUrl) {
|
|
32
44
|
const headers = {};
|
|
33
45
|
for (const [key, value] of Object.entries(req.headers)) {
|
|
34
46
|
if (!FETCH_PROXY_SKIP_HEADERS.has(key) && typeof value === 'string') {
|
|
@@ -82,7 +94,7 @@ function buildForwardHeaders(req, targetUrl) {
|
|
|
82
94
|
* URL carried credentials. Returns the forbidden descriptor when a masked
|
|
83
95
|
* secret is used against a domain it is not scoped to, else the cleaned URL.
|
|
84
96
|
*/
|
|
85
|
-
function injectRequestSecrets(secretProxy, headers, targetUrl, targetHostname) {
|
|
97
|
+
export function injectRequestSecrets(secretProxy, headers, targetUrl, targetHostname) {
|
|
86
98
|
if (!secretProxy.hasSecrets())
|
|
87
99
|
return { cleanedUrl: targetUrl };
|
|
88
100
|
const headerResult = secretProxy.unmaskHeaders(headers, targetHostname);
|
|
@@ -200,7 +212,7 @@ function forwardUpstreamHeaders(res, upstream, secretProxy) {
|
|
|
200
212
|
* boundary aren't corrupted (fatal for CJK/emoji model output). Pass-through
|
|
201
213
|
* when the body is non-text or no secrets are configured.
|
|
202
214
|
*/
|
|
203
|
-
function createScrubStream(secretProxy, isText) {
|
|
215
|
+
export function createScrubStream(secretProxy, isText) {
|
|
204
216
|
const utf8Decoder = new StringDecoder('utf8');
|
|
205
217
|
return new Transform({
|
|
206
218
|
transform(chunk, _enc, cb) {
|
|
@@ -241,7 +253,7 @@ function createScrubStream(secretProxy, isText) {
|
|
|
241
253
|
});
|
|
242
254
|
}
|
|
243
255
|
/** Stream the upstream body to the client through the secret-scrub transform. */
|
|
244
|
-
function streamUpstreamBody(res, upstream, secretProxy, detachClientClose) {
|
|
256
|
+
export function streamUpstreamBody(res, upstream, secretProxy, detachClientClose) {
|
|
245
257
|
const isText = isTextContentType(upstream.headers.get('content-type') ?? '');
|
|
246
258
|
const upstreamStream = Readable.fromWeb(upstream.body);
|
|
247
259
|
// Text only: a real `application/gzip` / `.tar.gz` body is a gzip member
|
|
@@ -349,13 +361,9 @@ export function registerFetchProxyRoute(app, deps) {
|
|
|
349
361
|
// streams (LLM SSE completions) are torn down promptly. Listen on
|
|
350
362
|
// `res.on('close')`, not `req.on('close')` — Node fires req close as soon
|
|
351
363
|
// as the request body is consumed, which would abort before fetch starts.
|
|
352
|
-
const
|
|
353
|
-
onClientClose =
|
|
354
|
-
|
|
355
|
-
abortController.abort();
|
|
356
|
-
};
|
|
357
|
-
res.on('close', onClientClose);
|
|
358
|
-
fetchInit.signal = abortController.signal;
|
|
364
|
+
const upstreamAbort = attachUpstreamAbort(res);
|
|
365
|
+
onClientClose = upstreamAbort.detach;
|
|
366
|
+
fetchInit.signal = upstreamAbort.controller.signal;
|
|
359
367
|
const upstream = await fetch(injection.cleanedUrl, fetchInit);
|
|
360
368
|
logger.log(`[fetch-proxy] ${req.method} ${targetUrl} ← ${upstream.status}`);
|
|
361
369
|
forwardUpstreamHeaders(res, upstream, secretProxy);
|
|
@@ -1,14 +1,36 @@
|
|
|
1
|
-
import { type Express } from 'express';
|
|
1
|
+
import { type Express, type Response } from 'express';
|
|
2
2
|
import type { EnvSecretStore } from '../secrets/env-secret-store.js';
|
|
3
3
|
import type { OauthSecretStore } from '../secrets/oauth-secret-store.js';
|
|
4
4
|
import type { SecretProxyManager } from '../secrets/proxy-manager.js';
|
|
5
|
+
import { handleDaSignAndForward, handleS3SignAndForward } from '../secrets/sign-and-forward.js';
|
|
5
6
|
export interface SecretRoutesDeps {
|
|
6
7
|
secretStore: EnvSecretStore;
|
|
7
8
|
secretProxy: SecretProxyManager;
|
|
8
9
|
oauthStore: OauthSecretStore;
|
|
9
10
|
/** When true, the full sign-and-forward error is logged for the local operator. */
|
|
10
11
|
devMode: boolean;
|
|
12
|
+
handleS3?: typeof handleS3SignAndForward;
|
|
13
|
+
handleDa?: typeof handleDaSignAndForward;
|
|
11
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Sign-and-forward failure responder. Logs only a generic line + trace id —
|
|
17
|
+
* the err.message can carry profile names, bucket names, or partial URLs we
|
|
18
|
+
* don't want in shared log aggregators. The trace id lets the user correlate
|
|
19
|
+
* the 500 they got with the server log; the detail goes to the file logger
|
|
20
|
+
* (above DEBUG) only when devMode is on.
|
|
21
|
+
*/
|
|
22
|
+
export declare function respondSignAndForwardError(res: Response, err: unknown, devMode: boolean, label: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* Scope edit handler — updates allowed domains of an existing secret without
|
|
25
|
+
* changing its value. Checks session store first, then falls back to persisted.
|
|
26
|
+
*/
|
|
27
|
+
export declare function handleScopeEdit(res: Response, name: unknown, domains: unknown, secretStore: EnvSecretStore, secretProxy: SecretProxyManager): Promise<Response>;
|
|
28
|
+
/**
|
|
29
|
+
* Persisted/session delete handler — checks the session store first so a
|
|
30
|
+
* session shadow does not leak through after deletion, then falls back to the
|
|
31
|
+
* persisted store. Reloads the masking pipeline either way.
|
|
32
|
+
*/
|
|
33
|
+
export declare function handleDeleteSecret(name: string | undefined, res: Response, secretStore: EnvSecretStore, secretProxy: SecretProxyManager): Promise<Response>;
|
|
12
34
|
/**
|
|
13
35
|
* Secret management API — direct .env file access (no browser needed). The
|
|
14
36
|
* `secretStore` is wired into `secretProxy` so the fetch-proxy and the
|