@clossys/launcher 0.3.0 → 0.4.0

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 (286) hide show
  1. package/README.md +1328 -60
  2. package/contracts/conversation-contract.md +2 -1
  3. package/contracts/product-ci-workflow.yml +74 -0
  4. package/contracts/repository-inventory.json +53 -0
  5. package/dist/admission-fixture.d.ts +168 -0
  6. package/dist/admission-fixture.d.ts.map +1 -0
  7. package/dist/admission-fixture.js +453 -0
  8. package/dist/admission-fixture.js.map +1 -0
  9. package/dist/admission.d.ts +124 -0
  10. package/dist/admission.d.ts.map +1 -0
  11. package/dist/admission.js +799 -0
  12. package/dist/admission.js.map +1 -0
  13. package/dist/agents-guide.d.ts +9 -0
  14. package/dist/agents-guide.d.ts.map +1 -0
  15. package/dist/agents-guide.js +26 -0
  16. package/dist/agents-guide.js.map +1 -0
  17. package/dist/apply-command-options.check.d.ts +12 -0
  18. package/dist/apply-command-options.check.d.ts.map +1 -0
  19. package/dist/apply-command-options.check.js +20 -0
  20. package/dist/apply-command-options.check.js.map +1 -0
  21. package/dist/apply-plan-cli.d.ts +39 -1
  22. package/dist/apply-plan-cli.d.ts.map +1 -1
  23. package/dist/apply-plan-cli.js +432 -15
  24. package/dist/apply-plan-cli.js.map +1 -1
  25. package/dist/apply-plan.d.ts +46 -59
  26. package/dist/apply-plan.d.ts.map +1 -1
  27. package/dist/apply-plan.js +112 -97
  28. package/dist/apply-plan.js.map +1 -1
  29. package/dist/apply-step-fixture.d.ts +87 -0
  30. package/dist/apply-step-fixture.d.ts.map +1 -0
  31. package/dist/apply-step-fixture.js +199 -0
  32. package/dist/apply-step-fixture.js.map +1 -0
  33. package/dist/apply-store.d.ts +93 -0
  34. package/dist/apply-store.d.ts.map +1 -0
  35. package/dist/apply-store.js +625 -0
  36. package/dist/apply-store.js.map +1 -0
  37. package/dist/approval-sheet.d.ts +21 -0
  38. package/dist/approval-sheet.d.ts.map +1 -0
  39. package/dist/approval-sheet.js +157 -0
  40. package/dist/approval-sheet.js.map +1 -0
  41. package/dist/body-command.d.ts +42 -0
  42. package/dist/body-command.d.ts.map +1 -0
  43. package/dist/body-command.js +143 -0
  44. package/dist/body-command.js.map +1 -0
  45. package/dist/change-set-contract.d.ts +381 -0
  46. package/dist/change-set-contract.d.ts.map +1 -0
  47. package/dist/change-set-contract.js +738 -0
  48. package/dist/change-set-contract.js.map +1 -0
  49. package/dist/change-set-digest.d.ts +28 -0
  50. package/dist/change-set-digest.d.ts.map +1 -0
  51. package/dist/change-set-digest.js +65 -0
  52. package/dist/change-set-digest.js.map +1 -0
  53. package/dist/check-cli.d.ts.map +1 -1
  54. package/dist/check-cli.js +14 -3
  55. package/dist/check-cli.js.map +1 -1
  56. package/dist/cli.d.ts +17 -6
  57. package/dist/cli.d.ts.map +1 -1
  58. package/dist/cli.js +84 -23
  59. package/dist/cli.js.map +1 -1
  60. package/dist/core.d.ts +79 -22
  61. package/dist/core.d.ts.map +1 -1
  62. package/dist/core.js +843 -268
  63. package/dist/core.js.map +1 -1
  64. package/dist/dry-materialize.d.ts +63 -0
  65. package/dist/dry-materialize.d.ts.map +1 -0
  66. package/dist/dry-materialize.js +330 -0
  67. package/dist/dry-materialize.js.map +1 -0
  68. package/dist/generated/contract-schema.generated.d.ts +97 -0
  69. package/dist/generated/contract-schema.generated.d.ts.map +1 -0
  70. package/dist/generated/contract-schema.generated.js +496 -0
  71. package/dist/generated/contract-schema.generated.js.map +1 -0
  72. package/dist/generated/package-scope.generated.d.ts +6 -0
  73. package/dist/generated/package-scope.generated.d.ts.map +1 -0
  74. package/dist/generated/package-scope.generated.js +10 -0
  75. package/dist/generated/package-scope.generated.js.map +1 -0
  76. package/dist/generated/plan-contracts.generated.d.ts +3 -0
  77. package/dist/generated/plan-contracts.generated.d.ts.map +1 -0
  78. package/dist/generated/plan-contracts.generated.js +2840 -0
  79. package/dist/generated/plan-contracts.generated.js.map +1 -0
  80. package/dist/host.d.ts.map +1 -1
  81. package/dist/host.js +11 -0
  82. package/dist/host.js.map +1 -1
  83. package/dist/identity.d.ts +15 -0
  84. package/dist/identity.d.ts.map +1 -0
  85. package/dist/identity.js +48 -0
  86. package/dist/identity.js.map +1 -0
  87. package/dist/index.d.ts +34 -5
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +19 -2
  90. package/dist/index.js.map +1 -1
  91. package/dist/inventory-adoption.d.ts +24 -5
  92. package/dist/inventory-adoption.d.ts.map +1 -1
  93. package/dist/inventory-adoption.js +70 -25
  94. package/dist/inventory-adoption.js.map +1 -1
  95. package/dist/inventory-choice.d.ts +40 -0
  96. package/dist/inventory-choice.d.ts.map +1 -0
  97. package/dist/inventory-choice.js +156 -0
  98. package/dist/inventory-choice.js.map +1 -0
  99. package/dist/inventory-contract.d.ts +89 -0
  100. package/dist/inventory-contract.d.ts.map +1 -0
  101. package/dist/inventory-contract.js +121 -0
  102. package/dist/inventory-contract.js.map +1 -0
  103. package/dist/key-editor.d.ts +30 -0
  104. package/dist/key-editor.d.ts.map +1 -0
  105. package/dist/key-editor.js +445 -0
  106. package/dist/key-editor.js.map +1 -0
  107. package/dist/ledger-contract.d.ts +187 -0
  108. package/dist/ledger-contract.d.ts.map +1 -0
  109. package/dist/ledger-contract.js +532 -0
  110. package/dist/ledger-contract.js.map +1 -0
  111. package/dist/ledger-trust.d.ts +90 -0
  112. package/dist/ledger-trust.d.ts.map +1 -0
  113. package/dist/ledger-trust.js +198 -0
  114. package/dist/ledger-trust.js.map +1 -0
  115. package/dist/lockfile-invariants.d.ts +48 -0
  116. package/dist/lockfile-invariants.d.ts.map +1 -0
  117. package/dist/lockfile-invariants.js +375 -0
  118. package/dist/lockfile-invariants.js.map +1 -0
  119. package/dist/lockfile-readers.d.ts +72 -0
  120. package/dist/lockfile-readers.d.ts.map +1 -0
  121. package/dist/lockfile-readers.js +713 -0
  122. package/dist/lockfile-readers.js.map +1 -0
  123. package/dist/lockfile-regen.d.ts +106 -0
  124. package/dist/lockfile-regen.d.ts.map +1 -0
  125. package/dist/lockfile-regen.js +760 -0
  126. package/dist/lockfile-regen.js.map +1 -0
  127. package/dist/lockfile-tool-env.d.ts +29 -0
  128. package/dist/lockfile-tool-env.d.ts.map +1 -0
  129. package/dist/lockfile-tool-env.js +111 -0
  130. package/dist/lockfile-tool-env.js.map +1 -0
  131. package/dist/materialize.d.ts +113 -0
  132. package/dist/materialize.d.ts.map +1 -0
  133. package/dist/materialize.js +840 -0
  134. package/dist/materialize.js.map +1 -0
  135. package/dist/observe-repository.d.ts +90 -0
  136. package/dist/observe-repository.d.ts.map +1 -0
  137. package/dist/observe-repository.js +1367 -0
  138. package/dist/observe-repository.js.map +1 -0
  139. package/dist/plan-bundle-setup-fixture.d.ts +68 -0
  140. package/dist/plan-bundle-setup-fixture.d.ts.map +1 -0
  141. package/dist/plan-bundle-setup-fixture.js +167 -0
  142. package/dist/plan-bundle-setup-fixture.js.map +1 -0
  143. package/dist/plan-bundle.d.ts +250 -0
  144. package/dist/plan-bundle.d.ts.map +1 -0
  145. package/dist/plan-bundle.js +827 -0
  146. package/dist/plan-bundle.js.map +1 -0
  147. package/dist/plan-command.d.ts +29 -0
  148. package/dist/plan-command.d.ts.map +1 -0
  149. package/dist/plan-command.js +493 -0
  150. package/dist/plan-command.js.map +1 -0
  151. package/dist/plan-contract.d.ts +153 -0
  152. package/dist/plan-contract.d.ts.map +1 -0
  153. package/dist/plan-contract.js +61 -0
  154. package/dist/plan-contract.js.map +1 -0
  155. package/dist/plan-digest.d.ts +25 -0
  156. package/dist/plan-digest.d.ts.map +1 -0
  157. package/dist/plan-digest.js +106 -0
  158. package/dist/plan-digest.js.map +1 -0
  159. package/dist/plan-rules.d.ts +23 -0
  160. package/dist/plan-rules.d.ts.map +1 -0
  161. package/dist/plan-rules.js +177 -0
  162. package/dist/plan-rules.js.map +1 -0
  163. package/dist/planned-bundle.d.ts +20 -0
  164. package/dist/planned-bundle.d.ts.map +1 -0
  165. package/dist/planned-bundle.js +191 -0
  166. package/dist/planned-bundle.js.map +1 -0
  167. package/dist/product-repository.d.ts +4 -0
  168. package/dist/product-repository.d.ts.map +1 -1
  169. package/dist/product-repository.js +9 -1
  170. package/dist/product-repository.js.map +1 -1
  171. package/dist/provenance-gate.d.ts +48 -0
  172. package/dist/provenance-gate.d.ts.map +1 -0
  173. package/dist/provenance-gate.js +324 -0
  174. package/dist/provenance-gate.js.map +1 -0
  175. package/dist/pull-request-body.d.ts +45 -0
  176. package/dist/pull-request-body.d.ts.map +1 -0
  177. package/dist/pull-request-body.js +232 -0
  178. package/dist/pull-request-body.js.map +1 -0
  179. package/dist/registry-snapshot.d.ts +141 -0
  180. package/dist/registry-snapshot.d.ts.map +1 -0
  181. package/dist/registry-snapshot.js +483 -0
  182. package/dist/registry-snapshot.js.map +1 -0
  183. package/dist/release-age-edit.d.ts +52 -0
  184. package/dist/release-age-edit.d.ts.map +1 -0
  185. package/dist/release-age-edit.js +413 -0
  186. package/dist/release-age-edit.js.map +1 -0
  187. package/dist/root-entries.d.ts +36 -0
  188. package/dist/root-entries.d.ts.map +1 -0
  189. package/dist/root-entries.js +80 -0
  190. package/dist/root-entries.js.map +1 -0
  191. package/dist/setup-template-scripts.d.ts +34 -0
  192. package/dist/setup-template-scripts.d.ts.map +1 -0
  193. package/dist/setup-template-scripts.js +557 -0
  194. package/dist/setup-template-scripts.js.map +1 -0
  195. package/dist/setup-templates.d.ts +54 -0
  196. package/dist/setup-templates.d.ts.map +1 -0
  197. package/dist/setup-templates.js +427 -0
  198. package/dist/setup-templates.js.map +1 -0
  199. package/dist/skills.d.ts +34 -1
  200. package/dist/skills.d.ts.map +1 -1
  201. package/dist/skills.js +129 -17
  202. package/dist/skills.js.map +1 -1
  203. package/dist/status.d.ts +63 -0
  204. package/dist/status.d.ts.map +1 -0
  205. package/dist/status.js +539 -0
  206. package/dist/status.js.map +1 -0
  207. package/dist/types.d.ts +151 -13
  208. package/dist/types.d.ts.map +1 -1
  209. package/package.json +4 -5
  210. package/skeleton/README.md +14 -9
  211. package/skeleton/package.json +2 -1
  212. package/skill/SKILL.md +20 -16
  213. package/skill-catalogue/advisor/SKILL.md +100 -11
  214. package/skill-catalogue/architect/SKILL.md +2 -13
  215. package/skill-catalogue/bouncer/SKILL.md +2 -13
  216. package/skill-catalogue/builder/SKILL.md +2 -13
  217. package/skill-catalogue/butler/SKILL.md +2 -13
  218. package/skill-catalogue/controller/SKILL.md +2 -13
  219. package/skill-catalogue/customer/SKILL.md +4 -13
  220. package/skill-catalogue/designer/SKILL.md +9 -14
  221. package/skill-catalogue/giver/SKILL.md +2 -13
  222. package/skill-catalogue/influencer/SKILL.md +2 -13
  223. package/skill-catalogue/inspector/SKILL.md +2 -13
  224. package/skill-catalogue/integrator/SKILL.md +2 -13
  225. package/skill-catalogue/keeper/SKILL.md +2 -13
  226. package/skill-catalogue/launcher/SKILL.md +20 -16
  227. package/skill-catalogue/locksmith/SKILL.md +2 -13
  228. package/skill-catalogue/messenger/SKILL.md +2 -13
  229. package/skill-catalogue/observer/SKILL.md +2 -13
  230. package/skill-catalogue/publisher/SKILL.md +11 -17
  231. package/skill-catalogue/starter/SKILL.md +3 -13
  232. package/skill-catalogue/strategist/SKILL.md +14 -17
  233. package/skill-catalogue/writer/SKILL.md +7 -14
  234. package/src/admission-fixture.ts +572 -0
  235. package/src/admission.ts +816 -0
  236. package/src/agents-guide.ts +29 -0
  237. package/src/apply-command-options.check.ts +27 -0
  238. package/src/apply-plan-cli.ts +454 -14
  239. package/src/apply-plan.ts +112 -124
  240. package/src/apply-step-fixture.ts +236 -0
  241. package/src/apply-store.ts +584 -0
  242. package/src/approval-sheet.ts +164 -0
  243. package/src/body-command.ts +162 -0
  244. package/src/change-set-contract.ts +937 -0
  245. package/src/change-set-digest.ts +70 -0
  246. package/src/check-cli.ts +14 -3
  247. package/src/cli.ts +90 -22
  248. package/src/core.ts +973 -275
  249. package/src/dry-materialize.ts +353 -0
  250. package/src/generated/contract-schema.generated.ts +520 -0
  251. package/src/generated/package-scope.generated.ts +10 -0
  252. package/src/generated/plan-contracts.generated.ts +2840 -0
  253. package/src/host.ts +10 -0
  254. package/src/identity.ts +51 -0
  255. package/src/index.ts +72 -3
  256. package/src/inventory-adoption.ts +107 -29
  257. package/src/inventory-choice.ts +172 -0
  258. package/src/inventory-contract.ts +166 -0
  259. package/src/key-editor.ts +446 -0
  260. package/src/ledger-contract.ts +637 -0
  261. package/src/ledger-trust.ts +267 -0
  262. package/src/lockfile-invariants.ts +421 -0
  263. package/src/lockfile-readers.ts +749 -0
  264. package/src/lockfile-regen.ts +851 -0
  265. package/src/lockfile-tool-env.ts +131 -0
  266. package/src/materialize.ts +886 -0
  267. package/src/observe-repository.ts +1365 -0
  268. package/src/plan-bundle-setup-fixture.ts +200 -0
  269. package/src/plan-bundle.ts +964 -0
  270. package/src/plan-command.ts +509 -0
  271. package/src/plan-contract.ts +179 -0
  272. package/src/plan-digest.ts +102 -0
  273. package/src/plan-rules.ts +188 -0
  274. package/src/planned-bundle.ts +211 -0
  275. package/src/product-repository.ts +10 -1
  276. package/src/provenance-gate.ts +352 -0
  277. package/src/pull-request-body.ts +261 -0
  278. package/src/registry-snapshot.ts +534 -0
  279. package/src/release-age-edit.ts +430 -0
  280. package/src/root-entries.ts +81 -0
  281. package/src/setup-template-scripts.ts +571 -0
  282. package/src/setup-templates.ts +471 -0
  283. package/src/skills.ts +161 -18
  284. package/src/status.ts +557 -0
  285. package/src/types.ts +148 -13
  286. package/CHANGELOG.md +0 -131
@@ -0,0 +1,156 @@
1
+ // Writing the hub inventory from the repositories a founder chose on
2
+ // Advisor's repository-choice card (issue #1179), so a founder never
3
+ // hand-writes JSON. `launcher --repositories` is the command; this module
4
+ // decides what that command may write, and never writes anything itself.
5
+ //
6
+ // The chosen ids and the document built from them are validated against
7
+ // docs/contracts/repository-inventory.json (in the public repository;
8
+ // that exact path does not ship in this package, but this package's
9
+ // build packs and ships its own copy of the contract) through the shared
10
+ // contract checker (./inventory-contract.ts) -- the same check every later read of
11
+ // the stored inventory applies, so what this writes, Launcher reads back.
12
+ //
13
+ // An inventory already on disk that names a different set of repositories
14
+ // is never merged into or overwritten silently: the choice is refused, with
15
+ // the difference in counts and positions, unless the caller passes an
16
+ // explicit replace approval (`--replace-inventory`).
17
+ //
18
+ // Every message this module builds -- the refusal and the success line
19
+ // alike -- names a repository only by its position, never by its id: a
20
+ // stored inventory file, and the `--repositories` argument, are both
21
+ // document-shaped input an agent may relay verbatim, and a repository id is
22
+ // exactly the kind of short string a hostile entry could shape as
23
+ // prompt-injection text (see ./inventory-contract.ts's own header). The
24
+ // caller looks the reported position up in its own copy of the inventory
25
+ // file or its own `--repositories` argument to learn which repository it
26
+ // names.
27
+ import { inventoryKey } from "./identity.js";
28
+ import { readInventoryDocument, renderInventoryDocument, validateInventoryDocument, validateInventoryValue } from "./inventory-contract.js";
29
+ /** The command a refusal names for an explicit replace approval. */
30
+ export const REPLACE_INVENTORY_FLAG = "--replace-inventory";
31
+ function repositories(count) {
32
+ return `${count} ${count === 1 ? "repository" : "repositories"}`;
33
+ }
34
+ /** Added ids are named by their position in the `--repositories` argument, never by the id itself. */
35
+ function listAddedPositions(positions) {
36
+ return positions.length === 0 ? "" : ` (${positions.map((index) => `--repositories[${index}]`).join(", ")})`;
37
+ }
38
+ /**
39
+ * Removed ids are named by their position in an inventory's `repositories`
40
+ * array, never by the id itself. `label` distinguishes which file that
41
+ * position indexes into: `"stored inventory"` for the refusal, where the
42
+ * file on disk is still the one the positions were computed against, and
43
+ * `"replaced inventory"` for the success line after `--replace-inventory`
44
+ * writes -- by the time that line prints, the file on disk is already the
45
+ * new one, so reusing "stored inventory" there would point a reader at the
46
+ * wrong document (#1179).
47
+ */
48
+ function listRemovedPositions(positions, label) {
49
+ return positions.length === 0 ? "" : ` (${positions.map((index) => `repositories[${index}] in the ${label}`).join(", ")})`;
50
+ }
51
+ /**
52
+ * Decides what `launcher --repositories` writes into a hub whose stored
53
+ * inventory is `onDisk` -- the file's exact bytes, or `null` when there is
54
+ * none.
55
+ *
56
+ * Every comparison uses `inventoryKey(id, hubOwner)`: a bare id names a
57
+ * repository of the hub's own owner, and letter case is ignored.
58
+ *
59
+ * - The chosen ids must form a valid inventory on their own: at least one
60
+ * id, each one satisfying the contract's repository id rule, and no two
61
+ * naming the same repository. A refusal names positions only.
62
+ * - The stored inventory is read by the same strict reader, with the same
63
+ * owner: one that fails its contract, including one listing a repository
64
+ * twice (for example `app` and `<hubOwner>/app`), is replaced only with
65
+ * `replace`, and then with the chosen ids alone. It is never merged.
66
+ * - No inventory, or an empty one: the chosen repositories are written.
67
+ * - An inventory naming exactly the same repositories: nothing is written.
68
+ * - An inventory naming a different set: refused, reporting how many
69
+ * repositories each side has and the positions that would be added and
70
+ * removed (never the ids themselves), unless `replace` is true. A
71
+ * replacement keeps each kept repository's existing entry as it was (its
72
+ * `packages` included).
73
+ *
74
+ * The document to write is checked again by the same reader every later
75
+ * read uses, before it is returned.
76
+ */
77
+ export function resolveChosenInventory(onDisk, chosenIds, hubOwner, replace) {
78
+ if (chosenIds.length === 0) {
79
+ return { kind: "refuse", message: "--repositories must name at least one repository" };
80
+ }
81
+ const chosenCheck = validateInventoryValue({ schemaVersion: 1, repositories: chosenIds.map((id) => ({ id })) }, { hubOwner });
82
+ if (!chosenCheck.valid)
83
+ return { kind: "refuse", message: `--repositories is not a valid inventory: ${chosenCheck.reason}` };
84
+ let entries;
85
+ let added = chosenIds;
86
+ let removed = [];
87
+ let addedPositions = chosenIds.map((_, index) => index);
88
+ let removedPositions = [];
89
+ let replaced = "nothing";
90
+ let previousCount = 0;
91
+ if (onDisk === null) {
92
+ entries = chosenIds.map((id) => ({ id }));
93
+ }
94
+ else {
95
+ const stored = readInventoryDocument(onDisk, { hubOwner });
96
+ if (!stored.valid) {
97
+ if (!replace) {
98
+ return {
99
+ kind: "refuse",
100
+ message: `the hub inventory ${stored.reason}; to replace it with the ${repositories(chosenIds.length)} chosen, run again with ${REPLACE_INVENTORY_FLAG}`,
101
+ };
102
+ }
103
+ entries = chosenIds.map((id) => ({ id }));
104
+ replaced = "invalid";
105
+ }
106
+ else {
107
+ previousCount = stored.ids.length;
108
+ // Keys are unique on both sides: the reader and the chosen-id check above both refuse two ids with one key.
109
+ const storedByKey = new Map(stored.entries.map((entry) => [inventoryKey(entry.id, hubOwner), entry]));
110
+ const chosenKeys = new Set(chosenIds.map((id) => inventoryKey(id, hubOwner)));
111
+ added = chosenIds.filter((id) => !storedByKey.has(inventoryKey(id, hubOwner)));
112
+ removed = stored.ids.filter((id) => !chosenKeys.has(inventoryKey(id, hubOwner)));
113
+ // Positions, not ids: `added` indexes into the --repositories argument the caller passed; `removed` indexes into the stored inventory's own `repositories` array.
114
+ addedPositions = added.map((id) => chosenIds.indexOf(id));
115
+ removedPositions = removed.map((id) => stored.ids.indexOf(id));
116
+ if (stored.ids.length > 0 && added.length === 0 && removed.length === 0) {
117
+ return { kind: "resolved", chosen: { kind: "unchanged", count: stored.ids.length } };
118
+ }
119
+ if (stored.ids.length > 0) {
120
+ if (!replace) {
121
+ return {
122
+ kind: "refuse",
123
+ message: `the hub inventory already lists ${repositories(stored.ids.length)} and the choice has ${chosenIds.length}: ` +
124
+ `${added.length} to add${listAddedPositions(addedPositions)}, ${removed.length} to remove${listRemovedPositions(removedPositions, "stored inventory")}. ` +
125
+ `Launcher never merges or overwrites an inventory silently; to replace it with the choice, run again with ${REPLACE_INVENTORY_FLAG}`,
126
+ };
127
+ }
128
+ replaced = "differing";
129
+ }
130
+ entries = chosenIds.map((id) => storedByKey.get(inventoryKey(id, hubOwner)) ?? { id });
131
+ }
132
+ }
133
+ const document = renderInventoryDocument(entries);
134
+ const written = validateInventoryDocument(document, { hubOwner });
135
+ if (!written.valid)
136
+ return { kind: "refuse", message: `the inventory to write ${written.reason}` };
137
+ return {
138
+ kind: "resolved",
139
+ chosen: { kind: "write", document, count: written.ids.length, previousCount, added, removed, addedPositions, removedPositions, replaced },
140
+ };
141
+ }
142
+ /** One line for the apply message saying what happened to the inventory. */
143
+ export function describeChosenInventory(chosen) {
144
+ if (chosen.kind === "unchanged") {
145
+ return `inventory: unchanged -- it already lists the ${repositories(chosen.count)} you chose`;
146
+ }
147
+ const count = repositories(chosen.count);
148
+ if (chosen.replaced === "invalid")
149
+ return `inventory: replaced one that failed its contract with the ${count} you chose`;
150
+ if (chosen.replaced === "differing") {
151
+ return (`inventory: replaced ${chosen.previousCount} with the ${count} you chose -- ` +
152
+ `${chosen.added.length} added${listAddedPositions(chosen.addedPositions)}, ${chosen.removed.length} removed${listRemovedPositions(chosen.removedPositions, "replaced inventory")}`);
153
+ }
154
+ return `inventory: wrote the ${count} you chose`;
155
+ }
156
+ //# sourceMappingURL=inventory-choice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inventory-choice.js","sourceRoot":"","sources":["../src/inventory-choice.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ /** The contract's file name, as packed. */
2
+ export declare const INVENTORY_CONTRACT = "repository-inventory.json";
3
+ /** Points a refusal at the one place the shape is written down. */
4
+ export declare const INVENTORY_CONTRACT_POINTER = "see docs/contracts/repository-inventory.json";
5
+ /**
6
+ * The result of checking one inventory document: its repository ids, in
7
+ * document order, or the reason it is refused. `reason` names every field
8
+ * at fault, separated by `; `, and ends with a pointer to the contract.
9
+ */
10
+ export type InventoryValidation = {
11
+ readonly valid: true;
12
+ readonly ids: readonly string[];
13
+ } | {
14
+ readonly valid: false;
15
+ readonly reason: string;
16
+ };
17
+ /** One inventory entry, exactly as the contract allows it. */
18
+ export interface InventoryEntry {
19
+ readonly id: string;
20
+ readonly packages?: readonly {
21
+ readonly name: string;
22
+ readonly version?: string;
23
+ readonly wiring?: string;
24
+ }[];
25
+ }
26
+ /**
27
+ * The first id, if any, that names the same repository as an earlier one
28
+ * under `inventoryKey()`: its position and the earlier position.
29
+ */
30
+ export declare function firstRepeatedRepository(ids: readonly string[], hubOwner?: string): {
31
+ readonly index: number;
32
+ readonly earlier: number;
33
+ } | undefined;
34
+ /** Options for reading an inventory document. */
35
+ export interface InventoryReadOptions {
36
+ /**
37
+ * The hub's owner, whenever the caller knows it. A bare id and
38
+ * `<hubOwner>/<id>` then name the same repository, so a document listing
39
+ * both is refused instead of carrying two entries for one repository.
40
+ */
41
+ readonly hubOwner?: string;
42
+ }
43
+ /** A valid document's entries, as internal readers need them; `InventoryValidation` exposes only the ids. */
44
+ export type InventoryRead = {
45
+ readonly valid: true;
46
+ readonly ids: readonly string[];
47
+ readonly entries: readonly InventoryEntry[];
48
+ } | {
49
+ readonly valid: false;
50
+ readonly reason: string;
51
+ };
52
+ /**
53
+ * Validates an already-parsed value against the inventory contract through
54
+ * the shared checker, then applies the duplicate rule. Never throws for a
55
+ * bad value.
56
+ */
57
+ export declare function validateInventoryValue(value: unknown, options?: InventoryReadOptions): InventoryValidation;
58
+ /**
59
+ * Reads and checks one inventory document. Bytes -- as every file read
60
+ * passes them, from `WorkspaceHost.readBytes()` -- go straight to the
61
+ * shared strict reader, so bytes that are not valid UTF-8 are refused
62
+ * rather than silently replaced. Text is accepted for a document built in
63
+ * memory, and one holding a lone surrogate is refused before encoding.
64
+ */
65
+ export declare function readInventoryDocument(raw: string | Uint8Array, options?: InventoryReadOptions): InventoryRead;
66
+ /**
67
+ * Strictly validates an inventory document against
68
+ * docs/contracts/repository-inventory.json. It is read as strict JSON by the
69
+ * shared reader -- bytes that are not valid UTF-8, a syntax error (reported
70
+ * by position only), a key repeated in any object, or a leading byte order
71
+ * mark is refused -- and the value is then checked by the shared contract
72
+ * checker and the duplicate rule. Every read of an inventory file passes
73
+ * its exact bytes: `--inventory`, the stored `clossys/.state/inventory.json`
74
+ * on every run, `readInventoryRepositories()`, and the document
75
+ * `launcher --repositories` writes, before it writes it. The contract's
76
+ * source path does not ship in this package -- this package's build packs
77
+ * and ships its own copy of it -- and the stored inventory path is
78
+ * generated on the hub at runtime and is not part of this package at all.
79
+ */
80
+ export declare function validateInventoryDocument(raw: string | Uint8Array, options?: InventoryReadOptions): InventoryValidation;
81
+ /**
82
+ * Whether one id satisfies the contract's `definitions/repositoryId` -- a
83
+ * bare repository name or `owner/name`, by the same GitHub owner and
84
+ * repository rules Launcher applies when it resolves a sibling clone.
85
+ */
86
+ export declare function isValidInventoryId(id: string): boolean;
87
+ /** The inventory document text Launcher writes for these entries: two-space JSON and a final newline. */
88
+ export declare function renderInventoryDocument(entries: readonly InventoryEntry[]): string;
89
+ //# sourceMappingURL=inventory-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inventory-contract.d.ts","sourceRoot":"","sources":["../src/inventory-contract.ts"],"names":[],"mappings":"AAoBA,2CAA2C;AAC3C,eAAO,MAAM,kBAAkB,8BAA8B,CAAC;AAE9D,mEAAmE;AACnE,eAAO,MAAM,0BAA0B,iDAAiD,CAAC;AAEzF;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAC3B;IAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GACzD;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC/G;AAMD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,SAAS,MAAM,EAAE,EACtB,QAAQ,CAAC,EAAE,MAAM,GAChB;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CASlE;AAED,iDAAiD;AACjD,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,6GAA6G;AAC7G,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAA;CAAE,GACtG;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AA2BvD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,oBAAyB,GAAG,mBAAmB,CAE9G;AAKD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,GAAE,oBAAyB,GAAG,aAAa,CAWjH;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,GAAE,oBAAyB,GAAG,mBAAmB,CAE3H;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,yGAAyG;AACzG,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,GAAG,MAAM,CAElF"}
@@ -0,0 +1,121 @@
1
+ // The hub repository inventory (issues #996, #1334 and #1179), validated
2
+ // against the shared contract docs/contracts/repository-inventory.json (in
3
+ // the public repository; that exact path does not ship in this package,
4
+ // but this package's build packs and ships its own copy of the contract).
5
+ // This package's build packs that file into src/generated/ beside the plan
6
+ // and brief contracts, and checks it with the generated copy of the one
7
+ // contract checker @clossys/advisor also uses -- so an inventory Launcher
8
+ // writes, an inventory Launcher reads back, and a repository id Advisor
9
+ // offers on its repository-choice card are all judged by one definition.
10
+ //
11
+ // Refusals follow the checker's conventions: they name a field by its
12
+ // position (for example `repositories[2].id`), never quote a repository id
13
+ // or any other value from the document, and report a JSON syntax error by
14
+ // position only.
15
+ import { readContractDocument, validateAgainstContract } from "./generated/contract-schema.generated.js";
16
+ import { inventoryKey } from "./identity.js";
17
+ import { loadContract } from "./plan-contract.js";
18
+ /** The contract's file name, as packed. */
19
+ export const INVENTORY_CONTRACT = "repository-inventory.json";
20
+ /** Points a refusal at the one place the shape is written down. */
21
+ export const INVENTORY_CONTRACT_POINTER = "see docs/contracts/repository-inventory.json";
22
+ function describeViolation(violation) {
23
+ return violation.path === "" ? violation.message : `${violation.path} ${violation.message}`;
24
+ }
25
+ /**
26
+ * The first id, if any, that names the same repository as an earlier one
27
+ * under `inventoryKey()`: its position and the earlier position.
28
+ */
29
+ export function firstRepeatedRepository(ids, hubOwner) {
30
+ const seen = new Map();
31
+ for (const [index, id] of ids.entries()) {
32
+ const key = inventoryKey(id, hubOwner);
33
+ const earlier = seen.get(key);
34
+ if (earlier !== undefined)
35
+ return { index, earlier };
36
+ seen.set(key, index);
37
+ }
38
+ return undefined;
39
+ }
40
+ function checkInventoryValue(value, options) {
41
+ const violations = validateAgainstContract(loadContract(INVENTORY_CONTRACT), value, loadContract);
42
+ if (violations.length > 0) {
43
+ return { valid: false, reason: `${violations.map(describeViolation).join("; ")} (${INVENTORY_CONTRACT_POINTER})` };
44
+ }
45
+ const entries = value.repositories;
46
+ const ids = entries.map((entry) => entry.id);
47
+ const repeated = firstRepeatedRepository(ids, options.hubOwner);
48
+ if (repeated !== undefined) {
49
+ const rule = options.hubOwner === undefined
50
+ ? "repository ids are compared case-insensitively"
51
+ : "repository ids are compared case-insensitively, and a bare id names a repository of the hub's own owner";
52
+ return {
53
+ valid: false,
54
+ reason: `repositories[${repeated.index}].id names the same repository as repositories[${repeated.earlier}].id (${rule}; ${INVENTORY_CONTRACT_POINTER})`,
55
+ };
56
+ }
57
+ return { valid: true, ids, entries };
58
+ }
59
+ function withoutEntries(read) {
60
+ return read.valid ? { valid: true, ids: read.ids } : read;
61
+ }
62
+ /**
63
+ * Validates an already-parsed value against the inventory contract through
64
+ * the shared checker, then applies the duplicate rule. Never throws for a
65
+ * bad value.
66
+ */
67
+ export function validateInventoryValue(value, options = {}) {
68
+ return withoutEntries(checkInventoryValue(value, options));
69
+ }
70
+ /** A lone surrogate in a JS string, which has no UTF-8 encoding; TextEncoder would silently replace it. */
71
+ const LONE_SURROGATE = /[\uD800-\uDFFF]/u;
72
+ /**
73
+ * Reads and checks one inventory document. Bytes -- as every file read
74
+ * passes them, from `WorkspaceHost.readBytes()` -- go straight to the
75
+ * shared strict reader, so bytes that are not valid UTF-8 are refused
76
+ * rather than silently replaced. Text is accepted for a document built in
77
+ * memory, and one holding a lone surrogate is refused before encoding.
78
+ */
79
+ export function readInventoryDocument(raw, options = {}) {
80
+ if (typeof raw === "string" && LONE_SURROGATE.test(raw)) {
81
+ return { valid: false, reason: `is not well-formed Unicode: it contains a lone surrogate, which has no UTF-8 encoding (${INVENTORY_CONTRACT_POINTER})` };
82
+ }
83
+ let value;
84
+ try {
85
+ value = readContractDocument(typeof raw === "string" ? new TextEncoder().encode(raw) : raw);
86
+ }
87
+ catch (cause) {
88
+ return { valid: false, reason: `${cause instanceof Error ? cause.message : String(cause)} (${INVENTORY_CONTRACT_POINTER})` };
89
+ }
90
+ return checkInventoryValue(value, options);
91
+ }
92
+ /**
93
+ * Strictly validates an inventory document against
94
+ * docs/contracts/repository-inventory.json. It is read as strict JSON by the
95
+ * shared reader -- bytes that are not valid UTF-8, a syntax error (reported
96
+ * by position only), a key repeated in any object, or a leading byte order
97
+ * mark is refused -- and the value is then checked by the shared contract
98
+ * checker and the duplicate rule. Every read of an inventory file passes
99
+ * its exact bytes: `--inventory`, the stored `clossys/.state/inventory.json`
100
+ * on every run, `readInventoryRepositories()`, and the document
101
+ * `launcher --repositories` writes, before it writes it. The contract's
102
+ * source path does not ship in this package -- this package's build packs
103
+ * and ships its own copy of it -- and the stored inventory path is
104
+ * generated on the hub at runtime and is not part of this package at all.
105
+ */
106
+ export function validateInventoryDocument(raw, options = {}) {
107
+ return withoutEntries(readInventoryDocument(raw, options));
108
+ }
109
+ /**
110
+ * Whether one id satisfies the contract's `definitions/repositoryId` -- a
111
+ * bare repository name or `owner/name`, by the same GitHub owner and
112
+ * repository rules Launcher applies when it resolves a sibling clone.
113
+ */
114
+ export function isValidInventoryId(id) {
115
+ return validateAgainstContract({ $ref: `${INVENTORY_CONTRACT}#/definitions/repositoryId` }, id, loadContract).length === 0;
116
+ }
117
+ /** The inventory document text Launcher writes for these entries: two-space JSON and a final newline. */
118
+ export function renderInventoryDocument(entries) {
119
+ return `${JSON.stringify({ schemaVersion: 1, repositories: entries }, null, 2)}\n`;
120
+ }
121
+ //# sourceMappingURL=inventory-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inventory-contract.js","sourceRoot":"","sources":["../src/inventory-contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /** One JSON-pointer edit. `remove` deletes the value the pointer addresses; otherwise `value` is written there. */
2
+ export interface JsonPointerEdit {
3
+ readonly pointer: string;
4
+ readonly value?: unknown;
5
+ readonly remove?: boolean;
6
+ }
7
+ /** What `valueAtJsonPointer` found. `found` is false when any token along the pointer is absent. */
8
+ export interface JsonPointerValue {
9
+ readonly found: boolean;
10
+ readonly value: unknown;
11
+ }
12
+ /** Thrown when an edit would rewrite bytes outside the edited tokens. */
13
+ export declare class JsonEditUnstableError extends TypeError {
14
+ constructor();
15
+ }
16
+ /**
17
+ * The value a JSON pointer addresses in `text`, or `{ found: false }` when
18
+ * a token along the pointer is absent. The text must be JSON; a parse
19
+ * failure throws a TypeError that does not quote the text.
20
+ */
21
+ export declare function valueAtJsonPointer(text: string, pointer: string): JsonPointerValue;
22
+ /**
23
+ * Applies `edits` to `text` and returns the edited document. An empty edit
24
+ * list returns `text` unchanged. Indent, the order of existing keys, and
25
+ * whether the text ended in a newline are taken from `text` and used for
26
+ * the render. The render is parsed and rendered a second time, and the two
27
+ * results must be the same bytes.
28
+ */
29
+ export declare function editJsonPointer(text: string, edits: readonly JsonPointerEdit[]): string;
30
+ //# sourceMappingURL=key-editor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-editor.d.ts","sourceRoot":"","sources":["../src/key-editor.ts"],"names":[],"mappings":"AASA,mHAAmH;AACnH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAUD,oGAAoG;AACpG,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,yEAAyE;AACzE,qBAAa,qBAAsB,SAAQ,SAAS;;CAInD;AAgWD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAmBlF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,eAAe,EAAE,GAAG,MAAM,CAyBvF"}