@hasna/instructions 0.6.1 → 0.7.1

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 (85) hide show
  1. package/README.md +141 -10
  2. package/dist/chunks/{apply-ynfn1av7.js → apply-c3sd4agz.js} +5 -3
  3. package/dist/chunks/{apply-sfzja1f3.js → apply-pnn7a3y7.js} +6 -3
  4. package/dist/chunks/database-wstmr56z.js +23 -0
  5. package/dist/chunks/{index-pkscjnj5.js → index-3djthcm6.js} +0 -1
  6. package/dist/chunks/index-5d7zdftc.js +186 -0
  7. package/dist/chunks/{index-1hpwskwd.js → index-6jncnnw7.js} +318 -329
  8. package/dist/chunks/index-8rb7ng2y.js +148 -0
  9. package/dist/chunks/{index-v7dkb384.js → index-aj66f6xr.js} +243 -100
  10. package/dist/chunks/index-fpeyzn3n.js +186 -0
  11. package/dist/chunks/{index-9p9gn7gh.js → index-jg8ptcwh.js} +5 -7
  12. package/dist/chunks/index-mvckkf15.js +363 -0
  13. package/dist/chunks/index-n3fjspr0.js +119 -0
  14. package/dist/chunks/{index-85n5wnd8.js → index-pdpg8a44.js} +5 -7
  15. package/dist/chunks/{index-e9epbsvs.js → index-r7nfef2g.js} +15 -4
  16. package/dist/chunks/{index-axhwg61p.js → index-ypcef3a2.js} +15 -4
  17. package/dist/chunks/legacy-store-migration-z2m5yzyk.js +322 -0
  18. package/dist/chunks/{local-a9nen65z.js → local-0a9gja58.js} +142 -2
  19. package/dist/chunks/{local-0d8qrc92.js → local-969ye538.js} +153 -188
  20. package/dist/chunks/s3-backup-hxe3zpfz.js +240 -0
  21. package/dist/chunks/s3-config-hhght91k.js +14 -0
  22. package/dist/chunks/s3-object-store-n0s82swt.js +14 -0
  23. package/dist/chunks/{sync-vy1yjvs6.js → sync-q5hvwq6e.js} +4 -4
  24. package/dist/chunks/{sync-m46fc257.js → sync-xtkbm5br.js} +5 -4
  25. package/dist/chunks/{template-02wjvv05.js → template-fsfxpe4p.js} +1 -1
  26. package/dist/chunks/{template-ke972qqe.js → template-wgnzdn7h.js} +1 -1
  27. package/dist/cli/index.js +1236 -256
  28. package/dist/data/config-store.d.ts +10 -1
  29. package/dist/data/config-store.d.ts.map +1 -1
  30. package/dist/db/configs.d.ts +4 -1
  31. package/dist/db/configs.d.ts.map +1 -1
  32. package/dist/db/local.d.ts +1 -1
  33. package/dist/db/local.d.ts.map +1 -1
  34. package/dist/index.js +1086 -215
  35. package/dist/lib/apply.d.ts +7 -0
  36. package/dist/lib/apply.d.ts.map +1 -1
  37. package/dist/lib/compact-output.d.ts +3 -18
  38. package/dist/lib/compact-output.d.ts.map +1 -1
  39. package/dist/lib/export.d.ts +22 -3
  40. package/dist/lib/export.d.ts.map +1 -1
  41. package/dist/lib/import.d.ts +18 -0
  42. package/dist/lib/import.d.ts.map +1 -1
  43. package/dist/lib/legacy-store-migration.d.ts +39 -0
  44. package/dist/lib/legacy-store-migration.d.ts.map +1 -0
  45. package/dist/lib/managed-skill-runtimes.d.ts +1 -0
  46. package/dist/lib/managed-skill-runtimes.d.ts.map +1 -1
  47. package/dist/lib/sync-dir.d.ts.map +1 -1
  48. package/dist/mcp/index.js +41 -30
  49. package/dist/mcp/server.d.ts.map +1 -1
  50. package/dist/sdk/index.d.ts +2 -0
  51. package/dist/sdk/index.d.ts.map +1 -1
  52. package/dist/sdk/index.js +229 -10
  53. package/dist/sdk/resolve.d.ts +1 -1
  54. package/dist/sdk/resolve.d.ts.map +1 -1
  55. package/dist/sdk/v1-client.d.ts +58 -0
  56. package/dist/sdk/v1-client.d.ts.map +1 -0
  57. package/dist/sdk/v1.generated.d.ts +242 -21
  58. package/dist/sdk/v1.generated.d.ts.map +1 -1
  59. package/dist/server/cloud.d.ts.map +1 -1
  60. package/dist/server/index.d.ts +13 -5
  61. package/dist/server/index.d.ts.map +1 -1
  62. package/dist/server/index.js +1261 -427
  63. package/dist/server/openapi.d.ts +1379 -93
  64. package/dist/server/openapi.d.ts.map +1 -1
  65. package/dist/server/v1.d.ts +25 -1
  66. package/dist/server/v1.d.ts.map +1 -1
  67. package/dist/storage/cloud-store.d.ts +45 -2
  68. package/dist/storage/cloud-store.d.ts.map +1 -1
  69. package/dist/storage/index.d.ts +7 -0
  70. package/dist/storage/index.d.ts.map +1 -0
  71. package/dist/storage/index.js +744 -0
  72. package/dist/storage/s3-backup.d.ts +76 -0
  73. package/dist/storage/s3-backup.d.ts.map +1 -0
  74. package/dist/storage/s3-config.d.ts +46 -0
  75. package/dist/storage/s3-config.d.ts.map +1 -0
  76. package/dist/storage/s3-object-store.d.ts +70 -0
  77. package/dist/storage/s3-object-store.d.ts.map +1 -0
  78. package/dist/types/index.d.ts +98 -3
  79. package/dist/types/index.d.ts.map +1 -1
  80. package/hasna.contract.json +110 -0
  81. package/migrations/0001_instructions.sql +63 -0
  82. package/migrations/0002_api_keys.sql +20 -0
  83. package/migrations/0003_idempotency_receipts.sql +24 -0
  84. package/package.json +24 -13
  85. package/assets/skills/inbox/SKILL.md +0 -86
@@ -0,0 +1,240 @@
1
+ // @bun
2
+ import {
3
+ assertSafeInstructionsObjectKey,
4
+ assertSafeInstructionsObjectVersionId
5
+ } from "./index-mvckkf15.js";
6
+ import {
7
+ normalizeInstructionsS3Prefix
8
+ } from "./index-8rb7ng2y.js";
9
+ import"./index-bsan4c16.js";
10
+
11
+ // src/storage/s3-backup.ts
12
+ var INSTRUCTIONS_BACKUP_MANIFEST_SCHEMA = "hasna.instructions.backup-object/v1";
13
+ var encoder = new TextEncoder;
14
+ var decoder = new TextDecoder;
15
+ var SHA256_PATTERN = /^[0-9a-f]{64}$/;
16
+ var CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/;
17
+ function buildInstructionsBackupKeys(prefix, backupId) {
18
+ const normalizedPrefix = normalizeInstructionsS3Prefix(prefix);
19
+ const encodedBackupId = encodeBackupId(backupId);
20
+ const rootKey = `${normalizedPrefix}backups/${encodedBackupId}`;
21
+ const keys = {
22
+ rootKey,
23
+ payloadKey: `${rootKey}/payload`,
24
+ manifestKey: `${rootKey}/manifest.json`
25
+ };
26
+ assertSafeInstructionsObjectKey(keys.payloadKey);
27
+ assertSafeInstructionsObjectKey(keys.manifestKey);
28
+ return keys;
29
+ }
30
+ async function planInstructionsBackupPush(input) {
31
+ const keys = buildInstructionsBackupKeys(input.prefix, input.backupId);
32
+ return {
33
+ operation: "push",
34
+ dryRun: true,
35
+ noNetwork: true,
36
+ backupId: input.backupId,
37
+ ...keys,
38
+ sha256: sha256(input.bytes),
39
+ sizeBytes: input.bytes.byteLength,
40
+ contentType: normalizeContentType(input.contentType)
41
+ };
42
+ }
43
+ async function pushInstructionsBackup(input) {
44
+ const plan = await planInstructionsBackupPush(input);
45
+ const existingManifestBytes = await input.store.get(plan.manifestKey);
46
+ if (existingManifestBytes) {
47
+ const existingManifest2 = parseManifest(existingManifestBytes, plan, input.backupId);
48
+ assertManifestMatchesPayload(existingManifest2, input.bytes);
49
+ const existingPayload2 = await input.store.get(existingManifest2.payloadKey);
50
+ if (!existingPayload2)
51
+ throw new Error("Instructions backup payload object is missing");
52
+ assertBytesMatch(existingPayload2, existingManifest2.sha256, existingManifest2.sizeBytes, "backup payload integrity");
53
+ return {
54
+ status: "existing",
55
+ manifest: existingManifest2,
56
+ ...await resolveCurrentVersions(input.store, existingManifest2)
57
+ };
58
+ }
59
+ const payloadCreate = await createImmutableObject(input.store, plan.payloadKey, input.bytes, plan.contentType);
60
+ if (payloadCreate.status === "existing") {
61
+ const existingPayload2 = await input.store.get(plan.payloadKey);
62
+ if (!existingPayload2)
63
+ throw new Error("Instructions backup payload object is missing");
64
+ assertBytesMatch(existingPayload2, plan.sha256, plan.sizeBytes, "existing immutable payload");
65
+ }
66
+ const manifest = {
67
+ schema: INSTRUCTIONS_BACKUP_MANIFEST_SCHEMA,
68
+ app: "instructions",
69
+ backupId: input.backupId,
70
+ payloadKey: plan.payloadKey,
71
+ manifestKey: plan.manifestKey,
72
+ sha256: plan.sha256,
73
+ sizeBytes: plan.sizeBytes,
74
+ contentType: plan.contentType,
75
+ createdAt: normalizeCreatedAt(input.createdAt)
76
+ };
77
+ const manifestCreate = await createImmutableObject(input.store, plan.manifestKey, serializeManifest(manifest), "application/json");
78
+ if (manifestCreate.status === "created") {
79
+ const versions = completeCreatedVersions(payloadCreate, manifestCreate);
80
+ return { status: "created", manifest, ...versions ? { versions } : {} };
81
+ }
82
+ const racedManifestBytes = await input.store.get(plan.manifestKey);
83
+ if (!racedManifestBytes)
84
+ throw new Error("Instructions backup manifest object is missing");
85
+ const existingManifest = parseManifest(racedManifestBytes, plan, input.backupId);
86
+ assertManifestMatchesPayload(existingManifest, input.bytes);
87
+ const existingPayload = await input.store.get(existingManifest.payloadKey);
88
+ if (!existingPayload)
89
+ throw new Error("Instructions backup payload object is missing");
90
+ assertBytesMatch(existingPayload, existingManifest.sha256, existingManifest.sizeBytes, "backup payload integrity");
91
+ return {
92
+ status: "existing",
93
+ manifest: existingManifest,
94
+ ...await resolveCurrentVersions(input.store, existingManifest)
95
+ };
96
+ }
97
+ async function pullInstructionsBackup(input) {
98
+ const versions = normalizeReadVersions(input);
99
+ const keys = buildInstructionsBackupKeys(input.prefix, input.backupId);
100
+ const manifestBytes = await input.store.get(keys.manifestKey, versions ? { versionId: versions.manifestVersionId } : undefined);
101
+ if (!manifestBytes)
102
+ throw new Error("Instructions backup manifest object is missing");
103
+ const manifest = parseManifest(manifestBytes, keys, input.backupId);
104
+ const bytes = await input.store.get(manifest.payloadKey, versions ? { versionId: versions.payloadVersionId } : undefined);
105
+ if (!bytes)
106
+ throw new Error("Instructions backup payload object is missing");
107
+ assertBytesMatch(bytes, manifest.sha256, manifest.sizeBytes, "backup payload integrity");
108
+ if (versions) {
109
+ const [manifestMetadata, payloadMetadata] = await Promise.all([
110
+ input.store.head(keys.manifestKey, { versionId: versions.manifestVersionId }),
111
+ input.store.head(manifest.payloadKey, { versionId: versions.payloadVersionId })
112
+ ]);
113
+ if (manifestMetadata?.versionId !== versions.manifestVersionId || payloadMetadata?.versionId !== versions.payloadVersionId) {
114
+ throw new Error("Instructions backup immutable version authority check failed");
115
+ }
116
+ if (payloadMetadata.size !== manifest.sizeBytes) {
117
+ throw new Error("Instructions backup payload version size check failed");
118
+ }
119
+ }
120
+ return {
121
+ manifest,
122
+ bytes,
123
+ versionPinned: Boolean(versions),
124
+ ...versions ? { versions } : {}
125
+ };
126
+ }
127
+ async function verifyInstructionsBackup(input) {
128
+ const result = await pullInstructionsBackup(input);
129
+ return {
130
+ ok: true,
131
+ backupId: result.manifest.backupId,
132
+ payloadKey: result.manifest.payloadKey,
133
+ manifestKey: result.manifest.manifestKey,
134
+ sha256: result.manifest.sha256,
135
+ sizeBytes: result.manifest.sizeBytes,
136
+ versionPinned: result.versionPinned,
137
+ ...result.versions ? { versions: result.versions } : {}
138
+ };
139
+ }
140
+ async function createImmutableObject(store, key, bytes, contentType) {
141
+ if (!store.putIfAbsent) {
142
+ throw new Error("Instructions object store does not support atomic immutable creation");
143
+ }
144
+ return store.putIfAbsent(key, bytes, { contentType });
145
+ }
146
+ function completeCreatedVersions(payload, manifest) {
147
+ if (!payload.versionId || !manifest.versionId)
148
+ return;
149
+ return { payloadVersionId: payload.versionId, manifestVersionId: manifest.versionId };
150
+ }
151
+ async function resolveCurrentVersions(store, manifest) {
152
+ const [payload, manifestObject] = await Promise.all([
153
+ store.head(manifest.payloadKey),
154
+ store.head(manifest.manifestKey)
155
+ ]);
156
+ if (!payload?.versionId || !manifestObject?.versionId)
157
+ return {};
158
+ return {
159
+ versions: {
160
+ payloadVersionId: payload.versionId,
161
+ manifestVersionId: manifestObject.versionId
162
+ }
163
+ };
164
+ }
165
+ function normalizeReadVersions(input) {
166
+ const payloadVersionId = input.payloadVersionId?.trim();
167
+ const manifestVersionId = input.manifestVersionId?.trim();
168
+ if (Boolean(payloadVersionId) !== Boolean(manifestVersionId)) {
169
+ throw new Error("Instructions backup payload and manifest version ids must be provided together");
170
+ }
171
+ if (!payloadVersionId || !manifestVersionId)
172
+ return;
173
+ assertSafeInstructionsObjectVersionId(payloadVersionId);
174
+ assertSafeInstructionsObjectVersionId(manifestVersionId);
175
+ return { payloadVersionId, manifestVersionId };
176
+ }
177
+ function encodeBackupId(backupId) {
178
+ const trimmed = backupId.trim();
179
+ if (!trimmed || trimmed.length > 128 || trimmed === "." || trimmed === ".." || trimmed.includes("\\") || CONTROL_CHARACTERS.test(trimmed)) {
180
+ throw new Error("Instructions backup id is invalid");
181
+ }
182
+ return encodeURIComponent(trimmed);
183
+ }
184
+ function normalizeContentType(value) {
185
+ const contentType = value?.trim() || "application/octet-stream";
186
+ if (contentType.length > 255 || CONTROL_CHARACTERS.test(contentType)) {
187
+ throw new Error("Instructions backup content type is invalid");
188
+ }
189
+ return contentType;
190
+ }
191
+ function normalizeCreatedAt(value) {
192
+ const date = value ?? new Date;
193
+ if (!Number.isFinite(date.getTime()))
194
+ throw new Error("Instructions backup creation timestamp is invalid");
195
+ return date.toISOString();
196
+ }
197
+ function serializeManifest(manifest) {
198
+ return encoder.encode(`${JSON.stringify(manifest, null, 2)}
199
+ `);
200
+ }
201
+ function parseManifest(bytes, keys, backupId) {
202
+ let value;
203
+ try {
204
+ value = JSON.parse(decoder.decode(bytes));
205
+ } catch {
206
+ throw new Error("Instructions backup manifest is not valid JSON");
207
+ }
208
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
209
+ throw new Error("Instructions backup manifest is invalid");
210
+ }
211
+ const manifest = value;
212
+ if (manifest.schema !== INSTRUCTIONS_BACKUP_MANIFEST_SCHEMA || manifest.app !== "instructions" || manifest.backupId !== backupId || manifest.payloadKey !== keys.payloadKey || manifest.manifestKey !== keys.manifestKey || typeof manifest.sha256 !== "string" || !SHA256_PATTERN.test(manifest.sha256) || typeof manifest.sizeBytes !== "number" || !Number.isSafeInteger(manifest.sizeBytes) || manifest.sizeBytes < 0 || typeof manifest.contentType !== "string" || !manifest.contentType || typeof manifest.createdAt !== "string" || !Number.isFinite(Date.parse(manifest.createdAt))) {
213
+ throw new Error("Instructions backup manifest failed validation");
214
+ }
215
+ assertSafeInstructionsObjectKey(manifest.payloadKey);
216
+ assertSafeInstructionsObjectKey(manifest.manifestKey);
217
+ return manifest;
218
+ }
219
+ function assertManifestMatchesPayload(manifest, bytes) {
220
+ const digest = sha256(bytes);
221
+ if (manifest.sha256 !== digest || manifest.sizeBytes !== bytes.byteLength) {
222
+ throw new Error("Instructions backup is immutable; the existing backup id refers to different bytes");
223
+ }
224
+ }
225
+ function assertBytesMatch(bytes, expectedSha256, expectedSize, label) {
226
+ if (bytes.byteLength !== expectedSize || sha256(bytes) !== expectedSha256) {
227
+ throw new Error(`Instructions ${label} check failed`);
228
+ }
229
+ }
230
+ function sha256(bytes) {
231
+ return new Bun.CryptoHasher("sha256").update(bytes).digest("hex");
232
+ }
233
+ export {
234
+ verifyInstructionsBackup,
235
+ pushInstructionsBackup,
236
+ pullInstructionsBackup,
237
+ planInstructionsBackupPush,
238
+ buildInstructionsBackupKeys,
239
+ INSTRUCTIONS_BACKUP_MANIFEST_SCHEMA
240
+ };
@@ -0,0 +1,14 @@
1
+ // @bun
2
+ import {
3
+ INSTRUCTIONS_S3_ALIAS_ENV,
4
+ INSTRUCTIONS_S3_ENV,
5
+ loadInstructionsS3Config,
6
+ normalizeInstructionsS3Prefix
7
+ } from "./index-8rb7ng2y.js";
8
+ import"./index-bsan4c16.js";
9
+ export {
10
+ normalizeInstructionsS3Prefix,
11
+ loadInstructionsS3Config,
12
+ INSTRUCTIONS_S3_ENV,
13
+ INSTRUCTIONS_S3_ALIAS_ENV
14
+ };
@@ -0,0 +1,14 @@
1
+ // @bun
2
+ import {
3
+ assertSafeInstructionsObjectKey,
4
+ assertSafeInstructionsObjectVersionId,
5
+ createInstructionsS3ObjectStore,
6
+ memoryInstructionsObjectStore
7
+ } from "./index-mvckkf15.js";
8
+ import"./index-bsan4c16.js";
9
+ export {
10
+ memoryInstructionsObjectStore,
11
+ createInstructionsS3ObjectStore,
12
+ assertSafeInstructionsObjectVersionId,
13
+ assertSafeInstructionsObjectKey
14
+ };
@@ -12,11 +12,11 @@ import {
12
12
  syncProject,
13
13
  syncToDir,
14
14
  syncToDisk
15
- } from "./index-9p9gn7gh.js";
16
- import"./index-axhwg61p.js";
17
- import"./index-v7dkb384.js";
15
+ } from "./index-jg8ptcwh.js";
16
+ import"./index-ypcef3a2.js";
17
+ import"./index-aj66f6xr.js";
18
18
  import"./index-35z65jsa.js";
19
- import"./index-pkscjnj5.js";
19
+ import"./index-3djthcm6.js";
20
20
  import"./index-nnwp92tk.js";
21
21
  export {
22
22
  syncToDisk,
@@ -12,10 +12,11 @@ import {
12
12
  syncProject,
13
13
  syncToDir,
14
14
  syncToDisk
15
- } from "./index-85n5wnd8.js";
16
- import"./index-e9epbsvs.js";
17
- import"./index-1hpwskwd.js";
18
- import"./index-pkscjnj5.js";
15
+ } from "./index-pdpg8a44.js";
16
+ import"./index-r7nfef2g.js";
17
+ import"./index-6jncnnw7.js";
18
+ import"./index-n3fjspr0.js";
19
+ import"./index-fpeyzn3n.js";
19
20
  import"./index-bsan4c16.js";
20
21
  export {
21
22
  syncToDisk,
@@ -5,7 +5,7 @@ import {
5
5
  parseTemplateVars,
6
6
  renderTemplate,
7
7
  renderTemplatePreview
8
- } from "./index-pkscjnj5.js";
8
+ } from "./index-3djthcm6.js";
9
9
  import"./index-nnwp92tk.js";
10
10
  export {
11
11
  renderTemplatePreview,
@@ -5,7 +5,7 @@ import {
5
5
  parseTemplateVars,
6
6
  renderTemplate,
7
7
  renderTemplatePreview
8
- } from "./index-pkscjnj5.js";
8
+ } from "./index-n3fjspr0.js";
9
9
  import"./index-bsan4c16.js";
10
10
  export {
11
11
  renderTemplatePreview,