@rightkit/release 0.2.165 → 0.2.172

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.
Files changed (77) hide show
  1. package/README.md +121 -4
  2. package/addon-command.mjs +6 -13
  3. package/android-aab-scan.mjs +265 -0
  4. package/android-dev.mjs +111 -0
  5. package/android-ndk.mjs +77 -0
  6. package/android-play-upload.mjs +262 -0
  7. package/android-release.mjs +344 -0
  8. package/android-test-fixtures.mjs +79 -0
  9. package/apple-profile-create.mjs +363 -0
  10. package/bin/darwin/rightkit-swap +0 -0
  11. package/bin/darwin/rightkit-swap.sign.json +6 -0
  12. package/build-release.mjs +73 -13
  13. package/cargo-guard.mjs +2 -0
  14. package/cargo-profile-contract.mjs +183 -0
  15. package/cargo-target.mjs +8 -0
  16. package/cli/right-release.mjs +14 -4
  17. package/committed-source.mjs +1 -1
  18. package/dev-artifact-refresh.mjs +176 -0
  19. package/dev-artifact-support.mjs +61 -0
  20. package/development-signing.mjs +71 -4
  21. package/development.mjs +78 -46
  22. package/direct-bootstrap.mjs +26 -41
  23. package/disk-command.mjs +298 -5
  24. package/disk-policy.json +7 -5
  25. package/disk-policy.mjs +105 -16
  26. package/disk-stable-root.mjs +19 -16
  27. package/engine-locator.mjs +116 -0
  28. package/external-bin.mjs +133 -0
  29. package/github-release.mjs +2 -2
  30. package/github-updater-release.mjs +154 -0
  31. package/hardeningscan.mjs +1 -1
  32. package/in-place-install.mjs +56 -0
  33. package/install-app-windows.mjs +63 -0
  34. package/install-app.mjs +53 -23
  35. package/local-publish.mjs +11 -1
  36. package/mac-signing-matrix.mjs +23 -11
  37. package/managed-cargo.mjs +6 -0
  38. package/media-sidecar-catalog.mjs +45 -0
  39. package/media-sidecar.mjs +306 -0
  40. package/ownership-approval-command.mjs +10 -44
  41. package/ownership-approval.mjs +3 -42
  42. package/ownership-catalog-config.json +2 -1
  43. package/ownership-catalog.mjs +4 -2
  44. package/ownership-command.mjs +7 -9
  45. package/ownership-dependency-allowlist.json +25 -1
  46. package/ownership-evidence.mjs +8 -5
  47. package/ownership-git-inventory.mjs +41 -7
  48. package/ownership-lockfiles.mjs +91 -8
  49. package/ownership-mandatory-set.json +36 -16
  50. package/ownership-report.mjs +5 -6
  51. package/ownership-scan-worker.mjs +2 -2
  52. package/ownership-verdict.mjs +10 -7
  53. package/ownership.mjs +47 -29
  54. package/package.json +7 -4
  55. package/portable-development.mjs +32 -115
  56. package/preflight.mjs +4 -6
  57. package/publication-artifacts.mjs +34 -0
  58. package/publish-update.mjs +72 -11
  59. package/qa-adoption-exemptions.json +5 -0
  60. package/qa-build-features.mjs +7 -0
  61. package/qa-contract.mjs +182 -0
  62. package/qa-control-scan.mjs +3 -3
  63. package/release-qa-command.mjs +38 -12
  64. package/release-qa-installed.mjs +136 -0
  65. package/release-qa-receipt.mjs +23 -2
  66. package/release.mjs +281 -13
  67. package/rightkit-ownership.json +537 -5
  68. package/rightkit-sync.mjs +26 -0
  69. package/rightkit-versions.json +109 -62
  70. package/runtime-budget-classes.json +28 -0
  71. package/runtime-budgets.mjs +4 -1
  72. package/sign-candidate.mjs +161 -0
  73. package/sign-dev-artifact.mjs +198 -0
  74. package/stage-binary.mjs +60 -0
  75. package/swap-helper-bundle.mjs +105 -0
  76. package/templates/cargo-profiles.toml +28 -0
  77. package/upload-release.mjs +11 -1
@@ -15,27 +15,27 @@
15
15
  "stagedNpm": {
16
16
  "@rightkit/app-update": "0.2.5",
17
17
  "@rightkit/ax": "0.3.2",
18
- "@rightkit/ci": "0.2.45",
18
+ "@rightkit/ci": "0.2.49",
19
19
  "@rightkit/doc-render": "0.1.6",
20
20
  "@rightkit/hooks": "0.1.2",
21
- "@rightkit/i18n": "0.1.0",
22
- "@rightkit/legal": "0.3.1",
23
- "@rightkit/legal-ui": "0.2.2",
21
+ "@rightkit/i18n": "0.1.1",
22
+ "@rightkit/legal": "0.5.0",
23
+ "@rightkit/legal-ui": "0.3.0",
24
24
  "@rightkit/license": "0.2.0",
25
25
  "@rightkit/logs": "0.1.4",
26
26
  "@rightkit/media-ui": "0.3.4",
27
- "@rightkit/onboarding": "0.3.3",
27
+ "@rightkit/onboarding": "0.5.0",
28
28
  "@rightkit/outdated": "0.2.0",
29
- "@rightkit/pdf-ui": "0.1.6",
29
+ "@rightkit/pdf-ui": "0.1.8",
30
30
  "@rightkit/platform-ui": "0.2.8",
31
- "@rightkit/qa": "0.4.8",
32
- "@rightkit/qa-chrome": "0.1.6",
33
- "@rightkit/react-shell": "0.6.4",
34
- "@rightkit/release": "0.2.164",
35
- "@rightkit/settings-ui": "0.4.4",
31
+ "@rightkit/qa": "0.4.9",
32
+ "@rightkit/qa-chrome": "0.1.8",
33
+ "@rightkit/react-shell": "0.6.8",
34
+ "@rightkit/release": "0.2.172",
35
+ "@rightkit/settings-ui": "0.5.0",
36
36
  "@rightkit/tauri": "0.2.1",
37
37
  "@rightkit/tauri-shell": "0.1.1",
38
- "@rightkit/theme": "0.2.3"
38
+ "@rightkit/theme": "0.2.4"
39
39
  },
40
40
  "legacyNpm": {
41
41
  "@rightkit/ax": [
@@ -92,14 +92,16 @@
92
92
  "0.1.2",
93
93
  "0.1.3",
94
94
  "0.2.0",
95
- "0.2.1"
95
+ "0.2.1",
96
+ "0.2.2"
96
97
  ],
97
98
  "@rightkit/hooks": [
98
99
  "0.1.0",
99
100
  "0.1.1"
100
101
  ],
101
102
  "@rightkit/legal": [
102
- "0.3.0"
103
+ "0.3.0",
104
+ "0.3.1"
103
105
  ],
104
106
  "@rightkit/license": [
105
107
  "0.1.6",
@@ -205,7 +207,9 @@
205
207
  "0.2.160",
206
208
  "0.2.161",
207
209
  "0.2.162",
208
- "0.2.163"
210
+ "0.2.163",
211
+ "0.2.164",
212
+ "0.2.165"
209
213
  ],
210
214
  "@rightkit/qa": [
211
215
  "0.1.0",
@@ -224,7 +228,8 @@
224
228
  "0.4.4",
225
229
  "0.4.5",
226
230
  "0.4.6",
227
- "0.4.7"
231
+ "0.4.7",
232
+ "0.4.8"
228
233
  ],
229
234
  "@rightkit/app-shell": [
230
235
  "0.1.0",
@@ -256,7 +261,8 @@
256
261
  "0.1.0",
257
262
  "0.2.0",
258
263
  "0.2.1",
259
- "0.2.2"
264
+ "0.2.2",
265
+ "0.2.3"
260
266
  ],
261
267
  "@rightkit/settings-ui": [
262
268
  "0.1.0",
@@ -275,7 +281,8 @@
275
281
  "0.4.0",
276
282
  "0.4.1",
277
283
  "0.4.2",
278
- "0.4.3"
284
+ "0.4.3",
285
+ "0.4.4"
279
286
  ],
280
287
  "@rightkit/onboarding": [
281
288
  "0.1.0",
@@ -285,7 +292,8 @@
285
292
  "0.2.3",
286
293
  "0.3.0",
287
294
  "0.3.1",
288
- "0.3.2"
295
+ "0.3.2",
296
+ "0.3.3"
289
297
  ],
290
298
  "@rightkit/doc-render": [
291
299
  "0.1.0",
@@ -313,7 +321,8 @@
313
321
  "0.1.2",
314
322
  "0.1.3",
315
323
  "0.1.4",
316
- "0.1.5"
324
+ "0.1.5",
325
+ "0.1.6"
317
326
  ],
318
327
  "@rightkit/qa-chrome": [
319
328
  "0.1.0",
@@ -321,7 +330,8 @@
321
330
  "0.1.2",
322
331
  "0.1.3",
323
332
  "0.1.4",
324
- "0.1.5"
333
+ "0.1.5",
334
+ "0.1.6"
325
335
  ],
326
336
  "@rightkit/updates": [
327
337
  "0.2.4"
@@ -335,7 +345,16 @@
335
345
  ],
336
346
  "@rightkit/ci": [
337
347
  "0.2.43",
338
- "0.2.44"
348
+ "0.2.44",
349
+ "0.2.45"
350
+ ],
351
+ "@rightkit/react-shell": [
352
+ "0.6.4",
353
+ "0.6.5",
354
+ "0.6.6"
355
+ ],
356
+ "@rightkit/i18n": [
357
+ "0.1.0"
339
358
  ]
340
359
  },
341
360
  "cargo": {
@@ -356,16 +375,16 @@
356
375
  "rightkit-tauri": "0.1.0"
357
376
  },
358
377
  "stagedCargo": {
359
- "rightkit-agent-tools": "0.1.3",
378
+ "rightkit-agent-tools": "0.1.4",
360
379
  "rightkit-agents": "0.1.0",
361
380
  "rightkit-app-settings": "0.2.0",
362
381
  "rightkit-asr": "0.2.1",
363
382
  "rightkit-audio": "0.2.3",
364
- "rightkit-automation": "0.3.0",
383
+ "rightkit-automation": "0.4.1",
365
384
  "rightkit-browser": "0.3.1",
366
385
  "rightkit-cache": "0.2.0",
367
386
  "rightkit-channel-memory": "0.1.0",
368
- "rightkit-child-process": "0.4.0",
387
+ "rightkit-child-process": "0.4.3",
369
388
  "rightkit-compose": "0.1.0",
370
389
  "rightkit-contracts": "0.1.5",
371
390
  "rightkit-crash": "0.3.0",
@@ -373,19 +392,21 @@
373
392
  "rightkit-daemon": "0.2.3",
374
393
  "rightkit-db": "0.4.0",
375
394
  "rightkit-device": "0.1.3",
376
- "rightkit-embed": "0.4.0",
395
+ "rightkit-docs": "0.1.0",
396
+ "rightkit-embed": "0.5.0",
377
397
  "rightkit-ffmpeg": "0.2.6",
378
398
  "rightkit-file-watch": "0.4.0",
379
- "rightkit-fs": "0.4.0",
380
- "rightkit-fsindex": "0.2.1",
381
- "rightkit-hotkeys": "0.1.0",
399
+ "rightkit-fs": "0.4.3",
400
+ "rightkit-fsindex": "0.3.0",
401
+ "rightkit-hooks": "0.1.0",
402
+ "rightkit-hotkeys": "0.1.1",
382
403
  "rightkit-http": "0.3.0",
383
404
  "rightkit-job-queue": "0.3.1",
384
405
  "rightkit-license": "0.2.0",
385
406
  "rightkit-llm": "0.2.1",
386
407
  "rightkit-logs": "0.3.0",
387
- "rightkit-mcp": "0.5.3",
388
- "rightkit-models": "0.3.2",
408
+ "rightkit-mcp": "0.6.0",
409
+ "rightkit-models": "0.4.4",
389
410
  "rightkit-oauth": "0.5.0",
390
411
  "rightkit-ocr": "0.2.2",
391
412
  "rightkit-onnx": "0.2.3",
@@ -393,8 +414,8 @@
393
414
  "rightkit-ownership": "0.2.1",
394
415
  "rightkit-pdf": "0.3.0",
395
416
  "rightkit-playback": "0.2.1",
396
- "rightkit-qa": "0.4.0",
397
- "rightkit-qa-driver": "0.4.0",
417
+ "rightkit-qa": "0.6.4",
418
+ "rightkit-qa-driver": "0.5.0",
398
419
  "rightkit-record": "0.3.0",
399
420
  "rightkit-review": "0.2.0",
400
421
  "rightkit-sandbox": "0.4.0",
@@ -426,16 +447,16 @@
426
447
  },
427
448
  "registry": {
428
449
  "cargo": {
429
- "rightkit-agent-tools": "0.1.3",
450
+ "rightkit-agent-tools": "0.1.4",
430
451
  "rightkit-agents": "0.1.0",
431
452
  "rightkit-app-settings": "0.2.0",
432
453
  "rightkit-asr": "0.2.1",
433
454
  "rightkit-audio": "0.2.3",
434
- "rightkit-automation": "0.3.0",
455
+ "rightkit-automation": "0.4.1",
435
456
  "rightkit-browser": "0.3.1",
436
457
  "rightkit-cache": "0.2.0",
437
458
  "rightkit-channel-memory": "0.1.0",
438
- "rightkit-child-process": "0.4.0",
459
+ "rightkit-child-process": "0.4.1",
439
460
  "rightkit-compose": "0.1.0",
440
461
  "rightkit-contracts": "0.1.5",
441
462
  "rightkit-control": "0.3.0",
@@ -444,20 +465,22 @@
444
465
  "rightkit-daemon": "0.2.3",
445
466
  "rightkit-db": "0.4.0",
446
467
  "rightkit-device": "0.1.3",
447
- "rightkit-embed": "0.4.0",
468
+ "rightkit-docs": "0.1.0",
469
+ "rightkit-embed": "0.5.0",
448
470
  "rightkit-ffmpeg": "0.2.6",
449
471
  "rightkit-file-watch": "0.4.0",
450
472
  "rightkit-framed-sidecar": "0.2.0",
451
- "rightkit-fs": "0.4.0",
452
- "rightkit-fsindex": "0.2.1",
453
- "rightkit-hotkeys": "0.1.0",
473
+ "rightkit-fs": "0.4.1",
474
+ "rightkit-fsindex": "0.3.0",
475
+ "rightkit-hooks": "0.1.0",
476
+ "rightkit-hotkeys": "0.1.1",
454
477
  "rightkit-http": "0.3.0",
455
478
  "rightkit-job-queue": "0.3.1",
456
479
  "rightkit-jobs": "0.3.0",
457
480
  "rightkit-license": "0.2.0",
458
481
  "rightkit-llm": "0.2.1",
459
482
  "rightkit-logs": "0.3.0",
460
- "rightkit-mcp": "0.5.3",
483
+ "rightkit-mcp": "0.6.0",
461
484
  "rightkit-media": "0.2.6",
462
485
  "rightkit-models": "0.3.2",
463
486
  "rightkit-oauth": "0.5.0",
@@ -469,8 +492,8 @@
469
492
  "rightkit-pdf": "0.3.0",
470
493
  "rightkit-playback": "0.2.1",
471
494
  "rightkit-process": "0.4.0",
472
- "rightkit-qa": "0.4.0",
473
- "rightkit-qa-driver": "0.4.0",
495
+ "rightkit-qa": "0.6.1",
496
+ "rightkit-qa-driver": "0.5.0",
474
497
  "rightkit-qts": "0.2.6",
475
498
  "rightkit-qts-ggml": "0.1.2",
476
499
  "rightkit-qts-ggml-sys": "0.1.2",
@@ -500,29 +523,29 @@
500
523
  "@rightkit/app-shell": "0.6.2",
501
524
  "@rightkit/app-update": "0.2.5",
502
525
  "@rightkit/ax": "0.3.2",
503
- "@rightkit/ci": "0.2.45",
526
+ "@rightkit/ci": "0.2.49",
504
527
  "@rightkit/doc-render": "0.1.6",
505
528
  "@rightkit/git": "0.2.42",
506
529
  "@rightkit/hooks": "0.1.2",
507
- "@rightkit/i18n": "0.1.0",
508
- "@rightkit/legal": "0.3.1",
509
- "@rightkit/legal-ui": "0.2.2",
530
+ "@rightkit/i18n": "0.1.1",
531
+ "@rightkit/legal": "0.5.0",
532
+ "@rightkit/legal-ui": "0.3.0",
510
533
  "@rightkit/license": "0.2.0",
511
534
  "@rightkit/logs": "0.1.4",
512
535
  "@rightkit/media-ui": "0.3.4",
513
- "@rightkit/onboarding": "0.3.3",
536
+ "@rightkit/onboarding": "0.5.0",
514
537
  "@rightkit/outdated": "0.2.0",
515
- "@rightkit/pdf-ui": "0.1.6",
538
+ "@rightkit/pdf-ui": "0.1.8",
516
539
  "@rightkit/platform-ui": "0.2.8",
517
540
  "@rightkit/qa": "0.4.8",
518
- "@rightkit/qa-chrome": "0.1.6",
519
- "@rightkit/react-shell": "0.6.4",
520
- "@rightkit/release": "0.2.164",
521
- "@rightkit/settings-ui": "0.4.4",
541
+ "@rightkit/qa-chrome": "0.1.8",
542
+ "@rightkit/react-shell": "0.6.8",
543
+ "@rightkit/release": "0.2.165",
544
+ "@rightkit/settings-ui": "0.5.0",
522
545
  "@rightkit/shell": "0.1.1",
523
546
  "@rightkit/tauri": "0.2.1",
524
547
  "@rightkit/tauri-shell": "0.1.1",
525
- "@rightkit/theme": "0.2.3",
548
+ "@rightkit/theme": "0.2.4",
526
549
  "@rightkit/updates": "0.2.5"
527
550
  }
528
551
  },
@@ -545,7 +568,8 @@
545
568
  "0.2.6",
546
569
  "0.2.7",
547
570
  "0.3.0",
548
- "0.3.1"
571
+ "0.3.1",
572
+ "0.3.2"
549
573
  ],
550
574
  "rightkit-audio": [
551
575
  "0.1.0",
@@ -563,7 +587,8 @@
563
587
  "0.2.2",
564
588
  "0.3.0",
565
589
  "0.3.1",
566
- "0.3.2"
590
+ "0.3.2",
591
+ "0.4.0"
567
592
  ],
568
593
  "rightkit-jobs": [
569
594
  "0.2.1",
@@ -581,7 +606,8 @@
581
606
  "0.4.0",
582
607
  "0.5.0",
583
608
  "0.5.1",
584
- "0.5.2"
609
+ "0.5.2",
610
+ "0.5.3"
585
611
  ],
586
612
  "rightkit-media": [
587
613
  "0.2.2",
@@ -619,7 +645,11 @@
619
645
  "0.2.12",
620
646
  "0.2.14",
621
647
  "0.2.16",
622
- "0.3.0"
648
+ "0.3.0",
649
+ "0.4.0",
650
+ "0.5.0",
651
+ "0.6.0",
652
+ "0.6.1"
623
653
  ],
624
654
  "rightkit-sandbox": [
625
655
  "0.1.0",
@@ -680,7 +710,9 @@
680
710
  "0.2.0",
681
711
  "0.3.0",
682
712
  "0.3.1",
683
- "0.3.2"
713
+ "0.3.2",
714
+ "0.4.0",
715
+ "0.4.1"
684
716
  ],
685
717
  "rightkit-service": [
686
718
  "0.1.0",
@@ -718,7 +750,8 @@
718
750
  ],
719
751
  "rightkit-fsindex": [
720
752
  "0.1.0",
721
- "0.2.0"
753
+ "0.2.0",
754
+ "0.2.1"
722
755
  ],
723
756
  "rightkit-asr": [
724
757
  "0.0.2",
@@ -760,10 +793,24 @@
760
793
  ],
761
794
  "rightkit-qa-driver": [
762
795
  "0.3.0",
763
- "0.3.1"
796
+ "0.3.1",
797
+ "0.4.0",
798
+ "0.4.1"
764
799
  ],
765
800
  "rightkit-agent-tools": [
766
- "0.1.2"
801
+ "0.1.2",
802
+ "0.1.3"
803
+ ],
804
+ "rightkit-automation": [
805
+ "0.3.0",
806
+ "0.4.0"
807
+ ],
808
+ "rightkit-child-process": [
809
+ "0.4.0",
810
+ "0.4.1"
811
+ ],
812
+ "rightkit-hotkeys": [
813
+ "0.1.0"
767
814
  ]
768
815
  }
769
816
  }
@@ -1,6 +1,14 @@
1
1
  {
2
2
  "schema": 1,
3
3
  "heldResource": {
4
+ "rightkit-agent-tools": {
5
+ "ceilings": null,
6
+ "requiredScale": null
7
+ },
8
+ "rightkit-agents": {
9
+ "ceilings": null,
10
+ "requiredScale": null
11
+ },
4
12
  "rightkit-asr": {
5
13
  "ceilings": null,
6
14
  "requiredScale": null
@@ -17,10 +25,22 @@
17
25
  "ceilings": null,
18
26
  "requiredScale": null
19
27
  },
28
+ "rightkit-hotkeys": {
29
+ "ceilings": null,
30
+ "requiredScale": null
31
+ },
32
+ "rightkit-mcp": {
33
+ "ceilings": null,
34
+ "requiredScale": null
35
+ },
20
36
  "rightkit-ocr": {
21
37
  "ceilings": null,
22
38
  "requiredScale": null
23
39
  },
40
+ "rightkit-onnx": {
41
+ "ceilings": null,
42
+ "requiredScale": null
43
+ },
24
44
  "rightkit-record": {
25
45
  "ceilings": null,
26
46
  "requiredScale": null
@@ -29,6 +49,14 @@
29
49
  "ceilings": null,
30
50
  "requiredScale": null
31
51
  },
52
+ "rightkit-tts": {
53
+ "ceilings": null,
54
+ "requiredScale": null
55
+ },
56
+ "rightkit-tts-qwen": {
57
+ "ceilings": null,
58
+ "requiredScale": null
59
+ },
32
60
  "rightkit-vad": {
33
61
  "ceilings": null,
34
62
  "requiredScale": null
@@ -345,6 +345,9 @@ function checkEvidence(m, index, budget, crateDir, { exists, readFile }) {
345
345
 
346
346
  const stripComments = (text) => text.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
347
347
 
348
+ // `use`, `pub use` and `pub(crate) use` declarations, including multi-line `{ ... }` lists: an import cannot start work.
349
+ const stripImports = (text) => text.replace(/^[ \t]*(?:pub(?:\([^)]*\))?\s+)?use\s[^;]*;/gm, "");
350
+
348
351
  /** Source with `#[cfg(test)]` modules removed (brace-balanced), for scanning shipped code only. */
349
352
  function withoutTestModules(text) {
350
353
  const out = [];
@@ -380,7 +383,7 @@ function rustFiles(dir) {
380
383
  export function scanContinuousFiles(crateDir) {
381
384
  const files = {};
382
385
  for (const file of rustFiles(path.join(crateDir, "src")).sort()) {
383
- const text = stripComments(withoutTestModules(fs.readFileSync(file, "utf8")));
386
+ const text = stripImports(stripComments(withoutTestModules(fs.readFileSync(file, "utf8"))));
384
387
  const ids = Object.entries(CONTINUOUS_PATTERNS).filter(([, pattern]) => pattern.test(text)).map(([id]) => id);
385
388
  if (ids.length) files[path.relative(crateDir, file).split(path.sep).join("/")] = ids;
386
389
  }
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env node
2
+ // right-release sign-candidate: sign the EXACT bytes of an unsigned release candidate instead of rebuilding from source
3
+ // (Orthic-Labs/rightkit#320, SwiftRight SR0 7a). The candidate job's artifact (MSIX / .exe on Windows, .app or loose executables on
4
+ // macOS) is the signing input. Steps: verify every file's sha256 against the candidate receipt (refuse on any mismatch, missing or
5
+ // unrecorded file), sign in place (Windows: Azure Artifact Signing through sign-windows.mjs; macOS: Developer ID through
6
+ // sign-dev-artifact), confirm each signed file changed, and write a receipt binding candidate sha -> signed sha. It never builds,
7
+ // notarizes or publishes, and a signing job calls it instead of `right-release build`.
8
+ //
9
+ // Candidate receipt (same shape as the dev artifact payload metadata): { schema: 1, files: [{ path, sha256 }] }, paths relative to --artifact.
10
+ import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
11
+ import os from "node:os";
12
+ import path from "node:path";
13
+ import { fileURLToPath, pathToFileURL } from "node:url";
14
+ import { spawnSync } from "./background-process.mjs";
15
+ import { fileEvidence } from "./macos-file-evidence.mjs";
16
+ import { readMsixSignerSubject } from "./msix-signer.mjs";
17
+ import { signDevArtifact, verifyDevArtifactPayload } from "./sign-dev-artifact.mjs";
18
+
19
+ export const SIGN_CANDIDATE_CONTRACT = "rightkit-sign-candidate-v1";
20
+ export const SIGN_CANDIDATE_SCHEMA = 1;
21
+
22
+ export class SignCandidateError extends Error {
23
+ constructor(code, message) {
24
+ super(message);
25
+ this.name = "SignCandidateError";
26
+ this.code = code;
27
+ }
28
+ }
29
+
30
+ const fail = (code, message) => { throw new SignCandidateError(code, message); };
31
+ const toPosix = (value) => value.split(path.sep).join("/");
32
+ const USAGE = "usage: right-release sign-candidate --platform win|mac --artifact <dir> --candidate-receipt <receipt.json> --out <binding.json> [--manifest <sign.json>] [--publisher <subject>] [--dry-run]";
33
+
34
+ const defaultSignWindows = ({ files, env, run }) => {
35
+ const script = path.join(path.dirname(fileURLToPath(import.meta.url)), "sign-windows.mjs");
36
+ const tempDir = mkdtempSync(path.join(os.tmpdir(), "sign-candidate-"));
37
+ try {
38
+ const result = run(process.execPath, [script, "--receipt", path.join(tempDir, "authenticode.json"), ...files], { env, encoding: "utf8", windowsHide: true, shell: false });
39
+ if (result.error || result.status !== 0) fail("SIGN_CANDIDATE_SIGNING_FAILED", `sign-windows failed: ${`${result.stdout ?? ""}\n${result.stderr ?? ""}`.trim() || result.error?.message}`);
40
+ } finally { rmSync(tempDir, { recursive: true, force: true }); }
41
+ };
42
+
43
+ /**
44
+ * Sign an existing candidate. Injectable for tests: `signWindows({ files })`, `signMac(options)` (defaults to signDevArtifact),
45
+ * `evidenceOf`, `readSigner`. Returns the binding receipt (also written to `outPath`). With `dryRun` only the hash check runs.
46
+ */
47
+ export function signCandidate({
48
+ platform, artifact, receiptPath, outPath, manifestPath, publisher, dryRun = false,
49
+ env = process.env, run = spawnSync, hostPlatform = process.platform,
50
+ evidenceOf = fileEvidence, readSigner = readMsixSignerSubject,
51
+ signWindows = (options) => defaultSignWindows({ ...options, env, run }),
52
+ signMac = (options) => signDevArtifact(options),
53
+ } = {}) {
54
+ if (!platform || !artifact || !receiptPath || !outPath) fail("SIGN_CANDIDATE_USAGE", USAGE);
55
+ if (platform !== "win" && platform !== "mac") fail("SIGN_CANDIDATE_USAGE", `--platform must be win or mac: ${JSON.stringify(platform)}`);
56
+ if (platform === "mac" && !manifestPath) fail("SIGN_CANDIDATE_USAGE", "--manifest <sign.json> is required for --platform mac (fixed identifier per file)");
57
+ const root = path.resolve(artifact);
58
+ if (!existsSync(root)) fail("SIGN_CANDIDATE_MISSING", `artifact directory does not exist: ${root}`);
59
+ const receiptFile = path.resolve(receiptPath);
60
+ if (!existsSync(receiptFile)) fail("SIGN_CANDIDATE_MISSING", `candidate receipt does not exist: ${receiptFile}`);
61
+
62
+ // Bytes must be the qualified candidate: every recorded hash is checked before anything is signed.
63
+ let candidate;
64
+ try { candidate = verifyDevArtifactPayload(root, receiptFile); } catch (error) {
65
+ fail(`SIGN_CANDIDATE_${String(error.code ?? "RECEIPT_INVALID").replace(/^SIGN_DEV_ARTIFACT_/, "")}`, error.message);
66
+ }
67
+ const candidateSha = new Map([...candidate.recorded].map(([file, item]) => [path.relative(root, file), item.sha256]));
68
+ const receiptSha256 = evidenceOf(receiptFile).sha256;
69
+ if (dryRun) return { schema: SIGN_CANDIDATE_SCHEMA, dryRun: true, platform, files: [...candidateSha].map(([rel, sha256]) => ({ path: toPosix(rel), candidateSha256: sha256 })) };
70
+ if (platform === "win" && hostPlatform !== "win32") fail("SIGN_CANDIDATE_PLATFORM", "--platform win must run on Windows");
71
+ if (platform === "mac" && hostPlatform !== "darwin") fail("SIGN_CANDIDATE_PLATFORM", "--platform mac must run on macOS");
72
+
73
+ let files;
74
+ let signing;
75
+ if (platform === "win") {
76
+ const absolute = [...candidateSha.keys()].map((rel) => path.resolve(root, rel));
77
+ signWindows({ files: absolute });
78
+ files = [...candidateSha].map(([rel, sha]) => {
79
+ const signedSha256 = evidenceOf(path.resolve(root, rel)).sha256;
80
+ if (signedSha256 === sha) fail("SIGN_CANDIDATE_UNCHANGED", `${toPosix(rel)} was not changed by signing`);
81
+ let signer;
82
+ if (/\.msix(bundle)?$/i.test(rel)) {
83
+ signer = readSigner(path.resolve(root, rel));
84
+ if (publisher && signer !== publisher) fail("SIGN_CANDIDATE_PUBLISHER_MISMATCH", `${toPosix(rel)} is signed as ${JSON.stringify(signer)}, expected ${JSON.stringify(publisher)} (a subject change changes the package family name)`);
85
+ }
86
+ return { path: toPosix(rel), candidateSha256: sha, signedSha256, ...(signer ? { signerSubject: signer } : {}) };
87
+ });
88
+ signing = "azure-artifact-signing";
89
+ } else {
90
+ // The candidate receipt doubles as the payload metadata, so work on a copy and keep the candidate receipt as submitted.
91
+ const workDir = mkdtempSync(path.join(os.tmpdir(), "sign-candidate-"));
92
+ try {
93
+ const metadata = path.join(workDir, "payload.json");
94
+ copyFileSync(receiptFile, metadata);
95
+ const evidence = signMac({ artifact: root, manifestPath, metadataPath: metadata, evidencePath: path.join(workDir, "evidence.json"), env, run, platform: hostPlatform });
96
+ const signedByPath = new Map(evidence.files.map((file) => [file.path, file]));
97
+ files = [...candidateSha].map(([rel, sha]) => {
98
+ const key = toPosix(rel);
99
+ const record = signedByPath.get(key);
100
+ if (record) {
101
+ if (record.sha256Before !== sha) fail("SIGN_CANDIDATE_HASH_MISMATCH", `${key}: signing started from ${record.sha256Before}, candidate receipt says ${sha}`);
102
+ return { path: key, candidateSha256: sha, signedSha256: record.sha256After, identifier: record.identifier, teamIdentifier: record.teamIdentifier };
103
+ }
104
+ const unchanged = evidenceOf(path.resolve(root, rel)).sha256;
105
+ if (unchanged !== sha) fail("SIGN_CANDIDATE_HASH_MISMATCH", `${key} is not in the sign manifest but changed during signing`);
106
+ return { path: key, candidateSha256: sha, signedSha256: unchanged, unsigned: true };
107
+ });
108
+ signing = `developer-id:${evidence.teamIdentifier}`;
109
+ } finally { rmSync(workDir, { recursive: true, force: true }); }
110
+ }
111
+
112
+ const binding = {
113
+ schema: SIGN_CANDIDATE_SCHEMA, contract: SIGN_CANDIDATE_CONTRACT, platform, signing,
114
+ rebuilt: false, built: false, notarized: false, published: false,
115
+ candidateReceiptSha256: receiptSha256, artifact: root, files,
116
+ };
117
+ const absoluteOut = path.resolve(outPath);
118
+ mkdirSync(path.dirname(absoluteOut), { recursive: true });
119
+ writeFileSync(absoluteOut, `${JSON.stringify(binding, null, 2)}\n`);
120
+ return binding;
121
+ }
122
+
123
+ /** Validate a binding receipt: contract, no rebuild, every file bound candidate sha -> signed sha. Throws SIGN_CANDIDATE_BINDING_INVALID. */
124
+ export function validateCandidateBinding(binding, expected = {}) {
125
+ const bad = (message) => fail("SIGN_CANDIDATE_BINDING_INVALID", `candidate binding receipt: ${message}`);
126
+ if (!binding || binding.schema !== SIGN_CANDIDATE_SCHEMA || binding.contract !== SIGN_CANDIDATE_CONTRACT) bad("unrecognized contract");
127
+ if (binding.rebuilt !== false) bad("signed bytes were rebuilt, not derived from the candidate");
128
+ if (!Array.isArray(binding.files) || !binding.files.length) bad("no files");
129
+ for (const file of binding.files) {
130
+ for (const key of ["candidateSha256", "signedSha256"]) if (!/^[0-9a-f]{64}$/.test(file?.[key] ?? "")) bad(`${file?.path}.${key} must be a SHA-256`);
131
+ if (!file.unsigned && file.candidateSha256 === file.signedSha256) bad(`${file.path} was not changed by signing`);
132
+ }
133
+ if (expected.platform && binding.platform !== expected.platform) bad(`platform ${binding.platform} is not ${expected.platform}`);
134
+ if (expected.candidateSha256s) {
135
+ const bound = new Set(binding.files.map((file) => file.candidateSha256));
136
+ for (const sha of expected.candidateSha256s) if (!bound.has(sha)) bad(`candidate ${sha} is not bound`);
137
+ }
138
+ return binding;
139
+ }
140
+
141
+ export function main(args = process.argv.slice(2), options = {}) {
142
+ const take = (flag) => {
143
+ const index = args.indexOf(flag);
144
+ if (index < 0) return undefined;
145
+ const value = args[index + 1];
146
+ if (!value || value.startsWith("--")) fail("SIGN_CANDIDATE_USAGE", `${flag} requires a value`);
147
+ return value;
148
+ };
149
+ try {
150
+ const result = signCandidate({
151
+ platform: take("--platform"), artifact: take("--artifact"), receiptPath: take("--candidate-receipt"), outPath: take("--out"),
152
+ manifestPath: take("--manifest"), publisher: take("--publisher"), dryRun: args.includes("--dry-run"), ...options,
153
+ });
154
+ console.log(result.dryRun ? `dry-run: candidate hashes verified; would sign ${result.files.map((file) => file.path).join(", ")}` : `signed ${result.files.length} candidate file(s) (${result.signing}); binding receipt written`);
155
+ } catch (error) {
156
+ console.error(`right-release sign-candidate: ${error.code ? `[${error.code}] ` : ""}${error.message}`);
157
+ process.exitCode = 1;
158
+ }
159
+ }
160
+
161
+ if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) main();