@hasna/instructions 0.6.0 → 0.7.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 (265) hide show
  1. package/README.md +149 -8
  2. package/dist/chunks/apply-7svah2qm.js +25 -0
  3. package/dist/chunks/apply-jp13sa1e.js +25 -0
  4. package/dist/chunks/database-wstmr56z.js +23 -0
  5. package/dist/chunks/index-1cnyjnd7.js +623 -0
  6. package/dist/chunks/index-35z65jsa.js +198 -0
  7. package/dist/chunks/index-3djthcm6.js +117 -0
  8. package/dist/chunks/index-4pfq42t5.js +7300 -0
  9. package/dist/chunks/index-5d7zdftc.js +186 -0
  10. package/dist/chunks/index-7m4j8a33.js +12578 -0
  11. package/dist/chunks/index-8rb7ng2y.js +148 -0
  12. package/dist/chunks/index-arrrx0ed.js +344 -0
  13. package/dist/chunks/index-bsan4c16.js +48 -0
  14. package/dist/chunks/index-bzyxvwcd.js +625 -0
  15. package/dist/chunks/index-fpeyzn3n.js +186 -0
  16. package/dist/chunks/index-fwbs6tma.js +534 -0
  17. package/dist/chunks/index-ma2xmp6c.js +14 -0
  18. package/dist/chunks/index-n3fjspr0.js +119 -0
  19. package/dist/chunks/index-nnwp92tk.js +19 -0
  20. package/dist/chunks/index-z3ma4aqk.js +532 -0
  21. package/dist/chunks/legacy-store-migration-z2m5yzyk.js +322 -0
  22. package/dist/chunks/local-5wvv5xv3.js +694 -0
  23. package/dist/chunks/local-zxa5dpf4.js +518 -0
  24. package/dist/chunks/package-manager-guard-x70xrwk2.js +391 -0
  25. package/dist/chunks/redact-hnq4sc4r.js +16 -0
  26. package/dist/chunks/s3-backup-n2z4fwyh.js +240 -0
  27. package/dist/chunks/s3-config-hhght91k.js +14 -0
  28. package/dist/chunks/s3-object-store-90tgbh6k.js +14 -0
  29. package/dist/chunks/sync-2075deve.js +34 -0
  30. package/dist/chunks/sync-r8t7zsp3.js +34 -0
  31. package/dist/chunks/template-fsfxpe4p.js +16 -0
  32. package/dist/chunks/template-wgnzdn7h.js +16 -0
  33. package/dist/cli/index.js +1681 -15643
  34. package/dist/data/config-store.d.ts +2 -1
  35. package/dist/data/config-store.d.ts.map +1 -1
  36. package/dist/db/database.d.ts +3 -5
  37. package/dist/db/database.d.ts.map +1 -1
  38. package/dist/db/local.d.ts +38 -0
  39. package/dist/db/local.d.ts.map +1 -0
  40. package/dist/index.d.ts +1 -1
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +13809 -12998
  43. package/dist/lib/apply.d.ts +7 -0
  44. package/dist/lib/apply.d.ts.map +1 -1
  45. package/dist/lib/compact-output.d.ts +1 -0
  46. package/dist/lib/compact-output.d.ts.map +1 -1
  47. package/dist/lib/export.d.ts +22 -3
  48. package/dist/lib/export.d.ts.map +1 -1
  49. package/dist/lib/ids.d.ts +4 -0
  50. package/dist/lib/ids.d.ts.map +1 -0
  51. package/dist/lib/import.d.ts +18 -0
  52. package/dist/lib/import.d.ts.map +1 -1
  53. package/dist/lib/instruction-graph.d.ts.map +1 -1
  54. package/dist/lib/legacy-store-migration.d.ts +39 -0
  55. package/dist/lib/legacy-store-migration.d.ts.map +1 -0
  56. package/dist/lib/managed-skill-runtimes.d.ts +1 -0
  57. package/dist/lib/managed-skill-runtimes.d.ts.map +1 -1
  58. package/dist/lib/session-render.d.ts +3 -0
  59. package/dist/lib/session-render.d.ts.map +1 -1
  60. package/dist/lib/sync-dir.d.ts.map +1 -1
  61. package/dist/mcp/index.js +73 -9462
  62. package/dist/sdk/index.d.ts +2 -0
  63. package/dist/sdk/index.d.ts.map +1 -1
  64. package/dist/sdk/index.js +205 -10
  65. package/dist/sdk/resolve.d.ts +1 -1
  66. package/dist/sdk/resolve.d.ts.map +1 -1
  67. package/dist/sdk/v1-client.d.ts +57 -0
  68. package/dist/sdk/v1-client.d.ts.map +1 -0
  69. package/dist/sdk/v1.generated.d.ts +212 -21
  70. package/dist/sdk/v1.generated.d.ts.map +1 -1
  71. package/dist/server/cloud.d.ts.map +1 -1
  72. package/dist/server/index.d.ts +13 -5
  73. package/dist/server/index.d.ts.map +1 -1
  74. package/dist/server/index.js +8337 -7495
  75. package/dist/server/openapi.d.ts +1264 -92
  76. package/dist/server/openapi.d.ts.map +1 -1
  77. package/dist/server/v1.d.ts +25 -1
  78. package/dist/server/v1.d.ts.map +1 -1
  79. package/dist/status.d.ts +12 -2
  80. package/dist/status.d.ts.map +1 -1
  81. package/dist/storage/cloud-store.d.ts +57 -1
  82. package/dist/storage/cloud-store.d.ts.map +1 -1
  83. package/dist/storage/index.d.ts +7 -0
  84. package/dist/storage/index.d.ts.map +1 -0
  85. package/dist/storage/index.js +725 -0
  86. package/dist/storage/s3-backup.d.ts +76 -0
  87. package/dist/storage/s3-backup.d.ts.map +1 -0
  88. package/dist/storage/s3-config.d.ts +46 -0
  89. package/dist/storage/s3-config.d.ts.map +1 -0
  90. package/dist/storage/s3-object-store.d.ts +70 -0
  91. package/dist/storage/s3-object-store.d.ts.map +1 -0
  92. package/dist/types/index.d.ts +65 -3
  93. package/dist/types/index.d.ts.map +1 -1
  94. package/hasna.contract.json +110 -0
  95. package/migrations/0001_instructions.sql +63 -0
  96. package/migrations/0002_api_keys.sql +20 -0
  97. package/migrations/0003_idempotency_receipts.sql +24 -0
  98. package/package.json +29 -14
  99. package/assets/skills/inbox/SKILL.md +0 -86
  100. package/dist/cli/add-duplicate-target.test.d.ts +0 -2
  101. package/dist/cli/add-duplicate-target.test.d.ts.map +0 -1
  102. package/dist/cli/add-reference-update.test.d.ts +0 -2
  103. package/dist/cli/add-reference-update.test.d.ts.map +0 -1
  104. package/dist/cli/apply-unchanged-snapshot-bloat.test.d.ts +0 -2
  105. package/dist/cli/apply-unchanged-snapshot-bloat.test.d.ts.map +0 -1
  106. package/dist/cli/doctor-reference-duplicates.test.d.ts +0 -2
  107. package/dist/cli/doctor-reference-duplicates.test.d.ts.map +0 -1
  108. package/dist/cli/fail-closed-no-env.test.d.ts +0 -2
  109. package/dist/cli/fail-closed-no-env.test.d.ts.map +0 -1
  110. package/dist/cli/output.test.d.ts +0 -2
  111. package/dist/cli/output.test.d.ts.map +0 -1
  112. package/dist/cli/profile-binding.test.d.ts +0 -2
  113. package/dist/cli/profile-binding.test.d.ts.map +0 -1
  114. package/dist/cli/profile-reads.test.d.ts +0 -2
  115. package/dist/cli/profile-reads.test.d.ts.map +0 -1
  116. package/dist/cli/profile-update.test.d.ts +0 -2
  117. package/dist/cli/profile-update.test.d.ts.map +0 -1
  118. package/dist/cli/project-context.test.d.ts +0 -2
  119. package/dist/cli/project-context.test.d.ts.map +0 -1
  120. package/dist/cli/raw-store-root.test.d.ts +0 -2
  121. package/dist/cli/raw-store-root.test.d.ts.map +0 -1
  122. package/dist/cli/session-apply-managed-input-bound.test.d.ts +0 -2
  123. package/dist/cli/session-apply-managed-input-bound.test.d.ts.map +0 -1
  124. package/dist/cli/session.test.d.ts +0 -2
  125. package/dist/cli/session.test.d.ts.map +0 -1
  126. package/dist/cli/station-profile.test.d.ts +0 -2
  127. package/dist/cli/station-profile.test.d.ts.map +0 -1
  128. package/dist/data/config-store.test.d.ts +0 -2
  129. package/dist/data/config-store.test.d.ts.map +0 -1
  130. package/dist/db/configs.test.d.ts +0 -2
  131. package/dist/db/configs.test.d.ts.map +0 -1
  132. package/dist/db/database.test.d.ts +0 -2
  133. package/dist/db/database.test.d.ts.map +0 -1
  134. package/dist/db/machines.test.d.ts +0 -2
  135. package/dist/db/machines.test.d.ts.map +0 -1
  136. package/dist/db/profile-assets.test.d.ts +0 -2
  137. package/dist/db/profile-assets.test.d.ts.map +0 -1
  138. package/dist/db/profile-config-bindings.test.d.ts +0 -2
  139. package/dist/db/profile-config-bindings.test.d.ts.map +0 -1
  140. package/dist/db/profiles.test.d.ts +0 -2
  141. package/dist/db/profiles.test.d.ts.map +0 -1
  142. package/dist/db/snapshots.test.d.ts +0 -2
  143. package/dist/db/snapshots.test.d.ts.map +0 -1
  144. package/dist/lib/app-home.test.d.ts +0 -2
  145. package/dist/lib/app-home.test.d.ts.map +0 -1
  146. package/dist/lib/apply-batch.test.d.ts +0 -2
  147. package/dist/lib/apply-batch.test.d.ts.map +0 -1
  148. package/dist/lib/apply-dry-run-verdict.test.d.ts +0 -2
  149. package/dist/lib/apply-dry-run-verdict.test.d.ts.map +0 -1
  150. package/dist/lib/apply-secret-placeholder-guard.test.d.ts +0 -2
  151. package/dist/lib/apply-secret-placeholder-guard.test.d.ts.map +0 -1
  152. package/dist/lib/apply-text-format-credential-guard.test.d.ts +0 -2
  153. package/dist/lib/apply-text-format-credential-guard.test.d.ts.map +0 -1
  154. package/dist/lib/apply.test.d.ts +0 -2
  155. package/dist/lib/apply.test.d.ts.map +0 -1
  156. package/dist/lib/asset-plan.test.d.ts +0 -2
  157. package/dist/lib/asset-plan.test.d.ts.map +0 -1
  158. package/dist/lib/client-types.test.d.ts +0 -2
  159. package/dist/lib/client-types.test.d.ts.map +0 -1
  160. package/dist/lib/codewith-shared-todos-storage-standard.test.d.ts +0 -2
  161. package/dist/lib/codewith-shared-todos-storage-standard.test.d.ts.map +0 -1
  162. package/dist/lib/compact-output.test.d.ts +0 -2
  163. package/dist/lib/compact-output.test.d.ts.map +0 -1
  164. package/dist/lib/config-target-identity.test.d.ts +0 -2
  165. package/dist/lib/config-target-identity.test.d.ts.map +0 -1
  166. package/dist/lib/cursor-authority.test.d.ts +0 -2
  167. package/dist/lib/cursor-authority.test.d.ts.map +0 -1
  168. package/dist/lib/dangerous-operation-guard-standard.test.d.ts +0 -2
  169. package/dist/lib/dangerous-operation-guard-standard.test.d.ts.map +0 -1
  170. package/dist/lib/diff-redaction.test.d.ts +0 -2
  171. package/dist/lib/diff-redaction.test.d.ts.map +0 -1
  172. package/dist/lib/export-import.test.d.ts +0 -2
  173. package/dist/lib/export-import.test.d.ts.map +0 -1
  174. package/dist/lib/global-agent-rules-render-integration.test.d.ts +0 -2
  175. package/dist/lib/global-agent-rules-render-integration.test.d.ts.map +0 -1
  176. package/dist/lib/global-agent-rules-standard.test.d.ts +0 -2
  177. package/dist/lib/global-agent-rules-standard.test.d.ts.map +0 -1
  178. package/dist/lib/global-agent-rules-stored-content.test.d.ts +0 -2
  179. package/dist/lib/global-agent-rules-stored-content.test.d.ts.map +0 -1
  180. package/dist/lib/global-source-coverage.test.d.ts +0 -2
  181. package/dist/lib/global-source-coverage.test.d.ts.map +0 -1
  182. package/dist/lib/instruction-graph-apply.test.d.ts +0 -2
  183. package/dist/lib/instruction-graph-apply.test.d.ts.map +0 -1
  184. package/dist/lib/instruction-graph.test.d.ts +0 -2
  185. package/dist/lib/instruction-graph.test.d.ts.map +0 -1
  186. package/dist/lib/machine.test.d.ts +0 -2
  187. package/dist/lib/machine.test.d.ts.map +0 -1
  188. package/dist/lib/managed-skill-runtimes.test.d.ts +0 -2
  189. package/dist/lib/managed-skill-runtimes.test.d.ts.map +0 -1
  190. package/dist/lib/package-manager-guard.test.d.ts +0 -2
  191. package/dist/lib/package-manager-guard.test.d.ts.map +0 -1
  192. package/dist/lib/project-context-prepared-file.test.d.ts +0 -2
  193. package/dist/lib/project-context-prepared-file.test.d.ts.map +0 -1
  194. package/dist/lib/project-context.test.d.ts +0 -2
  195. package/dist/lib/project-context.test.d.ts.map +0 -1
  196. package/dist/lib/project-dashboard-standard.test.d.ts +0 -2
  197. package/dist/lib/project-dashboard-standard.test.d.ts.map +0 -1
  198. package/dist/lib/provider-assets-adapters.test.d.ts +0 -2
  199. package/dist/lib/provider-assets-adapters.test.d.ts.map +0 -1
  200. package/dist/lib/provider-context.test.d.ts +0 -2
  201. package/dist/lib/provider-context.test.d.ts.map +0 -1
  202. package/dist/lib/provider-version-adapters.test.d.ts +0 -2
  203. package/dist/lib/provider-version-adapters.test.d.ts.map +0 -1
  204. package/dist/lib/redact.test.d.ts +0 -2
  205. package/dist/lib/redact.test.d.ts.map +0 -1
  206. package/dist/lib/session-apply.test.d.ts +0 -2
  207. package/dist/lib/session-apply.test.d.ts.map +0 -1
  208. package/dist/lib/session-authority.test.d.ts +0 -2
  209. package/dist/lib/session-authority.test.d.ts.map +0 -1
  210. package/dist/lib/session-render-managed-output-bound.test.d.ts +0 -2
  211. package/dist/lib/session-render-managed-output-bound.test.d.ts.map +0 -1
  212. package/dist/lib/session-render-ownership.test.d.ts +0 -2
  213. package/dist/lib/session-render-ownership.test.d.ts.map +0 -1
  214. package/dist/lib/session-render-rule-currency-floor.test.d.ts +0 -2
  215. package/dist/lib/session-render-rule-currency-floor.test.d.ts.map +0 -1
  216. package/dist/lib/session-render-silent-source-drop.test.d.ts +0 -2
  217. package/dist/lib/session-render-silent-source-drop.test.d.ts.map +0 -1
  218. package/dist/lib/session-render-state-hermeticity.test.d.ts +0 -2
  219. package/dist/lib/session-render-state-hermeticity.test.d.ts.map +0 -1
  220. package/dist/lib/session-render-state.test.d.ts +0 -2
  221. package/dist/lib/session-render-state.test.d.ts.map +0 -1
  222. package/dist/lib/session-render.test.d.ts +0 -2
  223. package/dist/lib/session-render.test.d.ts.map +0 -1
  224. package/dist/lib/station-profile.test.d.ts +0 -2
  225. package/dist/lib/station-profile.test.d.ts.map +0 -1
  226. package/dist/lib/sync-dir.test.d.ts +0 -2
  227. package/dist/lib/sync-dir.test.d.ts.map +0 -1
  228. package/dist/lib/sync-dry-run-unchanged.test.d.ts +0 -2
  229. package/dist/lib/sync-dry-run-unchanged.test.d.ts.map +0 -1
  230. package/dist/lib/sync-known.test.d.ts +0 -2
  231. package/dist/lib/sync-known.test.d.ts.map +0 -1
  232. package/dist/lib/sync.test.d.ts +0 -2
  233. package/dist/lib/sync.test.d.ts.map +0 -1
  234. package/dist/lib/template.test.d.ts +0 -2
  235. package/dist/lib/template.test.d.ts.map +0 -1
  236. package/dist/lib/transforms.test.d.ts +0 -2
  237. package/dist/lib/transforms.test.d.ts.map +0 -1
  238. package/dist/lib/transport-resolver.test.d.ts +0 -2
  239. package/dist/lib/transport-resolver.test.d.ts.map +0 -1
  240. package/dist/mcp/create-config-target-guard.test.d.ts +0 -2
  241. package/dist/mcp/create-config-target-guard.test.d.ts.map +0 -1
  242. package/dist/mcp/early-args.test.d.ts +0 -2
  243. package/dist/mcp/early-args.test.d.ts.map +0 -1
  244. package/dist/mcp/http.test.d.ts +0 -2
  245. package/dist/mcp/http.test.d.ts.map +0 -1
  246. package/dist/mcp/mcp.test.d.ts +0 -2
  247. package/dist/mcp/mcp.test.d.ts.map +0 -1
  248. package/dist/sdk/resolve.test.d.ts +0 -2
  249. package/dist/sdk/resolve.test.d.ts.map +0 -1
  250. package/dist/sdk/sdk-bundle-self-contained.test.d.ts +0 -2
  251. package/dist/sdk/sdk-bundle-self-contained.test.d.ts.map +0 -1
  252. package/dist/server/cloud-auth.test.d.ts +0 -2
  253. package/dist/server/cloud-auth.test.d.ts.map +0 -1
  254. package/dist/server/cloud.test.d.ts +0 -2
  255. package/dist/server/cloud.test.d.ts.map +0 -1
  256. package/dist/server/early-args.test.d.ts +0 -2
  257. package/dist/server/early-args.test.d.ts.map +0 -1
  258. package/dist/server/profile-contract.test.d.ts +0 -2
  259. package/dist/server/profile-contract.test.d.ts.map +0 -1
  260. package/dist/status.test.d.ts +0 -2
  261. package/dist/status.test.d.ts.map +0 -1
  262. package/dist/storage/cloud-store.test.d.ts +0 -2
  263. package/dist/storage/cloud-store.test.d.ts.map +0 -1
  264. package/dist/test-support/preload-state-home.d.ts +0 -2
  265. package/dist/test-support/preload-state-home.d.ts.map +0 -1
@@ -0,0 +1,518 @@
1
+ // @bun
2
+ import {
3
+ boundedReadPage,
4
+ detectMachineContext,
5
+ legacyProfileConfigBinding,
6
+ normalizeBoundedReadOptions,
7
+ normalizeOsFamily,
8
+ normalizeProfileAssetBinding,
9
+ normalizeProfileConfigBinding
10
+ } from "./index-7m4j8a33.js";
11
+ import {
12
+ ConfigNotFoundError,
13
+ ProfileNotFoundError
14
+ } from "./index-n3fjspr0.js";
15
+ import {
16
+ getDatabase,
17
+ insertFeedback,
18
+ resetLocalDatabase
19
+ } from "./index-5d7zdftc.js";
20
+ import {
21
+ now,
22
+ slugify,
23
+ uuid
24
+ } from "./index-ma2xmp6c.js";
25
+ import"./index-fpeyzn3n.js";
26
+ import"./index-bsan4c16.js";
27
+
28
+ // src/db/snapshots.ts
29
+ function createSnapshot(configId, content, version, db) {
30
+ const d = db || getDatabase();
31
+ const id = uuid();
32
+ const ts = now();
33
+ d.run("INSERT INTO config_snapshots (id, config_id, content, version, created_at) VALUES (?, ?, ?, ?, ?)", [id, configId, content, version, ts]);
34
+ return { id, config_id: configId, content, version, created_at: ts };
35
+ }
36
+ function listSnapshots(configId, db) {
37
+ const d = db || getDatabase();
38
+ return d.query("SELECT * FROM config_snapshots WHERE config_id = ? ORDER BY version DESC").all(configId);
39
+ }
40
+ function getSnapshot(id, db) {
41
+ const d = db || getDatabase();
42
+ return d.query("SELECT * FROM config_snapshots WHERE id = ?").get(id);
43
+ }
44
+ function getSnapshotByVersion(configId, version, db) {
45
+ const d = db || getDatabase();
46
+ return d.query("SELECT * FROM config_snapshots WHERE config_id = ? AND version = ?").get(configId, version);
47
+ }
48
+ function pruneSnapshots(configId, keep = 10, db) {
49
+ const d = db || getDatabase();
50
+ const result = d.run(`DELETE FROM config_snapshots WHERE config_id = ? AND id NOT IN (
51
+ SELECT id FROM config_snapshots WHERE config_id = ? ORDER BY version DESC LIMIT ?
52
+ )`, [configId, configId, keep]);
53
+ return result.changes;
54
+ }
55
+
56
+ // src/db/configs.ts
57
+ function rowToConfig(row) {
58
+ let outputs = [];
59
+ try {
60
+ const parsed = JSON.parse(row.outputs || "[]");
61
+ outputs = Array.isArray(parsed) ? parsed : [];
62
+ } catch {
63
+ outputs = [];
64
+ }
65
+ return {
66
+ ...row,
67
+ tags: JSON.parse(row.tags || "[]"),
68
+ outputs,
69
+ is_template: !!row.is_template,
70
+ kind: row.kind,
71
+ category: row.category,
72
+ agent: row.agent,
73
+ format: row.format
74
+ };
75
+ }
76
+ function uniqueSlug(name, db, excludeId) {
77
+ const base = slugify(name);
78
+ let slug = base;
79
+ let i = 1;
80
+ while (true) {
81
+ const existing = db.query("SELECT id FROM configs WHERE slug = ?").get(slug);
82
+ if (!existing || existing.id === excludeId)
83
+ return slug;
84
+ slug = `${base}-${i++}`;
85
+ }
86
+ }
87
+ function createConfig(input, db) {
88
+ const d = db || getDatabase();
89
+ const id = uuid();
90
+ const ts = now();
91
+ const slug = uniqueSlug(input.name, d);
92
+ const tags = JSON.stringify(input.tags || []);
93
+ const outputs = JSON.stringify(input.outputs || []);
94
+ return d.transaction(() => {
95
+ d.run(`INSERT INTO configs (id, name, slug, kind, category, agent, target_path, outputs, format, content, description, tags, is_template, version, created_at, updated_at, synced_at)
96
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, NULL)`, [
97
+ id,
98
+ input.name,
99
+ slug,
100
+ input.kind ?? "file",
101
+ input.category,
102
+ input.agent ?? "global",
103
+ input.target_path ?? null,
104
+ outputs,
105
+ input.format ?? "text",
106
+ input.content,
107
+ input.description ?? null,
108
+ tags,
109
+ input.is_template ? 1 : 0,
110
+ ts,
111
+ ts
112
+ ]);
113
+ createSnapshot(id, input.content, 1, d);
114
+ return getConfig(id, d);
115
+ })();
116
+ }
117
+ function getConfig(idOrSlug, db) {
118
+ const d = db || getDatabase();
119
+ const row = d.query("SELECT * FROM configs WHERE id = ? OR slug = ?").get(idOrSlug, idOrSlug);
120
+ if (!row)
121
+ throw new ConfigNotFoundError(idOrSlug);
122
+ return rowToConfig(row);
123
+ }
124
+ function getConfigById(id, db) {
125
+ const d = db || getDatabase();
126
+ const row = d.query("SELECT * FROM configs WHERE id = ?").get(id);
127
+ if (!row)
128
+ throw new ConfigNotFoundError(id);
129
+ return rowToConfig(row);
130
+ }
131
+ function listConfigs(filter, db) {
132
+ const d = db || getDatabase();
133
+ const conditions = [];
134
+ const params = [];
135
+ if (filter?.category) {
136
+ conditions.push("category = ?");
137
+ params.push(filter.category);
138
+ }
139
+ if (filter?.agent) {
140
+ conditions.push("agent = ?");
141
+ params.push(filter.agent);
142
+ }
143
+ if (filter?.kind) {
144
+ conditions.push("kind = ?");
145
+ params.push(filter.kind);
146
+ }
147
+ if (filter?.is_template !== undefined) {
148
+ conditions.push("is_template = ?");
149
+ params.push(filter.is_template ? 1 : 0);
150
+ }
151
+ if (filter?.search) {
152
+ conditions.push("(name LIKE ? OR description LIKE ? OR content LIKE ?)");
153
+ const q = `%${filter.search}%`;
154
+ params.push(q, q, q);
155
+ }
156
+ if (filter?.tags && filter.tags.length > 0) {
157
+ const tagConditions = filter.tags.map(() => "tags LIKE ?").join(" OR ");
158
+ conditions.push(`(${tagConditions})`);
159
+ for (const tag of filter.tags)
160
+ params.push(`%"${tag}"%`);
161
+ }
162
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
163
+ const rows = d.query(`SELECT * FROM configs ${where} ORDER BY category, name`).all(...params);
164
+ return rows.map(rowToConfig);
165
+ }
166
+ function updateConfig(idOrSlug, input, db) {
167
+ const d = db || getDatabase();
168
+ const existing = getConfig(idOrSlug, d);
169
+ const ts = now();
170
+ const updates = ["updated_at = ?", "version = version + 1"];
171
+ const params = [ts];
172
+ if (input.name !== undefined) {
173
+ updates.push("name = ?", "slug = ?");
174
+ params.push(input.name, uniqueSlug(input.name, d, existing.id));
175
+ }
176
+ if (input.kind !== undefined) {
177
+ updates.push("kind = ?");
178
+ params.push(input.kind);
179
+ }
180
+ if (input.category !== undefined) {
181
+ updates.push("category = ?");
182
+ params.push(input.category);
183
+ }
184
+ if (input.agent !== undefined) {
185
+ updates.push("agent = ?");
186
+ params.push(input.agent);
187
+ }
188
+ if (input.target_path !== undefined) {
189
+ updates.push("target_path = ?");
190
+ params.push(input.target_path);
191
+ }
192
+ if (input.outputs !== undefined) {
193
+ updates.push("outputs = ?");
194
+ params.push(JSON.stringify(input.outputs));
195
+ }
196
+ if (input.format !== undefined) {
197
+ updates.push("format = ?");
198
+ params.push(input.format);
199
+ }
200
+ if (input.content !== undefined) {
201
+ updates.push("content = ?");
202
+ params.push(input.content);
203
+ }
204
+ if (input.description !== undefined) {
205
+ updates.push("description = ?");
206
+ params.push(input.description);
207
+ }
208
+ if (input.tags !== undefined) {
209
+ updates.push("tags = ?");
210
+ params.push(JSON.stringify(input.tags));
211
+ }
212
+ if (input.is_template !== undefined) {
213
+ updates.push("is_template = ?");
214
+ params.push(input.is_template ? 1 : 0);
215
+ }
216
+ if (input.synced_at !== undefined) {
217
+ updates.push("synced_at = ?");
218
+ params.push(input.synced_at);
219
+ }
220
+ return d.transaction(() => {
221
+ params.push(existing.id);
222
+ d.run(`UPDATE configs SET ${updates.join(", ")} WHERE id = ?`, params);
223
+ const updated = getConfigById(existing.id, d);
224
+ createSnapshot(updated.id, updated.content, updated.version, d);
225
+ return updated;
226
+ })();
227
+ }
228
+ function deleteConfig(idOrSlug, db) {
229
+ const d = db || getDatabase();
230
+ const existing = getConfig(idOrSlug, d);
231
+ d.run("DELETE FROM configs WHERE id = ?", [existing.id]);
232
+ }
233
+ function getConfigStats(db) {
234
+ const d = db || getDatabase();
235
+ const rows = d.query("SELECT category, COUNT(*) as count FROM configs GROUP BY category").all();
236
+ const stats = { total: 0 };
237
+ for (const row of rows) {
238
+ stats[row.category] = row.count;
239
+ stats["total"] = (stats["total"] || 0) + row.count;
240
+ }
241
+ return stats;
242
+ }
243
+ // src/db/profiles.ts
244
+ function rowToProfile(row) {
245
+ return {
246
+ ...row,
247
+ selectors: JSON.parse(row.selectors || "{}"),
248
+ variables: JSON.parse(row.variables || "{}")
249
+ };
250
+ }
251
+ function uniqueProfileSlug(name, db, excludeId) {
252
+ const base = slugify(name);
253
+ let slug = base;
254
+ let i = 1;
255
+ while (true) {
256
+ const existing = db.query("SELECT id FROM profiles WHERE slug = ?").get(slug);
257
+ if (!existing || existing.id === excludeId)
258
+ return slug;
259
+ slug = `${base}-${i++}`;
260
+ }
261
+ }
262
+ function createProfile(input, db) {
263
+ const d = db || getDatabase();
264
+ const id = uuid();
265
+ const ts = now();
266
+ const slug = uniqueProfileSlug(input.name, d);
267
+ d.run("INSERT INTO profiles (id, name, slug, description, selectors, variables, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [
268
+ id,
269
+ input.name,
270
+ slug,
271
+ input.description ?? null,
272
+ JSON.stringify(input.selectors ?? {}),
273
+ JSON.stringify(input.variables ?? {}),
274
+ ts,
275
+ ts
276
+ ]);
277
+ return getProfile(id, d);
278
+ }
279
+ function getProfile(idOrSlug, db) {
280
+ const d = db || getDatabase();
281
+ const row = d.query("SELECT * FROM profiles WHERE id = ? OR slug = ?").get(idOrSlug, idOrSlug);
282
+ if (!row)
283
+ throw new ProfileNotFoundError(idOrSlug);
284
+ return rowToProfile(row);
285
+ }
286
+ function listProfilesPage(options = {}, db) {
287
+ const d = db || getDatabase();
288
+ const normalized = normalizeBoundedReadOptions(options);
289
+ const total = d.query("SELECT COUNT(*) AS total FROM profiles").get()?.total ?? 0;
290
+ const rows = d.query("SELECT * FROM profiles ORDER BY name LIMIT ? OFFSET ?").all(normalized.limit, normalized.cursor).map(rowToProfile);
291
+ return boundedReadPage(rows, total, normalized);
292
+ }
293
+ function updateProfile(idOrSlug, input, db) {
294
+ const d = db || getDatabase();
295
+ const existing = getProfile(idOrSlug, d);
296
+ const ts = now();
297
+ const updates = ["updated_at = ?"];
298
+ const params = [ts];
299
+ if (input.name !== undefined) {
300
+ updates.push("name = ?", "slug = ?");
301
+ params.push(input.name, uniqueProfileSlug(input.name, d, existing.id));
302
+ }
303
+ if (input.description !== undefined) {
304
+ updates.push("description = ?");
305
+ params.push(input.description);
306
+ }
307
+ if (input.selectors !== undefined) {
308
+ updates.push("selectors = ?");
309
+ params.push(JSON.stringify(input.selectors));
310
+ }
311
+ if (input.variables !== undefined) {
312
+ updates.push("variables = ?");
313
+ params.push(JSON.stringify(input.variables));
314
+ }
315
+ params.push(existing.id);
316
+ d.run(`UPDATE profiles SET ${updates.join(", ")} WHERE id = ?`, params);
317
+ return getProfile(existing.id, d);
318
+ }
319
+ function deleteProfile(idOrSlug, db) {
320
+ const d = db || getDatabase();
321
+ const existing = getProfile(idOrSlug, d);
322
+ d.run("DELETE FROM profiles WHERE id = ?", [existing.id]);
323
+ }
324
+ function addConfigToProfile(profileIdOrSlug, configId, db, binding) {
325
+ const d = db || getDatabase();
326
+ const profile = getProfile(profileIdOrSlug, d);
327
+ const maxRow = d.query("SELECT MAX(sort_order) as max_order FROM profile_configs WHERE profile_id = ?").get(profile.id);
328
+ const order = (maxRow?.max_order ?? -1) + 1;
329
+ d.run("INSERT OR IGNORE INTO profile_configs (profile_id, config_id, sort_order, binding) VALUES (?, ?, ?, ?)", [profile.id, configId, order, JSON.stringify(normalizeProfileConfigBinding(binding))]);
330
+ }
331
+ function setProfileConfigBinding(profileIdOrSlug, configId, binding, db) {
332
+ const d = db || getDatabase();
333
+ const profile = getProfile(profileIdOrSlug, d);
334
+ const normalized = normalizeProfileConfigBinding(binding);
335
+ const result = d.run("UPDATE profile_configs SET binding = ? WHERE profile_id = ? AND config_id = ?", [JSON.stringify(normalized), profile.id, configId]);
336
+ if (result.changes !== 1)
337
+ throw new Error(`Config ${configId} is not a member of profile ${profile.slug}.`);
338
+ return getProfileConfigBindings(profile.id, d).find((row) => row.config_id === configId);
339
+ }
340
+ function getProfileConfigBindings(profileIdOrSlug, db) {
341
+ const d = db || getDatabase();
342
+ const profile = getProfile(profileIdOrSlug, d);
343
+ return d.query("SELECT profile_id, config_id, sort_order, binding FROM profile_configs WHERE profile_id = ? ORDER BY sort_order, config_id").all(profile.id).map((row) => ({
344
+ profile_id: row.profile_id,
345
+ config_id: row.config_id,
346
+ sort_order: row.sort_order,
347
+ binding: row.binding ? normalizeProfileConfigBinding(row.binding) : legacyProfileConfigBinding()
348
+ }));
349
+ }
350
+ function removeConfigFromProfile(profileIdOrSlug, configId, db) {
351
+ const d = db || getDatabase();
352
+ const profile = getProfile(profileIdOrSlug, d);
353
+ d.run("DELETE FROM profile_configs WHERE profile_id = ? AND config_id = ?", [profile.id, configId]);
354
+ }
355
+ function addAssetToProfile(profileIdOrSlug, sourceConfigId, binding, db) {
356
+ const d = db || getDatabase();
357
+ const profile = getProfile(profileIdOrSlug, d);
358
+ getConfigById(sourceConfigId, d);
359
+ const normalized = normalizeProfileAssetBinding(binding);
360
+ const maxRow = d.query("SELECT MAX(sort_order) AS max_order FROM profile_assets WHERE profile_id = ?").get(profile.id);
361
+ const order = (maxRow?.max_order ?? -1) + 1;
362
+ d.run("INSERT INTO profile_assets (profile_id, source_config_id, asset_key, sort_order, binding) VALUES (?, ?, ?, ?, ?)", [profile.id, sourceConfigId, normalized.assetKey, order, JSON.stringify(normalized)]);
363
+ return getProfileAssetBindings(profile.id, d).find((row) => row.binding.assetKey === normalized.assetKey);
364
+ }
365
+ function setProfileAssetBinding(profileIdOrSlug, assetKey, binding, db) {
366
+ const d = db || getDatabase();
367
+ const profile = getProfile(profileIdOrSlug, d);
368
+ const normalized = normalizeProfileAssetBinding(binding);
369
+ if (normalized.assetKey !== assetKey)
370
+ throw new Error(`Asset binding key ${normalized.assetKey} does not match route key ${assetKey}.`);
371
+ const result = d.run("UPDATE profile_assets SET binding = ? WHERE profile_id = ? AND asset_key = ?", [JSON.stringify(normalized), profile.id, assetKey]);
372
+ if (result.changes !== 1)
373
+ throw new Error(`Asset ${assetKey} is not a member of profile ${profile.slug}.`);
374
+ return getProfileAssetBindings(profile.id, d).find((row) => row.binding.assetKey === assetKey);
375
+ }
376
+ function getProfileAssetBindings(profileIdOrSlug, db) {
377
+ const d = db || getDatabase();
378
+ const profile = getProfile(profileIdOrSlug, d);
379
+ return d.query("SELECT profile_id, source_config_id, sort_order, binding FROM profile_assets WHERE profile_id = ? ORDER BY sort_order, asset_key").all(profile.id).map((row) => ({
380
+ profile_id: row.profile_id,
381
+ source_config_id: row.source_config_id,
382
+ sort_order: row.sort_order,
383
+ binding: normalizeProfileAssetBinding(row.binding)
384
+ }));
385
+ }
386
+ function removeAssetFromProfile(profileIdOrSlug, assetKey, db) {
387
+ const d = db || getDatabase();
388
+ const profile = getProfile(profileIdOrSlug, d);
389
+ d.run("DELETE FROM profile_assets WHERE profile_id = ? AND asset_key = ?", [profile.id, assetKey]);
390
+ }
391
+ function getProfileConfigsPage(profileIdOrSlug, options = {}, db) {
392
+ const d = db || getDatabase();
393
+ const profile = getProfile(profileIdOrSlug, d);
394
+ const normalized = normalizeBoundedReadOptions(options);
395
+ const total = d.query("SELECT COUNT(*) AS total FROM profile_configs WHERE profile_id = ?").get(profile.id)?.total ?? 0;
396
+ const rows = d.query("SELECT config_id FROM profile_configs WHERE profile_id = ? ORDER BY sort_order LIMIT ? OFFSET ?").all(profile.id, normalized.limit, normalized.cursor);
397
+ return boundedReadPage(rows.map((row) => getConfigById(row.config_id, d)), total, normalized);
398
+ }
399
+ function profileHasSelectors(profile) {
400
+ const selectors = profile.selectors ?? {};
401
+ return (selectors.os?.length ?? 0) > 0 || (selectors.arch?.length ?? 0) > 0 || (selectors.hostnames?.length ?? 0) > 0;
402
+ }
403
+ function profileMatchesMachine(profile, machine) {
404
+ const selectors = profile.selectors ?? {};
405
+ const osMatches = !selectors.os?.length || selectors.os.some((candidate) => {
406
+ const value = candidate.trim().toLowerCase();
407
+ return value === machine.os_family || value === (machine.os ?? "").trim().toLowerCase() || normalizeOsFamily(candidate) === machine.os_family;
408
+ });
409
+ const archMatches = !selectors.arch?.length || selectors.arch.some((candidate) => candidate.trim().toLowerCase() === (machine.arch ?? "").trim().toLowerCase());
410
+ const hostnameMatches = !selectors.hostnames?.length || selectors.hostnames.some((candidate) => candidate.trim().toLowerCase() === machine.hostname.trim().toLowerCase());
411
+ return osMatches && archMatches && hostnameMatches;
412
+ }
413
+ function resolveProfileForMachineRead(machine = detectMachineContext(), options = {}, db) {
414
+ const d = db || getDatabase();
415
+ const { limit } = normalizeBoundedReadOptions(options);
416
+ let cursor = 0;
417
+ let scanned = 0;
418
+ let total = 0;
419
+ let selected = null;
420
+ while (true) {
421
+ const page = listProfilesPage({ limit, cursor }, d);
422
+ total = page.total;
423
+ scanned += page.items.length;
424
+ for (const profile of page.items) {
425
+ if (!profileHasSelectors(profile) || !profileMatchesMachine(profile, machine))
426
+ continue;
427
+ const selectors = profile.selectors;
428
+ const score = (selectors.hostnames?.length ? 100 : 0) + (selectors.os?.length ? 10 : 0) + (selectors.arch?.length ? 10 : 0);
429
+ if (!selected || score > selected.score || score === selected.score && profile.name.localeCompare(selected.profile.name) < 0) {
430
+ selected = { profile, score };
431
+ }
432
+ }
433
+ if (page.complete)
434
+ break;
435
+ cursor = page.next_cursor;
436
+ }
437
+ return {
438
+ profile: selected?.profile ?? null,
439
+ scanned,
440
+ total,
441
+ batch_limit: limit,
442
+ source_bounded: true,
443
+ complete: true,
444
+ truncated: false
445
+ };
446
+ }
447
+ // src/db/machines.ts
448
+ import { arch, hostname, type } from "os";
449
+ function currentHostname() {
450
+ return hostname();
451
+ }
452
+ function currentOs() {
453
+ return type();
454
+ }
455
+ function currentArch() {
456
+ return arch();
457
+ }
458
+ function registerMachine(hostnameStr, os, archStr, db) {
459
+ const d = db || getDatabase();
460
+ const h = hostnameStr ?? currentHostname();
461
+ const o = os ?? currentOs();
462
+ const a = archStr ?? currentArch();
463
+ const existing = d.query("SELECT * FROM machines WHERE hostname = ?").get(h);
464
+ if (existing) {
465
+ if (existing.os !== o || existing.arch !== a) {
466
+ d.run("UPDATE machines SET os = ?, arch = ? WHERE hostname = ?", [o, a, h]);
467
+ return d.query("SELECT * FROM machines WHERE hostname = ?").get(h);
468
+ }
469
+ return existing;
470
+ }
471
+ const id = uuid();
472
+ const ts = now();
473
+ d.run("INSERT INTO machines (id, hostname, os, arch, last_applied_at, created_at) VALUES (?, ?, ?, ?, NULL, ?)", [id, h, o, a, ts]);
474
+ return d.query("SELECT * FROM machines WHERE id = ?").get(id);
475
+ }
476
+ function updateMachineApplied(hostnameStr, db) {
477
+ const d = db || getDatabase();
478
+ const h = hostnameStr ?? currentHostname();
479
+ d.run("UPDATE machines SET last_applied_at = ? WHERE hostname = ?", [now(), h]);
480
+ }
481
+ function listMachines(db) {
482
+ const d = db || getDatabase();
483
+ return d.query("SELECT * FROM machines ORDER BY last_applied_at DESC NULLS LAST").all();
484
+ }
485
+ export {
486
+ updateProfile,
487
+ updateMachineApplied,
488
+ updateConfig,
489
+ setProfileConfigBinding,
490
+ setProfileAssetBinding,
491
+ resolveProfileForMachineRead,
492
+ resetLocalDatabase,
493
+ removeConfigFromProfile,
494
+ removeAssetFromProfile,
495
+ registerMachine,
496
+ pruneSnapshots,
497
+ listSnapshots,
498
+ listProfilesPage,
499
+ listMachines,
500
+ listConfigs,
501
+ insertFeedback,
502
+ getSnapshotByVersion,
503
+ getSnapshot,
504
+ getProfileConfigsPage,
505
+ getProfileConfigBindings,
506
+ getProfileAssetBindings,
507
+ getProfile,
508
+ getConfigStats,
509
+ getConfigById,
510
+ getConfig,
511
+ deleteProfile,
512
+ deleteConfig,
513
+ createSnapshot,
514
+ createProfile,
515
+ createConfig,
516
+ addConfigToProfile,
517
+ addAssetToProfile
518
+ };