@mono-agent/agent-app 0.11.2 → 0.11.4

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 (374) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +222 -26
  3. package/dist/adapter-send-tools-main.js +7 -1
  4. package/dist/adapter-send-tools-main.js.map +1 -1
  5. package/dist/adapter-send-tools.d.ts +19 -1
  6. package/dist/adapter-send-tools.d.ts.map +1 -1
  7. package/dist/adapter-send-tools.js +127 -13
  8. package/dist/adapter-send-tools.js.map +1 -1
  9. package/dist/app-config.d.ts.map +1 -1
  10. package/dist/app-config.js +15 -2
  11. package/dist/app-config.js.map +1 -1
  12. package/dist/app-controller-channels.d.ts +8 -0
  13. package/dist/app-controller-channels.d.ts.map +1 -0
  14. package/dist/app-controller-channels.js +207 -0
  15. package/dist/app-controller-channels.js.map +1 -0
  16. package/dist/app-controller-continuation.d.ts +26 -0
  17. package/dist/app-controller-continuation.d.ts.map +1 -0
  18. package/dist/app-controller-continuation.js +198 -0
  19. package/dist/app-controller-continuation.js.map +1 -0
  20. package/dist/app-controller-lifecycle.d.ts +6 -0
  21. package/dist/app-controller-lifecycle.d.ts.map +1 -0
  22. package/dist/app-controller-lifecycle.js +81 -0
  23. package/dist/app-controller-lifecycle.js.map +1 -0
  24. package/dist/app-controller-maintenance.d.ts +13 -0
  25. package/dist/app-controller-maintenance.d.ts.map +1 -0
  26. package/dist/app-controller-maintenance.js +180 -0
  27. package/dist/app-controller-maintenance.js.map +1 -0
  28. package/dist/app-controller-memory-health.d.ts +12 -0
  29. package/dist/app-controller-memory-health.d.ts.map +1 -0
  30. package/dist/app-controller-memory-health.js +160 -0
  31. package/dist/app-controller-memory-health.js.map +1 -0
  32. package/dist/app-controller-memory.d.ts +9 -0
  33. package/dist/app-controller-memory.d.ts.map +1 -0
  34. package/dist/app-controller-memory.js +83 -0
  35. package/dist/app-controller-memory.js.map +1 -0
  36. package/dist/app-controller-responder.d.ts +20 -0
  37. package/dist/app-controller-responder.d.ts.map +1 -0
  38. package/dist/app-controller-responder.js +246 -0
  39. package/dist/app-controller-responder.js.map +1 -0
  40. package/dist/app-controller-traceability.d.ts +25 -0
  41. package/dist/app-controller-traceability.d.ts.map +1 -0
  42. package/dist/app-controller-traceability.js +418 -0
  43. package/dist/app-controller-traceability.js.map +1 -0
  44. package/dist/app-controller-utils.d.ts +38 -0
  45. package/dist/app-controller-utils.d.ts.map +1 -0
  46. package/dist/app-controller-utils.js +185 -0
  47. package/dist/app-controller-utils.js.map +1 -0
  48. package/dist/app-controller.d.ts +400 -0
  49. package/dist/app-controller.d.ts.map +1 -0
  50. package/dist/app-controller.js +336 -0
  51. package/dist/app-controller.js.map +1 -0
  52. package/dist/app.d.ts +7 -115
  53. package/dist/app.d.ts.map +1 -1
  54. package/dist/app.js +6 -1912
  55. package/dist/app.js.map +1 -1
  56. package/dist/artifact-retention.d.ts +4 -0
  57. package/dist/artifact-retention.d.ts.map +1 -1
  58. package/dist/artifact-retention.js +38 -0
  59. package/dist/artifact-retention.js.map +1 -1
  60. package/dist/backfill.d.ts +3 -1
  61. package/dist/backfill.d.ts.map +1 -1
  62. package/dist/backfill.js +5 -1
  63. package/dist/backfill.js.map +1 -1
  64. package/dist/background-runtime.d.ts +27 -0
  65. package/dist/background-runtime.d.ts.map +1 -1
  66. package/dist/background-runtime.js +383 -205
  67. package/dist/background-runtime.js.map +1 -1
  68. package/dist/background-worker-lease.d.ts.map +1 -1
  69. package/dist/background-worker-lease.js +41 -241
  70. package/dist/background-worker-lease.js.map +1 -1
  71. package/dist/background.d.ts +31 -9
  72. package/dist/background.d.ts.map +1 -1
  73. package/dist/background.js +543 -272
  74. package/dist/background.js.map +1 -1
  75. package/dist/channel-drivers/cron.d.ts +11 -0
  76. package/dist/channel-drivers/cron.d.ts.map +1 -0
  77. package/dist/channel-drivers/cron.js +168 -0
  78. package/dist/channel-drivers/cron.js.map +1 -0
  79. package/dist/channel-drivers/live.d.ts +11 -0
  80. package/dist/channel-drivers/live.d.ts.map +1 -0
  81. package/dist/channel-drivers/live.js +47 -0
  82. package/dist/channel-drivers/live.js.map +1 -0
  83. package/dist/channel-drivers/native-notify.d.ts +27 -0
  84. package/dist/channel-drivers/native-notify.d.ts.map +1 -0
  85. package/dist/channel-drivers/native-notify.js +94 -0
  86. package/dist/channel-drivers/native-notify.js.map +1 -0
  87. package/dist/channel-drivers/openai-api.d.ts +7 -0
  88. package/dist/channel-drivers/openai-api.d.ts.map +1 -0
  89. package/dist/channel-drivers/openai-api.js +63 -0
  90. package/dist/channel-drivers/openai-api.js.map +1 -0
  91. package/dist/channel-drivers/shared.d.ts +4 -0
  92. package/dist/channel-drivers/shared.d.ts.map +1 -0
  93. package/dist/channel-drivers/shared.js +5 -0
  94. package/dist/channel-drivers/shared.js.map +1 -0
  95. package/dist/channel-drivers/slack.d.ts +14 -0
  96. package/dist/channel-drivers/slack.d.ts.map +1 -0
  97. package/dist/channel-drivers/slack.js +227 -0
  98. package/dist/channel-drivers/slack.js.map +1 -0
  99. package/dist/channel-drivers/telegram.d.ts +11 -0
  100. package/dist/channel-drivers/telegram.d.ts.map +1 -0
  101. package/dist/channel-drivers/telegram.js +244 -0
  102. package/dist/channel-drivers/telegram.js.map +1 -0
  103. package/dist/channel-drivers/tui.d.ts +15 -0
  104. package/dist/channel-drivers/tui.d.ts.map +1 -0
  105. package/dist/channel-drivers/tui.js +108 -0
  106. package/dist/channel-drivers/tui.js.map +1 -0
  107. package/dist/channel-drivers/webhook.d.ts +7 -0
  108. package/dist/channel-drivers/webhook.d.ts.map +1 -0
  109. package/dist/channel-drivers/webhook.js +115 -0
  110. package/dist/channel-drivers/webhook.js.map +1 -0
  111. package/dist/channels.d.ts +21 -81
  112. package/dist/channels.d.ts.map +1 -1
  113. package/dist/channels.js +11 -1174
  114. package/dist/channels.js.map +1 -1
  115. package/dist/cli-args.d.ts +121 -0
  116. package/dist/cli-args.d.ts.map +1 -0
  117. package/dist/cli-args.js +532 -0
  118. package/dist/cli-args.js.map +1 -0
  119. package/dist/cli-background-command.d.ts +59 -0
  120. package/dist/cli-background-command.d.ts.map +1 -0
  121. package/dist/cli-background-command.js +475 -0
  122. package/dist/cli-background-command.js.map +1 -0
  123. package/dist/cli-cancellation.d.ts +6 -0
  124. package/dist/cli-cancellation.d.ts.map +1 -0
  125. package/dist/cli-cancellation.js +23 -0
  126. package/dist/cli-cancellation.js.map +1 -0
  127. package/dist/cli-commands.d.ts +12 -0
  128. package/dist/cli-commands.d.ts.map +1 -0
  129. package/dist/cli-commands.js +306 -0
  130. package/dist/cli-commands.js.map +1 -0
  131. package/dist/cli-help.d.ts +11 -0
  132. package/dist/cli-help.d.ts.map +1 -0
  133. package/dist/cli-help.js +262 -0
  134. package/dist/cli-help.js.map +1 -0
  135. package/dist/cli-init-command.d.ts +60 -0
  136. package/dist/cli-init-command.d.ts.map +1 -0
  137. package/dist/cli-init-command.js +1910 -0
  138. package/dist/cli-init-command.js.map +1 -0
  139. package/dist/cli-install-skill-command.d.ts +3 -0
  140. package/dist/cli-install-skill-command.d.ts.map +1 -0
  141. package/dist/cli-install-skill-command.js +51 -0
  142. package/dist/cli-install-skill-command.js.map +1 -0
  143. package/dist/cli-sandbox-command.d.ts +10 -0
  144. package/dist/cli-sandbox-command.d.ts.map +1 -0
  145. package/dist/cli-sandbox-command.js +100 -0
  146. package/dist/cli-sandbox-command.js.map +1 -0
  147. package/dist/cli-validate-config-command.d.ts +36 -0
  148. package/dist/cli-validate-config-command.d.ts.map +1 -0
  149. package/dist/cli-validate-config-command.js +317 -0
  150. package/dist/cli-validate-config-command.js.map +1 -0
  151. package/dist/cli.d.ts +6 -248
  152. package/dist/cli.d.ts.map +1 -1
  153. package/dist/cli.js +6 -3792
  154. package/dist/cli.js.map +1 -1
  155. package/dist/config-reference.d.ts +2 -1
  156. package/dist/config-reference.d.ts.map +1 -1
  157. package/dist/config-reference.js +34 -8
  158. package/dist/config-reference.js.map +1 -1
  159. package/dist/configured-agent.d.ts +23 -0
  160. package/dist/configured-agent.d.ts.map +1 -1
  161. package/dist/configured-agent.js +92 -13
  162. package/dist/configured-agent.js.map +1 -1
  163. package/dist/continuation-dispatch-worker.d.ts +21 -0
  164. package/dist/continuation-dispatch-worker.d.ts.map +1 -0
  165. package/dist/continuation-dispatch-worker.js +425 -0
  166. package/dist/continuation-dispatch-worker.js.map +1 -0
  167. package/dist/continuation-http-routes.d.ts +15 -0
  168. package/dist/continuation-http-routes.d.ts.map +1 -0
  169. package/dist/continuation-http-routes.js +265 -0
  170. package/dist/continuation-http-routes.js.map +1 -0
  171. package/dist/continuation-operator-api.d.ts +18 -0
  172. package/dist/continuation-operator-api.d.ts.map +1 -0
  173. package/dist/continuation-operator-api.js +164 -0
  174. package/dist/continuation-operator-api.js.map +1 -0
  175. package/dist/continuation-origin-store.d.ts +19 -0
  176. package/dist/continuation-origin-store.d.ts.map +1 -0
  177. package/dist/continuation-origin-store.js +273 -0
  178. package/dist/continuation-origin-store.js.map +1 -0
  179. package/dist/continuation-service-errors.d.ts +18 -0
  180. package/dist/continuation-service-errors.d.ts.map +1 -0
  181. package/dist/continuation-service-errors.js +35 -0
  182. package/dist/continuation-service-errors.js.map +1 -0
  183. package/dist/continuation-service-helpers.d.ts +39 -0
  184. package/dist/continuation-service-helpers.d.ts.map +1 -0
  185. package/dist/continuation-service-helpers.js +308 -0
  186. package/dist/continuation-service-helpers.js.map +1 -0
  187. package/dist/continuation-service-origin.d.ts +55 -0
  188. package/dist/continuation-service-origin.d.ts.map +1 -0
  189. package/dist/continuation-service-origin.js +325 -0
  190. package/dist/continuation-service-origin.js.map +1 -0
  191. package/dist/continuation-service-types.d.ts +110 -0
  192. package/dist/continuation-service-types.d.ts.map +1 -0
  193. package/dist/continuation-service-types.js +11 -0
  194. package/dist/continuation-service-types.js.map +1 -0
  195. package/dist/continuation-service.d.ts +4 -97
  196. package/dist/continuation-service.d.ts.map +1 -1
  197. package/dist/continuation-service.js +9 -1525
  198. package/dist/continuation-service.js.map +1 -1
  199. package/dist/continuation-store-fs.d.ts +15 -0
  200. package/dist/continuation-store-fs.d.ts.map +1 -0
  201. package/dist/continuation-store-fs.js +247 -0
  202. package/dist/continuation-store-fs.js.map +1 -0
  203. package/dist/continuation-store-policy.d.ts +21 -0
  204. package/dist/continuation-store-policy.d.ts.map +1 -0
  205. package/dist/continuation-store-policy.js +278 -0
  206. package/dist/continuation-store-policy.js.map +1 -0
  207. package/dist/continuation-store-records.d.ts +9 -0
  208. package/dist/continuation-store-records.d.ts.map +1 -0
  209. package/dist/continuation-store-records.js +221 -0
  210. package/dist/continuation-store-records.js.map +1 -0
  211. package/dist/continuation-store-types.d.ts +173 -0
  212. package/dist/continuation-store-types.d.ts.map +1 -0
  213. package/dist/continuation-store-types.js +26 -0
  214. package/dist/continuation-store-types.js.map +1 -0
  215. package/dist/continuation-store.d.ts +4 -138
  216. package/dist/continuation-store.d.ts.map +1 -1
  217. package/dist/continuation-store.js +145 -1049
  218. package/dist/continuation-store.js.map +1 -1
  219. package/dist/doctor.d.ts +10 -5
  220. package/dist/doctor.d.ts.map +1 -1
  221. package/dist/doctor.js +629 -58
  222. package/dist/doctor.js.map +1 -1
  223. package/dist/init.d.ts +2 -0
  224. package/dist/init.d.ts.map +1 -1
  225. package/dist/init.js +92 -239
  226. package/dist/init.js.map +1 -1
  227. package/dist/interaction-bridge.d.ts +19 -0
  228. package/dist/interaction-bridge.d.ts.map +1 -1
  229. package/dist/interaction-bridge.js +130 -0
  230. package/dist/interaction-bridge.js.map +1 -1
  231. package/dist/launchd-logs.d.ts +91 -0
  232. package/dist/launchd-logs.d.ts.map +1 -0
  233. package/dist/launchd-logs.js +1499 -0
  234. package/dist/launchd-logs.js.map +1 -0
  235. package/dist/launchd.d.ts +27 -0
  236. package/dist/launchd.d.ts.map +1 -1
  237. package/dist/launchd.js +89 -10
  238. package/dist/launchd.js.map +1 -1
  239. package/dist/memory-command.d.ts +2 -0
  240. package/dist/memory-command.d.ts.map +1 -1
  241. package/dist/memory-command.js +161 -48
  242. package/dist/memory-command.js.map +1 -1
  243. package/dist/memory-recall-settings.d.ts +64 -0
  244. package/dist/memory-recall-settings.d.ts.map +1 -0
  245. package/dist/memory-recall-settings.js +51 -0
  246. package/dist/memory-recall-settings.js.map +1 -0
  247. package/dist/memory-recall.d.ts +7 -95
  248. package/dist/memory-recall.d.ts.map +1 -1
  249. package/dist/memory-recall.js +8 -143
  250. package/dist/memory-recall.js.map +1 -1
  251. package/dist/memory-rituals.d.ts +3 -3
  252. package/dist/memory-rituals.d.ts.map +1 -1
  253. package/dist/memory-rituals.js +16 -111
  254. package/dist/memory-rituals.js.map +1 -1
  255. package/dist/modules/catalog.js +2 -2
  256. package/dist/modules/catalog.js.map +1 -1
  257. package/dist/modules/known-tools.d.ts +8 -6
  258. package/dist/modules/known-tools.d.ts.map +1 -1
  259. package/dist/modules/known-tools.js +8 -6
  260. package/dist/modules/known-tools.js.map +1 -1
  261. package/dist/notify-destinations.d.ts +5 -0
  262. package/dist/notify-destinations.d.ts.map +1 -1
  263. package/dist/notify-destinations.js +7 -1
  264. package/dist/notify-destinations.js.map +1 -1
  265. package/dist/owner-private-lock.d.ts +95 -0
  266. package/dist/owner-private-lock.d.ts.map +1 -0
  267. package/dist/owner-private-lock.js +566 -0
  268. package/dist/owner-private-lock.js.map +1 -0
  269. package/dist/pi-oauth-login.d.ts +2 -0
  270. package/dist/pi-oauth-login.d.ts.map +1 -1
  271. package/dist/pi-oauth-login.js +39 -5
  272. package/dist/pi-oauth-login.js.map +1 -1
  273. package/dist/posted-message-index.d.ts +35 -16
  274. package/dist/posted-message-index.d.ts.map +1 -1
  275. package/dist/posted-message-index.js +1068 -52
  276. package/dist/posted-message-index.js.map +1 -1
  277. package/dist/posted-reply-history.d.ts +17 -0
  278. package/dist/posted-reply-history.d.ts.map +1 -0
  279. package/dist/posted-reply-history.js +169 -0
  280. package/dist/posted-reply-history.js.map +1 -0
  281. package/dist/proactive-notify.d.ts.map +1 -1
  282. package/dist/proactive-notify.js +6 -4
  283. package/dist/proactive-notify.js.map +1 -1
  284. package/dist/project-skills.d.ts +4 -0
  285. package/dist/project-skills.d.ts.map +1 -1
  286. package/dist/project-skills.js +99 -130
  287. package/dist/project-skills.js.map +1 -1
  288. package/dist/provider-setup.d.ts +0 -1
  289. package/dist/provider-setup.d.ts.map +1 -1
  290. package/dist/provider-setup.js +0 -3
  291. package/dist/provider-setup.js.map +1 -1
  292. package/dist/readiness-probe-worker.d.ts +61 -1
  293. package/dist/readiness-probe-worker.d.ts.map +1 -1
  294. package/dist/readiness-probe-worker.js +54 -58
  295. package/dist/readiness-probe-worker.js.map +1 -1
  296. package/dist/readiness-probe.d.ts +6 -9
  297. package/dist/readiness-probe.d.ts.map +1 -1
  298. package/dist/readiness-probe.js +30 -56
  299. package/dist/readiness-probe.js.map +1 -1
  300. package/dist/redact-secrets.d.ts +15 -0
  301. package/dist/redact-secrets.d.ts.map +1 -0
  302. package/dist/redact-secrets.js +65 -0
  303. package/dist/redact-secrets.js.map +1 -0
  304. package/dist/run-history.js +34 -24
  305. package/dist/run-history.js.map +1 -1
  306. package/dist/runtime-provenance.d.ts +8 -0
  307. package/dist/runtime-provenance.d.ts.map +1 -0
  308. package/dist/runtime-provenance.js +225 -0
  309. package/dist/runtime-provenance.js.map +1 -0
  310. package/dist/sandbox-manager.d.ts +4 -1
  311. package/dist/sandbox-manager.d.ts.map +1 -1
  312. package/dist/sandbox-manager.js +114 -273
  313. package/dist/sandbox-manager.js.map +1 -1
  314. package/dist/secure-file-replace.d.ts +49 -0
  315. package/dist/secure-file-replace.d.ts.map +1 -0
  316. package/dist/secure-file-replace.js +270 -0
  317. package/dist/secure-file-replace.js.map +1 -0
  318. package/dist/seen-conversations.d.ts +32 -5
  319. package/dist/seen-conversations.d.ts.map +1 -1
  320. package/dist/seen-conversations.js +145 -15
  321. package/dist/seen-conversations.js.map +1 -1
  322. package/dist/sessions.d.ts +20 -0
  323. package/dist/sessions.d.ts.map +1 -1
  324. package/dist/sessions.js +41 -6
  325. package/dist/sessions.js.map +1 -1
  326. package/dist/wizard/index.d.ts +2 -2
  327. package/dist/wizard/index.d.ts.map +1 -1
  328. package/dist/wizard/index.js +1 -1
  329. package/dist/wizard/index.js.map +1 -1
  330. package/dist/wizard/presets.d.ts +2 -1
  331. package/dist/wizard/presets.d.ts.map +1 -1
  332. package/dist/wizard/presets.js +2 -1
  333. package/dist/wizard/presets.js.map +1 -1
  334. package/dist/wizard/run.d.ts +0 -26
  335. package/dist/wizard/run.d.ts.map +1 -1
  336. package/dist/wizard/run.js +0 -53
  337. package/dist/wizard/run.js.map +1 -1
  338. package/package.json +22 -17
  339. package/schema/mono-agent.config.schema.json +45 -10
  340. package/skills/mono-agent-composer/SKILL.md +3 -3
  341. package/skills/mono-agent-composer/references/config-blueprint.md +123 -29
  342. package/skills/mono-agent-composer/references/discovery-questions.md +10 -8
  343. package/skills/mono-agent-composer/references/feature-coverage.md +90 -76
  344. package/skills/mono-agent-composer/references/package-map.md +6 -4
  345. package/skills/mono-agent-composer/references/playbooks.md +48 -27
  346. package/skills/mono-agent-composer/references/validation.md +16 -3
  347. package/dist/notify-runtime.d.ts +0 -26
  348. package/dist/notify-runtime.d.ts.map +0 -1
  349. package/dist/notify-runtime.js +0 -27
  350. package/dist/notify-runtime.js.map +0 -1
  351. package/dist/notify-tool.d.ts +0 -51
  352. package/dist/notify-tool.d.ts.map +0 -1
  353. package/dist/notify-tool.js +0 -182
  354. package/dist/notify-tool.js.map +0 -1
  355. package/dist/recipes/base.d.ts +0 -15
  356. package/dist/recipes/base.d.ts.map +0 -1
  357. package/dist/recipes/base.js +0 -51
  358. package/dist/recipes/base.js.map +0 -1
  359. package/dist/recipes/catalog.d.ts +0 -4
  360. package/dist/recipes/catalog.d.ts.map +0 -1
  361. package/dist/recipes/catalog.js +0 -525
  362. package/dist/recipes/catalog.js.map +0 -1
  363. package/dist/recipes/index.d.ts +0 -11
  364. package/dist/recipes/index.d.ts.map +0 -1
  365. package/dist/recipes/index.js +0 -14
  366. package/dist/recipes/index.js.map +0 -1
  367. package/dist/recipes/types.d.ts +0 -70
  368. package/dist/recipes/types.d.ts.map +0 -1
  369. package/dist/recipes/types.js +0 -15
  370. package/dist/recipes/types.js.map +0 -1
  371. package/dist/setup.d.ts +0 -29
  372. package/dist/setup.d.ts.map +0 -1
  373. package/dist/setup.js +0 -97
  374. package/dist/setup.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { execFile, spawn } from "node:child_process";
2
2
  import { createHash, randomUUID } from "node:crypto";
3
3
  import { constants as fsConstants } from "node:fs";
4
- import { chmod, copyFile, lstat, mkdir, mkdtemp, open, opendir, readFile, realpath, rename, rm, stat, } from "node:fs/promises";
4
+ import { access, chmod, copyFile, lstat, mkdir, mkdtemp, open, opendir, readFile, realpath, rename, rm, stat, } from "node:fs/promises";
5
5
  import { createServer } from "node:http";
6
6
  import { homedir, tmpdir } from "node:os";
7
7
  import { dirname, isAbsolute, relative, resolve } from "node:path";
@@ -10,7 +10,8 @@ import { fileURLToPath } from "node:url";
10
10
  import { promisify } from "node:util";
11
11
  import { CodedError } from "@mono-agent/agent-contracts";
12
12
  import { createSandboxPolicy, createSrtSandboxEngine, MANAGED_SRT_TREE_SHA256, } from "@mono-agent/runtime-adapter";
13
- import { currentProcessIncarnation, isSameProcessIncarnation as matchesProcessIncarnation, processIncarnationFromJson, } from "./process-incarnation.js";
13
+ import { currentProcessIncarnation, processIncarnationFromJson, } from "./process-incarnation.js";
14
+ import { acquireOwnerPrivateLock } from "./owner-private-lock.js";
14
15
  const execFileAsync = promisify(execFile);
15
16
  export const MANAGED_SRT_PACKAGE = "@anthropic-ai/sandbox-runtime";
16
17
  export const MANAGED_SRT_VERSION = "0.0.64";
@@ -19,7 +20,6 @@ export const MANAGED_SRT_MARKER = ".mono-agent-srt.json";
19
20
  const MANAGED_SRT_RESOURCE_ROOT = fileURLToPath(new URL("../resources/srt", import.meta.url));
20
21
  const INSTALL_GUARD_NAME = ".install.guard";
21
22
  const INSTALL_LOCK_NAME = ".install.lock";
22
- const INSTALL_LOCK_OWNER_NAME = "owner.json";
23
23
  const INSTALL_LOCK_WAIT_MS = 150_000;
24
24
  const INSTALL_LOCK_POLL_MS = 100;
25
25
  const INSTALL_LOCK_OWNERLESS_GRACE_MS = 5 * 60_000;
@@ -461,15 +461,31 @@ async function assertTrustedNodeExecutable(nodePath) {
461
461
  if (nodeStat.isSymbolicLink() || !nodeStat.isFile()) {
462
462
  throw new Error(`Node executable is not a regular non-symbolic file: ${nodePath}`);
463
463
  }
464
+ // Managed SRT checks the selected path against writable roots, but another
465
+ // hardlink could expose the same owner-writable inode through an allowed
466
+ // root. Alias enumeration is not portable, so a trusted launcher is single-link.
464
467
  if (nodeStat.nlink !== 1) {
465
- throw new Error(`Node executable has ${nodeStat.nlink} hard links instead of one: ${nodePath}`);
468
+ throw new Error(`Node executable has ${nodeStat.nlink} hard links instead of one; select a single-link Node installation so no writable-root alias can modify it: ${nodePath}`);
469
+ }
470
+ const getuid = process.getuid;
471
+ if (getuid === undefined) {
472
+ throw new Error(`Node executable ownership cannot be verified on this platform: ${nodePath}`);
466
473
  }
467
474
  if ((nodeStat.mode & 0o022) !== 0) {
468
475
  throw new Error(`Node executable is writable by group or other users: ${nodePath}`);
469
476
  }
470
- if (process.getuid !== undefined && nodeStat.uid !== process.getuid() && nodeStat.uid !== 0) {
477
+ if ((nodeStat.mode & 0o6000) !== 0) {
478
+ throw new Error(`Node executable has setuid or setgid privilege bits: ${nodePath}`);
479
+ }
480
+ if (nodeStat.uid !== getuid() && nodeStat.uid !== 0) {
471
481
  throw new Error(`Node executable is not owned by the current user or root: ${nodePath}`);
472
482
  }
483
+ try {
484
+ await access(nodePath, fsConstants.X_OK);
485
+ }
486
+ catch (error) {
487
+ throw new Error(`Node executable is not executable by the current user: ${nodePath} (${errorMessage(error)})`);
488
+ }
473
489
  }
474
490
  async function writeInstallMarker(stagingRoot, options) {
475
491
  const cliPath = managedCliPath(stagingRoot);
@@ -736,89 +752,96 @@ async function acquireInstallLock(options) {
736
752
  }
737
753
  const deadline = now() + INSTALL_LOCK_WAIT_MS;
738
754
  const incarnation = await (options.hooks?.currentProcessIncarnation ?? currentProcessIncarnation)();
739
- const isSameProcess = options.hooks?.isSameProcessIncarnation ?? matchesProcessIncarnation;
740
- while (true) {
741
- throwIfAborted(options.signal);
742
- const token = randomUUID();
743
- let createdIdentity;
755
+ let lock;
756
+ for (;;) {
757
+ await waitForLegacyInstallLock(lockPath, deadline, options);
744
758
  try {
745
- await mkdir(lockPath, { mode: 0o700 });
746
- const created = await assertPrivateInstallLockDirectory(lockPath);
747
- createdIdentity = installLockIdentity(created);
748
- await options.hooks?.afterInstallLockDirectoryCreated?.(lockPath);
749
- await assertSamePrivateInstallLockDirectory(lockPath, createdIdentity);
750
- const record = {
751
- schemaVersion: 2,
752
- pid: process.pid,
753
- uid: process.getuid?.() ?? null,
754
- token,
755
- startedAt: new Date(now()).toISOString(),
756
- incarnation,
757
- };
758
- const owner = await publishInstallLockOwner(lockPath, createdIdentity, record);
759
- await syncPrivateInstallLockDirectory(lockPath, createdIdentity);
760
- await assertSamePrivateInstallLockDirectory(lockPath, createdIdentity);
761
- return {
759
+ lock = await acquireOwnerPrivateLock({
762
760
  path: lockPath,
763
- identity: createdIdentity,
764
- owner,
765
- ...(options.hooks?.beforeInstallLockReleaseRename === undefined
761
+ label: "SRT install lock",
762
+ schemaTag: "mono-agent.managed-srt-install-lock.v1",
763
+ ownerlessGraceMs,
764
+ waitTimeoutMs: Math.max(0, deadline - now()),
765
+ pollIntervalMs: INSTALL_LOCK_POLL_MS,
766
+ now,
767
+ processIncarnation: incarnation,
768
+ ...(options.hooks?.isSameProcessIncarnation === undefined
766
769
  ? {}
767
- : { beforeRelease: options.hooks.beforeInstallLockReleaseRename }),
768
- };
770
+ : { isSameProcessIncarnation: options.hooks.isSameProcessIncarnation }),
771
+ ownerFields: (base) => ({
772
+ schemaVersion: 2,
773
+ uid: process.getuid?.() ?? null,
774
+ startedAt: base.createdAt,
775
+ }),
776
+ validateOwnerFields: (record) => validInstallLockRecordFields(record, 2),
777
+ parseLegacyOwner: (record) => {
778
+ const legacyIncarnation = processIncarnationFromJson(record.incarnation);
779
+ return validInstallLockRecordFields(record, 2) && legacyIncarnation !== undefined
780
+ ? { pid: record.pid, incarnation: legacyIncarnation }
781
+ : undefined;
782
+ },
783
+ invalidOwner: "error",
784
+ livenessError: (error) => installLockUnsafe(lockPath, `Cannot prove the SRT install lock owner's process incarnation: ${errorMessage(error)}`),
785
+ beforeIteration: () => throwIfAborted(options.signal),
786
+ ...(options.hooks?.afterInstallLockDirectoryCreated === undefined
787
+ ? {}
788
+ : { afterDirectoryCreated: options.hooks.afterInstallLockDirectoryCreated }),
789
+ afterInspected: (observed) => options.hooks?.afterInstallLockInspected?.(lockPath, observed.kind),
790
+ ...(options.hooks?.beforeStaleInstallLockRename === undefined
791
+ ? {}
792
+ : { beforeStaleRename: () => options.hooks.beforeStaleInstallLockRename(lockPath) }),
793
+ sleep: (milliseconds) => (options.hooks?.sleep ?? abortableSleep)(milliseconds, options.signal),
794
+ staleRace: "error",
795
+ timeoutError: (observed) => observed.kind === "ownerless"
796
+ ? installLockUnsafe(lockPath, "Another installer left a fresh ownerless SRT install lock in its bounded publication window.")
797
+ : installLockUnsafe(lockPath, `Another live process incarnation (PID ${observed.owner.pid}) still owns the SRT install lock.`, { pid: observed.owner.pid }),
798
+ unsafeError: (cause, details) => installLockUnsafe(lockPath, cause, details),
799
+ stalePath: ({ pid, token }) => `${lockPath}.stale.${pid}.${token}`,
800
+ releasedPath: ({ pid, token }) => `${lockPath}.released.${pid}.${token}`,
801
+ abandonedPath: ({ pid, token }) => `${lockPath}.abandoned.${pid}.${token}`,
802
+ });
803
+ break;
769
804
  }
770
805
  catch (error) {
771
- if (createdIdentity !== undefined) {
772
- try {
773
- await abandonCreatedInstallLock(lockPath, createdIdentity);
774
- }
775
- catch (cleanupError) {
776
- throw aggregateFailures([error, stageFailure("Failed install-lock acquisition cleanup", cleanupError)], `Managed SRT install-lock acquisition and cleanup both failed at ${lockPath}.`);
777
- }
778
- }
779
- if (!isNodeError(error, "EEXIST")) {
780
- throw error;
781
- }
782
- }
783
- const existing = await inspectExistingInstallLock(lockPath);
784
- if (existing === undefined)
785
- continue;
786
- await options.hooks?.afterInstallLockInspected?.(lockPath, existing.kind);
787
- if (existing.kind === "ownerless") {
788
- if (now() - existing.mtimeMs >= ownerlessGraceMs) {
789
- await quarantineStaleInstallLock(lockPath, existing, options);
790
- continue;
791
- }
792
- if (now() >= deadline) {
793
- throw installLockUnsafe(lockPath, "Another installer left a fresh ownerless SRT install lock in its bounded publication window.");
794
- }
795
- }
796
- else if (existing.kind === "owned") {
797
- let sameOwner;
798
- try {
799
- sameOwner = await isSameProcess(existing.owner.record.pid, existing.owner.record.incarnation);
800
- }
801
- catch (error) {
802
- throw installLockUnsafe(lockPath, `Cannot prove the SRT install lock owner's process incarnation: ${errorMessage(error)}`);
803
- }
804
- if (!sameOwner) {
805
- await quarantineStaleInstallLock(lockPath, existing, options);
806
+ const current = await inspectLegacyInstallLock(lockPath);
807
+ const wasNonDirectory = error instanceof SandboxManagerError
808
+ && error.details.lockFailure === "not-directory";
809
+ if (wasNonDirectory || (current !== undefined && current !== "directory"))
806
810
  continue;
807
- }
808
- if (now() >= deadline) {
809
- throw installLockUnsafe(lockPath, `Another live process incarnation (PID ${existing.owner.record.pid}) still owns the SRT install lock.`, { pid: existing.owner.record.pid });
810
- }
811
+ throw error;
811
812
  }
812
- else if (existing.kind === "legacy-publishing") {
813
+ }
814
+ if (lock === undefined) {
815
+ throw installLockUnsafe(lockPath, "SRT install lock acquisition ended without ownership or a timeout.");
816
+ }
817
+ return {
818
+ lock,
819
+ ...(options.hooks?.beforeInstallLockReleaseRename === undefined
820
+ ? {}
821
+ : { beforeRelease: options.hooks.beforeInstallLockReleaseRename }),
822
+ };
823
+ }
824
+ async function waitForLegacyInstallLock(lockPath, deadline, options) {
825
+ const now = options.hooks?.now ?? Date.now;
826
+ for (;;) {
827
+ throwIfAborted(options.signal);
828
+ const existing = await inspectLegacyInstallLock(lockPath);
829
+ if (existing === undefined || existing === "directory")
830
+ return;
831
+ await options.hooks?.afterInstallLockInspected?.(lockPath, existing.kind);
832
+ if (existing.kind === "legacy-publishing") {
813
833
  if (now() >= deadline) {
814
834
  throw installLockUnsafe(lockPath, "A legacy SRT installer did not finish publishing its lock record; the incomplete lock was left untouched.");
815
835
  }
816
836
  }
817
837
  else {
838
+ // Permanent v0.8-and-earlier compatibility: skipped-version upgrades can
839
+ // encounter crash debris indefinitely. New writes have used the v2
840
+ // directory/incarnation format since v0.9.0.
818
841
  const liveness = options.hooks?.processIsAlive?.(existing.record.pid)
819
842
  ?? processLiveness(existing.record.pid);
820
843
  if (liveness === "dead") {
821
- await quarantineStaleInstallLock(lockPath, existing, options);
844
+ await quarantineStaleLegacyInstallLock(lockPath, existing, options);
822
845
  continue;
823
846
  }
824
847
  if (liveness === "unknown") {
@@ -831,7 +854,7 @@ async function acquireInstallLock(options) {
831
854
  await (options.hooks?.sleep ?? abortableSleep)(INSTALL_LOCK_POLL_MS, options.signal);
832
855
  }
833
856
  }
834
- async function inspectExistingInstallLock(lockPath) {
857
+ async function inspectLegacyInstallLock(lockPath) {
835
858
  try {
836
859
  const details = await lstat(lockPath, { bigint: true });
837
860
  if (details.isSymbolicLink()) {
@@ -839,20 +862,8 @@ async function inspectExistingInstallLock(lockPath) {
839
862
  }
840
863
  assertInstallLockOwned(details, lockPath);
841
864
  const identity = installLockIdentity(details);
842
- if (details.isDirectory()) {
843
- assertPrivateInstallLockDirectoryDetails(details, lockPath);
844
- const owner = await readSecureInstallLockOwner(resolve(lockPath, INSTALL_LOCK_OWNER_NAME));
845
- if (owner === "detached")
846
- return undefined;
847
- const current = await samePrivateInstallLockDirectory(lockPath, identity);
848
- if (!current)
849
- return undefined;
850
- if (owner === undefined) {
851
- const ownerlessDetails = await assertSamePrivateInstallLockDirectory(lockPath, identity);
852
- return { kind: "ownerless", identity, mtimeMs: Number(ownerlessDetails.mtimeMs) };
853
- }
854
- return { kind: "owned", identity, owner };
855
- }
865
+ if (details.isDirectory())
866
+ return "directory";
856
867
  if (details.isFile()) {
857
868
  return await readSecureLegacyInstallLock(lockPath, identity);
858
869
  }
@@ -866,83 +877,15 @@ async function inspectExistingInstallLock(lockPath) {
866
877
  throw installLockUnsafe(lockPath, errorMessage(error));
867
878
  }
868
879
  }
869
- async function publishInstallLockOwner(lockPath, identity, record) {
870
- const ownerPath = resolve(lockPath, INSTALL_LOCK_OWNER_NAME);
871
- const temporaryPath = resolve(lockPath, `.owner.${process.pid}.${record.token}.tmp`);
872
- const handle = await open(temporaryPath, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY | (fsConstants.O_NOFOLLOW ?? 0), 0o600);
873
- try {
874
- await handle.writeFile(`${JSON.stringify(record)}\n`, "utf8");
875
- await handle.sync();
876
- assertPrivateInstallLockOwnerDetails(await handle.stat({ bigint: true }), temporaryPath);
877
- }
878
- finally {
879
- await handle.close();
880
- }
881
- await assertSamePrivateInstallLockDirectory(lockPath, identity);
882
- await rename(temporaryPath, ownerPath);
883
- await assertSamePrivateInstallLockDirectory(lockPath, identity);
884
- const owner = await readSecureInstallLockOwner(ownerPath);
885
- if (owner === undefined || owner === "detached" || owner.record.token !== record.token) {
886
- throw installLockUnsafe(lockPath, "The atomically published owner record could not be verified.");
887
- }
888
- return owner;
889
- }
890
- async function readSecureInstallLockOwner(ownerPath) {
891
- let handle;
892
- try {
893
- handle = await open(ownerPath, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));
894
- }
895
- catch (error) {
896
- if (isNodeError(error, "ENOENT"))
897
- return undefined;
898
- throw error;
899
- }
900
- try {
901
- const before = await handle.stat({ bigint: true });
902
- if (before.nlink === 0n)
903
- return "detached";
904
- assertPrivateInstallLockOwnerDetails(before, ownerPath);
905
- const content = await readBoundedInstallLockRecord(handle, ownerPath);
906
- const after = await handle.stat({ bigint: true });
907
- if (after.nlink === 0n)
908
- return "detached";
909
- assertPrivateInstallLockOwnerDetails(after, ownerPath);
910
- if (!sameInstallLockIdentity(after, installLockIdentity(before))) {
911
- throw new Error("owner record identity changed while it was read");
912
- }
913
- let parsed;
914
- try {
915
- parsed = JSON.parse(content);
916
- }
917
- catch (error) {
918
- throw new Error(`owner record JSON is malformed: ${errorMessage(error)}`);
919
- }
920
- const incarnation = processIncarnationFromJson(parsed.incarnation);
921
- if (!validInstallLockRecordFields(parsed, 2) || incarnation === undefined) {
922
- throw new Error("owner record is malformed or belongs to another user");
923
- }
924
- const valid = parsed;
925
- return {
926
- record: {
927
- schemaVersion: 2,
928
- pid: valid.pid,
929
- uid: valid.uid,
930
- token: valid.token,
931
- startedAt: valid.startedAt,
932
- incarnation,
933
- },
934
- content,
935
- identity: installLockIdentity(after),
936
- };
937
- }
938
- finally {
939
- await handle.close();
940
- }
941
- }
880
+ /**
881
+ * Read the permanent v0.8-and-earlier compatibility format. Keep this bounded,
882
+ * fail-closed parser indefinitely: a user can skip releases and later encounter
883
+ * an owner-only file left by a crashed old installer. New writes never use it.
884
+ */
942
885
  async function readSecureLegacyInstallLock(lockPath, observedIdentity) {
943
886
  let handle;
944
887
  try {
945
- handle = await open(lockPath, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));
888
+ handle = await open(lockPath, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0) | (fsConstants.O_NONBLOCK ?? 0));
946
889
  const before = await handle.stat({ bigint: true });
947
890
  if (!sameInstallLockIdentity(before, observedIdentity) || before.nlink === 0n)
948
891
  return undefined;
@@ -1019,12 +962,12 @@ function validInstallLockRecordFields(record, schemaVersion) {
1019
962
  && (record.uid === null || (typeof record.uid === "number" && Number.isSafeInteger(record.uid)))
1020
963
  && (process.getuid === undefined || record.uid === process.getuid());
1021
964
  }
1022
- async function quarantineStaleInstallLock(lockPath, expected, options) {
965
+ async function quarantineStaleLegacyInstallLock(lockPath, expected, options) {
1023
966
  await options.hooks?.beforeStaleInstallLockRename?.(lockPath);
1024
- const current = await inspectExistingInstallLock(lockPath);
967
+ const current = await inspectLegacyInstallLock(lockPath);
1025
968
  if (current === undefined)
1026
969
  return false;
1027
- if (!sameExistingInstallLock(current, expected)) {
970
+ if (current === "directory" || !sameExistingLegacyInstallLock(current, expected)) {
1028
971
  throw installLockUnsafe(lockPath, "SRT install lock changed during stale-lock verification; the replacement was left untouched.");
1029
972
  }
1030
973
  const quarantinePath = `${lockPath}.stale.${process.pid}.${randomUUID()}`;
@@ -1036,81 +979,17 @@ async function quarantineStaleInstallLock(lockPath, expected, options) {
1036
979
  return false;
1037
980
  throw error;
1038
981
  }
1039
- const moved = await inspectExistingInstallLock(quarantinePath);
1040
- if (moved === undefined || !sameExistingInstallLock(moved, expected)) {
982
+ const moved = await inspectLegacyInstallLock(quarantinePath);
983
+ if (moved === undefined || moved === "directory" || !sameExistingLegacyInstallLock(moved, expected)) {
1041
984
  throw installLockUnsafe(lockPath, `The stale lock changed across quarantine and was retained at ${quarantinePath}.`, { quarantinePath });
1042
985
  }
1043
986
  await rm(quarantinePath, { recursive: true, force: true });
1044
987
  return true;
1045
988
  }
1046
989
  async function releaseInstallLock(lock) {
1047
- const expected = {
1048
- kind: "owned",
1049
- identity: lock.identity,
1050
- owner: lock.owner,
1051
- };
1052
- const observed = await inspectExistingInstallLock(lock.path);
1053
- if (observed === undefined || !sameExistingInstallLock(observed, expected)) {
1054
- throw installLockUnsafe(lock.path, "SRT install lock identity or owner changed before release; another lock was left untouched.");
1055
- }
1056
- await lock.beforeRelease?.(lock.path);
1057
- const current = await inspectExistingInstallLock(lock.path);
1058
- if (current === undefined || !sameExistingInstallLock(current, expected)) {
1059
- throw installLockUnsafe(lock.path, "SRT install lock changed at the release boundary; the replacement was left untouched.");
1060
- }
1061
- const releasedPath = `${lock.path}.released.${process.pid}.${randomUUID()}`;
1062
- try {
1063
- await rename(lock.path, releasedPath);
1064
- }
1065
- catch (error) {
1066
- if (isNodeError(error, "ENOENT")) {
1067
- throw installLockUnsafe(lock.path, "SRT install lock disappeared during release.");
1068
- }
1069
- throw error;
1070
- }
1071
- const moved = await inspectExistingInstallLock(releasedPath);
1072
- if (moved === undefined || !sameExistingInstallLock(moved, expected)) {
1073
- throw installLockUnsafe(lock.path, `SRT install lock changed across release and was retained at ${releasedPath}.`, { releasedPath });
1074
- }
1075
- await rm(releasedPath, { recursive: true, force: true });
1076
- }
1077
- async function abandonCreatedInstallLock(lockPath, identity) {
1078
- if (!(await samePrivateInstallLockDirectory(lockPath, identity)))
1079
- return;
1080
- const abandonedPath = `${lockPath}.abandoned.${process.pid}.${randomUUID()}`;
1081
- try {
1082
- await rename(lockPath, abandonedPath);
1083
- }
1084
- catch (error) {
1085
- if (isNodeError(error, "ENOENT"))
1086
- return;
1087
- throw error;
1088
- }
1089
- const moved = await lstat(abandonedPath, { bigint: true });
1090
- if (!sameInstallLockIdentity(moved, identity)) {
1091
- throw installLockUnsafe(lockPath, `A failed acquisition changed across quarantine and was retained at ${abandonedPath}.`, { abandonedPath });
1092
- }
1093
- await rm(abandonedPath, { recursive: true, force: true });
1094
- }
1095
- async function assertPrivateInstallLockDirectory(path) {
1096
- const details = await lstat(path, { bigint: true });
1097
- assertPrivateInstallLockDirectoryDetails(details, path);
1098
- return details;
1099
- }
1100
- function assertPrivateInstallLockDirectoryDetails(details, path) {
1101
- if (!details.isDirectory() || details.isSymbolicLink()) {
1102
- throw new Error("lock path must be a real directory");
1103
- }
1104
- assertInstallLockOwned(details, path);
1105
- if ((details.mode & 63n) !== 0n) {
1106
- throw new Error("lock directory must be owner-only (mode 0700)");
1107
- }
1108
- }
1109
- function assertPrivateInstallLockOwnerDetails(details, path) {
1110
- if (!details.isFile() || details.isSymbolicLink() || details.nlink !== 1n || (details.mode & 63n) !== 0n) {
1111
- throw new Error(`lock owner ${path} must be an owner-only single-link regular file`);
1112
- }
1113
- assertInstallLockOwned(details, path);
990
+ await lock.lock.release({
991
+ ...(lock.beforeRelease === undefined ? {} : { beforeRename: lock.beforeRelease }),
992
+ });
1114
993
  }
1115
994
  function assertPrivateLegacyInstallLockDetails(details, path) {
1116
995
  if (!details.isFile() || details.isSymbolicLink() || details.nlink !== 1n || (details.mode & 63n) !== 0n) {
@@ -1123,23 +1002,6 @@ function assertInstallLockOwned(details, path) {
1123
1002
  throw new Error(`lock path ${path} is not owned by the current user`);
1124
1003
  }
1125
1004
  }
1126
- async function assertSamePrivateInstallLockDirectory(path, identity) {
1127
- const details = await assertPrivateInstallLockDirectory(path);
1128
- if (!sameInstallLockIdentity(details, identity)) {
1129
- throw installLockUnsafe(path, "SRT install lock directory identity changed.");
1130
- }
1131
- return details;
1132
- }
1133
- async function samePrivateInstallLockDirectory(path, identity) {
1134
- try {
1135
- return sameInstallLockIdentity(await assertPrivateInstallLockDirectory(path), identity);
1136
- }
1137
- catch (error) {
1138
- if (isNodeError(error, "ENOENT"))
1139
- return false;
1140
- throw error;
1141
- }
1142
- }
1143
1005
  async function sameInstallLockPathIdentity(path, identity) {
1144
1006
  try {
1145
1007
  return sameInstallLockIdentity(await lstat(path, { bigint: true }), identity);
@@ -1150,36 +1012,15 @@ async function sameInstallLockPathIdentity(path, identity) {
1150
1012
  throw error;
1151
1013
  }
1152
1014
  }
1153
- async function syncPrivateInstallLockDirectory(path, identity) {
1154
- const handle = await open(path, fsConstants.O_RDONLY | (fsConstants.O_DIRECTORY ?? 0) | (fsConstants.O_NOFOLLOW ?? 0));
1155
- try {
1156
- const details = await handle.stat({ bigint: true });
1157
- assertPrivateInstallLockDirectoryDetails(details, path);
1158
- if (!sameInstallLockIdentity(details, identity)) {
1159
- throw installLockUnsafe(path, "SRT install lock changed before its owner publication was synced.");
1160
- }
1161
- await handle.sync();
1162
- }
1163
- finally {
1164
- await handle.close();
1165
- }
1166
- }
1167
1015
  function installLockIdentity(details) {
1168
1016
  return { dev: details.dev, ino: details.ino };
1169
1017
  }
1170
1018
  function sameInstallLockIdentity(details, identity) {
1171
1019
  return details.dev === identity.dev && details.ino === identity.ino;
1172
1020
  }
1173
- function sameExistingInstallLock(left, right) {
1021
+ function sameExistingLegacyInstallLock(left, right) {
1174
1022
  if (left.kind !== right.kind || !sameInstallLockIdentity(left.identity, right.identity))
1175
1023
  return false;
1176
- if (left.kind === "ownerless" && right.kind === "ownerless")
1177
- return true;
1178
- if (left.kind === "owned" && right.kind === "owned") {
1179
- return left.owner.record.token === right.owner.record.token
1180
- && left.owner.content === right.owner.content
1181
- && sameInstallLockIdentity(left.owner.identity, right.owner.identity);
1182
- }
1183
1024
  if (left.kind === "legacy" && right.kind === "legacy") {
1184
1025
  return left.record.token === right.record.token && left.content === right.content;
1185
1026
  }