@rightkit/release 0.2.132 → 0.2.133

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.
@@ -29,7 +29,7 @@
29
29
  "@rightkit/platform-ui": "0.1.2",
30
30
  "@rightkit/qa": "0.3.0",
31
31
  "@rightkit/qa-chrome": "0.1.3",
32
- "@rightkit/release": "0.2.132",
32
+ "@rightkit/release": "0.2.133",
33
33
  "@rightkit/settings-ui": "0.2.7",
34
34
  "@rightkit/shell": "0.1.0",
35
35
  "@rightkit/tauri": "0.1.1",
@@ -155,7 +155,8 @@
155
155
  "0.2.128",
156
156
  "0.2.129",
157
157
  "0.2.130",
158
- "0.2.131"
158
+ "0.2.131",
159
+ "0.2.132"
159
160
  ],
160
161
  "@rightkit/qa": [
161
162
  "0.1.0",
@@ -237,7 +238,7 @@
237
238
  "rightkit-automation": "0.1.0",
238
239
  "rightkit-browser": "0.2.2",
239
240
  "rightkit-cache": "0.1.0",
240
- "rightkit-control": "0.1.7",
241
+ "rightkit-control": "0.1.8",
241
242
  "rightkit-crash": "0.1.0",
242
243
  "rightkit-db": "0.3.0",
243
244
  "rightkit-embed": "0.2.2",
@@ -247,28 +248,28 @@
247
248
  "rightkit-jobs": "0.2.2",
248
249
  "rightkit-license": "0.1.3",
249
250
  "rightkit-logs": "0.2.0",
250
- "rightkit-mcp": "0.3.0",
251
+ "rightkit-mcp": "0.3.1",
251
252
  "rightkit-media": "0.2.3",
252
253
  "rightkit-models": "0.2.2",
253
254
  "rightkit-oauth": "0.2.1",
254
255
  "rightkit-ort": "0.2.0",
255
256
  "rightkit-pdf": "0.1.0",
256
257
  "rightkit-playback": "0.1.1",
257
- "rightkit-process": "0.3.0",
258
- "rightkit-qa": "0.2.9",
258
+ "rightkit-process": "0.3.1",
259
+ "rightkit-qa": "0.2.10",
259
260
  "rightkit-qts": "0.2.1",
260
261
  "rightkit-qts-ggml": "0.1.0",
261
262
  "rightkit-qts-ggml-sys": "0.1.0",
262
- "rightkit-sandbox": "0.2.2",
263
+ "rightkit-sandbox": "0.2.4",
263
264
  "rightkit-search": "0.1.0",
264
265
  "rightkit-secrets": "0.1.0",
265
266
  "rightkit-service": "0.1.0",
266
267
  "rightkit-settings": "0.1.0",
267
- "rightkit-shell": "0.1.2",
268
+ "rightkit-shell": "0.1.3",
268
269
  "rightkit-speech-client": "0.3.0",
269
270
  "rightkit-sync": "0.1.0",
270
271
  "rightkit-tauri": "0.1.1",
271
- "rightkit-watch": "0.3.0"
272
+ "rightkit-watch": "0.3.1"
272
273
  },
273
274
  "swift": {
274
275
  "rightkit-swift": {
@@ -287,7 +288,7 @@
287
288
  "rightkit-audio": "0.1.1",
288
289
  "rightkit-browser": "0.2.2",
289
290
  "rightkit-cache": "0.1.0",
290
- "rightkit-control": "0.1.7",
291
+ "rightkit-control": "0.1.8",
291
292
  "rightkit-crash": "0.1.0",
292
293
  "rightkit-db": "0.3.0",
293
294
  "rightkit-embed": "0.2.2",
@@ -297,27 +298,27 @@
297
298
  "rightkit-jobs": "0.2.2",
298
299
  "rightkit-license": "0.1.3",
299
300
  "rightkit-logs": "0.2.0",
300
- "rightkit-mcp": "0.3.0",
301
+ "rightkit-mcp": "0.3.1",
301
302
  "rightkit-media": "0.2.3",
302
303
  "rightkit-models": "0.2.2",
303
304
  "rightkit-oauth": "0.2.1",
304
305
  "rightkit-ort": "0.2.0",
305
306
  "rightkit-pdf": "0.1.0",
306
307
  "rightkit-playback": "0.1.1",
307
- "rightkit-process": "0.3.0",
308
- "rightkit-qa": "0.2.9",
308
+ "rightkit-process": "0.3.1",
309
+ "rightkit-qa": "0.2.10",
309
310
  "rightkit-qts": "0.2.1",
310
311
  "rightkit-qts-ggml": "0.1.0",
311
312
  "rightkit-qts-ggml-sys": "0.1.0",
312
- "rightkit-sandbox": "0.2.2",
313
+ "rightkit-sandbox": "0.2.4",
313
314
  "rightkit-search": "0.1.0",
314
315
  "rightkit-secrets": "0.1.0",
315
316
  "rightkit-service": "0.1.0",
316
317
  "rightkit-settings": "0.1.0",
317
- "rightkit-shell": "0.1.2",
318
+ "rightkit-shell": "0.1.3",
318
319
  "rightkit-speech-client": "0.3.0",
319
320
  "rightkit-tauri": "0.1.1",
320
- "rightkit-watch": "0.3.0"
321
+ "rightkit-watch": "0.3.1"
321
322
  },
322
323
  "npm": {
323
324
  "@rightkit/app-shell": "0.2.1",
@@ -336,7 +337,7 @@
336
337
  "@rightkit/platform-ui": "0.1.2",
337
338
  "@rightkit/qa": "0.3.0",
338
339
  "@rightkit/qa-chrome": "0.1.3",
339
- "@rightkit/release": "0.2.131",
340
+ "@rightkit/release": "0.2.132",
340
341
  "@rightkit/settings-ui": "0.2.7",
341
342
  "@rightkit/shell": "0.1.0",
342
343
  "@rightkit/tauri": "0.1.1",
@@ -366,7 +367,8 @@
366
367
  ],
367
368
  "rightkit-mcp": [
368
369
  "0.1.1",
369
- "0.2.0"
370
+ "0.2.0",
371
+ "0.3.0"
370
372
  ],
371
373
  "rightkit-media": [
372
374
  "0.2.2"
@@ -381,30 +383,37 @@
381
383
  "0.1.0"
382
384
  ],
383
385
  "rightkit-process": [
384
- "0.2.1"
386
+ "0.2.1",
387
+ "0.3.0"
385
388
  ],
386
389
  "rightkit-qa": [
387
390
  "0.2.7",
388
- "0.2.8"
391
+ "0.2.8",
392
+ "0.2.9"
389
393
  ],
390
394
  "rightkit-sandbox": [
391
395
  "0.1.0",
392
396
  "0.2.0",
393
- "0.2.1"
397
+ "0.2.1",
398
+ "0.2.2",
399
+ "0.2.3"
394
400
  ],
395
401
  "rightkit-shell": [
396
402
  "0.1.0",
397
- "0.1.1"
403
+ "0.1.1",
404
+ "0.1.2"
398
405
  ],
399
406
  "rightkit-watch": [
400
407
  "0.1.0",
401
- "0.2.0"
408
+ "0.2.0",
409
+ "0.3.0"
402
410
  ],
403
411
  "rightkit-qts": [
404
412
  "0.2.0"
405
413
  ],
406
414
  "rightkit-control": [
407
- "0.1.6"
415
+ "0.1.6",
416
+ "0.1.7"
408
417
  ]
409
418
  }
410
419
  }
@@ -3,6 +3,7 @@ import { sign as edSign, verify as edVerify } from "node:crypto";
3
3
  export const RUNTIME_ARTIFACT_SCHEMA = 1;
4
4
  export const RUNTIME_ARTIFACT_KINDS = Object.freeze([
5
5
  "asr-model",
6
+ "tts-model",
6
7
  "ocr-model",
7
8
  "ort-runtime",
8
9
  "media-runtime",
@@ -15,6 +16,9 @@ export const RIGHT_SUITE_APP_KEYS = Object.freeze([
15
16
  "mailright",
16
17
  "scraperight",
17
18
  "coderight",
19
+ "screenright",
20
+ "genright",
21
+ "voiceright",
18
22
  ]);
19
23
 
20
24
  const TARGET_OSES = new Set(["windows", "darwin", "linux", "ios"]);
@@ -107,7 +111,7 @@ export function validateRuntimeArtifactManifest(value) {
107
111
  if (value.entitlement.tier !== lane.tier || value.distribution.bucket !== lane.bucket) {
108
112
  fail("distribution lane contradicts entitlement tier or bucket");
109
113
  }
110
- if (["asr-model", "ocr-model", "tokenizer"].includes(value.artifactKind)
114
+ if (["asr-model", "tts-model", "ocr-model", "tokenizer"].includes(value.artifactKind)
111
115
  && (value.distribution.delivery !== "private-r2" || value.entitlement.tier !== "pro")) {
112
116
  fail("model artifacts must use private-r2 with Pro entitlement");
113
117
  }
package/sign-macos.mjs CHANGED
@@ -1,20 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
  import { createHash } from "node:crypto";
3
- import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
3
+ import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, readlinkSync, statSync, writeFileSync } from "node:fs";
4
4
  import path from "node:path";
5
5
  import { spawnSync } from "./background-process.mjs";
6
6
  import { MACOS_PORTABLE_SIGNING_CONTRACT } from "./macos-signing-contract.mjs";
7
7
  import { resolveMacosDeveloperIdIdentity } from "./macos-signing-identity.mjs";
8
+ import { runMacCodesignCommands } from "./macos-signing-session.mjs";
8
9
 
9
10
  const args = process.argv.slice(2);
10
11
  const dryRun = args.includes("--dry-run");
11
12
  const receiptIndex = args.indexOf("--receipt");
12
13
  const receiptPath = receiptIndex >= 0 ? args[receiptIndex + 1] : null;
14
+ const entitlementsIndex = args.indexOf("--entitlements");
15
+ const entitlementsPath = entitlementsIndex >= 0 ? args[entitlementsIndex + 1] : null;
16
+ if (entitlementsIndex >= 0 && (!entitlementsPath || entitlementsPath.startsWith("--"))) fail("--entitlements requires a path");
13
17
  const files = args
14
- .filter((arg, index) => arg !== "--dry-run" && arg !== "--receipt" && (receiptIndex < 0 || index !== receiptIndex + 1))
18
+ .filter((arg, index) => arg !== "--dry-run" && arg !== "--receipt" && arg !== "--entitlements"
19
+ && (receiptIndex < 0 || index !== receiptIndex + 1)
20
+ && (entitlementsIndex < 0 || index !== entitlementsIndex + 1))
15
21
  .map((arg) => path.resolve(arg));
16
22
 
17
- if (!files.length) fail("usage: node sign-macos.mjs [--dry-run] --receipt <path> <file>...");
23
+ if (!files.length) fail("usage: node sign-macos.mjs [--dry-run] [--entitlements <path>] --receipt <path> <file>...");
18
24
  if (!receiptPath) fail("--receipt requires a path");
19
25
  if (dryRun) {
20
26
  console.log(`dry-run: Developer ID signing ${files.join(", ")}`);
@@ -24,11 +30,16 @@ if (process.platform !== "darwin") fail("macOS signing must run on macOS");
24
30
  let identity;
25
31
  try { identity = resolveMacosDeveloperIdIdentity(); } catch (error) { fail(error.message); }
26
32
  for (const file of files) if (!existsSync(file)) fail(`missing file: ${file}`);
33
+ if (entitlementsPath && !existsSync(entitlementsPath)) fail(`missing entitlements: ${entitlementsPath}`);
27
34
 
28
35
  const evidence = [];
29
36
  for (const file of files) {
30
37
  const before = fileEvidence(file);
31
- run("codesign", ["--force", "--options", "runtime", "--timestamp", "--sign", identity, file]);
38
+ try {
39
+ const [signed] = runMacCodesignCommands([["--force", "--options", "runtime", "--timestamp", "--sign", identity,
40
+ ...(entitlementsPath ? ["--entitlements", path.resolve(entitlementsPath)] : []), file]]);
41
+ if (signed.error || signed.status !== 0) fail(`codesign failed: ${signed.stderr ?? signed.error?.message ?? signed.status}`);
42
+ } catch (error) { fail(`${error.code ? `[${error.code}] ` : ""}${error.message}`); }
32
43
  const verification = run("codesign", ["--verify", "--strict", "--verbose=4", file]);
33
44
  const details = run("codesign", ["-dvv", file]);
34
45
  if (!/valid on disk/i.test(verification) || !/satisfies its designated requirement/i.test(verification)) {
@@ -46,6 +57,23 @@ mkdirSync(path.dirname(absoluteReceipt), { recursive: true });
46
57
  writeFileSync(absoluteReceipt, `${JSON.stringify({ schema: 1, signingContract: MACOS_PORTABLE_SIGNING_CONTRACT, identity, files: evidence }, null, 2)}\n`);
47
58
 
48
59
  function fileEvidence(file) {
60
+ if (statSync(file).isDirectory()) {
61
+ const digest = createHash("sha256");
62
+ let sizeBytes = 0;
63
+ function visit(directory) {
64
+ for (const name of readdirSync(directory).sort()) {
65
+ const entry = path.join(directory, name);
66
+ const info = lstatSync(entry);
67
+ const type = info.isSymbolicLink() ? "link" : info.isDirectory() ? "directory" : "file";
68
+ const bytes = type === "link" ? Buffer.from(readlinkSync(entry)) : type === "file" ? readFileSync(entry) : Buffer.alloc(0);
69
+ digest.update(`${path.relative(file, entry).split(path.sep).join("/")}\0${type}\0${bytes.length}\0`).update(bytes);
70
+ sizeBytes += bytes.length;
71
+ if (type === "directory") visit(entry);
72
+ }
73
+ }
74
+ visit(file);
75
+ return { sha256: digest.digest("hex"), sizeBytes, kind: "bundle-tree-v1" };
76
+ }
49
77
  const bytes = readFileSync(file);
50
78
  return { sha256: createHash("sha256").update(bytes).digest("hex"), sizeBytes: statSync(file).size };
51
79
  }
package/sign-updater.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { execFileSync, spawnSync } from "./background-process.mjs";
2
+ import { spawnSync } from "./background-process.mjs";
3
3
  import { existsSync, readFileSync, rmSync } from "node:fs";
4
- import os from "node:os";
5
4
  import path from "node:path";
5
+ import { signMacUpdaterFiles } from "./macos-signing-session.mjs";
6
6
 
7
7
  const args = process.argv.slice(2);
8
8
  const files = [];
@@ -20,17 +20,7 @@ if (!dryRun && !env.TAURI_SIGNING_PRIVATE_KEY) {
20
20
  const key = path.join(env.APPDATA || "", "ViewRight", "updater.key");
21
21
  if (!existsSync(key)) fail(`shared updater key not found: ${key}`);
22
22
  env.TAURI_SIGNING_PRIVATE_KEY = readFileSync(key, "utf8");
23
- } else if (process.platform === "darwin") {
24
- try {
25
- env.TAURI_SIGNING_PRIVATE_KEY = execFileSync(
26
- "security",
27
- ["find-generic-password", "-a", os.userInfo().username, "-s", "rightsuite-updater-key", "-w"],
28
- { encoding: "utf8" },
29
- ).trim();
30
- } catch {
31
- fail("shared updater key is missing from Keychain service rightsuite-updater-key");
32
- }
33
- } else {
23
+ } else if (process.platform !== "darwin") {
34
24
  fail("TAURI_SIGNING_PRIVATE_KEY is required on this platform");
35
25
  }
36
26
  }
@@ -41,6 +31,7 @@ for (const file of files) {
41
31
  rmSync(`${file}.sig`, { force: true });
42
32
  console.log(`${dryRun ? "[dry-run] " : ""}pnpm exec tauri signer sign ${file}`);
43
33
  if (dryRun) continue;
34
+ if (process.platform === "darwin") continue;
44
35
  const result = process.platform === "win32"
45
36
  ? spawnSync(
46
37
  process.env.ComSpec || "cmd.exe",
@@ -58,6 +49,10 @@ for (const file of files) {
58
49
  });
59
50
  if (result.status !== 0) process.exit(result.status ?? 1);
60
51
  }
52
+ if (!dryRun && process.platform === "darwin") {
53
+ try { signMacUpdaterFiles(files, { env }); }
54
+ catch (error) { fail(`${error.code ? `[${error.code}] ` : ""}${error.message}`); }
55
+ }
61
56
 
62
57
  function fail(message) {
63
58
  console.error(`sign-updater: ${message}`);
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+ import * as fs from "node:fs";
3
+ import net from "node:net";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { spawnSync } from "./background-process.mjs";
7
+ import { signingAgentPaths, privatePath, SigningAgentError, MAX_FRAME } from "./rightkit-signing-agent.mjs";
8
+
9
+ export async function requestSigningAgent(request, { paths = signingAgentPaths(), timeoutMs = 185000,
10
+ io = fs, connect = net.createConnection } = {}) {
11
+ try {
12
+ privatePath(paths.directory, 0o700, { io, kind: "directory" });
13
+ privatePath(paths.plist, 0o600, { io });
14
+ privatePath(paths.socket, 0o600, { io, kind: "socket" });
15
+ privatePath(paths.token, 0o600, { io });
16
+ } catch (error) {
17
+ if (error.code === "ENOENT") throw new SigningAgentError("MAC_SIGNING_AGENT_UNAVAILABLE", "Signing agent installation, socket, or token is missing");
18
+ throw error;
19
+ }
20
+ const token = io.readFileSync(paths.token, "utf8");
21
+ if (!/^[a-f0-9]{64}$/.test(token)) throw new SigningAgentError("MAC_SIGNING_AGENT_AUTH", "Invalid signing agent token");
22
+ const frame = Buffer.from(`${JSON.stringify({ ...request, token })}\n`);
23
+ if (frame.length > MAX_FRAME) throw new SigningAgentError("MAC_SIGNING_AGENT_PROTOCOL", "Signing request is too large");
24
+ return new Promise((resolve, reject) => {
25
+ const socket = connect({ path: paths.socket });
26
+ let input = Buffer.alloc(0), settled = false;
27
+ const finish = (error, report) => {
28
+ if (settled) return;
29
+ settled = true; clearTimeout(timer); socket.destroy();
30
+ if (error) reject(error); else resolve(report);
31
+ };
32
+ // Wall deadline, rather than an activity timeout which a stalled peer can extend.
33
+ const timer = setTimeout(() => finish(new SigningAgentError("MAC_SIGNING_AGENT_TIMEOUT", "Signing agent response timed out")), timeoutMs);
34
+ socket.on("connect", () => socket.write(frame));
35
+ socket.on("error", (error) => finish(new SigningAgentError(["ENOENT", "ECONNREFUSED"].includes(error.code)
36
+ ? "MAC_SIGNING_AGENT_UNAVAILABLE" : "MAC_SIGNING_AGENT_CONNECTION", "Signing agent connection failed")));
37
+ socket.on("end", () => { if (!settled) finish(new SigningAgentError("MAC_SIGNING_AGENT_PROTOCOL", "Signing agent returned no complete response")); });
38
+ socket.on("data", (chunk) => {
39
+ input = Buffer.concat([input, chunk]);
40
+ if (input.length > MAX_FRAME) { finish(new SigningAgentError("MAC_SIGNING_AGENT_PROTOCOL", "Signing agent response is too large")); return; }
41
+ const end = input.indexOf(10);
42
+ if (end < 0) return;
43
+ try {
44
+ const report = JSON.parse(input.subarray(0, end).toString("utf8"));
45
+ if (end !== input.length - 1 || report.schema !== 1 || typeof report.ok !== "boolean") throw new Error();
46
+ if (!report.ok) {
47
+ const code = typeof report.code === "string" && report.code.startsWith("MAC_SIGNING_") ? report.code : "MAC_SIGNING_AGENT_JOB_FAILED";
48
+ finish(new SigningAgentError(code, request.type === "updater" ? "Updater signing failed inside signing agent" : String(report.message ?? "Signing agent rejected request")));
49
+ return;
50
+ }
51
+ if (request.type === "health") {
52
+ if (report.service !== "dev.rightkit.signing-agent" || !Number.isInteger(report.pid)) throw new Error();
53
+ } else if (!Array.isArray(report.results) || report.results.length !== (request.items?.length ?? request.files?.length)
54
+ || report.results.some((r) => r.status !== 0 || typeof r.stdout !== "string" || typeof r.stderr !== "string")
55
+ || (request.type === "updater" && report.results.some((r) => r.stdout || r.stderr))) throw new Error();
56
+ finish(null, report);
57
+ } catch { finish(new SigningAgentError("MAC_SIGNING_AGENT_PROTOCOL", "Invalid signing agent response")); }
58
+ });
59
+ });
60
+ }
61
+ export function requestSigningAgentSync(request, { runClient = spawnSync, home, timeoutMs = 185000 } = {}) {
62
+ // Existing signing call sites are synchronous. Only typed JSON goes to this
63
+ // child's stdin; credentials & authentication token never enter argv/env.
64
+ const result = runClient(process.execPath, [fileURLToPath(import.meta.url)], { encoding: "utf8", stdio: "pipe", shell: false,
65
+ input: JSON.stringify({ request, home, timeoutMs }), timeout: timeoutMs + 1000, maxBuffer: MAX_FRAME,
66
+ env: { PATH: process.env.PATH, HOME: process.env.HOME } });
67
+ let report;
68
+ try { report = JSON.parse(result.stdout); } catch {
69
+ throw new SigningAgentError(result.error?.code === "ETIMEDOUT" ? "MAC_SIGNING_AGENT_TIMEOUT" : "MAC_SIGNING_AGENT_CONNECTION", "Signing agent client failed");
70
+ }
71
+ if (report.schema !== 1 || typeof report.ok !== "boolean") throw new SigningAgentError("MAC_SIGNING_AGENT_PROTOCOL", "Invalid signing agent client report");
72
+ if (!report.ok) throw new SigningAgentError(report.code ?? "MAC_SIGNING_AGENT_JOB_FAILED", report.message ?? "Signing agent failed");
73
+ if (result.error || result.status !== 0) throw new SigningAgentError("MAC_SIGNING_AGENT_CONNECTION", "Signing agent client failed");
74
+ return report;
75
+ }
76
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
77
+ try {
78
+ const { request, home, timeoutMs } = JSON.parse(fs.readFileSync(0, "utf8"));
79
+ const report = await requestSigningAgent(request, { paths: signingAgentPaths(home), timeoutMs });
80
+ process.stdout.write(JSON.stringify(report));
81
+ } catch (error) {
82
+ process.stdout.write(JSON.stringify({ schema: 1, ok: false, code: error.code ?? "MAC_SIGNING_AGENT_PROTOCOL", message: error.message }));
83
+ process.exitCode = 1;
84
+ }
85
+ }
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import { installSigningAgent, uninstallSigningAgent, signingAgentStatus } from "./signing-agent-install.mjs";
3
+
4
+ const [command, ...args] = process.argv.slice(2);
5
+ try {
6
+ let result;
7
+ if (command === "install") {
8
+ const options = {};
9
+ if (args.length) {
10
+ if (args.length !== 2 || args[0] !== "--managed-root" || !args[1]) throw new TypeError("Expected install [--managed-root <absolute-directory>]");
11
+ options.managedRoot = args[1];
12
+ }
13
+ result = installSigningAgent(options);
14
+ } else if (command === "uninstall" && !args.length) result = uninstallSigningAgent();
15
+ else if (command === "status" && !args.length) result = signingAgentStatus();
16
+ else throw new TypeError("Expected signing-agent install|uninstall|status");
17
+ console.log(JSON.stringify(result));
18
+ } catch (error) {
19
+ console.error(`right-release signing-agent: ${error.code ? `[${error.code}] ` : ""}${error.message}`);
20
+ process.exitCode = 1;
21
+ }
@@ -0,0 +1,100 @@
1
+ import * as fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { spawnSync } from "./background-process.mjs";
5
+ import { AGENT_LABEL, signingAgentPaths, defaultSigningRoots, ensurePrivateDirectory, privatePath, SigningAgentError } from "./rightkit-signing-agent.mjs";
6
+ import { requestSigningAgentSync } from "./signing-agent-client.mjs";
7
+
8
+ const capture = { encoding: "utf8", stdio: "pipe", shell: false, timeout: 10000 };
9
+ export function requireMacGuiSession({ run = spawnSync, uid = process.getuid?.() } = {}) {
10
+ const session = run("/bin/launchctl", ["print", `gui/${uid}`], capture);
11
+ if (session.error || session.status !== 0) throw new SigningAgentError("MAC_SIGNING_NO_GUI_SESSION", `No logged-in Aqua session for uid ${uid}`);
12
+ }
13
+ const xml = (value) => String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
14
+ export function signingAgentPlist({ paths, node = process.execPath, home }) {
15
+ return `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0"><dict>
16
+ <key>Label</key><string>${AGENT_LABEL}</string>
17
+ <key>ProgramArguments</key><array><string>${xml(node)}</string><string>${xml(path.join(paths.runtime, "rightkit-signing-agent.mjs"))}</string></array>
18
+ <key>LimitLoadToSessionType</key><string>Aqua</string>
19
+ <key>RunAtLoad</key><true/><key>KeepAlive</key><true/>
20
+ <key>ProcessType</key><string>Background</string>
21
+ <key>ThrottleInterval</key><integer>10</integer>
22
+ <key>Umask</key><integer>63</integer>
23
+ <key>EnvironmentVariables</key><dict><key>HOME</key><string>${xml(home)}</string></dict>
24
+ <key>StandardOutPath</key><string>/dev/null</string>
25
+ <key>StandardErrorPath</key><string>/dev/null</string>
26
+ </dict></plist>\n`;
27
+ }
28
+ export function installSigningAgent({ home = os.userInfo().homedir, username = os.userInfo().username,
29
+ uid = process.getuid?.(), platform = process.platform, run = spawnSync, io = fs, node = process.execPath,
30
+ managedRoot = "/Volumes/D/.rightkit-managed", health = requestSigningAgentSync,
31
+ now = Date.now, sleep = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms) } = {}) {
32
+ if (platform !== "darwin") throw new SigningAgentError("MAC_SIGNING_AGENT_PLATFORM", "Signing agent requires macOS");
33
+ requireMacGuiSession({ run, uid });
34
+ const paths = signingAgentPaths(home);
35
+ ensurePrivateDirectory(paths.directory, { io, uid });
36
+ ensurePrivateDirectory(paths.runtime, { io, uid });
37
+ ensurePrivateDirectory(path.join(paths.directory, "requests"), { io, uid });
38
+ ensurePrivateDirectory(path.join(paths.directory, "tmp"), { io, uid });
39
+ if (!path.isAbsolute(managedRoot) || ["/", home, "/Volumes", "/Volumes/D"].includes(path.resolve(managedRoot))) throw new SigningAgentError("MAC_SIGNING_AGENT_PATH_DENIED", "Managed signing root must be a dedicated absolute directory");
40
+ const found = run("/usr/bin/which", ["pnpm"], capture);
41
+ const pnpm = String(found.stdout ?? "").trim();
42
+ if (found.error || found.status !== 0 || !path.isAbsolute(pnpm) || pnpm.includes("\n")) throw new SigningAgentError("MAC_SIGNING_AGENT_INSTALL_FAILED", "Provisioned pnpm executable is unavailable");
43
+ // Resident runtime is self-contained; no package/checkout imports after install.
44
+ for (const name of ["rightkit-signing-agent.mjs", "background-process.mjs"]) {
45
+ const target = path.join(paths.runtime, name);
46
+ if (io.existsSync(target)) privatePath(target, 0o600, { io, uid });
47
+ io.writeFileSync(target, io.readFileSync(new URL(`./${name}`, import.meta.url)), { mode: 0o600 });
48
+ }
49
+ if (io.existsSync(paths.config)) privatePath(paths.config, 0o600, { io, uid });
50
+ io.writeFileSync(paths.config, JSON.stringify({ schema: 1, home, username, pnpm,
51
+ roots: defaultSigningRoots(home, path.resolve(managedRoot)),
52
+ environmentPath: [...new Set([path.dirname(node), path.dirname(pnpm), "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"])].join(":") }), { mode: 0o600 });
53
+ ensurePrivateDirectory(path.dirname(paths.plist), { io, uid });
54
+ if (io.existsSync(paths.plist)) privatePath(paths.plist, 0o600, { io, uid });
55
+ io.writeFileSync(paths.plist, signingAgentPlist({ paths, node, home }), { mode: 0o600 });
56
+ const loaded = run("/bin/launchctl", ["print", `gui/${uid}/${AGENT_LABEL}`], capture);
57
+ if (!loaded.error && loaded.status === 0) {
58
+ const stopped = run("/bin/launchctl", ["bootout", `gui/${uid}/${AGENT_LABEL}`], capture);
59
+ if (stopped.error || stopped.status !== 0) throw new SigningAgentError("MAC_SIGNING_AGENT_INSTALL_FAILED", "Could not stop existing signing agent");
60
+ }
61
+ const boot = run("/bin/launchctl", ["bootstrap", `gui/${uid}`, paths.plist], capture);
62
+ if (boot.error || boot.status !== 0) {
63
+ requireMacGuiSession({ run, uid });
64
+ throw new SigningAgentError("MAC_SIGNING_AGENT_INSTALL_FAILED", `Signing agent bootstrap failed (exit ${boot.status ?? "unavailable"})`);
65
+ }
66
+ const deadline = now() + 10000;
67
+ do {
68
+ try {
69
+ const report = health({ schema: 1, type: "health" }, { home, timeoutMs: 1000 });
70
+ return { schema: 1, installed: true, running: true, pid: report.pid, socket: paths.socket, plist: paths.plist };
71
+ } catch (error) {
72
+ if (!["MAC_SIGNING_AGENT_UNAVAILABLE", "MAC_SIGNING_AGENT_CONNECTION", "MAC_SIGNING_AGENT_TIMEOUT"].includes(error.code)) throw error;
73
+ sleep(100);
74
+ }
75
+ } while (now() < deadline);
76
+ throw new SigningAgentError("MAC_SIGNING_AGENT_INSTALL_FAILED", "Signing agent bootstrapped but health did not become ready");
77
+ }
78
+ export function signingAgentStatus({ home = os.userInfo().homedir, uid = process.getuid?.(), run = spawnSync,
79
+ io = fs, health = requestSigningAgentSync } = {}) {
80
+ const paths = signingAgentPaths(home);
81
+ const installed = io.existsSync(paths.plist);
82
+ let guiSession = false;
83
+ try { requireMacGuiSession({ run, uid }); guiSession = true; } catch {}
84
+ try {
85
+ const report = health({ schema: 1, type: "health" }, { home, timeoutMs: 1500 });
86
+ return { schema: 1, installed, guiSession, running: true, pid: report.pid, socket: paths.socket };
87
+ } catch (error) { return { schema: 1, installed, guiSession, running: false, code: error.code, socket: paths.socket }; }
88
+ }
89
+ export function uninstallSigningAgent({ home = os.userInfo().homedir, uid = process.getuid?.(), platform = process.platform, run = spawnSync, io = fs } = {}) {
90
+ if (platform !== "darwin") throw new SigningAgentError("MAC_SIGNING_AGENT_PLATFORM", "Signing agent requires macOS");
91
+ const paths = signingAgentPaths(home);
92
+ const loaded = run("/bin/launchctl", ["print", `gui/${uid}/${AGENT_LABEL}`], capture);
93
+ if (!loaded.error && loaded.status === 0) {
94
+ const result = run("/bin/launchctl", ["bootout", `gui/${uid}/${AGENT_LABEL}`], capture);
95
+ if (result.error || result.status !== 0) throw new SigningAgentError("MAC_SIGNING_AGENT_UNINSTALL_FAILED", "Could not stop signing agent");
96
+ }
97
+ if (io.existsSync(paths.plist)) { privatePath(paths.plist, 0o600, { io, uid }); io.unlinkSync(paths.plist); }
98
+ if (io.existsSync(paths.directory)) { privatePath(paths.directory, 0o700, { io, uid, kind: "directory" }); io.rmSync(paths.directory, { recursive: true, force: true }); }
99
+ return { schema: 1, installed: false, running: false };
100
+ }
@@ -0,0 +1,10 @@
1
+ import path from "node:path";
2
+
3
+ // One resolver for the worker that writes receipts & the build that seals them.
4
+ export function signingReceiptPath({ appRoot, target, platform, phase, stateRoot }) {
5
+ const configured = platform === "mac"
6
+ ? phase === "signing" ? target.sign?.receipt : target.notarize?.receipt
7
+ : phase === "raw-exe" ? target.sign?.receipt : phase === "installer" ? target.sign?.installerReceipt : undefined;
8
+ if (configured) return path.resolve(appRoot, configured);
9
+ return path.resolve(stateRoot ?? path.join(appRoot, ".right-release", "receipts"), `${platform === "mac" ? "macos" : "windows"}-${phase}.json`);
10
+ }
@@ -8,6 +8,7 @@ import { assertPrimaryReleaseCheckout } from "./release-invocation.mjs";
8
8
  import { verifySealedRelease } from "./release-state.mjs";
9
9
  import { assertCleanSource } from "./source-gate.mjs";
10
10
  import { verifyOwnershipEvidence } from "./ownership-evidence.mjs";
11
+ import { recordPublishedVersion } from "./artifact-storage.mjs";
11
12
 
12
13
  const args = process.argv.slice(2);
13
14
  let platform = process.platform === "win32" ? "win" : process.platform === "darwin" ? "mac" : process.platform;
@@ -75,6 +76,9 @@ if (!dryRun) writeJson(verifiedMarker, {
75
76
  tag: result.tag,
76
77
  verifiedAt: new Date().toISOString(),
77
78
  });
79
+ // This runs only after GitHub has returned successful remote byte verification.
80
+ // Candidate builds never enter published.json.
81
+ if (!dryRun) recordPublishedVersion({ app: sealed.manifest.app, version: sealed.manifest.version, platform });
78
82
  console.log(`right-release upload: ${dryRun ? "dry-run " : ""}${result.status} ${releaseId} tier=${tier} repo=${repo}`);
79
83
 
80
84
  function git(cwd, runArgs) {