sliccy 6.226.1 → 6.226.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/sudo/dialog-backends.d.ts +10 -5
- package/dist/node-server/sudo/dialog-backends.js +47 -10
- package/dist/node-server/sudo/electron-backend.d.ts +4 -3
- package/dist/node-server/sudo/electron-backend.js +6 -3
- package/dist/node-server/sudo/endpoint.d.ts +4 -3
- package/dist/node-server/sudo/endpoint.js +6 -4
- package/dist/node-server/sudo/select.d.ts +1 -1
- package/dist/node-server/sudo/select.js +1 -1
- package/dist/node-server/sudo/tty-backend.d.ts +4 -2
- package/dist/node-server/sudo/tty-backend.js +5 -2
- package/dist/node-server/sudo/types.d.ts +29 -1
- package/dist/node-server/sudo/types.js +8 -1
- package/dist/ui/.vite/manifest.json +182 -182
- package/dist/ui/assets/__vite-browser-external-ZetWQ5ax.js +1 -0
- package/dist/ui/assets/{account-store-BlJDP1-W.js → account-store-CckwuDB2.js} +1 -1
- package/dist/ui/assets/{account-store-Dup-dSte.js → account-store-DP_9wI9F.js} +1 -1
- package/dist/ui/assets/{adobe-BkQaujbn.js → adobe-BX6MOOVZ.js} +1 -1
- package/dist/ui/assets/{adobe-CQvTuyh_.js → adobe-CpN64YaE.js} +1 -1
- package/dist/ui/assets/{agent-panels-e39BAcGn.js → agent-panels-CGXjqFbQ.js} +1 -1
- package/dist/ui/assets/{almost-bash-shell-headless-DvTsKS1v.js → almost-bash-shell-headless-DANvPMM_.js} +33 -33
- package/dist/ui/assets/{apply-layout-COKznjFL.js → apply-layout-BqFOBUKs.js} +1 -1
- package/dist/ui/assets/{apps-BnILH4xd.js → apps-C4Y2CDb2.js} +1 -1
- package/dist/ui/assets/{azure-openai-DtOuOPJB.js → azure-openai-Djld28cS.js} +1 -1
- package/dist/ui/assets/{azure-openai-DSN7cshd.js → azure-openai-Wvaw8FEs.js} +1 -1
- package/dist/ui/assets/{backend-hostfs-KLShl7sQ.js → backend-hostfs-B383ob12.js} +1 -1
- package/dist/ui/assets/{cdp-Dxgj-kkt.js → cdp-B_M-UmZ-.js} +1 -1
- package/dist/ui/assets/{cerebras-DaTX2QuX.js → cerebras-CYnqzV28.js} +1 -1
- package/dist/ui/assets/{cerebras-uY6Xy8za.js → cerebras-CnYplQcE.js} +1 -1
- package/dist/ui/assets/{composer-speech-SCKDMr4i.js → composer-speech-F0aNDREI.js} +1 -1
- package/dist/ui/assets/{connect-surface-BzENdoiF.js → connect-surface-BnTWpdsP.js} +1 -1
- package/dist/ui/assets/{directed-approval-B_DRdwpJ.js → directed-approval-ClnG3YEs.js} +1 -1
- package/dist/ui/assets/{emscripten-vfs-hook-Dce5ZWJb.js → emscripten-vfs-hook-d03P2Jrb.js} +1 -1
- package/dist/ui/assets/{ensure-speech-assets-DUhEO7H0.js → ensure-speech-assets-BAwQVUZ-.js} +1 -1
- package/dist/ui/assets/{frozen-archive-writer-BGB_nYip.js → frozen-archive-writer-DlQ2pWzw.js} +1 -1
- package/dist/ui/assets/{fs-DKTyXngI.js → fs-B6V0zxAV.js} +2 -2
- package/dist/ui/assets/{fs-DwHLmdxe.js → fs-DFXgS-c1.js} +1 -1
- package/dist/ui/assets/{gelatiere-store-DQIudjLw.js → gelatiere-store-aggur0yp.js} +1 -1
- package/dist/ui/assets/{gelatiere-unit-CoeN1YRk.js → gelatiere-unit-is8dhW-d.js} +1 -1
- package/dist/ui/assets/{github-C_sejJLD.js → github-CWBn99Bd.js} +2 -2
- package/dist/ui/assets/{github-auth-CyLgcuwk.js → github-auth-yNHDwebn.js} +1 -1
- package/dist/ui/assets/{github-copilot-DSF4sN0A.js → github-copilot-BADuy5GH.js} +1 -1
- package/dist/ui/assets/{github-copilot-DNir8nPs.js → github-copilot-BOrHa5RN.js} +1 -1
- package/dist/ui/assets/{github-install-CypHkXWW.js → github-install-BIoofnjZ.js} +1 -1
- package/dist/ui/assets/{github-Chwcb38q.js → github-pVY7MyJd.js} +1 -1
- package/dist/ui/assets/{github-zip-DvrCoKij.js → github-zip-TmRPTM75.js} +1 -1
- package/dist/ui/assets/{hear-BYApgyRp.js → hear-JttBNQ7c.js} +1 -1
- package/dist/ui/assets/{help-BSRIYdTr.js → help-DvETwJex.js} +1 -1
- package/dist/ui/assets/{installer-DU0Ft-ta.js → installer-CmAHuSft.js} +1 -1
- package/dist/ui/assets/{ipk-mamba-BhzmAVqX.js → ipk-mamba-D1bLinsh.js} +1 -1
- package/dist/ui/assets/{js-realm-worker-CMoqILM7.js → js-realm-worker-CsvflECa.js} +2 -2
- package/dist/ui/assets/{jsh-executor-BSfT4Av-.js → jsh-executor-CEkiOk4d.js} +2 -2
- package/dist/ui/assets/{kernel-worker-jFET0gP4.js → kernel-worker-CSRynihq.js} +9 -9
- package/dist/ui/assets/{lick-ws-bridge-EXboAIWs.js → lick-ws-bridge-C_07Sgiv.js} +1 -1
- package/dist/ui/assets/{lick-ws-bridge-Bzqf-vDY.js → lick-ws-bridge-ZBncAqka.js} +1 -1
- package/dist/ui/assets/{live-session-curation-9JOZOk__.js → live-session-curation-lunfYQqv.js} +1 -1
- package/dist/ui/assets/{live-session-snapshot-lb3l3-2i.js → live-session-snapshot-BMzJEYH6.js} +1 -1
- package/dist/ui/assets/{local-llm-DeEO_kWk.js → local-llm-DYEdgnfp.js} +1 -1
- package/dist/ui/assets/{local-llm-DPdTCBrf.js → local-llm-FyJMIUux.js} +1 -1
- package/dist/ui/assets/{main-cherry-CbSGyKQc.js → main-cherry-BknGKE1w.js} +1 -1
- package/dist/ui/assets/{main-D4T6A3x0.js → main-mYxYChCJ.js} +3 -3
- package/dist/ui/assets/{memory-curation-seam-D86YzTcB.js → memory-curation-seam-ff-huuyf.js} +1 -1
- package/dist/ui/assets/{memory-health-CRaGX9nB.js → memory-health-hO_nQaAH.js} +1 -1
- package/dist/ui/assets/{memory-write-execute-DWf8irl0.js → memory-write-execute-CotJ6gZQ.js} +1 -1
- package/dist/ui/assets/{merge-folded-cost-into-frozen-Bu65Q9Cg.js → merge-folded-cost-into-frozen-85Z0pknu.js} +1 -1
- package/dist/ui/assets/{mount-D8OdNZ70.js → mount-BXI8X7dl.js} +1 -1
- package/dist/ui/assets/{mount-Dvq41z85.js → mount-dHmWGrgT.js} +3 -3
- package/dist/ui/assets/{new-session-pIOo08ug.js → new-session-CwPpsslH.js} +2 -2
- package/dist/ui/assets/{oauth-bootstrap-hI54waCl.js → oauth-bootstrap-DFH58dMK.js} +2 -2
- package/dist/ui/assets/{onboarding-orchestrator-C7IPNkaS.js → onboarding-orchestrator-IM_eYwPO.js} +1 -1
- package/dist/ui/assets/{openai-codex-Co8hzNYp.js → openai-codex-B5TF7s7X.js} +1 -1
- package/dist/ui/assets/{openai-codex-4RG71-r1.js → openai-codex-DOLy2I4o.js} +1 -1
- package/dist/ui/assets/{openrouter-BQpYDJi3.js → openrouter-CQSD-mOw.js} +1 -1
- package/dist/ui/assets/{openrouter-Czqj8XpT.js → openrouter-DQpeSY-A.js} +1 -1
- package/dist/ui/assets/{openrouter-free-BEKVrg00.js → openrouter-free-DB3Go5D-.js} +1 -1
- package/dist/ui/assets/{openrouter-free-ZF0WQe1F.js → openrouter-free-cjPrMokY.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-BDLDVcec.js → openrouter-oauth-B_Qr_P-V.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-Bvj7wwNp.js → openrouter-oauth-DQeQAIy-.js} +1 -1
- package/dist/ui/assets/{opfs-mount-CkEPMeKd.js → opfs-mount-tnswXF8V.js} +1 -1
- package/dist/ui/assets/{panel-rpc-handlers-BIaCpUWF.js → panel-rpc-handlers-DWiTg8Ql.js} +2 -2
- package/dist/ui/assets/{panelize-shell-ZDSWSUaz.js → panelize-shell-BKkiVZiC.js} +1 -1
- package/dist/ui/assets/{pi-edit-execution-Dxz6tqcr.js → pi-edit-execution-DoPTCkJU.js} +1 -1
- package/dist/ui/assets/{provider-C3R_g8Ig.js → provider-DHEg3RIC.js} +2 -2
- package/dist/ui/assets/{provider-8d5bGvbc.js → provider-Do8hIn0m.js} +1 -1
- package/dist/ui/assets/{provider-settings-DUOh4hPx.js → provider-settings-x2iD9c0h.js} +1 -1
- package/dist/ui/assets/provider-store-access-4-s6k7tf.js +1 -0
- package/dist/ui/assets/provider-store-access-DXOTVVCa.js +1 -0
- package/dist/ui/assets/{providers-CqM86e9A.js → providers-B8axP5LK.js} +2 -2
- package/dist/ui/assets/{providers-fZ3KX8Lg.js → providers-ChXzAurS.js} +1 -1
- package/dist/ui/assets/{py-realm-worker-BJhAN3Hi.js → py-realm-worker-D7ZPZyKh.js} +1 -1
- package/dist/ui/assets/{pyodide-BC3toCIx.js → pyodide-C89f2pno.js} +1 -1
- package/dist/ui/assets/{pyodide-65Z_RapY.js → pyodide-DdE1Qe05.js} +1 -1
- package/dist/ui/assets/{quick-llm-Do6-ghEG.js → quick-llm-DbTW7_qg.js} +1 -1
- package/dist/ui/assets/{realm-inprocess-BYC0jsGK.js → realm-inprocess-Ck6ydQwT.js} +2 -2
- package/dist/ui/assets/{realm-unhandled-rejections-DGcjCfBE.js → realm-unhandled-rejections-CeAkuLUL.js} +1 -1
- package/dist/ui/assets/{realm-unhandled-rejections-ZGmUnuk3.js → realm-unhandled-rejections-CnU72aPe.js} +1 -1
- package/dist/ui/assets/{recovery-screen-J3LoIlFr.js → recovery-screen-ty-N4sk6.js} +1 -1
- package/dist/ui/assets/{remote-terminal-view-_0YIhuqT.js → remote-terminal-view-CNpjHeSR.js} +2 -2
- package/dist/ui/assets/{require-guards-CdMtRPQA.js → require-guards-CMPvNDQg.js} +1 -1
- package/dist/ui/assets/{restore-DHQi2wzK.js → restore-CGbZluMQ.js} +1 -1
- package/dist/ui/assets/{run-uFO4egDg.js → run-B9eYT4Oj.js} +2 -2
- package/dist/ui/assets/{run-BhlaKL0o.js → run-BKqnD09g.js} +1 -1
- package/dist/ui/assets/{run-EPiuw1ru.js → run-Cka1zPhr.js} +1 -1
- package/dist/ui/assets/{run-BUv3t71i.js → run-DGl39wQp.js} +1 -1
- package/dist/ui/assets/{run-_oGn-1AP.js → run-DHBSR6nH.js} +1 -1
- package/dist/ui/assets/{run-CB-l85sr.js → run-DPEU0mPp.js} +2 -2
- package/dist/ui/assets/{session-command-memory-n0b0bNTG.js → session-command-memory-NaC_Sdn0.js} +2 -2
- package/dist/ui/assets/{session-jsonl-Dm2yw6F9.js → session-jsonl-CK1nnZwL.js} +1 -1
- package/dist/ui/assets/{session-jsonl-BNQAPUiL.js → session-jsonl-iSVMsOx7.js} +1 -1
- package/dist/ui/assets/{session-search-index-DW9ma2yj.js → session-search-index-MELCgtJ1.js} +1 -1
- package/dist/ui/assets/{setup-feature-flags-remote-D2waO-r4.js → setup-feature-flags-remote-DGeAaOhE.js} +1 -1
- package/dist/ui/assets/{setup-model-catalog-W9d0IwmJ.js → setup-model-catalog-B1uR2ova.js} +1 -1
- package/dist/ui/assets/{signed-fetch-CIdxcCyT.js → signed-fetch-bUBxSCEr.js} +1 -1
- package/dist/ui/assets/{silent-renew-backoff-CKXdzlDo.js → silent-renew-backoff-k-_ROUgc.js} +1 -1
- package/dist/ui/assets/slicc-version-DwS1sP4C.js +1 -0
- package/dist/ui/assets/{snapshot-KIIqV7oO.js → snapshot-DQ087sVE.js} +1 -1
- package/dist/ui/assets/{snapshot-features-BLo8PAIz.js → snapshot-features-DeJe8uji.js} +1 -1
- package/dist/ui/assets/{soundscape-DzqywR3L.js → soundscape-BnFzEyFw.js} +1 -1
- package/dist/ui/assets/{speak-Ck3_PA-W.js → speak-dfd9BW6L.js} +1 -1
- package/dist/ui/assets/{sprinkle-manager-BeKUo2tm.js → sprinkle-manager-yEepkH2c.js} +1 -1
- package/dist/ui/assets/{sprinkle-renderer-CRyiQKjI.js → sprinkle-renderer-k4VuPxiX.js} +1 -1
- package/dist/ui/assets/{state-BWDzjtHj.js → state-DOWkJUEg.js} +1 -1
- package/dist/ui/assets/{store-Czn1xbby.js → store-CD8HQQi0.js} +1 -1
- package/dist/ui/assets/{store-DNedyI8F.js → store-DMPHri86.js} +1 -1
- package/dist/ui/assets/{sudo-qoInIpBO.js → sudo-ChxuFQrD.js} +1 -1
- package/dist/ui/assets/{supervisor-Jdju-S84.js → supervisor-DsGHAeFb.js} +1 -1
- package/dist/ui/assets/{sync-dialog-D7E1y2Ec.js → sync-dialog-DJlE-_Qu.js} +1 -1
- package/dist/ui/assets/{transformers-env-BWQg6iPs.js → transformers-env-CrD3F-k5.js} +1 -1
- package/dist/ui/assets/{tray-leader-CGgHEzgP.js → tray-leader-Cj5ylIFZ.js} +1 -1
- package/dist/ui/assets/{tray-leave-runtime-BBUcylR0.js → tray-leave-runtime-B86ntR2C.js} +1 -1
- package/dist/ui/assets/{tray-sidecar-B3vE5G1l.js → tray-sidecar-BuTgfmG-.js} +1 -1
- package/dist/ui/assets/{upgrade-detection-RbwMSxeX.js → upgrade-detection-CdnTlzbk.js} +1 -1
- package/dist/ui/assets/{upskill-lick-skip-C0iou2rp.js → upskill-lick-skip-CwWHxj2D.js} +1 -1
- package/dist/ui/assets/{voice-reply-Bj1CpSf6.js → voice-reply-Ba6fFHwZ.js} +1 -1
- package/dist/ui/assets/{wc-attach-B631REQc.js → wc-attach-ClqP_QLV.js} +2 -2
- package/dist/ui/assets/{wc-browser-KZbo8H3B.js → wc-browser-DcJYOuwt.js} +1 -1
- package/dist/ui/assets/{wc-computers-Deq0yXKO.js → wc-computers-DZ8F8APM.js} +1 -1
- package/dist/ui/assets/{wc-detached-9lhlXJPy.js → wc-detached-CqurijdL.js} +1 -1
- package/dist/ui/assets/{wc-extension-CKQerQPt.js → wc-extension-upUnhYIA.js} +2 -2
- package/dist/ui/assets/{wc-follower-DP10htl7.js → wc-follower-lb7m208l.js} +3 -3
- package/dist/ui/assets/{wc-follower-oauth-ANEiGFkZ.js → wc-follower-oauth-T2WVyWZD.js} +1 -1
- package/dist/ui/assets/{wc-gelatiere-DsVbjYHN.js → wc-gelatiere-DsLb5fF7.js} +1 -1
- package/dist/ui/assets/{wc-live-BWEChou-.js → wc-live-_82dhqx_.js} +5 -5
- package/dist/ui/assets/{wc-nav-Bu0rCT1z.js → wc-nav-CWlfBhcg.js} +2 -2
- package/dist/ui/assets/{wc-onboarding-L8z2-1Ld.js → wc-onboarding-4dwuoncC.js} +2 -2
- package/dist/ui/assets/{wc-permissions-Dz0I-zKu.js → wc-permissions-BxfEjmt-.js} +2 -2
- package/dist/ui/assets/{wc-placeholder-BTHzaZLv.js → wc-placeholder-Co5efYux.js} +3 -3
- package/dist/ui/assets/{wc-secret-request-D6rDxI6g.js → wc-secret-request-BwJvFf3r.js} +1 -1
- package/dist/ui/assets/{wc-settings-DFhVBdll.js → wc-settings-CIwPnJJ7.js} +3 -3
- package/dist/ui/assets/{wc-shell-Dh9k1nMd.js → wc-shell-DAAFPsyf.js} +3 -3
- package/dist/ui/assets/{wc-shortcut-config-Bb94V6Av.js → wc-shortcut-config-DIYpCAoK.js} +1 -1
- package/dist/ui/assets/{wc-sprinkles-BkAJcrnZ.js → wc-sprinkles-B6Lpsc3h.js} +2 -2
- package/dist/ui/assets/{wc-transcript-export-Bh3AZeXa.js → wc-transcript-export-Dk603hfy.js} +2 -2
- package/dist/ui/assets/{wc-tray-DyBz3iBQ.js → wc-tray-tSlRM35e.js} +4 -4
- package/dist/ui/assets/welcome-detection-DZ0LEfXq.js +1 -0
- package/dist/ui/assets/{whisper-session-DTRVaIcc.js → whisper-session-DO32dZVE.js} +2 -2
- package/dist/ui/assets/{xai-grok-HD2wS7Sr.js → xai-grok-C5i9rD2B.js} +1 -1
- package/dist/ui/assets/{xai-grok-DEqHFn_v.js → xai-grok-CGWMCUeC.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/ui/packages/webapp/index.html +3 -3
- package/package.json +2 -2
- package/dist/ui/assets/__vite-browser-external-BLOvPDKs.js +0 -1
- package/dist/ui/assets/provider-store-access-D4qzS4fC.js +0 -1
- package/dist/ui/assets/provider-store-access-DEvTyUN8.js +0 -1
- package/dist/ui/assets/slicc-version-fbt_Uj4l.js +0 -1
- package/dist/ui/assets/welcome-detection-C4RN_MWp.js +0 -1
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
* {@link SudoDecision}. They are deliberately injectable via {@link ExecFn} so
|
|
7
7
|
* tests can assert the argv and parsing without spawning a real dialog.
|
|
8
8
|
*
|
|
9
|
-
* Fail-closed contract: a dismissed dialog
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* Fail-closed contract: a dismissed dialog or Deny button is a bare `deny`
|
|
10
|
+
* (a human refused). A spawn/exec plumbing failure, or no native channel, is
|
|
11
|
+
* `{ decision: 'deny', reason: 'unavailable' }` so callers never treat infra
|
|
12
|
+
* failures as owner refusal. An `always` result with an empty pattern falls
|
|
13
|
+
* back to the suggested default.
|
|
12
14
|
*/
|
|
13
|
-
import type
|
|
15
|
+
import { type SudoApproveRequest, type SudoBackend } from './types.js';
|
|
14
16
|
/** Minimal exec seam: run a binary with args, resolve its stdout. */
|
|
15
17
|
export type ExecFn = (cmd: string, args: string[]) => Promise<{
|
|
16
18
|
stdout: string;
|
|
@@ -33,5 +35,8 @@ export declare function createPowerShellBackend(exec?: ExecFn): SudoBackend;
|
|
|
33
35
|
export declare function createZenityBackend(exec?: ExecFn): SudoBackend;
|
|
34
36
|
/** Linux GUI: `kdialog --warningyesnocancel` (+ `--inputbox`). */
|
|
35
37
|
export declare function createKdialogBackend(exec?: ExecFn): SudoBackend;
|
|
36
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* A backend used when no native channel exists (headless, no DISPLAY/TTY).
|
|
40
|
+
* Returns `unavailable` — nobody was prompted — not a bare refuse.
|
|
41
|
+
*/
|
|
37
42
|
export declare function createDenyBackend(name?: string): SudoBackend;
|
|
@@ -6,12 +6,15 @@
|
|
|
6
6
|
* {@link SudoDecision}. They are deliberately injectable via {@link ExecFn} so
|
|
7
7
|
* tests can assert the argv and parsing without spawning a real dialog.
|
|
8
8
|
*
|
|
9
|
-
* Fail-closed contract: a dismissed dialog
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* Fail-closed contract: a dismissed dialog or Deny button is a bare `deny`
|
|
10
|
+
* (a human refused). A spawn/exec plumbing failure, or no native channel, is
|
|
11
|
+
* `{ decision: 'deny', reason: 'unavailable' }` so callers never treat infra
|
|
12
|
+
* failures as owner refusal. An `always` result with an empty pattern falls
|
|
13
|
+
* back to the suggested default.
|
|
12
14
|
*/
|
|
13
15
|
import { execFile as nodeExecFile } from 'child_process';
|
|
14
16
|
import { promisify } from 'util';
|
|
17
|
+
import { unavailableDecision, } from './types.js';
|
|
15
18
|
const defaultExec = promisify(nodeExecFile);
|
|
16
19
|
/**
|
|
17
20
|
* Human-readable description of the gated action, for every native dialog.
|
|
@@ -32,6 +35,29 @@ export function describeRequest(req) {
|
|
|
32
35
|
function fallbackPattern(req) {
|
|
33
36
|
return req.suggestedPattern?.trim() || req.detail.trim();
|
|
34
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Classify an exec failure from a real dialog binary.
|
|
40
|
+
*
|
|
41
|
+
* Node's `execFile` sets a **numeric** `code` when the process ran and exited
|
|
42
|
+
* non-zero (Deny / dismiss / Always-via-extra-button). A **string** errno
|
|
43
|
+
* (`ENOENT`, `EACCES`, …) or a generic `Error` without a numeric code means
|
|
44
|
+
* the binary never ran — plumbing, not a refusal.
|
|
45
|
+
*/
|
|
46
|
+
function denyFromDialogFailure(err) {
|
|
47
|
+
const code = err?.code;
|
|
48
|
+
if (typeof code === 'number')
|
|
49
|
+
return { decision: 'deny' };
|
|
50
|
+
return unavailableDecision();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* True when the dialog binary did not produce a real exit code.
|
|
54
|
+
* Matches {@link denyFromDialogFailure}: only a numeric `code` means the
|
|
55
|
+
* dialog ran (Deny / Always / dismiss). String errno or a code-less Error
|
|
56
|
+
* is plumbing — never a refusal.
|
|
57
|
+
*/
|
|
58
|
+
function isSpawnFailure(err) {
|
|
59
|
+
return typeof err?.code !== 'number';
|
|
60
|
+
}
|
|
35
61
|
/** macOS: single `osascript display dialog` with 3 buttons + a text field. */
|
|
36
62
|
export function createOsascriptBackend(exec = defaultExec) {
|
|
37
63
|
return {
|
|
@@ -53,8 +79,10 @@ export function createOsascriptBackend(exec = defaultExec) {
|
|
|
53
79
|
}
|
|
54
80
|
return { decision: 'deny' };
|
|
55
81
|
}
|
|
56
|
-
catch {
|
|
57
|
-
|
|
82
|
+
catch (err) {
|
|
83
|
+
// User cancel (Escape) exits non-zero with a numeric code; spawn failure
|
|
84
|
+
// is ENOENT / generic Error → unavailable.
|
|
85
|
+
return denyFromDialogFailure(err);
|
|
58
86
|
}
|
|
59
87
|
},
|
|
60
88
|
};
|
|
@@ -86,10 +114,12 @@ export function createPowerShellBackend(exec = defaultExec) {
|
|
|
86
114
|
pattern: pattern && pattern.length > 0 ? pattern : suggested,
|
|
87
115
|
};
|
|
88
116
|
}
|
|
117
|
+
// MessageBox "No" prints DENY — a genuine refusal.
|
|
89
118
|
return { decision: 'deny' };
|
|
90
119
|
}
|
|
91
|
-
catch {
|
|
92
|
-
|
|
120
|
+
catch (err) {
|
|
121
|
+
// PowerShell MessageBox never throws on No; a throw is spawn/plumbing.
|
|
122
|
+
return denyFromDialogFailure(err);
|
|
93
123
|
}
|
|
94
124
|
},
|
|
95
125
|
};
|
|
@@ -116,7 +146,9 @@ export function createZenityBackend(exec = defaultExec) {
|
|
|
116
146
|
allowed = true;
|
|
117
147
|
}
|
|
118
148
|
catch (err) {
|
|
119
|
-
//
|
|
149
|
+
// Spawn failure never reached a human; numeric exit is Deny / Always.
|
|
150
|
+
if (isSpawnFailure(err))
|
|
151
|
+
return unavailableDecision();
|
|
120
152
|
always = stdoutOf(err).trim() === 'Always';
|
|
121
153
|
allowed = always;
|
|
122
154
|
}
|
|
@@ -146,6 +178,8 @@ export function createKdialogBackend(exec = defaultExec) {
|
|
|
146
178
|
code = 0; // Yes
|
|
147
179
|
}
|
|
148
180
|
catch (err) {
|
|
181
|
+
if (isSpawnFailure(err))
|
|
182
|
+
return unavailableDecision();
|
|
149
183
|
code = exitCodeOf(err);
|
|
150
184
|
}
|
|
151
185
|
if (code === 0)
|
|
@@ -156,12 +190,15 @@ export function createKdialogBackend(exec = defaultExec) {
|
|
|
156
190
|
},
|
|
157
191
|
};
|
|
158
192
|
}
|
|
159
|
-
/**
|
|
193
|
+
/**
|
|
194
|
+
* A backend used when no native channel exists (headless, no DISPLAY/TTY).
|
|
195
|
+
* Returns `unavailable` — nobody was prompted — not a bare refuse.
|
|
196
|
+
*/
|
|
160
197
|
export function createDenyBackend(name = 'none') {
|
|
161
198
|
return {
|
|
162
199
|
name,
|
|
163
200
|
async prompt() {
|
|
164
|
-
return
|
|
201
|
+
return unavailableDecision();
|
|
165
202
|
},
|
|
166
203
|
};
|
|
167
204
|
}
|
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
* implementation runs `window.prompt` inside a transient offscreen
|
|
9
9
|
* `BrowserWindow`. Both seams are injectable so tests never touch Electron.
|
|
10
10
|
*
|
|
11
|
-
* Fail closed: a
|
|
12
|
-
*
|
|
11
|
+
* Fail closed: a cancelled prompt or Deny button is a bare `deny`; a thrown
|
|
12
|
+
* `showMessageBox` (no display / Electron gone) is `unavailable`. A cancelled
|
|
13
|
+
* Always-pattern prompt falls back to the suggested pattern.
|
|
13
14
|
*/
|
|
14
|
-
import type
|
|
15
|
+
import { type SudoBackend } from './types.js';
|
|
15
16
|
/** Result shape of `dialog.showMessageBox`. */
|
|
16
17
|
interface MessageBoxResult {
|
|
17
18
|
response: number;
|
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
* implementation runs `window.prompt` inside a transient offscreen
|
|
9
9
|
* `BrowserWindow`. Both seams are injectable so tests never touch Electron.
|
|
10
10
|
*
|
|
11
|
-
* Fail closed: a
|
|
12
|
-
*
|
|
11
|
+
* Fail closed: a cancelled prompt or Deny button is a bare `deny`; a thrown
|
|
12
|
+
* `showMessageBox` (no display / Electron gone) is `unavailable`. A cancelled
|
|
13
|
+
* Always-pattern prompt falls back to the suggested pattern.
|
|
13
14
|
*/
|
|
15
|
+
import { unavailableDecision, } from './types.js';
|
|
14
16
|
const BUTTONS = ['Deny', 'Allow Once', 'Always'];
|
|
15
17
|
/** Create the Electron backend. */
|
|
16
18
|
export function createElectronBackend(deps = {}) {
|
|
@@ -45,7 +47,8 @@ export function createElectronBackend(deps = {}) {
|
|
|
45
47
|
response = result.response;
|
|
46
48
|
}
|
|
47
49
|
catch {
|
|
48
|
-
|
|
50
|
+
// Dialog never appeared — plumbing, not a refusal.
|
|
51
|
+
return unavailableDecision();
|
|
49
52
|
}
|
|
50
53
|
if (response === 1)
|
|
51
54
|
return { decision: 'allow' };
|
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
* (the agent's browser `node` shim can't reach this process) and returns the
|
|
9
9
|
* human's decision. Loopback-only, like the other local node-server endpoints.
|
|
10
10
|
*
|
|
11
|
-
* Fail closed: an invalid body → 400; a backend that throws →
|
|
12
|
-
*
|
|
11
|
+
* Fail closed: an invalid body → 400; a backend that throws →
|
|
12
|
+
* `{ decision: 'deny', reason: 'unavailable' }` (200). A bare `deny` is reserved
|
|
13
|
+
* for a genuine human refusal. Requests never auto-resolve to allow.
|
|
13
14
|
*/
|
|
14
15
|
import { type Express } from 'express';
|
|
15
|
-
import type
|
|
16
|
+
import { type SudoBackend } from './types.js';
|
|
16
17
|
export interface SudoEndpointOptions {
|
|
17
18
|
/**
|
|
18
19
|
* Backend to use for every request. Defaults to lazy per-request selection
|
|
@@ -8,12 +8,14 @@
|
|
|
8
8
|
* (the agent's browser `node` shim can't reach this process) and returns the
|
|
9
9
|
* human's decision. Loopback-only, like the other local node-server endpoints.
|
|
10
10
|
*
|
|
11
|
-
* Fail closed: an invalid body → 400; a backend that throws →
|
|
12
|
-
*
|
|
11
|
+
* Fail closed: an invalid body → 400; a backend that throws →
|
|
12
|
+
* `{ decision: 'deny', reason: 'unavailable' }` (200). A bare `deny` is reserved
|
|
13
|
+
* for a genuine human refusal. Requests never auto-resolve to allow.
|
|
13
14
|
*/
|
|
14
15
|
import express from 'express';
|
|
15
16
|
import { requireLoopback } from '../cloud-status.js';
|
|
16
17
|
import { selectSudoBackend } from './select.js';
|
|
18
|
+
import { unavailableDecision, } from './types.js';
|
|
17
19
|
// Must list EVERY `SudoKind`. An omission is not a safe default: the endpoint
|
|
18
20
|
// 400s, the browser-side broker reads that as a denial, and the gate fails
|
|
19
21
|
// closed forever — so a missing kind silently makes that whole approval path
|
|
@@ -68,8 +70,8 @@ export function registerSudoApproveEndpoint(app, options = {}) {
|
|
|
68
70
|
decision = await backend.prompt(request);
|
|
69
71
|
}
|
|
70
72
|
catch (err) {
|
|
71
|
-
warn(`sudo-approve backend "${backend.name}" threw — denying: ${String(err)}`);
|
|
72
|
-
decision =
|
|
73
|
+
warn(`sudo-approve backend "${backend.name}" threw — denying as unavailable: ${String(err)}`);
|
|
74
|
+
decision = unavailableDecision();
|
|
73
75
|
}
|
|
74
76
|
res.json(decision);
|
|
75
77
|
});
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* 3. Windows → PowerShell
|
|
7
7
|
* 4. Linux+GUI → `zenity` / `kdialog`
|
|
8
8
|
* 5. headless → editable TTY
|
|
9
|
-
* 6. no channel → fail closed (
|
|
9
|
+
* 6. no channel → fail closed (`unavailable`) + log
|
|
10
10
|
*
|
|
11
11
|
* The environment probe is injectable so tests can force any branch without
|
|
12
12
|
* touching the real platform / spawning `which`.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* 3. Windows → PowerShell
|
|
7
7
|
* 4. Linux+GUI → `zenity` / `kdialog`
|
|
8
8
|
* 5. headless → editable TTY
|
|
9
|
-
* 6. no channel → fail closed (
|
|
9
|
+
* 6. no channel → fail closed (`unavailable`) + log
|
|
10
10
|
*
|
|
11
11
|
* The environment probe is injectable so tests can force any branch without
|
|
12
12
|
* touching the real platform / spawning `which`.
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* When there's no GUI but the node-server has a controlling terminal, the
|
|
5
5
|
* approval gesture is a keystroke on stdin: `[a]llow once / [d]eny / [A]lways`.
|
|
6
6
|
* Choosing "Always" then reads an editable pattern line (blank keeps the
|
|
7
|
-
* suggested default). Fail closed:
|
|
7
|
+
* suggested default). Fail closed: an explicit `d` / other keystroke is a bare
|
|
8
|
+
* deny; a closed stream, EOF mid-prompt, or any thrown error is `unavailable`
|
|
9
|
+
* (the request never reached an answering human).
|
|
8
10
|
*/
|
|
9
11
|
import type { Interface as ReadlineInterface } from 'readline';
|
|
10
|
-
import type
|
|
12
|
+
import { type SudoBackend } from './types.js';
|
|
11
13
|
/** Seam for tests: build a question-asking interface over arbitrary streams. */
|
|
12
14
|
export interface TtyDeps {
|
|
13
15
|
output?: NodeJS.WritableStream;
|
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
* When there's no GUI but the node-server has a controlling terminal, the
|
|
5
5
|
* approval gesture is a keystroke on stdin: `[a]llow once / [d]eny / [A]lways`.
|
|
6
6
|
* Choosing "Always" then reads an editable pattern line (blank keeps the
|
|
7
|
-
* suggested default). Fail closed:
|
|
7
|
+
* suggested default). Fail closed: an explicit `d` / other keystroke is a bare
|
|
8
|
+
* deny; a closed stream, EOF mid-prompt, or any thrown error is `unavailable`
|
|
9
|
+
* (the request never reached an answering human).
|
|
8
10
|
*/
|
|
9
11
|
import { createInterface } from 'readline';
|
|
12
|
+
import { unavailableDecision, } from './types.js';
|
|
10
13
|
export function defaultRl() {
|
|
11
14
|
return createInterface({ input: process.stdin, output: process.stdout });
|
|
12
15
|
}
|
|
@@ -40,7 +43,7 @@ export function createTtyBackend(deps = {}) {
|
|
|
40
43
|
return { decision: 'deny' };
|
|
41
44
|
}
|
|
42
45
|
catch {
|
|
43
|
-
return
|
|
46
|
+
return unavailableDecision();
|
|
44
47
|
}
|
|
45
48
|
finally {
|
|
46
49
|
try {
|
|
@@ -43,11 +43,39 @@ export interface SudoApproveRequest {
|
|
|
43
43
|
export interface SudoDecision {
|
|
44
44
|
decision: 'allow' | 'deny' | 'always';
|
|
45
45
|
pattern?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Why a `deny` was reached when nobody actually refused. Absent for a real
|
|
48
|
+
* gesture (Deny button, TTY `d`, dialog dismiss). Enforcement layers and
|
|
49
|
+
* biscotto review use it to tell a guest "unanswered", not "refused".
|
|
50
|
+
*
|
|
51
|
+
* Node-server only stamps `unavailable` (no approval surface, a dialog
|
|
52
|
+
* binary that could not be spawned, a backend that threw). Timeout reasons
|
|
53
|
+
* live in the webapp broker layer. Mirror of webapp `SudoDecision.reason`.
|
|
54
|
+
*
|
|
55
|
+
* Deliberately a field rather than a fourth `decision` value: every consumer
|
|
56
|
+
* branches on `decision === 'deny'`, so a new variant would fail OPEN.
|
|
57
|
+
*/
|
|
58
|
+
reason?: SudoUnansweredReason;
|
|
46
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Why a `deny` carries no human's refusal. See {@link SudoDecision.reason}.
|
|
62
|
+
* Every value means "unanswered"; only the absence of a reason means "refused".
|
|
63
|
+
* Node-server emits only `unavailable`; the timeout values are listed so the
|
|
64
|
+
* wire shape matches the webapp consumer.
|
|
65
|
+
*/
|
|
66
|
+
export type SudoUnansweredReason = 'user-timeout' | 'cone-timeout' | 'unavailable';
|
|
67
|
+
/**
|
|
68
|
+
* The fail-closed decision for a request that never reached an approver.
|
|
69
|
+
* Use this instead of a bare `{ decision: 'deny' }` on any plumbing path: a
|
|
70
|
+
* bare deny is indistinguishable from a human pressing "Deny".
|
|
71
|
+
*/
|
|
72
|
+
export declare function unavailableDecision(): SudoDecision;
|
|
47
73
|
/**
|
|
48
74
|
* A native approval channel. `name` is for logging/selection; `prompt` raises
|
|
49
75
|
* the actual gesture. Implementations MUST fail closed (resolve `deny`) on any
|
|
50
|
-
* error, dismissal, or timeout — never throw to the endpoint.
|
|
76
|
+
* error, dismissal, or timeout — never throw to the endpoint. Plumbing
|
|
77
|
+
* failures MUST carry {@link unavailableDecision}'s `reason`; only a genuine
|
|
78
|
+
* human refusal is a bare deny.
|
|
51
79
|
*/
|
|
52
80
|
export interface SudoBackend {
|
|
53
81
|
readonly name: string;
|
|
@@ -10,4 +10,11 @@
|
|
|
10
10
|
* gesture in this process — the agent's in-browser `node` shim cannot reach
|
|
11
11
|
* here.
|
|
12
12
|
*/
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* The fail-closed decision for a request that never reached an approver.
|
|
15
|
+
* Use this instead of a bare `{ decision: 'deny' }` on any plumbing path: a
|
|
16
|
+
* bare deny is indistinguishable from a human pressing "Deny".
|
|
17
|
+
*/
|
|
18
|
+
export function unavailableDecision() {
|
|
19
|
+
return { decision: 'deny', reason: 'unavailable' };
|
|
20
|
+
}
|