zyrk 0.8.1 → 0.8.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/README.md CHANGED
@@ -131,6 +131,11 @@ zyrk install
131
131
  zyrk rebuild --offline # rebuild the complete approved locked graph
132
132
  ```
133
133
 
134
+ The review labels every exact source as `PENDING`, `APPROVED`, or `DENIED`,
135
+ shows its lifecycle commands, and names the policy scope that made the decision.
136
+ Review mode never runs package code. A mandatory deny cannot be selected and
137
+ does not prevent a separate permitted source from being approved.
138
+
134
139
  Approvals are local to the exact graph, sources, patch contents, and script
135
140
  policy. Interactive selections are written to the checked-in
136
141
  `zyrk-workspace.json` file with exact package source identities, so collaborators
@@ -476,7 +481,11 @@ linking on stderr. Interactive terminals show the stages as a calm, multi-line
476
481
  sequence, followed by a success footer; package downloads show received bytes,
477
482
  and a checkmark lists each unique package/version linked after the project
478
483
  transaction commits. Both `zyrk add prettier` and `zyrk install prettier` show
479
- these details. Redirected output uses plain progress messages. Source counters
484
+ these details. Redirected output uses plain progress messages, with frequent
485
+ source and download updates limited to once per second. Stage changes and
486
+ completion flush the latest counts, including when an install fails.
487
+ Intentionally unbuilt installs use an attention footer and state which review
488
+ action is needed. Source counters
480
489
  distinguish verified reuse from newly downloaded sources. Source counts differ
481
490
  from dependency contexts when peers split a package. `--json` disables progress.
482
491
  Other native commands use the same terminal language: successful summaries get
package/VERSION CHANGED
@@ -1 +1 @@
1
- v0.8.1
1
+ v0.8.3
@@ -0,0 +1,4 @@
1
+ import type { BuildReview } from "../install/private.ts";
2
+ /** Human review text only. It never executes package code or changes policy. */
3
+ export declare function renderBuildReviews(reviews: readonly BuildReview[]): string;
4
+ //# sourceMappingURL=build-approval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-approval.d.ts","sourceRoot":"","sources":["../../src/cli/build-approval.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAsBzD,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAW1E"}
@@ -0,0 +1,31 @@
1
+ import { redactSecrets } from "../log.js";
2
+ const decisionLabel = {
3
+ approved: "APPROVED",
4
+ denied: "DENIED",
5
+ pending: "PENDING",
6
+ };
7
+ const scopeLabel = {
8
+ "user-deny": "mandatory user policy",
9
+ "project-deny": "project deny policy",
10
+ "project-policy": "project script policy",
11
+ "workspace-policy": "zyrk-workspace.json",
12
+ "local-session": "current graph approval",
13
+ none: "no approval recorded",
14
+ };
15
+ function safe(value) {
16
+ return redactSecrets(value).replace(/[\x00-\x1f\x7f-\x9f]/g, " ");
17
+ }
18
+ /** Human review text only. It never executes package code or changes policy. */
19
+ export function renderBuildReviews(reviews) {
20
+ if (!reviews.length)
21
+ return "No dependency builds require approval.\n";
22
+ const entries = reviews
23
+ .map((review) => {
24
+ const stages = review.stages
25
+ .map((stage) => ` ${safe(stage.name)}: ${safe(stage.command)}`)
26
+ .join("\n");
27
+ return `${decisionLabel[review.decision]} ${safe(review.name)}@${safe(review.version)}\n source: ${safe(review.sourceId)}\n policy: ${scopeLabel[review.policyScope]}\n lifecycle:\n${stages}`;
28
+ })
29
+ .join("\n\n");
30
+ return `Dependency build review (no package code ran)\n\n${entries}\n\nApprovals persist by exact source in zyrk-workspace.json. User and project denies take precedence.\nApproved scripts run with your OS account permissions; approval is not a sandbox.\n`;
31
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAoCA,OAAO,EAAe,SAAS,EAAe,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,CAAC;AACrB,OAAO,EAAoB,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAM3C,OAAO,EAAqC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAEhF,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAmFzC,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA0BhD;AA0MD,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AA6EF;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,GACnD,SAAS,CAgDX;AAmBD,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAC1B,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3D,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3D,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;CACxC,CAAC;AAuDF,gFAAgF;AAChF,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAqiBpE"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAqCA,OAAO,EAAe,SAAS,EAAe,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,CAAC;AACrB,OAAO,EAAoB,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAM3C,OAAO,EAAqC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAEhF,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAsFzC,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA0BhD;AA0MD,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AA6EF;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,GACnD,SAAS,CAgDX;AAmBD,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAC1B,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3D,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3D,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;CACxC,CAAC;AAuDF,gFAAgF;AAChF,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAuhBpE"}
package/dist/cli/main.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { runNextCommand } from "../commands/next.js";
2
2
  import { createInstallProgress } from "./progress.js";
3
+ import { renderBuildReviews } from "./build-approval.js";
3
4
  import { renderCommandError, renderCommandOutput } from "./terminal.js";
4
5
  import { runMaintenanceCommand } from "../commands/maintenance.js";
5
6
  import { runDeployCommand } from "../commands/deploy.js";
@@ -39,6 +40,7 @@ function installCompletion(readiness) {
39
40
  };
40
41
  case "intentionally-unbuilt":
41
42
  return {
43
+ tone: "attention",
42
44
  title: "Installed; some dependency builds were intentionally skipped.",
43
45
  detail: "Review build policy before running packages that need those builds.",
44
46
  };
@@ -220,7 +222,7 @@ Example: zyrk audit --audit-level high`,
220
222
  Reconstruct project links from verified sources selected by the existing lockfile.`,
221
223
  "approve-builds": `Usage: zyrk approve-builds [package ... | --all]
222
224
 
223
- Review build candidates without running package code. Select exact candidates to persist approval, or use --all to approve every candidate shown.
225
+ Review exact source identities, lifecycle commands, and current policy decisions without running package code. Selected approvals persist to zyrk-workspace.json; user and project denies take precedence. Approved scripts are not sandboxed.
224
226
 
225
227
  Example: zyrk approve-builds`,
226
228
  rebuild: `Usage: zyrk rebuild [--offline]
@@ -426,16 +428,16 @@ function readCliVersion() {
426
428
  }
427
429
  }
428
430
  async function promptForBuildApproval(builds, input, output) {
429
- output.write("\nDependency lifecycle scripts need approval. Review these exact package sources:\n");
431
+ output.write("\nDependency build approval\nScripts run with your OS account permissions; approval is not a sandbox.\nSelected exact-source approvals are saved to zyrk-workspace.json for collaborators.\n\n");
430
432
  builds.forEach((build, index) => {
431
433
  const stages = build.stages
432
434
  .map((stage) => `${stage.name}: ${redactSecrets(stage.command)
433
435
  .replace(/[\x00-\x1f\x7f-\x9f]/g, "")
434
436
  .slice(0, 180)}`)
435
437
  .join("; ");
436
- output.write(` ${index + 1}. ${build.name}@${build.version} (${build.sourceId}) — ${stages}\n`);
438
+ output.write(` ${index + 1}. [ ] ${build.name}@${build.version}\n source: ${build.sourceId}\n lifecycle: ${stages}\n`);
437
439
  });
438
- output.write("Enter numbers separated by commas to approve selected sources, `all` to approve all listed sources, or press Enter to skip their scripts and continue unbuilt.\n");
440
+ output.write("Enter numbers separated by commas to approve selected sources, `all` to approve all listed sources, or press Enter to skip pending scripts and continue intentionally unbuilt.\n");
439
441
  const prompt = createInterface({
440
442
  input,
441
443
  output: output,
@@ -616,6 +618,8 @@ export async function runCli(options) {
616
618
  const payload = (command === "audit" ? sanitizeDiagnostic(result) : result);
617
619
  if (Array.isArray(payload.builds))
618
620
  payload.builds = sanitizeDiagnostic(payload.builds);
621
+ if (Array.isArray(payload.buildReviews))
622
+ payload.buildReviews = sanitizeDiagnostic(payload.buildReviews);
619
623
  const exitCode = payload.status === "unavailable" || payload.thresholdExceeded === true
620
624
  ? 1
621
625
  : 0;
@@ -626,15 +630,10 @@ export async function runCli(options) {
626
630
  stderr: "",
627
631
  });
628
632
  if (command === "approve-builds") {
629
- const builds = (payload.builds ?? []);
633
+ const reviews = (payload.buildReviews ?? []);
630
634
  return emit({
631
635
  exitCode,
632
- stdout: builds.length
633
- ? builds
634
- .map((b) => `${b.name}@${b.version} ${b.sourceId}\n${b.stages.map((s) => ` ${s.name}: ${redactSecrets(s.command)}`).join("\n")}`)
635
- .join("\n") +
636
- "\nReview runs no package code. Approval trusts these exact candidate sources.\n"
637
- : "No dependency builds require approval.\n",
636
+ stdout: renderBuildReviews(reviews),
638
637
  stderr: "",
639
638
  });
640
639
  }
@@ -805,10 +804,7 @@ export async function runCli(options) {
805
804
  }
806
805
  : {}),
807
806
  });
808
- progress.close({
809
- title: `Dependency changes installed from the verified shared store.`,
810
- detail: `${result.packages} packages; ${result.reused} realizations reused.`,
811
- });
807
+ progress.close(installCompletion(result.readiness));
812
808
  if (parsed.json) {
813
809
  return emit({
814
810
  exitCode: 0,
@@ -2,6 +2,7 @@ import type { InstallProgress } from "../install/run.ts";
2
2
  export type InstallCompletion = {
3
3
  title: string;
4
4
  detail?: string;
5
+ tone?: "success" | "attention";
5
6
  };
6
7
  /** Presentation only: never writes to stdout or exposes package URLs or paths. */
7
8
  export declare function createInstallProgress(stream: {
@@ -1 +1 @@
1
- {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/cli/progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAYzD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,kFAAkF;AAClF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE;IAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EAC1D,KAAK,EAAE,OAAO,EACd,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAChC;IACD,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,UAAU,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;CAC7C,CA2FA"}
1
+ {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/cli/progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAYzD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC;CAChC,CAAC;AAEF,kFAAkF;AAClF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE;IAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EAC1D,KAAK,EAAE,OAAO,EACd,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAChC;IACD,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,UAAU,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;CAC7C,CAiIA"}
@@ -18,6 +18,9 @@ export function createInstallProgress(stream, quiet, options = {}) {
18
18
  let statusLine = false;
19
19
  let lastDownload = 0;
20
20
  let lastPlain = "";
21
+ let pendingPlain = "";
22
+ let lastPlainAt = -Infinity;
23
+ let downloadAnnounced = false;
21
24
  const write = (text) => {
22
25
  try {
23
26
  stream.write(text);
@@ -32,6 +35,14 @@ export function createInstallProgress(stream, quiet, options = {}) {
32
35
  write(`\r\u001b[2K${lineBreak ? "\n" : ""}`);
33
36
  statusLine = false;
34
37
  };
38
+ const flushPlain = () => {
39
+ if (pendingPlain && pendingPlain !== lastPlain) {
40
+ write(`zyrk: ${pendingPlain}\n`);
41
+ lastPlain = pendingPlain;
42
+ lastPlainAt = Date.now();
43
+ }
44
+ pendingPlain = "";
45
+ };
35
46
  return {
36
47
  update(event) {
37
48
  if (quiet || closed)
@@ -45,6 +56,8 @@ export function createInstallProgress(stream, quiet, options = {}) {
45
56
  if (pkg?.status === "linked") {
46
57
  if (tty)
47
58
  clearStatus(true);
59
+ else
60
+ flushPlain();
48
61
  write(`${green("✓")} ${packageLabel} linked\n`);
49
62
  return;
50
63
  }
@@ -63,7 +76,7 @@ export function createInstallProgress(stream, quiet, options = {}) {
63
76
  lastDownload = Date.now();
64
77
  const status = pkg?.status === "downloading"
65
78
  ? ` Downloading ${packageLabel}: ${((pkg.bytes ?? 0) / 1024).toFixed(1)} KiB`
66
- : ` Sources verified ${event.completed ?? 0}/${event.total ?? 0} · reused ${event.reused ?? 0} · downloaded ${event.downloaded ?? 0}`;
79
+ : ` Sources verified ${event.completed ?? 0}${event.total === undefined ? "" : `/${event.total}`} · reused ${event.reused ?? 0} · downloaded ${event.downloaded ?? 0}`;
67
80
  write(`\r\u001b[2K${dim(status)}`);
68
81
  statusLine = true;
69
82
  }
@@ -78,9 +91,25 @@ export function createInstallProgress(stream, quiet, options = {}) {
78
91
  (event.reused === undefined
79
92
  ? ""
80
93
  : ` — reused ${event.reused}, downloaded ${event.downloaded ?? 0}`);
81
- if (current !== lastPlain)
82
- write(`zyrk: ${current}\n`);
83
- lastPlain = current;
94
+ const newStage = stage !== currentStage;
95
+ if (newStage) {
96
+ flushPlain();
97
+ currentStage = stage;
98
+ downloadAnnounced = false;
99
+ }
100
+ const firstDownload = pkg?.status === "downloading" && !downloadAnnounced;
101
+ if (pkg?.status === "downloading")
102
+ downloadAnnounced = true;
103
+ pendingPlain = current;
104
+ const sourcesComplete = event.phase === "fetching" &&
105
+ pkg?.status !== "downloading" &&
106
+ event.total !== undefined &&
107
+ event.completed === event.total;
108
+ if (newStage ||
109
+ firstDownload ||
110
+ sourcesComplete ||
111
+ Date.now() - lastPlainAt >= 1000)
112
+ flushPlain();
84
113
  },
85
114
  close(completion) {
86
115
  if (closed)
@@ -88,11 +117,18 @@ export function createInstallProgress(stream, quiet, options = {}) {
88
117
  closed = true;
89
118
  if (quiet)
90
119
  return;
120
+ if (!tty)
121
+ flushPlain();
91
122
  if (tty) {
92
123
  clearStatus(true);
93
124
  if (completion) {
94
125
  write(`${dim("─".repeat(46))}\n`);
95
- write(`${green("✓")} ${completion.title}\n`);
126
+ const marker = completion.tone === "attention"
127
+ ? color
128
+ ? "\u001b[33m!\u001b[0m"
129
+ : "!"
130
+ : green("✓");
131
+ write(`${marker} ${completion.title}\n`);
96
132
  if (completion.detail)
97
133
  write(`${dim(completion.detail)}\n`);
98
134
  }
@@ -12,6 +12,10 @@ export type BuildCandidate = {
12
12
  command: string;
13
13
  }>;
14
14
  };
15
+ export type BuildReview = BuildCandidate & {
16
+ decision: "approved" | "denied" | "pending";
17
+ policyScope: "user-deny" | "project-deny" | "project-policy" | "workspace-policy" | "local-session" | "none";
18
+ };
15
19
  export declare const WORKSPACE_POLICY_FILE = "zyrk-workspace.json";
16
20
  export declare function candidates(nodes: readonly ResolvedOccurrence[], dirs: ReadonlyMap<string, string>): Promise<BuildCandidate[]>;
17
21
  export declare function candidateDigest(graph: GraphDoc, builds: readonly BuildCandidate[], policy: unknown, patches: string): string;
@@ -24,13 +28,14 @@ export declare function approveCandidate(options: {
24
28
  policy?: unknown;
25
29
  portable?: boolean;
26
30
  }): Promise<void>;
27
- export declare function approvedCandidates(options: {
31
+ export declare function reviewBuildCandidates(options: {
28
32
  project: string;
29
33
  env: NodeJS.ProcessEnv;
30
34
  digest: string;
31
35
  builds: readonly BuildCandidate[];
32
36
  policy: unknown;
33
- }): Promise<BuildCandidate[]>;
37
+ }): Promise<BuildReview[]>;
38
+ export declare function approvedCandidates(options: Parameters<typeof reviewBuildCandidates>[0]): Promise<BuildCandidate[]>;
34
39
  export declare function assertApproved(options: {
35
40
  project: string;
36
41
  env: NodeJS.ProcessEnv;
@@ -1 +1 @@
1
- {"version":3,"file":"private.d.ts","sourceRoot":"","sources":["../../src/install/private.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAG3C,OAAO,EAA2B,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAOjE,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD,CAAC;AACF,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAe3D,wBAAsB,UAAU,CAC9B,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,OAAO,CAAC,cAAc,EAAE,CAAC,CA8C3B;AAyBD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,SAAS,cAAc,EAAE,EACjC,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,GACd,MAAM,CASR;AACD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAClC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgChB;AA0ED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAClC,MAAM,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAgC5B;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAClC,MAAM,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAYhB;AACD,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,QAAQ,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,aAAa,CAAC,CAmGzB;AAID,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAClC,QAAQ,EAAE,aAAa,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsFhB"}
1
+ {"version":3,"file":"private.d.ts","sourceRoot":"","sources":["../../src/install/private.ts"],"names":[],"mappings":"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"}
@@ -88,25 +88,22 @@ export function candidateDigest(graph, builds, policy, patches) {
88
88
  }
89
89
  export async function approveCandidate(options) {
90
90
  const user = await readUserPolicy(options.env ?? process.env);
91
- for (const build of options.builds)
92
- if (denied(user.scripts, build) || denied(options.policy, build))
93
- fail(`Build of ${build.name}@${build.version} is denied by policy.`);
94
91
  const selected = new Set(options.selected);
95
92
  const all = selected.has("*");
96
93
  for (const name of selected)
97
94
  if (name !== "*" &&
98
95
  !options.builds.some((b) => b.name === name || b.sourceId === name))
99
96
  fail("An approval selector does not match a reviewed build candidate.");
97
+ const selectedBuilds = options.builds.filter((build) => all || selected.has(build.name) || selected.has(build.sourceId));
98
+ const deniedSelections = selectedBuilds.filter((build) => denied(user.scripts, build) || denied(options.policy, build));
99
+ for (const build of deniedSelections)
100
+ fail(`Build of ${build.name}@${build.version} is denied by policy.`);
100
101
  await writeFileAtomic(path.join(options.project, ".zyrk-local", "approvals.json"), JSON.stringify({
101
102
  schemaVersion: 1,
102
103
  candidate: options.digest,
103
- sources: options.builds
104
- .filter((b) => all || selected.has(b.name) || selected.has(b.sourceId))
105
- .map((b) => b.sourceId)
106
- .sort(),
104
+ sources: selectedBuilds.map((build) => build.sourceId).sort(),
107
105
  }), { mode: 0o600 });
108
106
  if (options.portable) {
109
- const selectedBuilds = options.builds.filter((build) => all || selected.has(build.name) || selected.has(build.sourceId));
110
107
  await writePortableApprovals(options.project, selectedBuilds);
111
108
  }
112
109
  }
@@ -164,7 +161,7 @@ async function readUserPolicy(env) {
164
161
  fail("Mandatory user build policy is malformed.");
165
162
  return user;
166
163
  }
167
- export async function approvedCandidates(options) {
164
+ export async function reviewBuildCandidates(options) {
168
165
  if (!options.builds.length)
169
166
  return [];
170
167
  const user = await readUserPolicy(options.env);
@@ -181,9 +178,11 @@ export async function approvedCandidates(options) {
181
178
  local = undefined;
182
179
  }
183
180
  const portable = await readPortableApprovals(options.project);
184
- return options.builds.filter((build) => {
185
- if (denied(user.scripts, build) || denied(options.policy, build))
186
- return false;
181
+ return options.builds.map((build) => {
182
+ if (denied(user.scripts, build))
183
+ return { ...build, decision: "denied", policyScope: "user-deny" };
184
+ if (denied(options.policy, build))
185
+ return { ...build, decision: "denied", policyScope: "project-deny" };
187
186
  const manifestAllowed = object(options.policy) &&
188
187
  Array.isArray(options.policy.allow) &&
189
188
  options.policy.allow.includes(build.sourceId);
@@ -193,9 +192,30 @@ export async function approvedCandidates(options) {
193
192
  local.candidate === options.digest &&
194
193
  Array.isArray(local.sources) &&
195
194
  local.sources.includes(build.sourceId);
196
- return manifestAllowed || workspaceAllowed || localAllowed;
195
+ if (manifestAllowed)
196
+ return {
197
+ ...build,
198
+ decision: "approved",
199
+ policyScope: "project-policy",
200
+ };
201
+ if (workspaceAllowed)
202
+ return {
203
+ ...build,
204
+ decision: "approved",
205
+ policyScope: "workspace-policy",
206
+ };
207
+ if (localAllowed)
208
+ return {
209
+ ...build,
210
+ decision: "approved",
211
+ policyScope: "local-session",
212
+ };
213
+ return { ...build, decision: "pending", policyScope: "none" };
197
214
  });
198
215
  }
216
+ export async function approvedCandidates(options) {
217
+ return (await reviewBuildCandidates(options)).filter((review) => review.decision === "approved");
218
+ }
199
219
  export async function assertApproved(options) {
200
220
  const approved = await approvedCandidates(options);
201
221
  if (approved.length !== options.builds.length) {
@@ -62,6 +62,7 @@ export type InstallResult = {
62
62
  generationId?: string;
63
63
  candidateDigest?: string;
64
64
  builds?: BuildCandidate[];
65
+ buildReviews?: import("./private.ts").BuildReview[];
65
66
  };
66
67
  export declare function installProject(options: InstallOptions): Promise<InstallResult>;
67
68
  //# sourceMappingURL=run.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/install/run.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/install/run.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@ import { findWorkspaceRoot } from "./workspaces.js";
4
4
  import { assertSupportedSourcePayload } from "./source-policy.js";
5
5
  import { prepareRepair } from "./repair.js";
6
6
  import { createFetchOperation } from "../fetch/package.js";
7
- import { candidates, candidateDigest, approveCandidate, approvedCandidates, materializePrivate, buildPrivate, } from "./private.js";
7
+ import { candidates, candidateDigest, approveCandidate, reviewBuildCandidates, materializePrivate, buildPrivate, } from "./private.js";
8
8
  import { loadProjectPatches, applyProjectPatches } from "../commands/patch.js";
9
9
  import { registerGeneration } from "./metadata.js";
10
10
  import { randomId } from "../util/ids.js";
@@ -565,6 +565,13 @@ export async function installProject(options) {
565
565
  policy: scriptPolicy(manifest),
566
566
  portable: true,
567
567
  });
568
+ const buildReviews = await reviewBuildCandidates({
569
+ project,
570
+ env: options.env ?? process.env,
571
+ digest: reviewedDigest,
572
+ builds,
573
+ policy: scriptPolicy(manifest),
574
+ });
568
575
  return {
569
576
  readiness: "build-review",
570
577
  packages: activeNodes.length,
@@ -572,6 +579,7 @@ export async function installProject(options) {
572
579
  warnings,
573
580
  candidateDigest: reviewedDigest,
574
581
  builds,
582
+ buildReviews,
575
583
  };
576
584
  }
577
585
  let approvedBuilds = [];
@@ -583,11 +591,14 @@ export async function installProject(options) {
583
591
  builds,
584
592
  policy: scriptPolicy(manifest),
585
593
  };
586
- approvedBuilds = await approvedCandidates(approvalOptions);
594
+ let reviews = await reviewBuildCandidates(approvalOptions);
595
+ approvedBuilds = reviews.filter((review) => review.decision === "approved");
587
596
  if (approvedBuilds.length < builds.length &&
588
597
  options.requestBuildApproval) {
589
- const pending = builds.filter((build) => !approvedBuilds.some((item) => item.sourceId === build.sourceId));
590
- const selected = await options.requestBuildApproval(pending);
598
+ const pending = reviews.filter((review) => review.decision === "pending");
599
+ const selected = pending.length
600
+ ? await options.requestBuildApproval(pending)
601
+ : [];
591
602
  if (selected.length) {
592
603
  await approveCandidate({
593
604
  project,
@@ -598,7 +609,8 @@ export async function installProject(options) {
598
609
  policy: scriptPolicy(manifest),
599
610
  portable: true,
600
611
  });
601
- approvedBuilds = await approvedCandidates(approvalOptions);
612
+ reviews = await reviewBuildCandidates(approvalOptions);
613
+ approvedBuilds = reviews.filter((review) => review.decision === "approved");
602
614
  }
603
615
  }
604
616
  }
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-install.d.ts","sourceRoot":"","sources":["../../src/install/workspace-install.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAuH9D,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAqtBxB"}
1
+ {"version":3,"file":"workspace-install.d.ts","sourceRoot":"","sources":["../../src/install/workspace-install.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAuH9D,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAouBxB"}
@@ -32,7 +32,7 @@ import { loadProjectPatches, applyProjectPatches } from "../commands/patch.js";
32
32
  import { prepareRepair } from "./repair.js";
33
33
  import { assertSupportedSourcePayload } from "./source-policy.js";
34
34
  import { registerGeneration } from "./metadata.js";
35
- import { candidates, candidateDigest, approveCandidate, approvedCandidates, buildPrivate, } from "./private.js";
35
+ import { candidates, candidateDigest, approveCandidate, reviewBuildCandidates, buildPrivate, } from "./private.js";
36
36
  const object = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
37
37
  const unsupported = (message) => {
38
38
  throw new ZyrkError("ZYRK_NOT_IMPLEMENTED", message, {
@@ -446,6 +446,13 @@ export async function installWorkspaceProject(options) {
446
446
  policy,
447
447
  portable: true,
448
448
  });
449
+ const buildReviews = await reviewBuildCandidates({
450
+ project: root,
451
+ env: options.env ?? process.env,
452
+ digest,
453
+ builds,
454
+ policy,
455
+ });
449
456
  return {
450
457
  readiness: "build-review",
451
458
  packages: nodes.length,
@@ -453,6 +460,7 @@ export async function installWorkspaceProject(options) {
453
460
  warnings,
454
461
  candidateDigest: digest,
455
462
  builds,
463
+ buildReviews,
456
464
  };
457
465
  }
458
466
  let approvedBuilds = [];
@@ -464,11 +472,14 @@ export async function installWorkspaceProject(options) {
464
472
  builds,
465
473
  policy,
466
474
  };
467
- approvedBuilds = await approvedCandidates(approvalOptions);
475
+ let reviews = await reviewBuildCandidates(approvalOptions);
476
+ approvedBuilds = reviews.filter((review) => review.decision === "approved");
468
477
  if (approvedBuilds.length < builds.length &&
469
478
  options.requestBuildApproval) {
470
- const pending = builds.filter((build) => !approvedBuilds.some((item) => item.sourceId === build.sourceId));
471
- const selected = await options.requestBuildApproval(pending);
479
+ const pending = reviews.filter((review) => review.decision === "pending");
480
+ const selected = pending.length
481
+ ? await options.requestBuildApproval(pending)
482
+ : [];
472
483
  if (selected.length) {
473
484
  await approveCandidate({
474
485
  project: root,
@@ -479,7 +490,8 @@ export async function installWorkspaceProject(options) {
479
490
  policy,
480
491
  portable: true,
481
492
  });
482
- approvedBuilds = await approvedCandidates(approvalOptions);
493
+ reviews = await reviewBuildCandidates(approvalOptions);
494
+ approvedBuilds = reviews.filter((review) => review.decision === "approved");
483
495
  }
484
496
  }
485
497
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zyrk",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "Storage-efficient Node package manager: one verified store per user, shared across projects through links.",
5
5
  "license": "MIT",
6
6
  "type": "module",