@mnemonik/cli 0.0.0 → 7.99.5

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 (177) hide show
  1. package/LICENSE +38 -0
  2. package/dist/auth/credentials.d.ts +38 -0
  3. package/dist/auth/credentials.d.ts.map +1 -0
  4. package/dist/auth/credentials.js +7464 -0
  5. package/dist/auth/credentials.js.map +1 -0
  6. package/dist/auth/device.d.ts +23 -0
  7. package/dist/auth/device.d.ts.map +1 -0
  8. package/dist/auth/device.js +108 -0
  9. package/dist/auth/device.js.map +1 -0
  10. package/dist/auth/index.d.ts +34 -0
  11. package/dist/auth/index.d.ts.map +1 -0
  12. package/dist/auth/index.js +5048 -0
  13. package/dist/auth/index.js.map +1 -0
  14. package/dist/auth/installSession.d.ts +22 -0
  15. package/dist/auth/installSession.d.ts.map +1 -0
  16. package/dist/auth/installSession.js +4717 -0
  17. package/dist/auth/installSession.js.map +1 -0
  18. package/dist/auth/pkce.d.ts +35 -0
  19. package/dist/auth/pkce.d.ts.map +1 -0
  20. package/dist/auth/pkce.js +182 -0
  21. package/dist/auth/pkce.js.map +1 -0
  22. package/dist/auth/status.d.ts +30 -0
  23. package/dist/auth/status.d.ts.map +1 -0
  24. package/dist/auth/status.js +4799 -0
  25. package/dist/auth/status.js.map +1 -0
  26. package/dist/bin.d.ts +3 -0
  27. package/dist/bin.d.ts.map +1 -0
  28. package/dist/bin.js +28 -0
  29. package/dist/bin.js.map +1 -0
  30. package/dist/diagnostics.d.ts +42 -0
  31. package/dist/diagnostics.d.ts.map +1 -0
  32. package/dist/diagnostics.js +7405 -0
  33. package/dist/diagnostics.js.map +1 -0
  34. package/dist/digests.json +175 -0
  35. package/dist/identity/migrate.d.ts +62 -0
  36. package/dist/identity/migrate.d.ts.map +1 -0
  37. package/dist/identity/migrate.js +5416 -0
  38. package/dist/identity/migrate.js.map +1 -0
  39. package/dist/index.d.ts +9 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +11 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/install/adapters.d.ts +65 -0
  44. package/dist/install/adapters.d.ts.map +1 -0
  45. package/dist/install/adapters.js +91 -0
  46. package/dist/install/adapters.js.map +1 -0
  47. package/dist/install/hosts.d.ts +83 -0
  48. package/dist/install/hosts.d.ts.map +1 -0
  49. package/dist/install/hosts.js +5733 -0
  50. package/dist/install/hosts.js.map +1 -0
  51. package/dist/install/journal.d.ts +113 -0
  52. package/dist/install/journal.d.ts.map +1 -0
  53. package/dist/install/journal.js +6836 -0
  54. package/dist/install/journal.js.map +1 -0
  55. package/dist/install/journey.d.ts +11 -0
  56. package/dist/install/journey.d.ts.map +1 -0
  57. package/dist/install/journey.js +5594 -0
  58. package/dist/install/journey.js.map +1 -0
  59. package/dist/install/ownership.d.ts +55 -0
  60. package/dist/install/ownership.d.ts.map +1 -0
  61. package/dist/install/ownership.js +4942 -0
  62. package/dist/install/ownership.js.map +1 -0
  63. package/dist/install/transaction.d.ts +71 -0
  64. package/dist/install/transaction.d.ts.map +1 -0
  65. package/dist/install/transaction.js +5254 -0
  66. package/dist/install/transaction.js.map +1 -0
  67. package/dist/install/ui.d.ts +14 -0
  68. package/dist/install/ui.d.ts.map +1 -0
  69. package/dist/install/ui.js +4925 -0
  70. package/dist/install/ui.js.map +1 -0
  71. package/dist/installSession.d.ts +33 -0
  72. package/dist/installSession.d.ts.map +1 -0
  73. package/dist/installSession.js +4893 -0
  74. package/dist/installSession.js.map +1 -0
  75. package/dist/installation.d.ts +11 -0
  76. package/dist/installation.d.ts.map +1 -0
  77. package/dist/installation.js +6618 -0
  78. package/dist/installation.js.map +1 -0
  79. package/dist/launcher.d.ts +27 -0
  80. package/dist/launcher.d.ts.map +1 -0
  81. package/dist/launcher.js +6862 -0
  82. package/dist/launcher.js.map +1 -0
  83. package/dist/output.d.ts +22 -0
  84. package/dist/output.d.ts.map +1 -0
  85. package/dist/output.js +68 -0
  86. package/dist/output.js.map +1 -0
  87. package/dist/preflight.d.ts +43 -0
  88. package/dist/preflight.d.ts.map +1 -0
  89. package/dist/preflight.js +5265 -0
  90. package/dist/preflight.js.map +1 -0
  91. package/dist/project/eligibility.d.ts +19 -0
  92. package/dist/project/eligibility.d.ts.map +1 -0
  93. package/dist/project/eligibility.js +4835 -0
  94. package/dist/project/eligibility.js.map +1 -0
  95. package/dist/project/records.d.ts +14 -0
  96. package/dist/project/records.d.ts.map +1 -0
  97. package/dist/project/records.js +6598 -0
  98. package/dist/project/records.js.map +1 -0
  99. package/dist/project.d.ts +127 -0
  100. package/dist/project.d.ts.map +1 -0
  101. package/dist/project.js +7857 -0
  102. package/dist/project.js.map +1 -0
  103. package/dist/router.d.ts +63 -0
  104. package/dist/router.d.ts.map +1 -0
  105. package/dist/router.js +6168 -0
  106. package/dist/router.js.map +1 -0
  107. package/dist/runtime/bootstrap.d.ts +31 -0
  108. package/dist/runtime/bootstrap.d.ts.map +1 -0
  109. package/dist/runtime/bootstrap.js +386 -0
  110. package/dist/runtime/bootstrap.js.map +1 -0
  111. package/dist/runtime/releaseSource.d.ts +26 -0
  112. package/dist/runtime/releaseSource.d.ts.map +1 -0
  113. package/dist/runtime/releaseSource.js +4992 -0
  114. package/dist/runtime/releaseSource.js.map +1 -0
  115. package/dist/runtime/selfUpdate.d.ts +12 -0
  116. package/dist/runtime/selfUpdate.d.ts.map +1 -0
  117. package/dist/runtime/selfUpdate.js +4943 -0
  118. package/dist/runtime/selfUpdate.js.map +1 -0
  119. package/dist/runtime/signers.d.ts +2 -0
  120. package/dist/runtime/signers.d.ts.map +1 -0
  121. package/dist/runtime/signers.js +1697 -0
  122. package/dist/runtime/signers.js.map +1 -0
  123. package/dist/runtime/store.d.ts +30 -0
  124. package/dist/runtime/store.d.ts.map +1 -0
  125. package/dist/runtime/store.js +977 -0
  126. package/dist/runtime/store.js.map +1 -0
  127. package/dist/sbom.json +308 -0
  128. package/dist/scanner/control.d.ts +38 -0
  129. package/dist/scanner/control.d.ts.map +1 -0
  130. package/dist/scanner/control.js +6568 -0
  131. package/dist/scanner/control.js.map +1 -0
  132. package/dist/scanner/data.d.ts +9 -0
  133. package/dist/scanner/data.d.ts.map +1 -0
  134. package/dist/scanner/data.js +4715 -0
  135. package/dist/scanner/data.js.map +1 -0
  136. package/dist/scanner/discover.d.ts +35 -0
  137. package/dist/scanner/discover.d.ts.map +1 -0
  138. package/dist/scanner/discover.js +5266 -0
  139. package/dist/scanner/discover.js.map +1 -0
  140. package/dist/scanner/enable.d.ts +69 -0
  141. package/dist/scanner/enable.d.ts.map +1 -0
  142. package/dist/scanner/enable.js +6923 -0
  143. package/dist/scanner/enable.js.map +1 -0
  144. package/dist/scanner/picker.d.ts +58 -0
  145. package/dist/scanner/picker.d.ts.map +1 -0
  146. package/dist/scanner/picker.js +4993 -0
  147. package/dist/scanner/picker.js.map +1 -0
  148. package/dist/scanner/service.d.ts +49 -0
  149. package/dist/scanner/service.d.ts.map +1 -0
  150. package/dist/scanner/service.js +4888 -0
  151. package/dist/scanner/service.js.map +1 -0
  152. package/dist/scanner/update.d.ts +4 -0
  153. package/dist/scanner/update.d.ts.map +1 -0
  154. package/dist/scanner/update.js +6569 -0
  155. package/dist/scanner/update.js.map +1 -0
  156. package/dist/scanner-release.json +129 -0
  157. package/dist/screens/journey.d.ts +23 -0
  158. package/dist/screens/journey.d.ts.map +1 -0
  159. package/dist/screens/journey.js +110 -0
  160. package/dist/screens/journey.js.map +1 -0
  161. package/dist/screens.d.ts +18 -0
  162. package/dist/screens.d.ts.map +1 -0
  163. package/dist/screens.js +79 -0
  164. package/dist/screens.js.map +1 -0
  165. package/dist/status.d.ts +64 -0
  166. package/dist/status.d.ts.map +1 -0
  167. package/dist/status.js +5281 -0
  168. package/dist/status.js.map +1 -0
  169. package/dist/transport/server.d.ts +79 -0
  170. package/dist/transport/server.d.ts.map +1 -0
  171. package/dist/transport/server.js +136 -0
  172. package/dist/transport/server.js.map +1 -0
  173. package/dist/vendor/local-setup.js +6971 -0
  174. package/dist/vendor/shared/runtimeReader.js +408 -0
  175. package/dist/vendor/shared/runtimeSigners.js +512 -0
  176. package/package.json +258 -7
  177. package/README.md +0 -3
@@ -0,0 +1,1697 @@
1
+ import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res, err) => function __init() {
5
+ if (err) throw err[0];
6
+ try {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ } catch (e) {
9
+ throw err = [e], e;
10
+ }
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+
17
+ // packages/shared/dist/runtimeSigners.js
18
+ import { lstatSync, mkdirSync, readFileSync, writeFileSync, renameSync, unlinkSync } from "node:fs";
19
+ import { readFile } from "node:fs/promises";
20
+ import { basename, dirname, join, resolve, sep, win32 } from "node:path";
21
+ import { createHash, createPublicKey, randomUUID, timingSafeEqual, verify as verifySignature } from "node:crypto";
22
+ import { execFile, execFileSync } from "node:child_process";
23
+ import { promisify } from "node:util";
24
+ function decodeBase64(text, length) {
25
+ const bytes = Buffer.from(text, "base64");
26
+ if (bytes.length !== length || bytes.toString("base64") !== text)
27
+ throw new Error("unsigned");
28
+ return bytes;
29
+ }
30
+ async function verifyMinisign(path, signaturePath, identity) {
31
+ const [message, signatureText] = await Promise.all([
32
+ readFile(path),
33
+ readFile(signaturePath, "utf8")
34
+ ]);
35
+ const publicPacket = decodeBase64(identity, 42);
36
+ const lines = signatureText.trimEnd().split(/\r?\n/);
37
+ const [untrustedComment, encodedSignature, trustedLine, encodedGlobalSignature] = lines;
38
+ if (publicPacket.subarray(0, 2).toString() !== "Ed" || lines.length !== 4 || !untrustedComment?.startsWith("untrusted comment:") || !encodedSignature || !trustedLine?.startsWith("trusted comment: ") || !encodedGlobalSignature)
39
+ throw new Error("unsigned");
40
+ const signaturePacket = decodeBase64(encodedSignature, 74);
41
+ const globalSignature = decodeBase64(encodedGlobalSignature, 64);
42
+ const algorithm = signaturePacket.subarray(0, 2).toString();
43
+ if (algorithm !== "ED" && algorithm !== "Ed" || !timingSafeEqual(publicPacket.subarray(2, 10), signaturePacket.subarray(2, 10)))
44
+ throw new Error("unsigned");
45
+ const publicKey = createPublicKey({
46
+ key: Buffer.concat([Buffer.from("302a300506032b6570032100", "hex"), publicPacket.subarray(10)]),
47
+ format: "der",
48
+ type: "spki"
49
+ });
50
+ const fileSignature = signaturePacket.subarray(10);
51
+ const signedMessage = algorithm === "ED" ? createHash("blake2b512").update(message).digest() : message;
52
+ const trustedComment = trustedLine.slice("trusted comment: ".length);
53
+ if (!verifySignature(null, signedMessage, publicKey, fileSignature) || !verifySignature(null, Buffer.concat([fileSignature, Buffer.from(trustedComment)]), publicKey, globalSignature))
54
+ throw new Error("unsigned");
55
+ }
56
+ async function verifySigner(path, signer, run = execute) {
57
+ if (signer.platform === "darwin" && /^[A-Z0-9]{10}$/.test(signer.identity)) {
58
+ await run("/usr/bin/codesign", [
59
+ "--verify",
60
+ "--strict",
61
+ "-R",
62
+ `=anchor apple generic and certificate leaf[subject.OU] = "${signer.identity}"`,
63
+ path
64
+ ]);
65
+ } else if (signer.platform === "win32" && /^[A-Fa-f0-9]{40}$/.test(signer.identity)) {
66
+ await run("powershell.exe", [
67
+ "-NoProfile",
68
+ "-NonInteractive",
69
+ "-Command",
70
+ `$ErrorActionPreference = 'Stop'; $s = Get-AuthenticodeSignature -LiteralPath ${quote(path)}; if ($s.Status -ne 'Valid' -or $s.SignerCertificate.Thumbprint -ne ${quote(signer.identity)}) { exit 1 }`
71
+ ]);
72
+ } else if (signer.platform === "linux") {
73
+ try {
74
+ await verifyMinisign(path, signer.signature, signer.identity);
75
+ } catch {
76
+ throw new Error("unsigned");
77
+ }
78
+ } else
79
+ throw new Error("unsigned");
80
+ }
81
+ async function verifyWindowsPermission(path, run = hookExecute, state) {
82
+ const verdicts = await auditWindowsPermissions(path, [path], run, false, state);
83
+ if (verdicts.get(path) === "acl_unavailable")
84
+ throw new Error("acl_unavailable");
85
+ if (verdicts.get(path) !== "ok")
86
+ throw new Error("acl_permissions");
87
+ }
88
+ function windowsCommand(file) {
89
+ const root = process.env.SystemRoot;
90
+ if (!root || !win32.isAbsolute(root))
91
+ throw new Error("acl_system_root_unavailable");
92
+ return win32.join(root, "System32", file);
93
+ }
94
+ function readIdentity(stdout2) {
95
+ const match = /^"([^"\r\n]+)","(S-1-(?:\d+-)+\d+)"$/.exec(stdout2.trim());
96
+ if (!match?.[1] || !match[2] || stdout2.includes("\uFFFD"))
97
+ throw new Error("acl_identity_unavailable");
98
+ return { name: match[1], sid: match[2] };
99
+ }
100
+ function windowsCurrentAccountSync() {
101
+ return currentIdentity ??= readIdentity(hookExecuteSync(windowsCommand("whoami.exe"), identityArgs));
102
+ }
103
+ async function windowsIdentity(run) {
104
+ if (currentIdentity)
105
+ return currentIdentity;
106
+ identityPending ??= run(windowsCommand("whoami.exe"), identityArgs).then((result) => currentIdentity = readIdentity(stdout(result))).finally(() => {
107
+ identityPending = void 0;
108
+ });
109
+ return identityPending;
110
+ }
111
+ async function verifyWindowsAcl(path, run = hookExecute, state) {
112
+ const user = await windowsIdentity(run);
113
+ if (!privateEntries((await drive(saveAcl(path, false, [path], state), run)).get(path), user, false))
114
+ throw new Error("acl_permissions");
115
+ }
116
+ function hookInvocation(file, args) {
117
+ if (win32.basename(file) === "whoami.exe") {
118
+ if ([file, ...args].some((arg) => /[%!"\r\n]/.test(arg)))
119
+ throw new Error("acl_path_unavailable");
120
+ const command = args.map((arg) => /^(?:[a-z0-9]+|\/[a-z]+)$/i.test(arg) ? arg : `"${arg}"`).join(" ");
121
+ return [
122
+ windowsCommand("cmd.exe"),
123
+ ["/d", "/v:off", "/s", "/c", `"chcp 65001>nul & "${file}" ${command}"`]
124
+ ];
125
+ }
126
+ if (win32.basename(file) === "cmd.exe")
127
+ return [file, ["/u", ...args]];
128
+ return [file, args];
129
+ }
130
+ function hookExecuteSync(file, args, input) {
131
+ [file, args] = hookInvocation(file, args);
132
+ return execFileSync(file, args, { ...hookOptions(file, args), input });
133
+ }
134
+ function aclRecords(output, root, paths, onLookup) {
135
+ const records = /* @__PURE__ */ new Map();
136
+ const wanted = new Set(paths);
137
+ const lines = output.replace(/^\uFEFF/, "").trimEnd().split(/\r?\n/);
138
+ if (lines.length % 2)
139
+ throw new Error("acl_unavailable");
140
+ for (let index = 0; index < lines.length; index += 2) {
141
+ onLookup?.();
142
+ const relativePath = lines[index];
143
+ const descriptor = lines[index + 1];
144
+ if (relativePath === void 0 || descriptor === void 0)
145
+ throw new Error("acl_unavailable");
146
+ const path = resolve(dirname(root), relativePath.replaceAll("\\", sep));
147
+ if (!wanted.has(path))
148
+ continue;
149
+ if (records.has(path))
150
+ throw new Error("acl_unavailable");
151
+ const dacl = /^D:(?:P|AI|AR)*((?:\([^()]*\))*)$/.exec(descriptor);
152
+ records.set(path, dacl?.[1]?.match(/\([^()]*\)/g) ?? []);
153
+ }
154
+ return records;
155
+ }
156
+ function privateEntries(entries, user, directory) {
157
+ if (!entries?.length)
158
+ throw new Error("acl_unavailable");
159
+ return entries.every((line) => {
160
+ const ace = /^\(([AD]);((?:OI|CI|NP|IO|ID|SA|FA)*);[A-Za-z0-9]+;;;((?:S-1-(?:\d+-)+\d+)|[A-Z]{2})\)$/.exec(line);
161
+ if (!ace)
162
+ throw new Error("acl_unavailable");
163
+ return (!directory || !ace[2]?.includes("ID")) && (ace[1] === "D" || ace[3] === user.sid);
164
+ });
165
+ }
166
+ function* prepareAclDirectory(state) {
167
+ const directory = join(state, "audit-tmp");
168
+ if (auditDirectories.has(directory)) {
169
+ const stat2 = lstatSync(directory);
170
+ if (!stat2.isDirectory() || stat2.isSymbolicLink())
171
+ throw new Error("acl_permissions");
172
+ return directory;
173
+ }
174
+ let created = false;
175
+ try {
176
+ mkdirSync(directory, { mode: 448 });
177
+ created = true;
178
+ } catch (error) {
179
+ if (error.code !== "EEXIST")
180
+ throw error;
181
+ }
182
+ const stat = lstatSync(directory);
183
+ if (!stat.isDirectory() || stat.isSymbolicLink())
184
+ throw new Error("acl_permissions");
185
+ if (!auditDirectories.has(directory)) {
186
+ currentIdentity ??= readIdentity(yield [windowsCommand("whoami.exe"), identityArgs]);
187
+ yield [windowsCommand("icacls.exe"), aclArgs(directory, currentIdentity.sid)];
188
+ if (!created) {
189
+ const temp = join(directory, `.acl-${randomUUID()}.tmp`);
190
+ writeFileSync(temp, "", { flag: "wx", mode: 384 });
191
+ try {
192
+ yield [windowsCommand("icacls.exe"), [directory, "/save", temp, "/l"]];
193
+ if (!privateEntries(aclRecords(readFileSync(temp, "utf16le"), directory, [directory]).get(directory), currentIdentity, true))
194
+ throw new Error("acl_permissions");
195
+ } finally {
196
+ unlinkSync(temp);
197
+ }
198
+ }
199
+ auditDirectories.add(directory);
200
+ }
201
+ return directory;
202
+ }
203
+ async function prepareWindowsAclDirectory(state, run = hookExecute) {
204
+ let pending = directoryPending.get(state);
205
+ if (!pending) {
206
+ pending = drive(prepareAclDirectory(state), run).then(() => {
207
+ }).finally(() => directoryPending.delete(state));
208
+ directoryPending.set(state, pending);
209
+ }
210
+ await pending;
211
+ }
212
+ function* saveAcl(path, recursive, paths = [path], state = lstatSync(path).isDirectory() ? path : dirname(path)) {
213
+ const directory = yield* prepareAclDirectory(state);
214
+ const temp = join(directory, `.acl-${randomUUID()}.tmp`);
215
+ writeFileSync(temp, "", { flag: "wx", mode: 384 });
216
+ try {
217
+ yield [windowsCommand("icacls.exe"), [path, "/save", temp, ...recursive ? ["/t"] : [], "/l"]];
218
+ return aclRecords(readFileSync(temp, "utf16le"), path, paths.filter((item) => item !== directory && !item.startsWith(directory + sep)));
219
+ } finally {
220
+ unlinkSync(temp);
221
+ }
222
+ }
223
+ async function drive(commands, run) {
224
+ try {
225
+ let step = commands.next();
226
+ while (!step.done)
227
+ step = commands.next(stdout(await run(...step.value)));
228
+ return step.value;
229
+ } finally {
230
+ commands.return(void 0);
231
+ }
232
+ }
233
+ function dirEntry(line) {
234
+ const metadata = /^\S+\s+\S+(?:\s+\S+)?\s+(?:<DIR>|\d[\d,.\s]*)\s+/.exec(line)?.[0];
235
+ if (!metadata)
236
+ return void 0;
237
+ const ownerColumn = metadata.length;
238
+ const owner = line.slice(ownerColumn, ownerColumn + ownerWidth).trim().toLowerCase();
239
+ const name = line.slice(ownerColumn + ownerWidth);
240
+ return owner && name ? { owner, name } : void 0;
241
+ }
242
+ function ownerLookup(paths) {
243
+ if (paths.some((path) => /[\t\r\n]/.test(path)))
244
+ throw new Error("acl_path_unavailable");
245
+ const script = `$ErrorActionPreference = 'Stop'
246
+ [Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)
247
+ [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
248
+ while ($null -ne ($path = [Console]::ReadLine())) {
249
+ $owner = (Get-Acl -LiteralPath $path).GetOwner([System.Security.Principal.SecurityIdentifier]).Value
250
+ [Console]::WriteLine($path + "\`t" + $owner)
251
+ }`;
252
+ return [
253
+ windowsCommand("WindowsPowerShell\\v1.0\\powershell.exe"),
254
+ ["-NoProfile", "-NonInteractive", "-Command", script],
255
+ paths.join("\n") + "\n"
256
+ ];
257
+ }
258
+ function ownerSids(output) {
259
+ const owners = /* @__PURE__ */ new Map();
260
+ for (const line of output.split(/\r?\n/)) {
261
+ const [path, sid] = line.split(" ");
262
+ if (path && sid && /^S-1-(?:\d+-)+\d+$/.test(sid))
263
+ owners.set(path, sid.toLowerCase());
264
+ }
265
+ return owners;
266
+ }
267
+ function* hookPermission(path, created, session, directory = true) {
268
+ const stat = lstatSync(path);
269
+ if (stat.isSymbolicLink() || (directory ? !stat.isDirectory() : !stat.isFile()))
270
+ throw new Error("cache unavailable");
271
+ const marker = join(session, ".windows-acl.json");
272
+ let receipt = receipts.get(session);
273
+ if (!receipt) {
274
+ try {
275
+ if (lstatSync(session).isSymbolicLink() || !lstatSync(marker).isFile() || lstatSync(marker).isSymbolicLink())
276
+ throw new Error("cache unavailable");
277
+ receipt = JSON.parse(readFileSync(marker, "utf8"));
278
+ if (!receipt || !receipt.paths || !receipt.identity || !/^S-1-(?:\d+-)+\d+$/.test(receipt.identity.sid) || typeof receipt.identity.name !== "string")
279
+ receipt = void 0;
280
+ } catch {
281
+ }
282
+ }
283
+ if (receipt && currentIdentity && receipt.identity.sid !== currentIdentity.sid)
284
+ receipt = void 0;
285
+ if (!created && receipt && Object.hasOwn(receipt.paths, path) && fresh(receipt.paths[path])) {
286
+ currentIdentity ??= receipt.identity;
287
+ receipts.set(session, receipt);
288
+ return;
289
+ }
290
+ currentIdentity ??= readIdentity(yield [windowsCommand("whoami.exe"), identityArgs]);
291
+ const user = currentIdentity;
292
+ if (receipt && receipt.identity.sid !== user.sid)
293
+ receipt = void 0;
294
+ const parent = dirname(path);
295
+ if (/[%!"\r\n]/.test(parent))
296
+ throw new Error("acl_path_unavailable");
297
+ const listing = yield [
298
+ windowsCommand("cmd.exe"),
299
+ ["/d", "/v:off", "/s", "/c", `dir /q ${directory ? "/a:d" : "/a"} "${parent}"`]
300
+ ];
301
+ const owners = listing.split(/\r?\n/).flatMap((line) => {
302
+ const entry = dirEntry(line);
303
+ return entry?.name.toLowerCase() === basename(path).toLowerCase() ? [entry.owner] : [];
304
+ });
305
+ let owner = owners[0];
306
+ if (owners.length === 1 && owner && unknownOwner(owner, user))
307
+ owner = ownerSids(yield ownerLookup([path])).get(path);
308
+ if (owners.length !== 1 || ![user.name.toLowerCase(), user.sid.toLowerCase()].includes(owner ?? ""))
309
+ throw new Error("acl_owner");
310
+ const icacls = windowsCommand("icacls.exe");
311
+ if (created)
312
+ yield [icacls, aclArgs(path, user.sid)];
313
+ let valid = privateEntries((yield* saveAcl(path, false)).get(path), user, directory);
314
+ if (!valid && !created && directory) {
315
+ yield [icacls, aclArgs(path, user.sid)];
316
+ valid = privateEntries((yield* saveAcl(path, false)).get(path), user, directory);
317
+ }
318
+ if (!valid)
319
+ throw new Error("acl_permissions");
320
+ receipt ??= { identity: user, paths: {} };
321
+ receipt.paths[path] = Date.now();
322
+ receipts.set(session, receipt);
323
+ try {
324
+ const temp = `${marker}.${randomUUID()}`;
325
+ writeFileSync(temp, JSON.stringify(receipt), { flag: "wx", mode: 384 });
326
+ renameSync(temp, marker);
327
+ } catch {
328
+ }
329
+ }
330
+ async function windowsCurrentUserDirectoryAcl(path, run = hookExecute) {
331
+ currentIdentity ??= readIdentity(stdout(await run(windowsCommand("whoami.exe"), identityArgs)));
332
+ await run(windowsCommand("icacls.exe"), aclArgs(path, currentIdentity.sid));
333
+ }
334
+ async function protectWindowsDirectory(path, created, run = hookExecute, session = path) {
335
+ await drive(hookPermission(path, created, session), run);
336
+ }
337
+ function protectSync(path, created, session, directory = true) {
338
+ const audit = hookPermission(path, created, session, directory);
339
+ try {
340
+ let step = audit.next();
341
+ while (!step.done)
342
+ step = audit.next(hookExecuteSync(...step.value));
343
+ } finally {
344
+ audit.return();
345
+ }
346
+ }
347
+ function verifyWindowsPermissionSync(path) {
348
+ protectSync(path, false, dirname(path), false);
349
+ }
350
+ function ensureWindowsPrivateDirectorySync(path, session = path) {
351
+ if (process.platform !== "win32")
352
+ return;
353
+ const created = mkdirSync(path, { recursive: true, mode: 448 });
354
+ protectSync(path, created !== void 0, session);
355
+ }
356
+ async function auditWindowsPermissions(state, paths, run = hookExecute, recursive = true, tempState = lstatSync(state).isDirectory() ? state : dirname(state)) {
357
+ const user = await windowsIdentity(run);
358
+ const root = resolve(state);
359
+ for (const path of paths)
360
+ if (resolve(path) !== root && !resolve(path).startsWith(root + sep))
361
+ throw new Error("acl_path_unavailable");
362
+ if (/[%!"\r\n]/.test(root))
363
+ throw new Error("acl_path_unavailable");
364
+ const owners = /* @__PURE__ */ new Map();
365
+ for (const [directory, descend] of [
366
+ [dirname(root), false],
367
+ ...recursive ? [[root, true]] : []
368
+ ]) {
369
+ if (/[%!"\r\n]/.test(directory))
370
+ throw new Error("acl_path_unavailable");
371
+ const output = stdout(await run(windowsCommand("cmd.exe"), [
372
+ "/d",
373
+ "/v:off",
374
+ "/s",
375
+ "/c",
376
+ `dir /q /a ${descend ? "/s " : ""}"${directory}"`
377
+ ]));
378
+ let current = directory;
379
+ for (const line of output.split(/\r?\n/)) {
380
+ const entry = dirEntry(line);
381
+ if (entry) {
382
+ if (entry.name !== "..")
383
+ owners.set(join(current, entry.name), unknownOwner(entry.owner, user) ? "..." : entry.owner);
384
+ } else {
385
+ const header = /(?:^|\s)((?:[A-Za-z]:\\|\\\\|\/).*)$/.exec(line.trim());
386
+ if (header?.[1])
387
+ current = header[1];
388
+ }
389
+ }
390
+ }
391
+ const verdicts = /* @__PURE__ */ new Map();
392
+ const ownerPaths = [...owners.keys()].filter((path) => path === root || path.startsWith(root + sep));
393
+ const truncated = new Set(ownerPaths.filter((path) => owners.get(path)?.includes("...")));
394
+ if (truncated.size) {
395
+ const result = await run(...ownerLookup([...truncated])).catch(() => ({ stdout: "" }));
396
+ for (const [path, sid] of ownerSids(stdout(result)))
397
+ if (truncated.has(path))
398
+ owners.set(path, sid);
399
+ }
400
+ for (const [path, entries] of await drive(saveAcl(root, recursive, ownerPaths, tempState), run)) {
401
+ const owner = owners.get(path) ?? "...";
402
+ const owned = owner === user.name.toLowerCase() || owner === user.sid.toLowerCase();
403
+ try {
404
+ if (!entries.length || owner.includes("..."))
405
+ throw new Error("acl_unavailable");
406
+ verdicts.set(path, !owned ? "owner" : privateEntries(entries, user, false) ? "ok" : "ace");
407
+ } catch {
408
+ verdicts.set(path, "acl_unavailable");
409
+ }
410
+ }
411
+ return verdicts;
412
+ }
413
+ var execute, quote, identityArgs, currentIdentity, receipts, ttl, fresh, identityPending, aclArgs, hookOptions, hookExecute, stdout, auditDirectories, directoryPending, ownerWidth, unknownOwner;
414
+ var init_runtimeSigners = __esm({
415
+ "packages/shared/dist/runtimeSigners.js"() {
416
+ "use strict";
417
+ execute = (file, args, input) => {
418
+ const result = promisify(execFile)(file, args);
419
+ if (input === void 0)
420
+ return result;
421
+ return new Promise((resolve4, reject2) => {
422
+ void result.then(resolve4, reject2);
423
+ result.child.stdin?.on("error", reject2);
424
+ result.child.stdin?.end(input);
425
+ });
426
+ };
427
+ quote = (text) => `'${text.replaceAll("'", "''")}'`;
428
+ identityArgs = ["/user", "/fo", "csv", "/nh"];
429
+ receipts = /* @__PURE__ */ new Map();
430
+ ttl = 10 * 6e4;
431
+ fresh = (time) => time !== void 0 && Date.now() >= time && Date.now() - time < ttl;
432
+ aclArgs = (path, sid) => [
433
+ path,
434
+ "/inheritance:r",
435
+ "/grant:r",
436
+ `*${sid}:(OI)(CI)F`
437
+ ];
438
+ hookOptions = (file, args) => ({
439
+ encoding: args.includes("/u") ? "utf16le" : "utf8",
440
+ timeout: win32.basename(file) === "powershell.exe" ? 3e4 : 5e3,
441
+ windowsHide: true,
442
+ maxBuffer: 16 * 1024 * 1024,
443
+ windowsVerbatimArguments: win32.basename(file) === "cmd.exe"
444
+ });
445
+ hookExecute = (file, args, input) => {
446
+ const treeListing = win32.basename(file) === "icacls.exe" && args.includes("/t") || win32.basename(file) === "cmd.exe" && args.some((arg) => arg.startsWith("dir /q /a /s "));
447
+ [file, args] = hookInvocation(file, args);
448
+ const options = hookOptions(file, args);
449
+ if (treeListing)
450
+ options.timeout = 3e4;
451
+ return new Promise((resolve4, reject2) => {
452
+ const child = execFile(file, args, options, (error, stdout2) => error ? reject2(error) : resolve4({ stdout: stdout2 }));
453
+ if (input !== void 0) {
454
+ child.stdin?.on("error", reject2);
455
+ child.stdin?.end(input);
456
+ }
457
+ });
458
+ };
459
+ stdout = (value) => value.stdout;
460
+ auditDirectories = /* @__PURE__ */ new Set();
461
+ directoryPending = /* @__PURE__ */ new Map();
462
+ ownerWidth = 23;
463
+ unknownOwner = (owner, user) => user.name.length >= ownerWidth || owner.length >= ownerWidth || owner.includes("...");
464
+ }
465
+ });
466
+
467
+ // packages/shared/dist/projectIdentityFile.js
468
+ import { lstat, readFile as readFile2 } from "node:fs/promises";
469
+ import { join as join2 } from "node:path";
470
+ function parseIdentityFile(text) {
471
+ let value;
472
+ try {
473
+ value = JSON.parse(text);
474
+ } catch (error) {
475
+ return { kind: "malformed", detail: `unparseable JSON: ${error.message}` };
476
+ }
477
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
478
+ return { kind: "malformed", detail: "identity file must contain a JSON object" };
479
+ }
480
+ const record2 = value;
481
+ if (record2.schemaVersion !== 1) {
482
+ return { kind: "unknown_version", version: record2.schemaVersion };
483
+ }
484
+ const unknownKey = Object.keys(record2).find((key) => !IDENTITY_KEYS.has(key));
485
+ if (unknownKey)
486
+ return { kind: "malformed", detail: `unknown key "${unknownKey}"` };
487
+ if (!isCanonicalUuid(record2.projectId)) {
488
+ return { kind: "malformed", detail: '"projectId" must be a canonical RFC 4122 UUID string' };
489
+ }
490
+ if ("projectName" in record2 && typeof record2.projectName !== "string") {
491
+ return { kind: "malformed", detail: '"projectName" must be a string when present' };
492
+ }
493
+ if ("repositoryFingerprint" in record2 && (!record2.repositoryFingerprint || typeof record2.repositoryFingerprint !== "object" || Array.isArray(record2.repositoryFingerprint))) {
494
+ return { kind: "malformed", detail: '"repositoryFingerprint" must be an object when present' };
495
+ }
496
+ const fingerprint = record2.repositoryFingerprint;
497
+ const unknownFingerprintKey = fingerprint ? Object.keys(fingerprint).find((key) => !FINGERPRINT_KEYS.has(key)) : void 0;
498
+ if (unknownFingerprintKey) {
499
+ return {
500
+ kind: "malformed",
501
+ detail: `unknown repositoryFingerprint key "${unknownFingerprintKey}"`
502
+ };
503
+ }
504
+ if (fingerprint && (fingerprint.algorithmVersion !== 1 || typeof fingerprint.hash !== "string")) {
505
+ return {
506
+ kind: "malformed",
507
+ detail: '"repositoryFingerprint" must contain algorithmVersion 1 and a string hash'
508
+ };
509
+ }
510
+ const repositoryFingerprint = fingerprint ? { algorithmVersion: 1, hash: fingerprint.hash } : void 0;
511
+ return {
512
+ kind: "ok",
513
+ identity: {
514
+ schemaVersion: 1,
515
+ projectId: record2.projectId,
516
+ ...record2.projectName === void 0 ? {} : { projectName: record2.projectName },
517
+ ...repositoryFingerprint ? { repositoryFingerprint } : {}
518
+ }
519
+ };
520
+ }
521
+ async function readIdentityFile(dir) {
522
+ const path = join2(dir, ".mnemonik.json");
523
+ try {
524
+ const stat = await lstat(path);
525
+ if (stat.isSymbolicLink())
526
+ return { kind: "malformed", detail: "symlink" };
527
+ if (!stat.isFile())
528
+ return { kind: "malformed", detail: "identity path is not a regular file" };
529
+ return parseIdentityFile(await readFile2(path, "utf8"));
530
+ } catch (error) {
531
+ if (error.code === "ENOENT")
532
+ return { kind: "absent" };
533
+ return { kind: "malformed", detail: `cannot read identity file: ${error.message}` };
534
+ }
535
+ }
536
+ var CANONICAL_UUID, isCanonicalUuid, IDENTITY_KEYS, FINGERPRINT_KEYS;
537
+ var init_projectIdentityFile = __esm({
538
+ "packages/shared/dist/projectIdentityFile.js"() {
539
+ "use strict";
540
+ CANONICAL_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
541
+ isCanonicalUuid = (value) => typeof value === "string" && CANONICAL_UUID.test(value);
542
+ IDENTITY_KEYS = /* @__PURE__ */ new Set([
543
+ "schemaVersion",
544
+ "projectId",
545
+ "projectName",
546
+ "repositoryFingerprint"
547
+ ]);
548
+ FINGERPRINT_KEYS = /* @__PURE__ */ new Set(["algorithmVersion", "hash"]);
549
+ }
550
+ });
551
+
552
+ // packages/shared/dist/repositoryRoot.js
553
+ import { lstat as lstat2, readFile as readFile3 } from "node:fs/promises";
554
+ import { execFile as execFile2 } from "node:child_process";
555
+ import { homedir } from "node:os";
556
+ import { dirname as dirname2, join as join3, resolve as resolve2 } from "node:path";
557
+ function gitEnvironment() {
558
+ const env = {};
559
+ for (const key of GIT_ENV_KEYS) {
560
+ if (process.env[key] !== void 0)
561
+ env[key] = process.env[key];
562
+ }
563
+ env.LC_ALL = "C";
564
+ return env;
565
+ }
566
+ function runGit(cwd, args) {
567
+ return new Promise((resolvePromise, reject2) => {
568
+ execFile2("git", args, { cwd, timeout: GIT_TIMEOUT_MS, encoding: "utf8", env: gitEnvironment() }, (error, stdout2, stderr2) => {
569
+ if (error)
570
+ reject2(Object.assign(error, { stdout: stdout2, stderr: stderr2 }));
571
+ else
572
+ resolvePromise({ stdout: stdout2, stderr: stderr2 });
573
+ });
574
+ });
575
+ }
576
+ async function boundaryAt(path) {
577
+ try {
578
+ const stat = await lstat2(join3(path, ".git"));
579
+ if (stat.isDirectory())
580
+ return { path, kind: "directory" };
581
+ if (stat.isFile())
582
+ return { path, kind: "file" };
583
+ } catch (error) {
584
+ const code = error.code;
585
+ if (code !== "ENOENT" && code !== "ENOTDIR")
586
+ throw error;
587
+ }
588
+ return void 0;
589
+ }
590
+ async function confirmedBoundaryAt(path) {
591
+ const boundary = await boundaryAt(path);
592
+ if (!boundary)
593
+ return void 0;
594
+ try {
595
+ const { stdout: stdout2 } = await runGit(path, ["rev-parse", "--git-dir"]);
596
+ return stdout2.trim() ? boundary : void 0;
597
+ } catch {
598
+ return void 0;
599
+ }
600
+ }
601
+ async function boundariesBetween(cwd, root) {
602
+ const boundaries = [];
603
+ let directory = cwd;
604
+ while (directory !== root) {
605
+ const boundary = await confirmedBoundaryAt(directory);
606
+ if (boundary)
607
+ boundaries.push(boundary);
608
+ const parent = dirname2(directory);
609
+ if (parent === directory)
610
+ break;
611
+ directory = parent;
612
+ }
613
+ return boundaries;
614
+ }
615
+ async function containingRepository(root) {
616
+ let directory = dirname2(root);
617
+ while (true) {
618
+ const boundary = await confirmedBoundaryAt(directory);
619
+ if (boundary)
620
+ return boundary;
621
+ const parent = dirname2(directory);
622
+ if (parent === directory)
623
+ return void 0;
624
+ directory = parent;
625
+ }
626
+ }
627
+ async function canonicalBoundaryRoot(boundary) {
628
+ if (boundary.kind === "directory")
629
+ return boundary.path;
630
+ const pointer = /^gitdir:\s*(.+)\s*$/im.exec(await readFile3(join3(boundary.path, ".git"), "utf8"))?.[1];
631
+ if (!pointer)
632
+ return boundary.path;
633
+ const gitDir = resolve2(boundary.path, pointer);
634
+ try {
635
+ const commonDir = resolve2(gitDir, (await readFile3(join3(gitDir, "commondir"), "utf8")).trim());
636
+ return dirname2(commonDir);
637
+ } catch (error) {
638
+ if (error.code === "ENOENT")
639
+ return boundary.path;
640
+ throw error;
641
+ }
642
+ }
643
+ async function resolveRepositoryRoot(cwd) {
644
+ const absoluteCwd = resolve2(cwd);
645
+ try {
646
+ const { stdout: stdout2 } = await runGit(absoluteCwd, [
647
+ "rev-parse",
648
+ "--show-toplevel",
649
+ "--git-common-dir"
650
+ ]);
651
+ const [rootLine, commonLine, ...extra] = stdout2.trim().split(/\r?\n/);
652
+ if (!rootLine || !commonLine || extra.length) {
653
+ return { kind: "git_unavailable", detail: "git rev-parse returned an unexpected response" };
654
+ }
655
+ const root = resolve2(rootLine);
656
+ const commonDir = resolve2(root, commonLine);
657
+ const isLinkedWorktree = commonDir !== join3(root, ".git");
658
+ const nested = await boundariesBetween(absoluteCwd, root);
659
+ if (!isLinkedWorktree && await containingRepository(root)) {
660
+ const boundary = await boundaryAt(root);
661
+ if (boundary)
662
+ nested.push(boundary);
663
+ }
664
+ return {
665
+ kind: "git",
666
+ root,
667
+ commonDir,
668
+ isLinkedWorktree,
669
+ nested
670
+ };
671
+ } catch (error) {
672
+ const failure = error;
673
+ if (failure.code === 128 && /not a git repository/i.test(failure.stderr ?? failure.message)) {
674
+ return { kind: "plain", root: absoluteCwd };
675
+ }
676
+ return { kind: "git_unavailable", detail: failure.message };
677
+ }
678
+ }
679
+ async function readPlainIdentity(start) {
680
+ const home = resolve2(homedir());
681
+ let directory = start;
682
+ while (true) {
683
+ const result = await readIdentityFile(directory);
684
+ if (result.kind !== "absent")
685
+ return { root: directory, result };
686
+ if (directory === home)
687
+ break;
688
+ const parent = dirname2(directory);
689
+ if (parent === directory)
690
+ break;
691
+ directory = parent;
692
+ }
693
+ return { root: start, result: { kind: "absent" } };
694
+ }
695
+ function withBase(repository, root, nested) {
696
+ return { root, repository, nested };
697
+ }
698
+ async function resolveProjectIdentity(cwd, options = {}) {
699
+ const repository = await resolveRepositoryRoot(cwd);
700
+ if (repository.kind === "git_unavailable")
701
+ return repository;
702
+ if (repository.kind === "plain") {
703
+ const { root, result } = await readPlainIdentity(repository.root);
704
+ const base2 = withBase(repository, root, []);
705
+ if (result.kind === "ok")
706
+ return { ...base2, ...result };
707
+ if (result.kind === "unknown_version")
708
+ return { ...base2, ...result, path: root };
709
+ if (result.kind === "malformed")
710
+ return { ...base2, ...result, path: root };
711
+ return { ...base2, kind: "absent" };
712
+ }
713
+ const ownRoot = repository.isLinkedWorktree ? dirname2(repository.commonDir) : repository.root;
714
+ const outer = repository.isLinkedWorktree ? void 0 : await containingRepository(repository.root);
715
+ const own = await readIdentityFile(ownRoot);
716
+ if (!outer) {
717
+ const base2 = withBase(repository, ownRoot, repository.nested);
718
+ if (own.kind === "ok")
719
+ return { ...base2, ...own };
720
+ if (own.kind === "unknown_version")
721
+ return { ...base2, ...own, path: ownRoot };
722
+ if (own.kind === "malformed")
723
+ return { ...base2, ...own, path: ownRoot };
724
+ return { ...base2, kind: "absent" };
725
+ }
726
+ const nested = repository.nested;
727
+ const parentRoot = await canonicalBoundaryRoot(outer);
728
+ const parent = await readIdentityFile(parentRoot);
729
+ const base = withBase(repository, parentRoot, nested);
730
+ if (own.kind === "unknown_version")
731
+ return { ...base, ...own, path: ownRoot };
732
+ if (own.kind === "malformed")
733
+ return { ...base, ...own, path: ownRoot };
734
+ if (parent.kind === "unknown_version")
735
+ return { ...base, ...parent, path: parentRoot };
736
+ if (parent.kind === "malformed")
737
+ return { ...base, ...parent, path: parentRoot };
738
+ if (own.kind === "ok" && parent.kind === "ok") {
739
+ return {
740
+ ...base,
741
+ kind: "conflict",
742
+ rootIdentity: parent.identity,
743
+ nestedIdentity: own.identity
744
+ };
745
+ }
746
+ if (own.kind === "ok") {
747
+ return { ...withBase(repository, ownRoot, nested), kind: "ok", identity: own.identity };
748
+ }
749
+ if (!options.allowNestedInherit) {
750
+ return {
751
+ ...base,
752
+ kind: "nested",
753
+ ...parent.kind === "ok" ? { parentIdentity: parent.identity } : {}
754
+ };
755
+ }
756
+ if (parent.kind === "ok")
757
+ return { ...base, kind: "ok", identity: parent.identity };
758
+ return { ...base, kind: "absent" };
759
+ }
760
+ var GIT_TIMEOUT_MS, GIT_ENV_KEYS;
761
+ var init_repositoryRoot = __esm({
762
+ "packages/shared/dist/repositoryRoot.js"() {
763
+ "use strict";
764
+ init_projectIdentityFile();
765
+ GIT_TIMEOUT_MS = 2e3;
766
+ GIT_ENV_KEYS = [
767
+ "PATH",
768
+ "HOME",
769
+ "LANG",
770
+ "LC_ALL",
771
+ "SYSTEMROOT",
772
+ "TEMP",
773
+ "TMP",
774
+ "USERPROFILE"
775
+ ];
776
+ }
777
+ });
778
+
779
+ // packages/shared/dist/runtimeReader.js
780
+ import { createHash as createHash2 } from "node:crypto";
781
+ import { constants } from "node:fs";
782
+ import { lstat as lstat3, open, readdir } from "node:fs/promises";
783
+ import { dirname as dirname3, join as join4, parse, resolve as resolve3, sep as sep2 } from "node:path";
784
+ function safePath(path) {
785
+ if (typeof path !== "string" || !/^[a-zA-Z0-9_@.+/-]+$/.test(path) || path.split("/").some((p) => !p || p === "." || p === "..") || path.includes(":"))
786
+ throw new RuntimeError("permission");
787
+ return path;
788
+ }
789
+ var RuntimeError, hash, missing, versionName, statIdentity, RuntimeReader;
790
+ var init_runtimeReader = __esm({
791
+ "packages/shared/dist/runtimeReader.js"() {
792
+ "use strict";
793
+ init_runtimeSigners();
794
+ init_runtimeSigners();
795
+ RuntimeError = class extends Error {
796
+ reason;
797
+ constructor(reason) {
798
+ super(reason);
799
+ this.reason = reason;
800
+ this.name = "RuntimeError";
801
+ }
802
+ };
803
+ hash = (bytes) => createHash2("sha256").update(bytes).digest("hex");
804
+ missing = (e) => e.code === "ENOENT";
805
+ versionName = (v) => {
806
+ if (!/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(v))
807
+ throw new RuntimeError("permission");
808
+ return v;
809
+ };
810
+ statIdentity = (stat) => [stat.dev, stat.ino, stat.mode, stat.uid, stat.size, stat.mtimeMs, stat.ctimeMs].join(":");
811
+ RuntimeReader = class _RuntimeReader {
812
+ run;
813
+ options;
814
+ now;
815
+ state;
816
+ static windowsSignatures = /* @__PURE__ */ new Map();
817
+ // Omitted batch verdicts use the per-path fallback before entering the cache.
818
+ windowsAudit = /* @__PURE__ */ new Map();
819
+ windowsStats = /* @__PURE__ */ new Map();
820
+ windowsDirectories = /* @__PURE__ */ new Set();
821
+ audits = Promise.resolve();
822
+ auditsInFlight = /* @__PURE__ */ new Map();
823
+ activeOperation = false;
824
+ operations = Promise.resolve();
825
+ constructor(state, run, options = {}, now = () => performance.now()) {
826
+ this.run = run;
827
+ this.options = options;
828
+ this.now = now;
829
+ this.state = resolve3(state);
830
+ }
831
+ pointerPath(artifact) {
832
+ if (!["cli", "scanner", "claude-code", "codex", "cursor", "grok"].includes(artifact))
833
+ throw new RuntimeError("permission");
834
+ return join4(this.state, "runtimes", artifact, "current");
835
+ }
836
+ guardedPath(path) {
837
+ const absolute = resolve3(path);
838
+ if (absolute !== this.state && !absolute.startsWith(this.state + sep2) || process.platform === "win32" && /[\r\n\t]/.test(absolute))
839
+ throw new RuntimeError("permission");
840
+ return absolute;
841
+ }
842
+ auditRoot(path) {
843
+ const [area, artifact] = path.slice(this.state.length + 1).split(sep2);
844
+ return area === "runtimes" ? join4(this.state, area, ...artifact ? [artifact] : []) : path;
845
+ }
846
+ traceAudit(event) {
847
+ const level = process.env.MNEMONIK_AUDIT_TRACE;
848
+ if (level !== "1" && (level !== "misses" || event.cache === "hit"))
849
+ return;
850
+ process.stderr.write(JSON.stringify({
851
+ type: "runtime-audit",
852
+ pid: process.pid,
853
+ module: import.meta.url,
854
+ activeOperation: this.activeOperation,
855
+ ...event
856
+ }) + "\n");
857
+ }
858
+ invalidateAudit(path) {
859
+ const root = this.auditRoot(path);
860
+ for (const key of this.windowsAudit.keys())
861
+ if (key === root || (!this.options.cacheDirectories || root.startsWith(join4(this.state, "runtimes") + sep2)) && key.startsWith(root + sep2))
862
+ this.windowsAudit.delete(key);
863
+ }
864
+ audit(paths) {
865
+ const root = this.auditRoot(paths.at(-1) ?? this.state);
866
+ const pending = this.auditsInFlight.get(root);
867
+ if (pending)
868
+ return pending;
869
+ const audit = this.audits.then(() => this.auditPending(paths)).finally(() => this.auditsInFlight.delete(root));
870
+ this.auditsInFlight.set(root, audit);
871
+ this.audits = audit.catch(() => {
872
+ });
873
+ return audit;
874
+ }
875
+ async auditPending(paths) {
876
+ if (process.platform !== "win32")
877
+ return;
878
+ const pending = /* @__PURE__ */ new Set();
879
+ for (const path of paths) {
880
+ let part = this.guardedPath(path);
881
+ while (true) {
882
+ const cached = this.windowsAudit.get(part);
883
+ const expired = !!cached && !this.activeOperation && !(this.options.cacheDirectories && this.windowsDirectories.has(part)) && this.now() - cached.checkedAt >= 2e3;
884
+ this.traceAudit({
885
+ event: "cache",
886
+ requested: path,
887
+ path: part,
888
+ root: this.auditRoot(path),
889
+ cache: !cached ? "miss" : expired ? "ttl-expired" : "hit"
890
+ });
891
+ if (!cached || expired)
892
+ pending.add(part);
893
+ if (part === this.state)
894
+ break;
895
+ part = dirname3(part);
896
+ }
897
+ }
898
+ if (!pending.size)
899
+ return;
900
+ try {
901
+ const root = this.auditRoot(paths.at(-1) ?? this.state);
902
+ const ancestors = [...pending].filter((path) => path !== root && !path.startsWith(root + sep2));
903
+ const tree = [...pending].filter((path) => !ancestors.includes(path));
904
+ for (const part of [...ancestors, ...tree.length ? [root] : []]) {
905
+ const recursive = part === root && root.startsWith(join4(this.state, "runtimes") + sep2);
906
+ this.traceAudit({ event: "audit", root: part, recursive });
907
+ const requested = part === root ? tree : [part];
908
+ if (this.options.cacheDirectories && !this.windowsDirectories.has(part) && this.windowsAudit.get(part)?.verdict === "ok") {
909
+ await verifyWindowsAcl(part, this.run, this.state);
910
+ this.windowsAudit.set(part, { verdict: "ok", checkedAt: this.now() });
911
+ continue;
912
+ }
913
+ const verdicts = await auditWindowsPermissions(part, requested, this.run, recursive, this.state);
914
+ const checkedAt = this.now();
915
+ for (const path of /* @__PURE__ */ new Set([...requested, ...verdicts.keys()])) {
916
+ let verdict = verdicts.get(path);
917
+ if (verdict === void 0) {
918
+ await verifyWindowsPermission(path, this.run, this.state);
919
+ verdict = "ok";
920
+ }
921
+ this.windowsAudit.set(path, { verdict, checkedAt });
922
+ }
923
+ }
924
+ } catch (error) {
925
+ throw new RuntimeError(error instanceof Error && error.message === "acl_permissions" ? "permission" : "acl_unavailable");
926
+ }
927
+ }
928
+ async inspect(path, directory = false, allowMissing = false, replacing = false) {
929
+ const absolute = this.guardedPath(path);
930
+ const windowsPaths = [];
931
+ let part = parse(absolute).root;
932
+ for (const name of absolute.slice(part.length).split(sep2)) {
933
+ part = join4(part, name);
934
+ const stat = await lstat3(part).catch((e) => {
935
+ if (allowMissing && missing(e))
936
+ return null;
937
+ throw e;
938
+ });
939
+ if (!stat)
940
+ break;
941
+ if (stat.isSymbolicLink() || (part === absolute ? directory ? !stat.isDirectory() : !stat.isFile() : !stat.isDirectory()))
942
+ throw new RuntimeError("permission");
943
+ if (part !== this.state && !part.startsWith(this.state + sep2))
944
+ continue;
945
+ if (process.platform === "win32") {
946
+ if (replacing && part === absolute)
947
+ continue;
948
+ if (part === this.state && !this.windowsStats.has(part)) {
949
+ await prepareWindowsAclDirectory(this.state, this.run).catch((error) => {
950
+ throw new RuntimeError(error instanceof Error && error.message === "acl_permissions" ? "permission" : "acl_unavailable");
951
+ });
952
+ Object.assign(stat, await lstat3(part));
953
+ }
954
+ if (stat.isDirectory())
955
+ this.windowsDirectories.add(part);
956
+ const identity = statIdentity(stat);
957
+ const previous = this.windowsStats.get(part);
958
+ if (previous !== void 0 && previous !== identity) {
959
+ this.traceAudit({
960
+ event: "invalidate",
961
+ requested: absolute,
962
+ path: part,
963
+ root: this.auditRoot(part),
964
+ reason: "stat-identity",
965
+ previous,
966
+ current: identity
967
+ });
968
+ this.invalidateAudit(part);
969
+ }
970
+ this.windowsStats.set(part, identity);
971
+ windowsPaths.push(part);
972
+ } else if (stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
973
+ throw new RuntimeError("permission");
974
+ }
975
+ await this.audit(windowsPaths);
976
+ for (const part2 of windowsPaths) {
977
+ const verdict = this.windowsAudit.get(part2)?.verdict;
978
+ if (verdict !== "ok")
979
+ throw new RuntimeError(verdict === void 0 || verdict === "acl_unavailable" ? "acl_unavailable" : "permission");
980
+ }
981
+ }
982
+ /** Call only after an owned atomic replacement: its parent ctime is our own write. */
983
+ async recordReplacement(path) {
984
+ if (process.platform !== "win32" || !this.options.cacheDirectories)
985
+ return;
986
+ const absolute = this.guardedPath(path);
987
+ const parent = dirname3(absolute);
988
+ const stat = await lstat3(parent);
989
+ if (!stat.isDirectory() || stat.isSymbolicLink() || !this.windowsStats.get(parent)?.startsWith(`${stat.dev}:${stat.ino}:`))
990
+ throw new RuntimeError("permission");
991
+ this.windowsStats.set(parent, statIdentity(stat));
992
+ this.windowsAudit.delete(absolute);
993
+ this.windowsStats.delete(absolute);
994
+ }
995
+ async bytes(path) {
996
+ await this.inspect(path);
997
+ const fd = await open(path, constants.O_RDONLY | (process.platform === "win32" ? 0 : constants.O_NOFOLLOW));
998
+ try {
999
+ return await fd.readFile();
1000
+ } finally {
1001
+ await fd.close();
1002
+ }
1003
+ }
1004
+ /** An owned inheritable ACL update can change every cached descendant's ctime. */
1005
+ async recordAclWrite(path) {
1006
+ const root = this.guardedPath(path);
1007
+ for (const known of this.windowsStats.keys())
1008
+ if (known === root || known.startsWith(root + sep2))
1009
+ this.windowsStats.set(known, statIdentity(await lstat3(known)));
1010
+ this.invalidateAudit(root);
1011
+ }
1012
+ async pointer(artifact) {
1013
+ try {
1014
+ const p = JSON.parse((await this.bytes(this.pointerPath(artifact))).toString());
1015
+ for (const ref of [p.current, ...p.previous ? [p.previous] : []]) {
1016
+ versionName(ref.version);
1017
+ if (!/^[a-f0-9]{64}$/.test(ref.manifestSha256))
1018
+ throw new RuntimeError("digest_mismatch");
1019
+ }
1020
+ return p;
1021
+ } catch (e) {
1022
+ if (missing(e))
1023
+ return void 0;
1024
+ throw e instanceof RuntimeError ? e : new RuntimeError("digest_mismatch");
1025
+ }
1026
+ }
1027
+ async operation(work) {
1028
+ const operation = this.operations.then(async () => {
1029
+ this.activeOperation = true;
1030
+ try {
1031
+ return await work();
1032
+ } finally {
1033
+ this.activeOperation = false;
1034
+ }
1035
+ });
1036
+ this.operations = operation.catch(() => void 0);
1037
+ return operation;
1038
+ }
1039
+ verifyAt(artifact, ref, directory) {
1040
+ return this.operation(() => this.verifyOpen(artifact, ref, directory));
1041
+ }
1042
+ async verifyOpen(artifact, ref, directory) {
1043
+ let bytes;
1044
+ try {
1045
+ bytes = await this.bytes(join4(directory, "manifest.json"));
1046
+ } catch (e) {
1047
+ if (missing(e))
1048
+ throw new RuntimeError("manifest_missing");
1049
+ throw e;
1050
+ }
1051
+ if (hash(bytes) !== ref.manifestSha256)
1052
+ throw new RuntimeError("digest_mismatch");
1053
+ let m;
1054
+ try {
1055
+ m = JSON.parse(bytes.toString());
1056
+ } catch {
1057
+ throw new RuntimeError("digest_mismatch");
1058
+ }
1059
+ if (!m || m.schemaVersion !== 1 || !m.source || m.artifact !== artifact || m.version !== ref.version || !m.files || !m.files[safePath(m.entry)])
1060
+ throw new RuntimeError("digest_mismatch");
1061
+ if (artifact === "scanner" && (m.source.kind !== "release" || typeof m.source.url !== "string" || !m.source.url.startsWith("https://") || (!m.signer || m.signer.platform !== process.platform) && !(this.options.allowUnsigned && !m.signer && m.signingStatus === "unsigned")))
1062
+ throw new RuntimeError("unsigned");
1063
+ if (artifact === "cli" && (m.source.kind !== "npm" || !Array.isArray(m.source.packages) || !m.source.packages.some((p) => p.name === "@mnemonik/cli" && p.version === m.version)))
1064
+ throw new RuntimeError("unsigned");
1065
+ if (process.platform === "win32")
1066
+ await this.audit(Object.keys(m.files).map((name) => join4(directory, safePath(name))));
1067
+ const actual = [];
1068
+ const walk = async (dir, prefix = "") => {
1069
+ await this.inspect(dir, true);
1070
+ for (const name of await readdir(dir)) {
1071
+ const key = prefix + name;
1072
+ if (!prefix && name === "manifest.json")
1073
+ continue;
1074
+ const path = join4(dir, name);
1075
+ if ((await lstat3(path)).isDirectory())
1076
+ await walk(path, key + "/");
1077
+ else {
1078
+ await this.inspect(path);
1079
+ actual.push(key);
1080
+ }
1081
+ }
1082
+ };
1083
+ await walk(directory);
1084
+ if (actual.sort().join("\n") !== Object.keys(m.files).sort().join("\n"))
1085
+ throw new RuntimeError("digest_mismatch");
1086
+ let total = 0;
1087
+ for (const [name, expected] of Object.entries(m.files)) {
1088
+ if (!expected || !/^[a-f0-9]{64}$/.test(expected.sha256) || !Number.isSafeInteger(expected.size) || expected.size < 0 || typeof expected.executable !== "boolean")
1089
+ throw new RuntimeError("digest_mismatch");
1090
+ const path = join4(directory, safePath(name));
1091
+ const contents = await this.bytes(path);
1092
+ if (hash(contents) !== expected.sha256 || contents.length !== expected.size)
1093
+ throw new RuntimeError("digest_mismatch");
1094
+ if (process.platform !== "win32" && ((await lstat3(path)).mode & 511) !== (expected.executable ? 448 : 384))
1095
+ throw new RuntimeError("permission");
1096
+ total += contents.length;
1097
+ }
1098
+ if (total !== m.totalSize)
1099
+ throw new RuntimeError("digest_mismatch");
1100
+ if (m.signer) {
1101
+ const signer = m.signer.platform === "linux" ? { ...m.signer, signature: join4(directory, safePath(m.signer.signature)) } : m.signer;
1102
+ if (m.signer.platform === "linux" && !m.files[m.signer.signature])
1103
+ throw new RuntimeError("unsigned");
1104
+ try {
1105
+ const entry = join4(directory, m.entry);
1106
+ if (signer.platform === "win32") {
1107
+ const key = JSON.stringify([entry, m.files[m.entry]?.sha256, signer.identity]);
1108
+ let verification = _RuntimeReader.windowsSignatures.get(key);
1109
+ if (!verification) {
1110
+ verification = verifySigner(entry, signer, this.run).catch((error) => {
1111
+ _RuntimeReader.windowsSignatures.delete(key);
1112
+ throw error;
1113
+ });
1114
+ _RuntimeReader.windowsSignatures.set(key, verification);
1115
+ }
1116
+ await verification;
1117
+ } else
1118
+ await verifySigner(entry, signer, this.run);
1119
+ } catch {
1120
+ throw new RuntimeError("unsigned");
1121
+ }
1122
+ }
1123
+ return { directory, manifest: m, entry: join4(directory, m.entry), reference: ref };
1124
+ }
1125
+ async verifyRuntime(artifact) {
1126
+ return this.operation(async () => {
1127
+ const p = await this.pointer(artifact);
1128
+ if (!p)
1129
+ throw new RuntimeError("manifest_missing");
1130
+ return this.verifyOpen(artifact, p.current, join4(dirname3(this.pointerPath(artifact)), p.current.version));
1131
+ });
1132
+ }
1133
+ };
1134
+ }
1135
+ });
1136
+
1137
+ // packages/shared/dist/projectSetupHandoff.js
1138
+ import { execFile as execFile3 } from "node:child_process";
1139
+ import { constants as constants2 } from "node:fs";
1140
+ import { lstat as lstat4, mkdir, open as open2, readdir as readdir2, realpath, rename, rm } from "node:fs/promises";
1141
+ import { tmpdir } from "node:os";
1142
+ import { dirname as dirname4, join as join5, relative } from "node:path";
1143
+ import { randomUUID as randomUUID2 } from "node:crypto";
1144
+ function projectSetupActions(value) {
1145
+ return Array.isArray(value) ? value.slice(0, 20).map(words).join(", ") : "retry, cancel";
1146
+ }
1147
+ function setupResponse(value) {
1148
+ if (typeof value === "string") {
1149
+ if (value.length > 64 * 1024)
1150
+ return;
1151
+ try {
1152
+ value = JSON.parse(value);
1153
+ } catch {
1154
+ return;
1155
+ }
1156
+ }
1157
+ if (!record(value))
1158
+ return;
1159
+ if (record(value.structuredContent))
1160
+ value = value.structuredContent;
1161
+ else {
1162
+ const keys = ["status", "state", "allowedActions", "requestId", "expiresAt"];
1163
+ const envelope = value;
1164
+ if (!keys.every((key) => Object.hasOwn(envelope, key)) || Object.keys(value).some((key) => ![...keys, "manualAction"].includes(key)) || typeof value.state !== "string" || !Array.isArray(value.allowedActions) || !value.allowedActions.every((action) => typeof action === "string") || typeof value.requestId !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value.requestId) || typeof value.expiresAt !== "string" || !Number.isFinite(Date.parse(value.expiresAt)) || Object.hasOwn(value, "manualAction") && typeof value.manualAction !== "string")
1165
+ return;
1166
+ }
1167
+ if (record(value) && value.status === "project_setup_required")
1168
+ return value;
1169
+ return;
1170
+ }
1171
+ async function privateDirectory(path, session) {
1172
+ const created = await mkdir(path, { recursive: true, mode: 448 });
1173
+ const st = await lstat4(path);
1174
+ if (!st.isDirectory() || st.isSymbolicLink() || process.getuid && (st.uid !== process.getuid() || st.mode & 63))
1175
+ throw new Error("diagnostic_unavailable");
1176
+ if (process.platform === "win32") {
1177
+ await protectWindowsDirectory(path, created !== void 0, void 0, session);
1178
+ }
1179
+ }
1180
+ async function readPrivate(path) {
1181
+ const file = await open2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW);
1182
+ try {
1183
+ const st = await file.stat();
1184
+ if (!st.isFile() || st.size > 64 * 1024 || process.getuid && (st.uid !== process.getuid() || st.mode & 63))
1185
+ throw new Error("diagnostic_unavailable");
1186
+ return await file.readFile("utf8");
1187
+ } finally {
1188
+ await file.close();
1189
+ }
1190
+ }
1191
+ async function writeDiagnostic(path, value) {
1192
+ await privateDirectory(dirname4(dirname4(path)), dirname4(path));
1193
+ await privateDirectory(dirname4(path), dirname4(path));
1194
+ const temp = `${path}.${randomUUID2()}`;
1195
+ try {
1196
+ const file = await open2(temp, "wx", 384);
1197
+ try {
1198
+ await file.writeFile(JSON.stringify(value) + "\n");
1199
+ } finally {
1200
+ await file.close();
1201
+ }
1202
+ await rename(temp, path);
1203
+ } finally {
1204
+ await rm(temp, { force: true });
1205
+ }
1206
+ }
1207
+ async function handoffProjectSetup(input) {
1208
+ const response = setupResponse(input.response);
1209
+ if (!response)
1210
+ return;
1211
+ let root;
1212
+ let action = PROJECT_SETUP_MANUAL;
1213
+ let outcome = "failed";
1214
+ let reason = "manual_setup_required";
1215
+ const requestId = typeof response.requestId === "string" && response.requestId.length <= 4096 ? response.requestId : void 0;
1216
+ try {
1217
+ if (!input.cwd)
1218
+ throw new Error("safe_cwd_unavailable");
1219
+ const cache = JSON.parse(await readPrivate(join5(dirname4(input.stateFile), "bound-context.json")));
1220
+ const [, family, body] = JSON.parse(cache.key);
1221
+ if (!input.familyId || family !== input.familyId || body.host !== input.host || body.hostSessionId !== input.hostSessionId || cache.expiresAt <= Date.now() || !cache.canonicalRoot)
1222
+ throw new Error("bound_context_unavailable");
1223
+ root = await realpath(cache.canonicalRoot);
1224
+ const { findProjectIdentityDetailed: findProjectIdentityDetailed2 } = await Promise.resolve().then(() => (init_hookRuntime(), hookRuntime_exports));
1225
+ const identity = await findProjectIdentityDetailed2(await realpath(input.cwd));
1226
+ if (identity.kind === "git_unavailable" || root !== await realpath(identity.root))
1227
+ throw new Error("bound_root_changed");
1228
+ if (!requestId)
1229
+ throw new Error("manual_setup_required");
1230
+ const runtime = await new RuntimeReader(input.stateDir).verifyRuntime("cli").catch(() => {
1231
+ throw new Error("verified_cli_runtime_unavailable");
1232
+ });
1233
+ const bin = join5(dirname4(runtime.entry), "bin.js");
1234
+ if (!runtime.manifest.files[relative(runtime.directory, bin).replaceAll("\\", "/")])
1235
+ throw new Error("verified_cli_bin_missing");
1236
+ const result = await new Promise((resolve4, reject2) => {
1237
+ const child = execFile3(process.execPath, [bin, "project", "ensure", "--agent", "--json"], {
1238
+ cwd: root,
1239
+ shell: false,
1240
+ timeout: 3e4,
1241
+ killSignal: "SIGKILL",
1242
+ maxBuffer: 64 * 1024,
1243
+ // Runtime injection variables and repository/PATH executables are not inherited.
1244
+ env: Object.fromEntries([
1245
+ "HOME",
1246
+ "USERPROFILE",
1247
+ "LOCALAPPDATA",
1248
+ "XDG_STATE_HOME",
1249
+ "MNEMONIK_STATE_DIR",
1250
+ "MNEMONIK_SERVER",
1251
+ "MNEMONIK_API_RESOURCE",
1252
+ "MNEMONIK_OAUTH_ISSUER",
1253
+ "SYSTEMROOT",
1254
+ "TEMP",
1255
+ "TMP",
1256
+ "TMPDIR"
1257
+ ].flatMap((key) => process.env[key] === void 0 ? [] : [[key, process.env[key]]]))
1258
+ }, (error, stdout2) => {
1259
+ if (error && (error.killed || error.code !== 3))
1260
+ return reject2(new Error("helper_failed"));
1261
+ try {
1262
+ resolve4(JSON.parse(stdout2));
1263
+ } catch {
1264
+ reject2(new Error("helper_invalid_json"));
1265
+ }
1266
+ });
1267
+ child.stdin?.on("error", () => {
1268
+ });
1269
+ child.stdin?.end(JSON.stringify({ requestId }));
1270
+ });
1271
+ if (!record(result))
1272
+ throw new Error("helper_invalid_json");
1273
+ if (result.status === "done") {
1274
+ outcome = "done";
1275
+ reason = "complete";
1276
+ action = "Mnemonik project setup is complete; call session_bootstrap again.";
1277
+ } else if (["ACTION_REQUIRED", "action_required", "project_setup_required"].includes(String(result.status))) {
1278
+ outcome = "ACTION_REQUIRED";
1279
+ reason = "choice_required";
1280
+ action = result.action === "mnemonik auth renew" || Array.isArray(result.allowedActions) && result.allowedActions.includes("mnemonik auth renew") ? "mnemonik auth renew" : result.allowedActions ? `Mnemonik project setup needs attention; allowed actions: ${projectSetupActions(result.allowedActions)}.` : PROJECT_SETUP_MANUAL;
1281
+ } else
1282
+ throw new Error("helper_invalid_json");
1283
+ } catch (error) {
1284
+ const code = error instanceof Error ? error.message : "";
1285
+ reason = /^[a-z_]+$/.test(code) ? code : "helper_unavailable";
1286
+ process.stderr.write(`mnemonik-hook: project setup ${reason}; manual action required
1287
+ `);
1288
+ }
1289
+ try {
1290
+ await writeDiagnostic(input.stateFile, {
1291
+ requestId,
1292
+ outcome,
1293
+ time: (/* @__PURE__ */ new Date()).toISOString(),
1294
+ rootHash: root ? hash(root) : input.cwd ? hash(await realpath(input.cwd)) : null,
1295
+ action,
1296
+ reason
1297
+ });
1298
+ } catch {
1299
+ process.stderr.write("mnemonik-hook: project setup diagnostic unavailable\n");
1300
+ }
1301
+ return action;
1302
+ }
1303
+ async function pendingProjectSetup(root) {
1304
+ const rootHash = hash(await realpath(root));
1305
+ const results = [];
1306
+ const uid = process.getuid?.() ?? "user";
1307
+ for (const host of ["credit", "cursor", "grok"]) {
1308
+ const directory = join5(tmpdir(), `mnemonik-${host}-${uid}`);
1309
+ try {
1310
+ const stat = await lstat4(directory);
1311
+ if (stat.isSymbolicLink() || !stat.isDirectory())
1312
+ continue;
1313
+ for (const name of await readdir2(directory)) {
1314
+ if (!/^[a-f0-9]{64}$/.test(name))
1315
+ continue;
1316
+ try {
1317
+ if ((await lstat4(join5(directory, name))).isSymbolicLink())
1318
+ continue;
1319
+ const diagnostic = JSON.parse(await readPrivate(join5(directory, name, "project-setup.json")));
1320
+ if ((diagnostic.rootHash === rootHash || diagnostic.rootHash === null) && ["done", "ACTION_REQUIRED", "failed"].includes(diagnostic.outcome) && typeof diagnostic.action === "string" && diagnostic.action.length < 2e3 && Date.now() - Date.parse(diagnostic.time) < 24 * 60 * 60 * 1e3)
1321
+ results.push(diagnostic);
1322
+ } catch {
1323
+ }
1324
+ }
1325
+ } catch {
1326
+ }
1327
+ }
1328
+ results.sort((a, b) => Date.parse(b.time) - Date.parse(a.time));
1329
+ return results[0]?.outcome === "done" ? [] : results.slice(0, 1);
1330
+ }
1331
+ var PROJECT_SETUP_MANUAL, words, record;
1332
+ var init_projectSetupHandoff = __esm({
1333
+ "packages/shared/dist/projectSetupHandoff.js"() {
1334
+ "use strict";
1335
+ init_runtimeSigners();
1336
+ init_runtimeReader();
1337
+ PROJECT_SETUP_MANUAL = "Mnemonik project setup needs attention; ask the person to run `mnemonik project init` in this folder.";
1338
+ words = (value) => typeof value === "string" && /^[a-z][a-z0-9_]{0,79}$/.test(value) ? value.replaceAll("_", " ") : "needs attention";
1339
+ record = (value) => !!value && typeof value === "object" && !Array.isArray(value);
1340
+ }
1341
+ });
1342
+
1343
+ // packages/shared/dist/repositoryFingerprint.js
1344
+ var repositoryFingerprint_exports = {};
1345
+ __export(repositoryFingerprint_exports, {
1346
+ REPOSITORY_PROVIDER_RULES: () => REPOSITORY_PROVIDER_RULES,
1347
+ canonicalizeRemote: () => canonicalizeRemote,
1348
+ selectRemote: () => selectRemote
1349
+ });
1350
+ import { createHash as createHash3 } from "node:crypto";
1351
+ import { domainToASCII } from "node:url";
1352
+ function canonicalizeRemote(url, { providerRules = REPOSITORY_PROVIDER_RULES } = {}) {
1353
+ if (controls.test(url))
1354
+ return reject("control_character");
1355
+ if (url.includes("\\"))
1356
+ return reject("backslash");
1357
+ if (/^file:/i.test(url))
1358
+ return reject("local_remote");
1359
+ const input = url.split(/[?#]/, 1)[0] ?? "";
1360
+ const scheme = /^([a-z][a-z0-9+.-]*):\/\//i.exec(input);
1361
+ let transport;
1362
+ let authority;
1363
+ let path;
1364
+ if (scheme) {
1365
+ const protocol = (scheme[1] ?? "").toLowerCase();
1366
+ if (protocol !== "https" && protocol !== "ssh")
1367
+ return reject("unsupported_transport");
1368
+ transport = protocol;
1369
+ const parts = /^([^/]*)(?:\/(.*))?$/.exec(input.slice(scheme[0].length));
1370
+ if (!parts)
1371
+ return reject("invalid_remote");
1372
+ authority = parts[1] ?? "";
1373
+ path = parts[2] ?? "";
1374
+ } else {
1375
+ const scp = /^((?:[^@/:]+@)?(?:\[[^\]]+\]|[^/:]+)):(.*)$/.exec(input);
1376
+ if (!scp)
1377
+ return reject("invalid_remote");
1378
+ transport = "ssh";
1379
+ authority = scp[1] ?? "";
1380
+ path = scp[2] ?? "";
1381
+ }
1382
+ const at = authority.lastIndexOf("@");
1383
+ let user = "";
1384
+ if (at >= 0) {
1385
+ if (transport === "https")
1386
+ return reject("https_userinfo");
1387
+ user = authority.slice(0, at);
1388
+ if (user.includes(":"))
1389
+ return reject("ssh_password");
1390
+ if (!/^[A-Za-z0-9._~!$&'()*+,;=-]+$/.test(user))
1391
+ return reject("invalid_ssh_user");
1392
+ authority = authority.slice(at + 1);
1393
+ }
1394
+ if (transport === "ssh" && !user)
1395
+ return reject("ssh_user_required");
1396
+ const hostPort = /^(\[[^\]]+\]|[^:[\]]+)(?::([0-9]+))?$/.exec(authority);
1397
+ if (!hostPort)
1398
+ return reject(authority.includes(":") ? "invalid_port" : "invalid_host");
1399
+ let host = hostPort[1] ?? "";
1400
+ let port = hostPort[2] ?? "";
1401
+ if (port && (Number(port) < 1 || Number(port) > 65535))
1402
+ return reject("invalid_port");
1403
+ if (port)
1404
+ port = String(Number(port));
1405
+ if (port === (transport === "https" ? "443" : "22"))
1406
+ port = "";
1407
+ if (/[\s%/@?#]/u.test(host))
1408
+ return reject("invalid_host");
1409
+ try {
1410
+ host = host.startsWith("[") ? new URL(`https://${host}/`).hostname : domainToASCII(host).toLowerCase();
1411
+ if (!host)
1412
+ return reject("invalid_host");
1413
+ } catch {
1414
+ return reject("invalid_host");
1415
+ }
1416
+ const rule = providerRules.find((rule2) => rule2.host === host && !port && (transport === "https" || user === rule2.sshUser));
1417
+ if (!scheme && !rule)
1418
+ return reject("scp_unreviewed_remote");
1419
+ if (!scheme && path.startsWith("/"))
1420
+ path = path.slice(1);
1421
+ if (path.endsWith("/"))
1422
+ path = path.slice(0, -1);
1423
+ const segments = [];
1424
+ for (const raw of path.split("/")) {
1425
+ if (/%(?:2f|5c)/i.test(raw))
1426
+ return reject("escaped_separator");
1427
+ let segment;
1428
+ try {
1429
+ segment = decodeURIComponent(raw).normalize("NFC");
1430
+ } catch {
1431
+ return reject("invalid_encoding");
1432
+ }
1433
+ if (controls.test(segment))
1434
+ return reject("control_character");
1435
+ if (segment.includes("\\"))
1436
+ return reject("backslash");
1437
+ if (!segment)
1438
+ return reject("empty_segment");
1439
+ if (segment === "." || segment === "..")
1440
+ return reject("dot_segment");
1441
+ try {
1442
+ segments.push(encodeSegment(segment));
1443
+ } catch {
1444
+ return reject("invalid_encoding");
1445
+ }
1446
+ }
1447
+ const parsed = { transport, host, user, port, path: segments.join("/") };
1448
+ if (rule?.removeTerminalDotGit) {
1449
+ parsed.path = parsed.path.replace(/\.git$/, "");
1450
+ const last = parsed.path.split("/").at(-1);
1451
+ if (!last)
1452
+ return reject("empty_segment");
1453
+ if (last === "." || last === "..")
1454
+ return reject("dot_segment");
1455
+ }
1456
+ const prefix = rule ? `${rule.name}:${host}` : `${transport}:${transport === "ssh" ? `${user}@` : ""}${host}${port ? `:${port}` : ""}`;
1457
+ const canonical = `repo-v1:${prefix}/${parsed.path}`;
1458
+ return {
1459
+ canonical,
1460
+ hash: createHash3("sha256").update(canonical, "utf8").digest("hex"),
1461
+ algorithmVersion: 1
1462
+ };
1463
+ }
1464
+ function selectRemote(remotes) {
1465
+ if (!remotes.length)
1466
+ return { status: "none", reason: "no_remote" };
1467
+ const origins = remotes.filter((remote) => remote.name === "origin");
1468
+ const selected = origins.length === 1 ? origins[0] : remotes.length === 1 ? remotes[0] : void 0;
1469
+ if (!selected)
1470
+ return {
1471
+ status: "choice_required",
1472
+ reason: "multiple_remotes",
1473
+ remotes: remotes.map((r) => r.name)
1474
+ };
1475
+ if (!selected.fetchUrls.length)
1476
+ return { status: "none", reason: "no_fetch_url" };
1477
+ const results = [...selected.fetchUrls, ...selected.pushUrls ?? []].map((url) => canonicalizeRemote(url));
1478
+ const first = results[0];
1479
+ if (!first)
1480
+ return { status: "none", reason: "no_fetch_url" };
1481
+ if (results.length === 1 && "reason" in first)
1482
+ return { status: "rejected", remote: selected.name, reason: first.reason };
1483
+ if ("reason" in first || results.some((result) => "reason" in result || result.canonical !== first.canonical)) {
1484
+ return { status: "choice_required", reason: "different_urls", remotes: [selected.name] };
1485
+ }
1486
+ return { status: "fingerprint", remote: selected.name, fingerprint: first };
1487
+ }
1488
+ var REPOSITORY_PROVIDER_RULES, controls, reject, encodeSegment;
1489
+ var init_repositoryFingerprint = __esm({
1490
+ "packages/shared/dist/repositoryFingerprint.js"() {
1491
+ "use strict";
1492
+ REPOSITORY_PROVIDER_RULES = Object.freeze([
1493
+ Object.freeze({ name: "github", host: "github.com", sshUser: "git", removeTerminalDotGit: true })
1494
+ ]);
1495
+ controls = new RegExp("\\p{Cc}", "u");
1496
+ reject = (reason) => ({ status: "rejected", reason });
1497
+ encodeSegment = (segment) => encodeURIComponent(segment).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
1498
+ }
1499
+ });
1500
+
1501
+ // packages/shared/dist/hookRuntime.js
1502
+ var hookRuntime_exports = {};
1503
+ __export(hookRuntime_exports, {
1504
+ PROJECT_SETUP_MANUAL: () => PROJECT_SETUP_MANUAL,
1505
+ RuntimeError: () => RuntimeError,
1506
+ RuntimeReader: () => RuntimeReader,
1507
+ aclRecords: () => aclRecords,
1508
+ auditWindowsPermissions: () => auditWindowsPermissions,
1509
+ bindHookContext: () => bindHookContext,
1510
+ ensureWindowsPrivateDirectorySync: () => ensureWindowsPrivateDirectorySync,
1511
+ execute: () => execute,
1512
+ findProjectIdentity: () => findProjectIdentity,
1513
+ findProjectIdentityDetailed: () => findProjectIdentityDetailed,
1514
+ handoffProjectSetup: () => handoffProjectSetup,
1515
+ hash: () => hash,
1516
+ missing: () => missing,
1517
+ parseHookResponse: () => parseHookResponse,
1518
+ pendingProjectSetup: () => pendingProjectSetup,
1519
+ postHookBoundJson: () => postHookBoundJson,
1520
+ prepareWindowsAclDirectory: () => prepareWindowsAclDirectory,
1521
+ projectSetupActions: () => projectSetupActions,
1522
+ protectWindowsDirectory: () => protectWindowsDirectory,
1523
+ safePath: () => safePath,
1524
+ setupResponse: () => setupResponse,
1525
+ validHookSessionId: () => validHookSessionId,
1526
+ verifySigner: () => verifySigner,
1527
+ verifyWindowsAcl: () => verifyWindowsAcl,
1528
+ verifyWindowsPermission: () => verifyWindowsPermission,
1529
+ verifyWindowsPermissionSync: () => verifyWindowsPermissionSync,
1530
+ versionName: () => versionName,
1531
+ windowsCurrentAccountSync: () => windowsCurrentAccountSync,
1532
+ windowsCurrentUserDirectoryAcl: () => windowsCurrentUserDirectoryAcl
1533
+ });
1534
+ import { stderr } from "node:process";
1535
+ async function findProjectIdentity(startCwd, options = {}) {
1536
+ if (!startCwd)
1537
+ return null;
1538
+ const result = await findProjectIdentityDetailed(startCwd, options);
1539
+ if (result.kind !== "ok")
1540
+ return null;
1541
+ return {
1542
+ projectId: result.identity.projectId,
1543
+ projectName: result.identity.projectName,
1544
+ projectRoot: result.root
1545
+ };
1546
+ }
1547
+ async function findProjectIdentityDetailed(startCwd, options = {}) {
1548
+ return resolveProjectIdentity(startCwd, options);
1549
+ }
1550
+ async function parseHookResponse(response) {
1551
+ const body = await response.json();
1552
+ const result = body;
1553
+ if (!mismatchEmitted && result?.ok === false && result.reason === "identity_mismatch") {
1554
+ stderr.write("mnemonik-hook: project identity mismatch - .mnemonik.json points to one project, but this directory is registered to a different one on the server. Fix .mnemonik.json (delete it and let session_bootstrap rewrite it) or cd to the correct project root.\n");
1555
+ mismatchEmitted = true;
1556
+ }
1557
+ return body;
1558
+ }
1559
+ function validHookSessionId(value) {
1560
+ return typeof value === "string" && /^[a-zA-Z0-9@._+:-]{1,255}$/.test(value) && !["cursor-unknown-session", "codex-unknown-session"].includes(value);
1561
+ }
1562
+ async function postHookBoundJson(server, path, token, body) {
1563
+ const response = await fetch(`${server.replace(/\/$/, "")}${path}`, {
1564
+ method: "POST",
1565
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
1566
+ body: JSON.stringify(body),
1567
+ signal: AbortSignal.timeout(2e3)
1568
+ });
1569
+ return { status: response.status, body: await response.json() };
1570
+ }
1571
+ async function bindHookContext(input, familyId, hmac, post, options = {}) {
1572
+ const { execFile: execFile4 } = await import("node:child_process");
1573
+ const { constants: constants3 } = await import("node:fs");
1574
+ const { lstat: lstat5, mkdir: mkdir2, open: open3, realpath: realpath2 } = await import("node:fs/promises");
1575
+ const { dirname: dirname5 } = await import("node:path");
1576
+ const { selectRemote: selectRemote2 } = await Promise.resolve().then(() => (init_repositoryFingerprint(), repositoryFingerprint_exports));
1577
+ const identity = await findProjectIdentityDetailed(await realpath2(input.cwd));
1578
+ if (identity.kind === "git_unavailable")
1579
+ throw new Error("context unavailable");
1580
+ const root = await realpath2(identity.root);
1581
+ const binding = await hmac(root);
1582
+ const gitEnv = { LC_ALL: "C" };
1583
+ for (const key2 of ["PATH", "HOME", "LANG", "SYSTEMROOT", "TEMP", "TMP", "USERPROFILE"])
1584
+ if (process.env[key2] !== void 0)
1585
+ gitEnv[key2] = process.env[key2];
1586
+ const git = (args) => new Promise((resolve4) => {
1587
+ execFile4("git", args, { cwd: root, env: gitEnv, timeout: 2e3, encoding: "utf8" }, (error, stdout2) => resolve4(error ? [] : stdout2.trim().split(/\r?\n/).filter(Boolean)));
1588
+ });
1589
+ const remotes = identity.repository.kind === "git" ? await git(["remote"]) : [];
1590
+ const selection = selectRemote2(await Promise.all(remotes.map(async (name) => ({
1591
+ name,
1592
+ fetchUrls: await git(["remote", "get-url", "--all", name]),
1593
+ pushUrls: await git(["remote", "get-url", "--push", "--all", name])
1594
+ }))));
1595
+ const body = {
1596
+ host: input.host,
1597
+ hostSessionId: input.hostSessionId,
1598
+ deviceRootContext: {
1599
+ algorithmVersion: binding.version,
1600
+ hash: Buffer.from(binding.hmac, "base64url").toString("hex")
1601
+ },
1602
+ repositoryFingerprint: selection.status === "fingerprint" ? {
1603
+ algorithmVersion: selection.fingerprint.algorithmVersion,
1604
+ hash: selection.fingerprint.hash
1605
+ } : null,
1606
+ // Automatic hooks never select a non-git root for the user.
1607
+ rootKind: identity.repository.kind === "git" ? "git" : "ineligible",
1608
+ identityState: identity.kind === "ok" ? "valid" : identity.kind === "absent" ? "absent" : "invalid",
1609
+ ...identity.kind === "ok" ? { projectId: identity.identity.projectId } : {}
1610
+ };
1611
+ const key = JSON.stringify([input.server, familyId, body]);
1612
+ const directory = dirname5(input.stateFile);
1613
+ for (const path of [dirname5(directory), directory]) {
1614
+ const created = await mkdir2(path, { recursive: true, mode: 448 });
1615
+ const stat = await lstat5(path);
1616
+ if (!stat.isDirectory() || stat.isSymbolicLink() || (options.platform ?? process.platform) !== "win32" && (process.getuid && stat.uid !== process.getuid() || stat.mode & 63))
1617
+ throw new Error("cache unavailable");
1618
+ if ((options.platform ?? process.platform) === "win32") {
1619
+ await protectWindowsDirectory(path, created !== void 0, options.run, directory);
1620
+ }
1621
+ }
1622
+ const file = await open3(input.stateFile, constants3.O_CREAT | constants3.O_RDWR | constants3.O_NOFOLLOW, 384);
1623
+ try {
1624
+ let cache;
1625
+ try {
1626
+ cache = JSON.parse(await file.readFile("utf8"));
1627
+ } catch {
1628
+ }
1629
+ const now = Date.now();
1630
+ if (cache && cache.expiresAt > now && (cache.key === key || cache.retryKey === key))
1631
+ return;
1632
+ const status = await post(body);
1633
+ if (status === 200)
1634
+ cache = { key, expiresAt: Date.now() + 12e4, canonicalRoot: root };
1635
+ else {
1636
+ stderr.write(`mnemonik-hook: bound-context post failed (${status})
1637
+ `);
1638
+ if (status !== 409)
1639
+ return;
1640
+ cache = {
1641
+ key: cache?.key ?? "",
1642
+ expiresAt: cache && cache.expiresAt > now ? cache.expiresAt : now + 12e4,
1643
+ retryKey: key
1644
+ };
1645
+ }
1646
+ await file.truncate(0);
1647
+ await file.write(JSON.stringify(cache), 0, "utf8");
1648
+ } finally {
1649
+ await file.close();
1650
+ }
1651
+ }
1652
+ var mismatchEmitted;
1653
+ var init_hookRuntime = __esm({
1654
+ "packages/shared/dist/hookRuntime.js"() {
1655
+ "use strict";
1656
+ init_runtimeSigners();
1657
+ init_repositoryRoot();
1658
+ init_runtimeReader();
1659
+ init_runtimeSigners();
1660
+ init_projectSetupHandoff();
1661
+ mismatchEmitted = false;
1662
+ }
1663
+ });
1664
+
1665
+ // packages/cli/src/runtime/signers.ts
1666
+ init_hookRuntime();
1667
+ export {
1668
+ PROJECT_SETUP_MANUAL,
1669
+ RuntimeError,
1670
+ RuntimeReader,
1671
+ aclRecords,
1672
+ auditWindowsPermissions,
1673
+ bindHookContext,
1674
+ ensureWindowsPrivateDirectorySync,
1675
+ execute,
1676
+ findProjectIdentity,
1677
+ findProjectIdentityDetailed,
1678
+ handoffProjectSetup,
1679
+ hash,
1680
+ missing,
1681
+ parseHookResponse,
1682
+ pendingProjectSetup,
1683
+ postHookBoundJson,
1684
+ prepareWindowsAclDirectory,
1685
+ projectSetupActions,
1686
+ protectWindowsDirectory,
1687
+ safePath,
1688
+ setupResponse,
1689
+ validHookSessionId,
1690
+ verifySigner,
1691
+ verifyWindowsAcl,
1692
+ verifyWindowsPermission,
1693
+ verifyWindowsPermissionSync,
1694
+ versionName,
1695
+ windowsCurrentAccountSync,
1696
+ windowsCurrentUserDirectoryAcl
1697
+ };