permdock 0.0.0 → 0.2.0
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/README.md +327 -1
- package/dist/a2a/index.d.ts +169 -0
- package/dist/a2a/index.js +232 -0
- package/dist/adapter-context-mlWsVGGg.js +23 -0
- package/dist/ai-sdk/index.d.ts +95 -0
- package/dist/ai-sdk/index.js +154 -0
- package/dist/approval-required-TzNB9oQA.js +6 -0
- package/dist/approvals/index.d.ts +3 -0
- package/dist/approvals/index.js +5 -0
- package/dist/approvals-DbYAuNkG.js +224 -0
- package/dist/approvers-DT4AVyIg.js +52 -0
- package/dist/arazzo-BAgw9ap6.js +70 -0
- package/dist/arazzo-cmvX2jDe.js +54 -0
- package/dist/authzen/index.d.ts +39 -0
- package/dist/authzen/index.js +302 -0
- package/dist/better-auth/index.d.ts +109 -0
- package/dist/better-auth/index.js +344 -0
- package/dist/better-supabase/index.d.ts +206 -0
- package/dist/better-supabase/index.js +620 -0
- package/dist/bind-CVy3Espv.d.ts +25 -0
- package/dist/bind-VnYn2HKq.js +97 -0
- package/dist/boundary-BM8a_P6E.js +13 -0
- package/dist/boundary-BmHxVH1J.d.ts +15 -0
- package/dist/budget-JqcRqyhK.d.ts +11 -0
- package/dist/budget-NbEtbJC_.js +11 -0
- package/dist/canonical-json-DemiK4Vp.js +26 -0
- package/dist/capability-4qPdO9gk.js +173 -0
- package/dist/capability-Dq43tT1u.d.ts +99 -0
- package/dist/catalog/index.d.ts +12 -0
- package/dist/catalog/index.js +2 -0
- package/dist/catalog-BjZSovXQ.js +2 -0
- package/dist/catalog-doc-Dsc9y7sd.js +309 -0
- package/dist/catalog-h2tlKFkq.js +48 -0
- package/dist/catalog-path-CnI0XTFK.js +12 -0
- package/dist/catalog-z8q6bi_r.js +55 -0
- package/dist/claude-agent/index.d.ts +72 -0
- package/dist/claude-agent/index.js +77 -0
- package/dist/clerk/index.d.ts +65 -0
- package/dist/clerk/index.js +301 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +20 -0
- package/dist/cli/index.d.ts +46 -0
- package/dist/cli/index.js +5 -0
- package/dist/clients-C7zLFuWT.d.ts +12 -0
- package/dist/clients-DjU5ABMy.js +23 -0
- package/dist/cloud/index.d.ts +90 -0
- package/dist/cloud/index.js +404 -0
- package/dist/cloud-BGhHS-lY.js +50 -0
- package/dist/cloud-Bo3e9Ak3.js +118 -0
- package/dist/collect-DDDv_ESs.js +502 -0
- package/dist/collect-DLlujdHC.js +83 -0
- package/dist/collect-rG2IQ3t0.js +2 -0
- package/dist/commands-DWOrSQic.js +97 -0
- package/dist/compact-y6vfu2_f.js +19 -0
- package/dist/compare-pfyVnBjE.js +12 -0
- package/dist/compile/index.d.ts +3 -0
- package/dist/compile/index.js +2 -0
- package/dist/compile-Ba4blROR.js +613 -0
- package/dist/compile-CNnl6uIt.d.ts +94 -0
- package/dist/config-BJiM-xAm.js +157 -0
- package/dist/config-Bn00Z3ks.js +36 -0
- package/dist/config-report-Dw_JcUqT.js +44 -0
- package/dist/connection-CU8MQxRi.js +194 -0
- package/dist/context-Ttp0ekMT.js +82 -0
- package/dist/convex/index.d.ts +45 -0
- package/dist/convex/index.js +65 -0
- package/dist/create-BWyx3jua.d.ts +245 -0
- package/dist/create-CPhDL6qx.js +253 -0
- package/dist/credential-8P81ff4u.js +306 -0
- package/dist/delegation-D8pUEuce.js +103 -0
- package/dist/describe-B_gVZEVi.js +94 -0
- package/dist/describe-PdoqESvg.d.ts +36 -0
- package/dist/diff-B8Tunc3o.js +451 -0
- package/dist/diff-CzqLxZ77.js +48 -0
- package/dist/digest-B3kc_PgQ.d.ts +19 -0
- package/dist/digest-BI-6aa6d.js +27 -0
- package/dist/doctor-WpvG6p4k.js +1194 -0
- package/dist/doctor-collect-ByfYlNv1.js +664 -0
- package/dist/doctor-rOr-4ZfS.js +53 -0
- package/dist/drizzle/index.d.ts +73 -0
- package/dist/drizzle/index.js +160 -0
- package/dist/duration-xK2jGFVc.js +25 -0
- package/dist/elysia/index.d.ts +49 -0
- package/dist/elysia/index.js +59 -0
- package/dist/errors-CWfoNg9N.js +47 -0
- package/dist/errors-Cz5zn0mW.js +123 -0
- package/dist/errors-DAqevpNk.d.ts +125 -0
- package/dist/errors-DRnGZHzF.js +30 -0
- package/dist/evaluation-items-CfCCejQd.js +40 -0
- package/dist/evaluations-tz6D9A2G.js +461 -0
- package/dist/eve/index.d.ts +86 -0
- package/dist/eve/index.js +176 -0
- package/dist/expiry-BoBJ8Tvc.js +7 -0
- package/dist/express/index.d.ts +23 -0
- package/dist/express/index.js +78 -0
- package/dist/fastify/index.d.ts +33 -0
- package/dist/fastify/index.js +92 -0
- package/dist/fields-BMl1RxoX.js +26 -0
- package/dist/files-DWUN9PzI.js +125 -0
- package/dist/fixtures-ht28Cplr.js +67 -0
- package/dist/freeze-CrU8nQeX.js +60 -0
- package/dist/from-snapshot-C3sHpZNS.js +569 -0
- package/dist/generate-CH7ThJ14.js +173 -0
- package/dist/grantee-Cv5Sogfv.js +602 -0
- package/dist/graph-sql-B-dsAT6j.d.ts +29 -0
- package/dist/headers-BZSDieNT.js +6 -0
- package/dist/headers-Cf-zWvsO.d.ts +6 -0
- package/dist/helpers-H_KARDVC.js +421 -0
- package/dist/hono/index.d.ts +48 -0
- package/dist/hono/index.js +99 -0
- package/dist/hosted-DJQw46hn.js +266 -0
- package/dist/http-BIhmLfCc.js +95 -0
- package/dist/http-DPuDaChb.d.ts +13 -0
- package/dist/http-SO7qGttf.js +44 -0
- package/dist/ids-DC4V4CFu.js +18 -0
- package/dist/index-A0YNL8jZ.d.ts +177 -0
- package/dist/index-CtE07CBF.d.ts +6 -0
- package/dist/index-D3vbkoSI.d.ts +483 -0
- package/dist/index-D6_cSa1z.d.ts +201 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +26 -0
- package/dist/instance-options-BK_rNOSC.d.ts +11 -0
- package/dist/instance-options-Dhhcnm3y.js +22 -0
- package/dist/json-schema-BsAflUGW.js +106 -0
- package/dist/jwt/index.d.ts +91 -0
- package/dist/jwt/index.js +590 -0
- package/dist/kernel-GSQnMz3O.js +208 -0
- package/dist/kysely/index.d.ts +78 -0
- package/dist/kysely/index.js +129 -0
- package/dist/live-options-H2eG8dfJ.js +60 -0
- package/dist/load-DhWEB9P1.js +75 -0
- package/dist/load-DwJUafYN.js +2 -0
- package/dist/load-jose-BL6LH4bL.js +11 -0
- package/dist/lru-8q4WZjBV.js +28 -0
- package/dist/manifest-iaoBXHKc.js +783 -0
- package/dist/manifest-uDS9y5de.d.ts +206 -0
- package/dist/map-AcxK9o_D.js +147 -0
- package/dist/map-claims-Z4QZOiFF.js +369 -0
- package/dist/map-error-pgUWNNtG.js +23 -0
- package/dist/markers-B0hYLvMD.js +60 -0
- package/dist/may-use-DndQ5tQ-.js +77 -0
- package/dist/mcp/index.d.ts +159 -0
- package/dist/mcp/index.js +603 -0
- package/dist/nest/index.d.ts +75 -0
- package/dist/nest/index.js +261 -0
- package/dist/next/client.d.ts +23 -0
- package/dist/next/client.js +33 -0
- package/dist/next/index.d.ts +93 -0
- package/dist/next/index.js +180 -0
- package/dist/next/plugin.d.ts +14 -0
- package/dist/next/plugin.js +33 -0
- package/dist/node/index.d.ts +24 -0
- package/dist/node/index.js +34 -0
- package/dist/openai/index.d.ts +62 -0
- package/dist/openai/index.js +92 -0
- package/dist/openapi/index.d.ts +176 -0
- package/dist/openapi/index.js +3 -0
- package/dist/openapi-Bbg4wGVL.js +164 -0
- package/dist/openapi-BqRBvAng.js +538 -0
- package/dist/openapi-CqqEbHsL.js +250 -0
- package/dist/openapi-import-cmwetbzw.js +5959 -0
- package/dist/openapi-schema--ARjyfQr.js +6902 -0
- package/dist/orpc/index.d.ts +51 -0
- package/dist/orpc/index.js +145 -0
- package/dist/otel/index.d.ts +13 -0
- package/dist/otel/index.js +202 -0
- package/dist/package-root-zGkxO3oN.js +30 -0
- package/dist/parse-BuyjxhZV.js +53 -0
- package/dist/parse-local-manifest-BkGQNzAh.d.ts +10 -0
- package/dist/parse-local-manifest-BrMozJCo.js +36 -0
- package/dist/pdp/index.d.ts +22 -0
- package/dist/pdp/index.js +682 -0
- package/dist/peer-BOr_kJE1.js +31 -0
- package/dist/permdock-CzqXjCpU.js +3471 -0
- package/dist/permissions-CDgvKG5L.d.ts +52 -0
- package/dist/permissions-CpZrivYv.js +142 -0
- package/dist/permissions-DVoF6dMt.js +662 -0
- package/dist/permissions-DttnqqxC.d.ts +214 -0
- package/dist/pins-8ILTftC9.js +12 -0
- package/dist/policy-CFZAr_Al.d.ts +2252 -0
- package/dist/policy-D8UFyERw.d.ts +20 -0
- package/dist/policy-xyWg7QdR.js +1122 -0
- package/dist/powersync-A8x_Yyvh.js +2 -0
- package/dist/powersync-C7kLglqX.js +540 -0
- package/dist/powersync-yW1UUFzp.js +62 -0
- package/dist/prisma/index.d.ts +136 -0
- package/dist/prisma/index.js +455 -0
- package/dist/problem-B43ztKIm.js +191 -0
- package/dist/problem-details-CkL50EEf.d.ts +7 -0
- package/dist/problem-details-_AMOYz-w.js +144 -0
- package/dist/prompt-CXYhLfsS.js +32 -0
- package/dist/protected-CNsMzrVd.d.ts +27 -0
- package/dist/protected-Cs6kTaHo.js +100 -0
- package/dist/protected-bZs6rbT1.d.ts +40 -0
- package/dist/provider-Cjr62YMV.d.ts +6 -0
- package/dist/provider-DLzpb2Xz.js +52 -0
- package/dist/provider-store-DNyC1y48.js +43 -0
- package/dist/react/index.d.ts +8 -0
- package/dist/react/index.js +7 -0
- package/dist/react/provider-client.d.ts +2 -0
- package/dist/react/provider-client.js +3 -0
- package/dist/react-native/index.d.ts +198 -0
- package/dist/react-native/index.js +696 -0
- package/dist/refs-KYb4ntaz.js +91 -0
- package/dist/relations-cjOL01hZ.js +505 -0
- package/dist/replay-BsOyTrdV.js +28 -0
- package/dist/replay-sqBGMnNs.d.ts +5 -0
- package/dist/resource-ref-CaS9yVPi.js +20 -0
- package/dist/revocations-Bhr0aMIC.js +27 -0
- package/dist/revocations-Cy06JNx2.d.ts +21 -0
- package/dist/rls-BMNydpHk.js +2521 -0
- package/dist/rls-CEvs64Kj.js +238 -0
- package/dist/rls-fields-BMasREhR.js +3502 -0
- package/dist/rls-graph-pc9fuXYm.js +435 -0
- package/dist/rls-import-D8F7j35Q.js +943 -0
- package/dist/rls-migrate-7wikfrVI.js +367 -0
- package/dist/rls-ownership-B5Lkq62W.js +843 -0
- package/dist/rls-shared-aOQ4hGTO.js +493 -0
- package/dist/rls-verify-D8B7dOTh.js +745 -0
- package/dist/row-check-Dt35czLy.d.ts +40 -0
- package/dist/rpc-DOK54_CS.js +89 -0
- package/dist/run-YXCJDNYk.js +174 -0
- package/dist/runtime-CB8gQFvS.js +11 -0
- package/dist/saas-Bj_FFkjN.js +1203 -0
- package/dist/schema-D3MnDMxa.js +300 -0
- package/dist/scim/index.d.ts +2 -0
- package/dist/scim/index.js +2 -0
- package/dist/scim-BFGBBAqR.js +1445 -0
- package/dist/scopes-CVdPrWN-.js +239 -0
- package/dist/server/index.d.ts +134 -0
- package/dist/server/index.js +11 -0
- package/dist/server-DlZ28Oz5.js +268 -0
- package/dist/sha256-BO0swRXL.js +150 -0
- package/dist/shared-DRu8VFVM.js +123 -0
- package/dist/sink-Bd9PSyfq.js +141 -0
- package/dist/sink-Gs6TTVa-.d.ts +104 -0
- package/dist/skills-Biq5uup3.js +192 -0
- package/dist/skills-JaymNCdK.js +56 -0
- package/dist/snapshot-Enxd0n-s.js +975 -0
- package/dist/snapshot-cache-D6jLelln.js +44 -0
- package/dist/snapshot-cache-DwQH8ufx.d.ts +45 -0
- package/dist/solid/index.d.ts +93 -0
- package/dist/solid/index.js +188 -0
- package/dist/sources-B0UvrJ21.js +534 -0
- package/dist/sources-BcJgw0lt.d.ts +192 -0
- package/dist/sql-xdg5f4Fi.js +27 -0
- package/dist/src-CSbUiozF.js +568 -0
- package/dist/ssf/index.d.ts +4 -0
- package/dist/ssf/index.js +596 -0
- package/dist/store-DMAWwAJV.js +371 -0
- package/dist/store-D_pjn91-.d.ts +36 -0
- package/dist/store-options-yO0V6gRn.js +716 -0
- package/dist/stores-4_sqiOA1.js +87 -0
- package/dist/stores-DVR5NHLE.d.ts +52 -0
- package/dist/stream-DDpvynSB.js +66 -0
- package/dist/stream-DlvG2hs-.d.ts +12 -0
- package/dist/style-CTm1AXDT.js +22 -0
- package/dist/subject-C6dLxqHU.js +24 -0
- package/dist/subject-settings-BwBmCdTR.js +128 -0
- package/dist/supabase/index.d.ts +183 -0
- package/dist/supabase/index.js +5 -0
- package/dist/supabase/middleware.d.ts +82 -0
- package/dist/supabase/middleware.js +102 -0
- package/dist/supabase-CwafwxxF.js +83 -0
- package/dist/supabase-DTBaC20O.js +774 -0
- package/dist/supabase-config-Bs44dare.js +472 -0
- package/dist/supabase-hook-BuxLQLpU.js +1814 -0
- package/dist/supabase-hook-D5qVd_D8.js +2 -0
- package/dist/supautils-DYZnzRWV.js +91 -0
- package/dist/svelte/PermissionBoundary.svelte +37 -0
- package/dist/svelte/Protected.svelte +40 -0
- package/dist/svelte/index.d.ts +13 -0
- package/dist/svelte/index.js +107 -0
- package/dist/svelte/runtime.d.ts +7 -0
- package/dist/svelte/runtime.js +4 -0
- package/dist/svelte/source.d.ts +13 -0
- package/dist/svelte/source.js +11 -0
- package/dist/terminal/index.d.ts +163 -0
- package/dist/terminal/index.js +833 -0
- package/dist/testing/index.d.ts +745 -0
- package/dist/testing/index.js +3672 -0
- package/dist/testing/saas/index.d.ts +194 -0
- package/dist/testing/saas/index.js +3 -0
- package/dist/testing/saas/permissions.d.ts +2 -0
- package/dist/testing/saas/permissions.js +2 -0
- package/dist/text-diff-BdkkYSbi.js +15 -0
- package/dist/thenable-okc2xDsj.js +10 -0
- package/dist/timeout-D51niuQP.js +8 -0
- package/dist/token-CIWxJOCh.js +50 -0
- package/dist/trpc/index.d.ts +61 -0
- package/dist/trpc/index.js +122 -0
- package/dist/types-9AWO9EGt.d.ts +197 -0
- package/dist/types-BPxgszlT.d.ts +550 -0
- package/dist/types-BVyImy5I.d.ts +120 -0
- package/dist/types-Bvc4oWT6.d.ts +9 -0
- package/dist/types-C3s9xVJG.d.ts +48 -0
- package/dist/types-Dnemwd77.d.ts +183 -0
- package/dist/types-kxznc7cn.d.ts +52 -0
- package/dist/types-vKyAMtcX.d.ts +157 -0
- package/dist/types-vNSDF7Dq.d.ts +66 -0
- package/dist/types-yF5LmQVD.d.ts +306 -0
- package/dist/unplugin/index.d.ts +7 -0
- package/dist/unplugin/index.js +31 -0
- package/dist/usage-D07N9QJ3.js +42 -0
- package/dist/usage-D82l1CwK.js +2 -0
- package/dist/usage-DG1oeCca.js +693 -0
- package/dist/validation-error-CnjZ4t92.js +33 -0
- package/dist/validation-error-CtF23SrC.d.ts +26 -0
- package/dist/validity-uOp8UpYn.js +245 -0
- package/dist/verifier-BxBo6I44.js +466 -0
- package/dist/version-BvacVTZR.js +17 -0
- package/dist/views-Soe7bGSC.js +95 -0
- package/dist/vocabulary-DA8a8zif.d.ts +56 -0
- package/dist/vue/index.d.ts +79 -0
- package/dist/vue/index.js +196 -0
- package/dist/watch-Etoa_Mvr.js +2 -0
- package/dist/watch-UihESpsJ.js +128 -0
- package/dist/webmcp/index.d.ts +75 -0
- package/dist/webmcp/index.js +307 -0
- package/package.json +450 -5
- package/rulesets/permdock.yaml +29 -0
- package/schemas/approval-request-v1.json +175 -0
- package/schemas/catalog-v1.json +535 -0
- package/schemas/openapi/oas-3.1.json +1298 -0
- package/schemas/openapi/oas-3.2.json +1516 -0
- package/schemas/openapi/overlay-1.1.json +79 -0
- package/schemas/supabase-claims-v1.json +144 -0
- package/schemas/supabase-manifest-v1.json +361 -0
- package/skills/permdock/SKILL.md +66 -0
- package/skills/permdock-agents/SKILL.md +77 -0
- package/skills/permdock-agents/references/runtimes.md +213 -0
- package/skills/permdock-approvals/SKILL.md +80 -0
- package/skills/permdock-approvals/references/stores-and-resume.md +94 -0
- package/skills/permdock-audit/SKILL.md +67 -0
- package/skills/permdock-credentials/SKILL.md +55 -0
- package/skills/permdock-credentials/references/tokens-keys-links.md +132 -0
- package/skills/permdock-data/SKILL.md +57 -0
- package/skills/permdock-data/references/queries-and-rls.md +62 -0
- package/skills/permdock-data/references/relationships.md +67 -0
- package/skills/permdock-tenancy/SKILL.md +80 -0
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +89 -0
- package/skills/permdock-wire/SKILL.md +116 -0
- package/skills/permdock-wire/references/adapters.md +492 -0
- package/skills/permdock-wire/references/policy-options.md +45 -0
|
@@ -0,0 +1,833 @@
|
|
|
1
|
+
import { t as compact } from "../compact-y6vfu2_f.js";
|
|
2
|
+
import { a as deniedMessage, i as approvalMessage, n as PermDockDeniedError, t as PermDockApprovalRequiredError } from "../errors-Cz5zn0mW.js";
|
|
3
|
+
import { t as createPermDock$1 } from "../permdock-CzqXjCpU.js";
|
|
4
|
+
import { n as resourceRef } from "../resource-ref-CaS9yVPi.js";
|
|
5
|
+
import { i as isSubject } from "../subject-C6dLxqHU.js";
|
|
6
|
+
import { l as storedApprovalToken, s as resumeDecision } from "../helpers-H_KARDVC.js";
|
|
7
|
+
import { t as timeoutSignal } from "../timeout-D51niuQP.js";
|
|
8
|
+
import { t as instanceOptions } from "../instance-options-Dhhcnm3y.js";
|
|
9
|
+
import { mkdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { createInterface } from "node:readline";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
//#region src/terminal/device.ts
|
|
14
|
+
/** Milliseconds an authorization server request may take. */
|
|
15
|
+
const TERMINAL_TIMEOUT_MS = 1e4;
|
|
16
|
+
function isRecord$1(value) {
|
|
17
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
18
|
+
}
|
|
19
|
+
async function readJson(response) {
|
|
20
|
+
try {
|
|
21
|
+
const body = await response.json();
|
|
22
|
+
return isRecord$1(body) ? body : null;
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** POSTs a form and reads a JSON object back; any network or parse failure is `null`. */
|
|
28
|
+
async function postForm(runtime, url, fields) {
|
|
29
|
+
const fetchImpl = runtime.fetch ?? fetch;
|
|
30
|
+
try {
|
|
31
|
+
return await readJson(await fetchImpl(url, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
34
|
+
body: new URLSearchParams(fields),
|
|
35
|
+
signal: timeoutSignal(TERMINAL_TIMEOUT_MS)
|
|
36
|
+
}));
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function discoverDeviceEndpoints(issuer, runtime) {
|
|
42
|
+
const fetchImpl = runtime.fetch ?? fetch;
|
|
43
|
+
const url = issuer.endsWith("/") ? `${issuer}.well-known/oauth-authorization-server` : `${issuer}/.well-known/oauth-authorization-server`;
|
|
44
|
+
try {
|
|
45
|
+
const body = await readJson(await fetchImpl(url, { signal: timeoutSignal(TERMINAL_TIMEOUT_MS) }));
|
|
46
|
+
if (body === null) return {};
|
|
47
|
+
return compact({
|
|
48
|
+
authorizationEndpoint: typeof body["device_authorization_endpoint"] === "string" ? body["device_authorization_endpoint"] : void 0,
|
|
49
|
+
tokenEndpoint: typeof body["token_endpoint"] === "string" ? body["token_endpoint"] : void 0,
|
|
50
|
+
revocationEndpoint: typeof body["revocation_endpoint"] === "string" ? body["revocation_endpoint"] : void 0
|
|
51
|
+
});
|
|
52
|
+
} catch {
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function parseAuthorization(body) {
|
|
57
|
+
if (typeof body["device_code"] !== "string" || typeof body["user_code"] !== "string" || typeof body["verification_uri"] !== "string" || typeof body["expires_in"] !== "number") return null;
|
|
58
|
+
return compact({
|
|
59
|
+
device_code: body["device_code"],
|
|
60
|
+
user_code: body["user_code"],
|
|
61
|
+
verification_uri: body["verification_uri"],
|
|
62
|
+
verification_uri_complete: typeof body["verification_uri_complete"] === "string" ? body["verification_uri_complete"] : void 0,
|
|
63
|
+
expires_in: body["expires_in"],
|
|
64
|
+
interval: typeof body["interval"] === "number" ? body["interval"] : void 0
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function parseToken(body, now) {
|
|
68
|
+
if (typeof body["error"] === "string") return {
|
|
69
|
+
ok: false,
|
|
70
|
+
error: body["error"]
|
|
71
|
+
};
|
|
72
|
+
if (typeof body["access_token"] !== "string") return {
|
|
73
|
+
ok: false,
|
|
74
|
+
error: "invalid-token"
|
|
75
|
+
};
|
|
76
|
+
return {
|
|
77
|
+
ok: true,
|
|
78
|
+
credential: compact({
|
|
79
|
+
access_token: body["access_token"],
|
|
80
|
+
refresh_token: typeof body["refresh_token"] === "string" ? body["refresh_token"] : void 0,
|
|
81
|
+
expires_at: typeof body["expires_in"] === "number" ? now + body["expires_in"] : void 0,
|
|
82
|
+
token_type: typeof body["token_type"] === "string" ? body["token_type"] : void 0
|
|
83
|
+
})
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
async function runDeviceFlow(device, runtime, write) {
|
|
87
|
+
const sleep = runtime.sleep ?? defaultSleep;
|
|
88
|
+
const now = runtime.now ?? (() => Math.floor(Date.now() / 1e3));
|
|
89
|
+
let authorizationEndpoint = device.authorizationEndpoint;
|
|
90
|
+
let tokenEndpoint = device.tokenEndpoint;
|
|
91
|
+
if ((authorizationEndpoint === void 0 || tokenEndpoint === void 0) && device.issuer !== void 0) {
|
|
92
|
+
const discovered = await discoverDeviceEndpoints(device.issuer, runtime);
|
|
93
|
+
authorizationEndpoint = authorizationEndpoint ?? discovered.authorizationEndpoint;
|
|
94
|
+
tokenEndpoint = tokenEndpoint ?? discovered.tokenEndpoint;
|
|
95
|
+
}
|
|
96
|
+
if (authorizationEndpoint === void 0 || tokenEndpoint === void 0) return null;
|
|
97
|
+
const startedBody = await postForm(runtime, authorizationEndpoint, {
|
|
98
|
+
client_id: device.clientId,
|
|
99
|
+
scope: device.scope ?? ""
|
|
100
|
+
});
|
|
101
|
+
const authorization = startedBody === null ? null : parseAuthorization(startedBody);
|
|
102
|
+
if (authorization === null) return null;
|
|
103
|
+
device.onPrompt?.(compact({
|
|
104
|
+
user_code: authorization.user_code,
|
|
105
|
+
verification_uri: authorization.verification_uri,
|
|
106
|
+
verification_uri_complete: authorization.verification_uri_complete
|
|
107
|
+
}));
|
|
108
|
+
write(`Visit ${authorization.verification_uri} and enter ${authorization.user_code}\n`);
|
|
109
|
+
if (authorization.verification_uri_complete !== void 0) device.open?.(authorization.verification_uri_complete);
|
|
110
|
+
let interval = authorization.interval ?? 5;
|
|
111
|
+
const deadline = now() + authorization.expires_in;
|
|
112
|
+
while (now() < deadline) {
|
|
113
|
+
await sleep(interval * 1e3);
|
|
114
|
+
const polledBody = await postForm(runtime, tokenEndpoint, {
|
|
115
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
116
|
+
device_code: authorization.device_code,
|
|
117
|
+
client_id: device.clientId
|
|
118
|
+
});
|
|
119
|
+
if (polledBody === null) return null;
|
|
120
|
+
const token = parseToken(polledBody, now());
|
|
121
|
+
if (token.ok) return token.credential;
|
|
122
|
+
if (token.error === "slow_down") {
|
|
123
|
+
interval += 5;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (token.error === "authorization_pending") continue;
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
async function refreshCredential(device, credential, runtime) {
|
|
132
|
+
if (credential.refresh_token === void 0) return null;
|
|
133
|
+
const now = runtime.now ?? (() => Math.floor(Date.now() / 1e3));
|
|
134
|
+
let tokenEndpoint = device.tokenEndpoint;
|
|
135
|
+
if (tokenEndpoint === void 0 && device.issuer !== void 0) tokenEndpoint = (await discoverDeviceEndpoints(device.issuer, runtime)).tokenEndpoint;
|
|
136
|
+
if (tokenEndpoint === void 0) return null;
|
|
137
|
+
const body = await postForm(runtime, tokenEndpoint, {
|
|
138
|
+
grant_type: "refresh_token",
|
|
139
|
+
refresh_token: credential.refresh_token,
|
|
140
|
+
client_id: device.clientId
|
|
141
|
+
});
|
|
142
|
+
if (body === null) return null;
|
|
143
|
+
const token = parseToken(body, now());
|
|
144
|
+
return token.ok ? token.credential : null;
|
|
145
|
+
}
|
|
146
|
+
async function revokeCredential(device, credential, runtime) {
|
|
147
|
+
if (device === void 0 || credential.refresh_token === void 0) return;
|
|
148
|
+
let revocationEndpoint = device.revocationEndpoint;
|
|
149
|
+
if (revocationEndpoint === void 0 && device.issuer !== void 0) revocationEndpoint = (await discoverDeviceEndpoints(device.issuer, runtime)).revocationEndpoint;
|
|
150
|
+
if (revocationEndpoint === void 0) return;
|
|
151
|
+
await postForm(runtime, revocationEndpoint, {
|
|
152
|
+
token: credential.refresh_token,
|
|
153
|
+
token_type_hint: "refresh_token",
|
|
154
|
+
client_id: device.clientId
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function defaultSleep(ms) {
|
|
158
|
+
return new Promise((resolve) => {
|
|
159
|
+
setTimeout(resolve, ms);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
//#endregion
|
|
163
|
+
//#region src/terminal/exit.ts
|
|
164
|
+
const EX_OK = 0;
|
|
165
|
+
const EX_USAGE = 64;
|
|
166
|
+
const EX_TEMPFAIL = 75;
|
|
167
|
+
const EX_NOPERM = 77;
|
|
168
|
+
const EX_CONFIG = 78;
|
|
169
|
+
var TerminalExit = class extends Error {
|
|
170
|
+
name = "TerminalExit";
|
|
171
|
+
code;
|
|
172
|
+
constructor(code) {
|
|
173
|
+
super(`exit ${String(code)}`);
|
|
174
|
+
this.code = code;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
function defaultExit(code) {
|
|
178
|
+
process.exit.bind(process)(code);
|
|
179
|
+
throw new TerminalExit(code);
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/terminal/filter.ts
|
|
183
|
+
function snapshotAllows(permdock, permission) {
|
|
184
|
+
const snapshot = permdock.snapshot();
|
|
185
|
+
if (typeof snapshot === "string" || snapshot instanceof Promise) return false;
|
|
186
|
+
return snapshot.grants.some((grant) => grant.permission === permission.key && grant.effect === "allow");
|
|
187
|
+
}
|
|
188
|
+
function commandAllowed(permdock, permission) {
|
|
189
|
+
if (permission.kind === "collection") return permdock.can(permission);
|
|
190
|
+
return snapshotAllows(permdock, permission);
|
|
191
|
+
}
|
|
192
|
+
function filterCommandEntries(permdock, entries, options) {
|
|
193
|
+
const mode = options.mode;
|
|
194
|
+
if (permdock === void 0) {
|
|
195
|
+
if (mode === "hide") return [];
|
|
196
|
+
return entries.map((entry) => ({
|
|
197
|
+
...entry,
|
|
198
|
+
description: `${entry.description} (requires ${entry.permission.scope})`
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
201
|
+
const visible = [];
|
|
202
|
+
for (const entry of entries) {
|
|
203
|
+
if (commandAllowed(permdock, entry.permission)) {
|
|
204
|
+
visible.push(entry);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (mode === "annotate") visible.push({
|
|
208
|
+
...entry,
|
|
209
|
+
description: `${entry.description} (requires ${entry.permission.scope})`
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
return visible;
|
|
213
|
+
}
|
|
214
|
+
//#endregion
|
|
215
|
+
//#region src/terminal/format.ts
|
|
216
|
+
function exitCode(decision) {
|
|
217
|
+
switch (decision.outcome) {
|
|
218
|
+
case "granted": return 0;
|
|
219
|
+
case "approval-required": return 75;
|
|
220
|
+
case "denied": return 77;
|
|
221
|
+
default: return decision;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function permissionKey(decision, permission) {
|
|
225
|
+
if (permission !== void 0) return permission.key;
|
|
226
|
+
if (decision.outcome === "granted") return decision.matched.permission;
|
|
227
|
+
if (decision.outcome === "approval-required") return decision.grant.permission;
|
|
228
|
+
return "unknown";
|
|
229
|
+
}
|
|
230
|
+
function permissionScope(permission) {
|
|
231
|
+
return permission?.scope ?? "";
|
|
232
|
+
}
|
|
233
|
+
function resourceOf(permission) {
|
|
234
|
+
return { type: permission?.resource ?? "unknown" };
|
|
235
|
+
}
|
|
236
|
+
function formatDecision(decision, options = {}) {
|
|
237
|
+
const json = options.json === true;
|
|
238
|
+
const key = permissionKey(decision, options.permission);
|
|
239
|
+
const scope = permissionScope(options.permission);
|
|
240
|
+
const resource = resourceOf(options.permission);
|
|
241
|
+
const subject = options.subject;
|
|
242
|
+
if (decision.outcome === "granted") return json ? `${JSON.stringify({
|
|
243
|
+
outcome: "granted",
|
|
244
|
+
permission: key
|
|
245
|
+
})}\n` : "";
|
|
246
|
+
if (decision.outcome === "approval-required") {
|
|
247
|
+
const error = new PermDockApprovalRequiredError({
|
|
248
|
+
decision,
|
|
249
|
+
permission: key,
|
|
250
|
+
scope,
|
|
251
|
+
resource,
|
|
252
|
+
message: approvalMessage(key, decision.reason, decision.token)
|
|
253
|
+
});
|
|
254
|
+
const details = compact({
|
|
255
|
+
...error.toProblemDetails(compact({ instance: options.instance })),
|
|
256
|
+
approval: options.approval
|
|
257
|
+
});
|
|
258
|
+
if (json) return `${JSON.stringify(details)}\n`;
|
|
259
|
+
return `${details.detail}\n`;
|
|
260
|
+
}
|
|
261
|
+
const details = new PermDockDeniedError({
|
|
262
|
+
decision,
|
|
263
|
+
permission: key,
|
|
264
|
+
scope,
|
|
265
|
+
resource,
|
|
266
|
+
subject: subject ?? {
|
|
267
|
+
principal: null,
|
|
268
|
+
context: {}
|
|
269
|
+
},
|
|
270
|
+
message: deniedMessage(key, subject?.principal?.id, decision.denials, decision.alternatives.map((leaf) => leaf.key))
|
|
271
|
+
}).toProblemDetails(compact({ instance: options.instance }));
|
|
272
|
+
if (json) return `${JSON.stringify(details)}\n`;
|
|
273
|
+
return `${details.detail}\n`;
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/terminal/storage.ts
|
|
277
|
+
const FILE_MODE = 384;
|
|
278
|
+
const DIR_MODE = 448;
|
|
279
|
+
const GROUP_OR_WORLD = 63;
|
|
280
|
+
function credentialsPath(service, runtime) {
|
|
281
|
+
if (runtime.configDir !== void 0) return path.join(runtime.configDir, "credentials.json");
|
|
282
|
+
const home = runtime.homedir ?? homedir;
|
|
283
|
+
const env = runtime.env ?? process.env;
|
|
284
|
+
if ((runtime.platform ?? process.platform) === "win32") {
|
|
285
|
+
const appData = env["APPDATA"] ?? path.join(home(), "AppData", "Roaming");
|
|
286
|
+
return path.join(appData, service, "credentials.json");
|
|
287
|
+
}
|
|
288
|
+
const xdg = env["XDG_CONFIG_HOME"] ?? path.join(home(), ".config");
|
|
289
|
+
return path.join(xdg, service, "credentials.json");
|
|
290
|
+
}
|
|
291
|
+
function isRecord(value) {
|
|
292
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
293
|
+
}
|
|
294
|
+
function parseCredential(value) {
|
|
295
|
+
if (!isRecord(value) || typeof value["access_token"] !== "string") return null;
|
|
296
|
+
return compact({
|
|
297
|
+
access_token: value["access_token"],
|
|
298
|
+
refresh_token: typeof value["refresh_token"] === "string" ? value["refresh_token"] : void 0,
|
|
299
|
+
expires_at: typeof value["expires_at"] === "number" ? value["expires_at"] : void 0,
|
|
300
|
+
token_type: typeof value["token_type"] === "string" ? value["token_type"] : void 0
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
function keyringEntry(storage, profile) {
|
|
304
|
+
if (storage.keyring === void 0) return null;
|
|
305
|
+
try {
|
|
306
|
+
return new storage.keyring(storage.service, profile);
|
|
307
|
+
} catch {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function readKeyring(storage, profile) {
|
|
312
|
+
const entry = keyringEntry(storage, profile);
|
|
313
|
+
if (entry === null) return null;
|
|
314
|
+
try {
|
|
315
|
+
const raw = entry.getPassword();
|
|
316
|
+
return typeof raw === "string" ? parseCredential(JSON.parse(raw)) : null;
|
|
317
|
+
} catch {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function readCredentials(storage, profile, runtime) {
|
|
322
|
+
return readKeyring(storage, profile) ?? readFile(storage.service, profile, runtime);
|
|
323
|
+
}
|
|
324
|
+
function readFile(service, profile, runtime) {
|
|
325
|
+
const file = credentialsPath(service, runtime);
|
|
326
|
+
let raw;
|
|
327
|
+
try {
|
|
328
|
+
const stat = statSync(file);
|
|
329
|
+
if ((runtime.platform ?? process.platform) !== "win32" && (stat.mode & GROUP_OR_WORLD) !== 0) return null;
|
|
330
|
+
raw = readFileSync(file, "utf8");
|
|
331
|
+
} catch {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
const parsed = JSON.parse(raw);
|
|
336
|
+
if (!isRecord(parsed) || !isRecord(parsed["profiles"])) return null;
|
|
337
|
+
return parseCredential(parsed["profiles"][profile]);
|
|
338
|
+
} catch {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/** The OS keychain when `storage.keyring` is set and works; otherwise the mode-0600 file. */
|
|
343
|
+
function writeCredentials(storage, profile, credential, runtime) {
|
|
344
|
+
const entry = keyringEntry(storage, profile);
|
|
345
|
+
if (entry !== null) try {
|
|
346
|
+
entry.setPassword(JSON.stringify(credential));
|
|
347
|
+
deleteFile(storage.service, profile, runtime);
|
|
348
|
+
return;
|
|
349
|
+
} catch {}
|
|
350
|
+
writeFile(storage.service, profile, credential, runtime);
|
|
351
|
+
}
|
|
352
|
+
function writeFile(service, profile, credential, runtime) {
|
|
353
|
+
const file = credentialsPath(service, runtime);
|
|
354
|
+
mkdirSync(path.dirname(file), {
|
|
355
|
+
recursive: true,
|
|
356
|
+
mode: DIR_MODE
|
|
357
|
+
});
|
|
358
|
+
let profiles = {};
|
|
359
|
+
try {
|
|
360
|
+
const existing = JSON.parse(readFileSync(file, "utf8"));
|
|
361
|
+
if (isRecord(existing) && isRecord(existing["profiles"])) profiles = existing["profiles"];
|
|
362
|
+
} catch {
|
|
363
|
+
profiles = {};
|
|
364
|
+
}
|
|
365
|
+
profiles[profile] = credential;
|
|
366
|
+
writeFileSync(file, `${JSON.stringify({ profiles }, null, 2)}\n`, {
|
|
367
|
+
encoding: "utf8",
|
|
368
|
+
mode: FILE_MODE
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
function deleteCredentials(storage, profile, runtime) {
|
|
372
|
+
const entry = keyringEntry(storage, profile);
|
|
373
|
+
if (entry !== null) try {
|
|
374
|
+
entry.deletePassword();
|
|
375
|
+
} catch {}
|
|
376
|
+
deleteFile(storage.service, profile, runtime);
|
|
377
|
+
}
|
|
378
|
+
function deleteFile(service, profile, runtime) {
|
|
379
|
+
const file = credentialsPath(service, runtime);
|
|
380
|
+
let parsed;
|
|
381
|
+
try {
|
|
382
|
+
parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
383
|
+
} catch {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (!isRecord(parsed) || !isRecord(parsed["profiles"])) return;
|
|
387
|
+
const profiles = {};
|
|
388
|
+
for (const key of Object.keys(parsed["profiles"])) if (key !== profile) profiles[key] = parsed["profiles"][key];
|
|
389
|
+
if (Object.keys(profiles).length === 0) {
|
|
390
|
+
try {
|
|
391
|
+
unlinkSync(file);
|
|
392
|
+
} catch {}
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
writeFileSync(file, `${JSON.stringify({ profiles }, null, 2)}\n`, {
|
|
396
|
+
encoding: "utf8",
|
|
397
|
+
mode: FILE_MODE
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
//#endregion
|
|
401
|
+
//#region src/terminal/token.ts
|
|
402
|
+
const JWT_PART = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/u;
|
|
403
|
+
function looksLikeJwt(value) {
|
|
404
|
+
return JWT_PART.test(value);
|
|
405
|
+
}
|
|
406
|
+
function warnJwtInArgv(argv, write) {
|
|
407
|
+
for (const arg of argv) if (looksLikeJwt(arg)) {
|
|
408
|
+
write("warning: a JWT-shaped value was found in argv; tokens must not be passed as flags\n");
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
function profileFromArgv(argv) {
|
|
413
|
+
const index = argv.indexOf("--as");
|
|
414
|
+
if (index === -1) return;
|
|
415
|
+
const next = argv[index + 1];
|
|
416
|
+
return next === void 0 || next.startsWith("-") ? void 0 : next;
|
|
417
|
+
}
|
|
418
|
+
function sourceName(source) {
|
|
419
|
+
if (typeof source === "string") return source;
|
|
420
|
+
if ("source" in source) return source.source;
|
|
421
|
+
return "env";
|
|
422
|
+
}
|
|
423
|
+
function envName(source, fallback) {
|
|
424
|
+
if (typeof source === "string") return fallback;
|
|
425
|
+
return source.env ?? fallback;
|
|
426
|
+
}
|
|
427
|
+
async function fromCiOidc(runtime, source) {
|
|
428
|
+
const env = runtime.env ?? process.env;
|
|
429
|
+
const fetchImpl = runtime.fetch ?? fetch;
|
|
430
|
+
const named = typeof source === "string" ? void 0 : source.env;
|
|
431
|
+
const audience = typeof source === "object" && "audience" in source ? source.audience : void 0;
|
|
432
|
+
if (named !== void 0) {
|
|
433
|
+
const value = env[named];
|
|
434
|
+
return typeof value === "string" && value !== "" ? value : null;
|
|
435
|
+
}
|
|
436
|
+
if (typeof env["CI_JOB_JWT_V2"] === "string" && env["CI_JOB_JWT_V2"] !== "") return env["CI_JOB_JWT_V2"];
|
|
437
|
+
if (typeof env["ACTIONS_ID_TOKEN_REQUEST_URL"] === "string" && typeof env["ACTIONS_ID_TOKEN_REQUEST_TOKEN"] === "string") try {
|
|
438
|
+
const url = new URL(env["ACTIONS_ID_TOKEN_REQUEST_URL"]);
|
|
439
|
+
if (audience !== void 0) url.searchParams.set("audience", audience);
|
|
440
|
+
const body = await (await fetchImpl(url, {
|
|
441
|
+
headers: { Authorization: `Bearer ${env["ACTIONS_ID_TOKEN_REQUEST_TOKEN"]}` },
|
|
442
|
+
signal: timeoutSignal(TERMINAL_TIMEOUT_MS)
|
|
443
|
+
})).json();
|
|
444
|
+
if (body !== null && typeof body === "object" && "value" in body && typeof body.value === "string") return body.value;
|
|
445
|
+
} catch {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
async function resolveToken(sources, options) {
|
|
451
|
+
const walk = options.force === void 0 ? sources : [options.force];
|
|
452
|
+
const now = options.runtime.now ?? (() => Math.floor(Date.now() / 1e3));
|
|
453
|
+
for (const source of walk) {
|
|
454
|
+
const name = sourceName(source);
|
|
455
|
+
switch (name) {
|
|
456
|
+
case "env": {
|
|
457
|
+
const key = envName(source, "PERMDOCK_TOKEN");
|
|
458
|
+
const value = (options.runtime.env ?? process.env)[key];
|
|
459
|
+
if (typeof value === "string" && value !== "") return value;
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
case "keychain": {
|
|
463
|
+
if (options.storage === void 0) break;
|
|
464
|
+
const stored = readCredentials(options.storage, options.profile, options.runtime);
|
|
465
|
+
if (stored === null) break;
|
|
466
|
+
if (stored.expires_at !== void 0 && stored.expires_at <= now()) {
|
|
467
|
+
if (options.device !== void 0) {
|
|
468
|
+
const refreshed = await refreshCredential(options.device, stored, options.runtime);
|
|
469
|
+
if (refreshed !== null) {
|
|
470
|
+
writeCredentials(options.storage, options.profile, refreshed, options.runtime);
|
|
471
|
+
return refreshed.access_token;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
return stored.access_token;
|
|
477
|
+
}
|
|
478
|
+
case "ci-oidc": {
|
|
479
|
+
const oidc = await fromCiOidc(options.runtime, source);
|
|
480
|
+
if (oidc !== null) return oidc;
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
case "device": {
|
|
484
|
+
if (options.device === void 0) break;
|
|
485
|
+
const credential = await runDeviceFlow(options.device, options.runtime, options.write);
|
|
486
|
+
if (credential === null) break;
|
|
487
|
+
if (options.storage !== void 0) writeCredentials(options.storage, options.profile, credential, options.runtime);
|
|
488
|
+
return credential.access_token;
|
|
489
|
+
}
|
|
490
|
+
default: return name;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
//#endregion
|
|
496
|
+
//#region src/terminal/create.ts
|
|
497
|
+
/** A thrown subject, loader or decision, or an instance check without a row. */
|
|
498
|
+
const FAILED_CLOSED = {
|
|
499
|
+
outcome: "denied",
|
|
500
|
+
denials: [{
|
|
501
|
+
role: null,
|
|
502
|
+
reason: "validation"
|
|
503
|
+
}],
|
|
504
|
+
alternatives: []
|
|
505
|
+
};
|
|
506
|
+
function writeOf(options) {
|
|
507
|
+
return options.runtime?.write ?? ((text) => {
|
|
508
|
+
process.stderr.write(text);
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
function argvOf(options) {
|
|
512
|
+
return options.runtime?.argv ?? process.argv;
|
|
513
|
+
}
|
|
514
|
+
function envOf(options) {
|
|
515
|
+
return options.runtime?.env ?? process.env;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* The local y/N is the requester answering for themselves, so it stands in
|
|
519
|
+
* for an approval only where the grant allows that (`distinct: false`) and no
|
|
520
|
+
* agent is acting for the user.
|
|
521
|
+
*/
|
|
522
|
+
function answersLocally(decision, subject) {
|
|
523
|
+
const approval = decision.grant.approval;
|
|
524
|
+
return typeof approval === "object" && approval.distinct === false && subject.actor === void 0;
|
|
525
|
+
}
|
|
526
|
+
function isInteractive(options) {
|
|
527
|
+
if (typeof options.interactive === "boolean") return options.interactive;
|
|
528
|
+
if (options.interactive !== void 0) return true;
|
|
529
|
+
return (options.runtime?.stdoutIsTTY ?? process.stdout.isTTY) === true && envOf(options)["CI"] === void 0;
|
|
530
|
+
}
|
|
531
|
+
function jsonOutput(options) {
|
|
532
|
+
if (options.output?.json !== void 0) return options.output.json;
|
|
533
|
+
return argvOf(options).includes("--json");
|
|
534
|
+
}
|
|
535
|
+
function flag(options, value, names) {
|
|
536
|
+
if (value !== void 0) return value;
|
|
537
|
+
const argv = argvOf(options);
|
|
538
|
+
return names.some((name) => argv.includes(name));
|
|
539
|
+
}
|
|
540
|
+
function actorFromResolved(value) {
|
|
541
|
+
if (value === null || value === void 0) return {};
|
|
542
|
+
if (isSubject(value)) {
|
|
543
|
+
const principal = value.principal;
|
|
544
|
+
const actor = value.actor ?? (principal === null ? void 0 : {
|
|
545
|
+
id: principal.id,
|
|
546
|
+
kind: principal.kind ?? "oauth-client"
|
|
547
|
+
});
|
|
548
|
+
return compact({
|
|
549
|
+
actor,
|
|
550
|
+
delegation: value.delegation
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
if (typeof value === "object" && value !== null && "id" in value && "kind" in value && typeof value.id === "string" && typeof value.kind === "string") return { actor: value };
|
|
554
|
+
return {};
|
|
555
|
+
}
|
|
556
|
+
function defaultConfirm(input) {
|
|
557
|
+
const rl = createInterface({
|
|
558
|
+
input: process.stdin,
|
|
559
|
+
output: process.stderr
|
|
560
|
+
});
|
|
561
|
+
return new Promise((resolve) => {
|
|
562
|
+
rl.question(`${input.permission} on ${describeResource(input.resource)} (${input.reason}). Continue? [y/N] `, (answer) => {
|
|
563
|
+
rl.close();
|
|
564
|
+
resolve(answer.trim().toLowerCase() === "y");
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
function describeResource(resource) {
|
|
569
|
+
return resource.id === void 0 ? resource.type : `${resource.type} ${resource.id}`;
|
|
570
|
+
}
|
|
571
|
+
const defaultTyped = (input) => {
|
|
572
|
+
const rl = createInterface({
|
|
573
|
+
input: process.stdin,
|
|
574
|
+
output: process.stderr
|
|
575
|
+
});
|
|
576
|
+
return new Promise((resolve) => {
|
|
577
|
+
rl.question(`${input.permission} on ${describeResource(input.resource)} cannot be undone. Type ${input.expected} to continue: `, (answer) => {
|
|
578
|
+
rl.close();
|
|
579
|
+
resolve(answer);
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
};
|
|
583
|
+
function createPermDock(policy, options) {
|
|
584
|
+
const write = writeOf(options);
|
|
585
|
+
const exit = options.runtime?.exit ?? defaultExit;
|
|
586
|
+
const runtime = compact({
|
|
587
|
+
...options.runtime,
|
|
588
|
+
configDir: options.runtime?.configDir ?? options.storage?.dir
|
|
589
|
+
});
|
|
590
|
+
warnJwtInArgv(argvOf(options), write);
|
|
591
|
+
let cached;
|
|
592
|
+
let last;
|
|
593
|
+
let lastProfile = "default";
|
|
594
|
+
const tokenFor = (profile, force) => (sources) => resolveToken(sources, compact({
|
|
595
|
+
profile,
|
|
596
|
+
storage: options.storage,
|
|
597
|
+
device: options.device,
|
|
598
|
+
runtime,
|
|
599
|
+
write,
|
|
600
|
+
force
|
|
601
|
+
}));
|
|
602
|
+
const resolve = async (resolveOptions = {}) => {
|
|
603
|
+
const profile = resolveOptions.as ?? profileFromArgv(argvOf(options)) ?? "default";
|
|
604
|
+
lastProfile = profile;
|
|
605
|
+
const context = {
|
|
606
|
+
token: tokenFor(profile, resolveOptions.source),
|
|
607
|
+
profile
|
|
608
|
+
};
|
|
609
|
+
let user = null;
|
|
610
|
+
try {
|
|
611
|
+
user = await options.subject(context);
|
|
612
|
+
} catch {
|
|
613
|
+
user = null;
|
|
614
|
+
}
|
|
615
|
+
let actor;
|
|
616
|
+
let delegation;
|
|
617
|
+
if (options.actor !== void 0) try {
|
|
618
|
+
const resolved = actorFromResolved(await options.actor(context));
|
|
619
|
+
actor = resolved.actor;
|
|
620
|
+
delegation = resolved.delegation;
|
|
621
|
+
} catch {
|
|
622
|
+
actor = void 0;
|
|
623
|
+
delegation = void 0;
|
|
624
|
+
}
|
|
625
|
+
const built = await createPermDock$1(policy, user, compact({
|
|
626
|
+
tenant: options.tenant,
|
|
627
|
+
actor,
|
|
628
|
+
delegation,
|
|
629
|
+
...instanceOptions(options)
|
|
630
|
+
}));
|
|
631
|
+
last = built;
|
|
632
|
+
return built;
|
|
633
|
+
};
|
|
634
|
+
const permdock = (resolveOptions = {}) => {
|
|
635
|
+
if (resolveOptions.refresh === true || cached === void 0) cached = resolve(resolveOptions);
|
|
636
|
+
return cached;
|
|
637
|
+
};
|
|
638
|
+
const format = (decision, formatOptions = {}) => formatDecision(decision, compact({
|
|
639
|
+
json: formatOptions.json ?? jsonOutput(options),
|
|
640
|
+
permission: formatOptions.permission,
|
|
641
|
+
instance: formatOptions.instance,
|
|
642
|
+
subject: formatOptions.subject ?? last?.subject,
|
|
643
|
+
approval: formatOptions.approval ?? options.approval
|
|
644
|
+
}));
|
|
645
|
+
const filterCommands = (entries, filterOptions) => {
|
|
646
|
+
const next = filterOptions ?? { mode: "hide" };
|
|
647
|
+
return filterCommandEntries(next.permdock ?? last, entries, next);
|
|
648
|
+
};
|
|
649
|
+
const protect = (permission, load) => (action) => async (...args) => {
|
|
650
|
+
const dryRun = flag(options, options.dryRun, ["--dry-run"]);
|
|
651
|
+
let instance;
|
|
652
|
+
let data;
|
|
653
|
+
let decide;
|
|
654
|
+
let first;
|
|
655
|
+
try {
|
|
656
|
+
instance = await permdock();
|
|
657
|
+
if (load !== void 0) {
|
|
658
|
+
data = await load(...args);
|
|
659
|
+
if (permission.kind === "instance" && (data === null || data === void 0)) {
|
|
660
|
+
write(format(FAILED_CLOSED, {
|
|
661
|
+
permission,
|
|
662
|
+
subject: instance.subject
|
|
663
|
+
}));
|
|
664
|
+
return exit(exitCode(FAILED_CLOSED));
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
decide = instance.decide;
|
|
668
|
+
first = decide(permission, data, dryRun ? {
|
|
669
|
+
source: "simulate",
|
|
670
|
+
adapter: "terminal"
|
|
671
|
+
} : {
|
|
672
|
+
source: "adapter",
|
|
673
|
+
adapter: "terminal"
|
|
674
|
+
});
|
|
675
|
+
} catch {
|
|
676
|
+
write(format(FAILED_CLOSED, { permission }));
|
|
677
|
+
return exit(exitCode(FAILED_CLOSED));
|
|
678
|
+
}
|
|
679
|
+
if (dryRun) {
|
|
680
|
+
write(first.outcome === "granted" ? jsonOutput(options) ? `${JSON.stringify({
|
|
681
|
+
outcome: "granted",
|
|
682
|
+
permission: permission.key,
|
|
683
|
+
dryRun: true
|
|
684
|
+
})}\n` : `dry run: ${permission.key} on ${describeResource(resourceRef(permission, data))} is granted; nothing ran\n` : format(first, {
|
|
685
|
+
permission,
|
|
686
|
+
subject: instance.subject
|
|
687
|
+
}));
|
|
688
|
+
return exit(exitCode(first));
|
|
689
|
+
}
|
|
690
|
+
if (first.outcome === "denied") {
|
|
691
|
+
write(format(first, {
|
|
692
|
+
permission,
|
|
693
|
+
subject: instance.subject
|
|
694
|
+
}));
|
|
695
|
+
return exit(exitCode(first));
|
|
696
|
+
}
|
|
697
|
+
let granted;
|
|
698
|
+
if (first.outcome === "granted") granted = first;
|
|
699
|
+
else if (answersLocally(first, instance.subject)) {
|
|
700
|
+
if (!isInteractive(options)) {
|
|
701
|
+
write(format(first, {
|
|
702
|
+
permission,
|
|
703
|
+
subject: instance.subject
|
|
704
|
+
}));
|
|
705
|
+
return exit(exitCode(first));
|
|
706
|
+
}
|
|
707
|
+
const confirm = typeof options.interactive === "object" ? options.interactive.confirm ?? defaultConfirm : defaultConfirm;
|
|
708
|
+
const promptToken = first.token;
|
|
709
|
+
if (!await confirm({
|
|
710
|
+
permission: permission.key,
|
|
711
|
+
resource: resourceRef(permission, data),
|
|
712
|
+
reason: first.reason,
|
|
713
|
+
token: promptToken
|
|
714
|
+
})) {
|
|
715
|
+
write(format({
|
|
716
|
+
outcome: "denied",
|
|
717
|
+
denials: [{
|
|
718
|
+
role: null,
|
|
719
|
+
reason: "approval"
|
|
720
|
+
}],
|
|
721
|
+
alternatives: []
|
|
722
|
+
}, {
|
|
723
|
+
permission,
|
|
724
|
+
subject: instance.subject
|
|
725
|
+
}));
|
|
726
|
+
return exit(77);
|
|
727
|
+
}
|
|
728
|
+
const again = decide(permission, data, compact({
|
|
729
|
+
source: "adapter",
|
|
730
|
+
adapter: "terminal"
|
|
731
|
+
}));
|
|
732
|
+
if (again.outcome === "denied") {
|
|
733
|
+
write(format(again, {
|
|
734
|
+
permission,
|
|
735
|
+
subject: instance.subject
|
|
736
|
+
}));
|
|
737
|
+
return exit(exitCode(again));
|
|
738
|
+
}
|
|
739
|
+
if (again.outcome === "granted") granted = again;
|
|
740
|
+
else if (again.token === promptToken) {
|
|
741
|
+
const principal = instance.subject.principal;
|
|
742
|
+
if (principal === null) {
|
|
743
|
+
write(format(again, {
|
|
744
|
+
permission,
|
|
745
|
+
subject: instance.subject
|
|
746
|
+
}));
|
|
747
|
+
return exit(77);
|
|
748
|
+
}
|
|
749
|
+
granted = {
|
|
750
|
+
outcome: "granted",
|
|
751
|
+
subject: {
|
|
752
|
+
...instance.subject,
|
|
753
|
+
principal
|
|
754
|
+
},
|
|
755
|
+
matched: again.grant,
|
|
756
|
+
token: again.token
|
|
757
|
+
};
|
|
758
|
+
} else {
|
|
759
|
+
write(format(again, {
|
|
760
|
+
permission,
|
|
761
|
+
subject: instance.subject
|
|
762
|
+
}));
|
|
763
|
+
return exit(77);
|
|
764
|
+
}
|
|
765
|
+
} else {
|
|
766
|
+
if (options.store === void 0) {
|
|
767
|
+
write(format(first, {
|
|
768
|
+
permission,
|
|
769
|
+
subject: instance.subject
|
|
770
|
+
}));
|
|
771
|
+
if (!jsonOutput(options)) write(`${permission.key} needs someone other than you to approve it: pass a store to createPermDock so the request can be approved and the command rerun\n`);
|
|
772
|
+
return exit(77);
|
|
773
|
+
}
|
|
774
|
+
const resumed = await resumeDecision({
|
|
775
|
+
decision: first,
|
|
776
|
+
permission,
|
|
777
|
+
subject: instance.subject,
|
|
778
|
+
store: options.store,
|
|
779
|
+
resource: resourceRef(permission, data),
|
|
780
|
+
adapter: "terminal",
|
|
781
|
+
token: await storedApprovalToken(options.store, first)
|
|
782
|
+
});
|
|
783
|
+
if (resumed.outcome !== "granted") {
|
|
784
|
+
write(format(resumed, {
|
|
785
|
+
permission,
|
|
786
|
+
subject: instance.subject
|
|
787
|
+
}));
|
|
788
|
+
return exit(exitCode(resumed));
|
|
789
|
+
}
|
|
790
|
+
granted = resumed;
|
|
791
|
+
}
|
|
792
|
+
if (permission.meta.destructive === true && !flag(options, options.yes, ["--yes", "-y"])) {
|
|
793
|
+
const resource = resourceRef(permission, data);
|
|
794
|
+
if (!isInteractive(options)) {
|
|
795
|
+
write(`${permission.key} on ${describeResource(resource)} is destructive: pass --yes to run it without a terminal\n`);
|
|
796
|
+
return exit(64);
|
|
797
|
+
}
|
|
798
|
+
const typed = typeof options.interactive === "object" ? options.interactive.typed ?? defaultTyped : defaultTyped;
|
|
799
|
+
const expected = resource.id ?? permission.key;
|
|
800
|
+
if ((await typed({
|
|
801
|
+
permission: permission.key,
|
|
802
|
+
resource,
|
|
803
|
+
expected
|
|
804
|
+
})).trim() !== expected) {
|
|
805
|
+
write(`${permission.key}: confirmation did not match; nothing ran\n`);
|
|
806
|
+
return exit(77);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
return action({
|
|
810
|
+
permdock: instance,
|
|
811
|
+
data,
|
|
812
|
+
decision: granted
|
|
813
|
+
}, ...args);
|
|
814
|
+
};
|
|
815
|
+
const logout = async () => {
|
|
816
|
+
if (options.storage === void 0) return;
|
|
817
|
+
const stored = readCredentials(options.storage, lastProfile, runtime);
|
|
818
|
+
if (stored !== null) await revokeCredential(options.device, stored, runtime);
|
|
819
|
+
deleteCredentials(options.storage, lastProfile, runtime);
|
|
820
|
+
cached = void 0;
|
|
821
|
+
last = void 0;
|
|
822
|
+
};
|
|
823
|
+
return {
|
|
824
|
+
permdock,
|
|
825
|
+
protect,
|
|
826
|
+
filterCommands,
|
|
827
|
+
format,
|
|
828
|
+
exitCode,
|
|
829
|
+
logout
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
//#endregion
|
|
833
|
+
export { EX_CONFIG, EX_NOPERM, EX_OK, EX_TEMPFAIL, EX_USAGE, TerminalExit, createPermDock, exitCode, formatDecision, looksLikeJwt };
|