@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
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ var __export = (target, all) => {
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
17
 
18
18
  // src/types/index.ts
19
- var CONFIG_KINDS, CONFIG_CATEGORIES, CONFIG_AGENTS, CONFIG_TRANSFORMS, CONFIG_FORMATS, PROFILE_CONFIG_BINDING_SCHEMA = "hasna.instructions.profile-config-binding/v1", INSTRUCTION_ACTIVATION_MODES, INSTRUCTION_FALLBACKS, PROFILE_ASSET_BINDING_SCHEMA = "hasna.instructions.profile-asset-binding/v1", ASSET_KINDS, ASSET_DESTINATION_STRATEGIES, ASSET_SCOPES, ASSET_UNINSTALL_POLICIES, ASSET_ROLLBACK_POLICIES, ConfigNotFoundError, ProfileNotFoundError, ConfigApplyError, TemplateRenderError;
19
+ var CONFIG_KINDS, CONFIG_CATEGORIES, CONFIG_AGENTS, CONFIG_TRANSFORMS, CONFIG_FORMATS, PROFILE_CONFIG_BINDING_SCHEMA = "hasna.instructions.profile-config-binding/v1", INSTRUCTION_ACTIVATION_MODES, INSTRUCTION_FALLBACKS, PROFILE_ASSET_BINDING_SCHEMA = "hasna.instructions.profile-asset-binding/v1", ASSET_KINDS, ASSET_DESTINATION_STRATEGIES, ASSET_SCOPES, ASSET_UNINSTALL_POLICIES, ASSET_ROLLBACK_POLICIES, INSTRUCTIONS_DOMAIN_ARCHIVE_SCHEMA = "hasna.instructions.domain-archive/v2", ConfigNotFoundError, ProfileNotFoundError, ConfigApplyError, TemplateRenderError;
20
20
  var init_types = __esm(() => {
21
21
  CONFIG_KINDS = ["file", "reference"];
22
22
  CONFIG_CATEGORIES = [
@@ -108,9 +108,9 @@ function parseCursor(value) {
108
108
  const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
109
109
  if (!Number.isFinite(parsed) || parsed < 0)
110
110
  return 0;
111
- return Math.floor(parsed);
111
+ return Math.min(Math.floor(parsed), MAX_LIST_CURSOR);
112
112
  }
113
- var DEFAULT_LIST_LIMIT = 20, MAX_LIST_LIMIT = 100;
113
+ var DEFAULT_LIST_LIMIT = 20, MAX_LIST_LIMIT = 100, MAX_LIST_CURSOR = 1e5;
114
114
 
115
115
  // src/lib/bounded-read.ts
116
116
  function normalizeBoundedReadOptions(options = {}) {
@@ -10925,7 +10925,7 @@ var init_instruction_graph = __esm(() => {
10925
10925
  };
10926
10926
  });
10927
10927
 
10928
- // ../contracts/dist/client/transport.js
10928
+ // ../../node_modules/.bun/@hasna+contracts@1.0.2+e8014c875821e0be/node_modules/@hasna/contracts/dist/client/transport.js
10929
10929
  import { isIP } from "net";
10930
10930
  import { spawnSync } from "child_process";
10931
10931
  import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync5 } from "fs";
@@ -11658,7 +11658,7 @@ var init_transport = __esm(() => {
11658
11658
  ]);
11659
11659
  });
11660
11660
 
11661
- // ../contracts/dist/client/storage.js
11661
+ // ../../node_modules/.bun/@hasna+contracts@1.0.2+e8014c875821e0be/node_modules/@hasna/contracts/dist/client/storage.js
11662
11662
  import { isIP as isIP2 } from "net";
11663
11663
  import { spawnSync as spawnSync2 } from "child_process";
11664
11664
  import { closeSync as closeSync3, fstatSync as fstatSync3, openSync as openSync3, readFileSync as readFileSync6 } from "fs";
@@ -13326,6 +13326,143 @@ function listConfigs(filter, db) {
13326
13326
  const rows = d.query(`SELECT * FROM configs ${where} ORDER BY category, name`).all(...params);
13327
13327
  return rows.map(rowToConfig);
13328
13328
  }
13329
+ function listConfigsPage(filter = {}, options = {}, db) {
13330
+ const d = db || getDatabase();
13331
+ const conditions = [];
13332
+ const params = [];
13333
+ if (filter.category) {
13334
+ conditions.push("category = ?");
13335
+ params.push(filter.category);
13336
+ }
13337
+ if (filter.agent) {
13338
+ conditions.push("agent = ?");
13339
+ params.push(filter.agent);
13340
+ }
13341
+ if (filter.kind) {
13342
+ conditions.push("kind = ?");
13343
+ params.push(filter.kind);
13344
+ }
13345
+ if (filter.is_template !== undefined) {
13346
+ conditions.push("is_template = ?");
13347
+ params.push(filter.is_template ? 1 : 0);
13348
+ }
13349
+ if (filter.search) {
13350
+ conditions.push("(name LIKE ? OR description LIKE ? OR content LIKE ?)");
13351
+ const q = `%${filter.search}%`;
13352
+ params.push(q, q, q);
13353
+ }
13354
+ if (filter.tags?.length) {
13355
+ conditions.push(`(${filter.tags.map(() => "tags LIKE ?").join(" AND ")})`);
13356
+ for (const tag of filter.tags)
13357
+ params.push(`%"${tag}"%`);
13358
+ }
13359
+ const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
13360
+ const normalized = normalizeBoundedReadOptions(options);
13361
+ const total = d.query(`SELECT COUNT(*) AS total FROM configs ${where}`).get(...params)?.total ?? 0;
13362
+ const pageParams = [...params, normalized.limit, normalized.cursor];
13363
+ const rows = d.query(`SELECT * FROM configs ${where} ORDER BY id LIMIT ? OFFSET ?`).all(...pageParams);
13364
+ return boundedReadPage(rows.map(rowToConfig), total, normalized);
13365
+ }
13366
+ function listConfigIdentitiesPage(filter = {}, options = {}, db) {
13367
+ const d = db || getDatabase();
13368
+ const conditions = [];
13369
+ const params = [];
13370
+ if (filter.category) {
13371
+ conditions.push("category = ?");
13372
+ params.push(filter.category);
13373
+ }
13374
+ if (filter.agent) {
13375
+ conditions.push("agent = ?");
13376
+ params.push(filter.agent);
13377
+ }
13378
+ if (filter.kind) {
13379
+ conditions.push("kind = ?");
13380
+ params.push(filter.kind);
13381
+ }
13382
+ if (filter.is_template !== undefined) {
13383
+ conditions.push("is_template = ?");
13384
+ params.push(filter.is_template ? 1 : 0);
13385
+ }
13386
+ if (filter.search) {
13387
+ conditions.push("(name LIKE ? OR description LIKE ? OR content LIKE ?)");
13388
+ const q = `%${filter.search}%`;
13389
+ params.push(q, q, q);
13390
+ }
13391
+ if (filter.tags?.length) {
13392
+ conditions.push(`(${filter.tags.map(() => "tags LIKE ?").join(" AND ")})`);
13393
+ for (const tag of filter.tags)
13394
+ params.push(`%"${tag}"%`);
13395
+ }
13396
+ const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
13397
+ const normalized = normalizeBoundedReadOptions(options);
13398
+ const total = d.query(`SELECT COUNT(*) AS total FROM configs ${where}`).get(...params)?.total ?? 0;
13399
+ const pageParams = [...params, normalized.limit, normalized.cursor];
13400
+ const rows = d.query(`SELECT id, name, slug, kind, category, agent, format, is_template, version, created_at, updated_at, synced_at
13401
+ FROM configs ${where} ORDER BY id LIMIT ? OFFSET ?`).all(...pageParams);
13402
+ return boundedReadPage(rows.map((row) => ({
13403
+ ...row,
13404
+ kind: row.kind,
13405
+ category: row.category,
13406
+ agent: row.agent,
13407
+ format: row.format,
13408
+ is_template: Boolean(row.is_template)
13409
+ })), total, normalized);
13410
+ }
13411
+ function listConfigSummariesPage(filter = {}, options = {}, db) {
13412
+ const d = db || getDatabase();
13413
+ const conditions = [];
13414
+ const params = [];
13415
+ if (filter.category) {
13416
+ conditions.push("category = ?");
13417
+ params.push(filter.category);
13418
+ }
13419
+ if (filter.agent) {
13420
+ conditions.push("agent = ?");
13421
+ params.push(filter.agent);
13422
+ }
13423
+ if (filter.kind) {
13424
+ conditions.push("kind = ?");
13425
+ params.push(filter.kind);
13426
+ }
13427
+ if (filter.is_template !== undefined) {
13428
+ conditions.push("is_template = ?");
13429
+ params.push(filter.is_template ? 1 : 0);
13430
+ }
13431
+ if (filter.search) {
13432
+ conditions.push("(name LIKE ? OR description LIKE ? OR content LIKE ?)");
13433
+ const q = `%${filter.search}%`;
13434
+ params.push(q, q, q);
13435
+ }
13436
+ if (filter.tags && filter.tags.length > 0) {
13437
+ conditions.push(`(${filter.tags.map(() => "tags LIKE ?").join(" AND ")})`);
13438
+ for (const tag of filter.tags)
13439
+ params.push(`%"${tag}"%`);
13440
+ }
13441
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
13442
+ const normalized = normalizeBoundedReadOptions(options);
13443
+ const total = d.query(`SELECT COUNT(*) AS total FROM configs ${where}`).get(...params)?.total ?? 0;
13444
+ const pageParams = [...params, normalized.limit, normalized.cursor];
13445
+ const rows = d.query(`SELECT id, name, slug, kind, category, agent, target_path, format,
13446
+ CASE WHEN json_valid(outputs) AND json_type(outputs) = 'array' THEN json_array_length(outputs) ELSE 0 END AS output_count, description, tags,
13447
+ is_template, version, updated_at
13448
+ FROM configs ${where} ORDER BY category, name, id LIMIT ? OFFSET ?`).all(...pageParams);
13449
+ return boundedReadPage(rows.map((row) => ({
13450
+ id: row.id,
13451
+ slug: row.slug,
13452
+ name: row.name,
13453
+ category: row.category,
13454
+ agent: row.agent,
13455
+ kind: row.kind,
13456
+ format: row.format,
13457
+ target_path: row.target_path,
13458
+ output_count: Number(row.output_count ?? 0),
13459
+ version: row.version,
13460
+ is_template: Boolean(row.is_template),
13461
+ updated_at: row.updated_at,
13462
+ description: row.description,
13463
+ tags: JSON.parse(row.tags || "[]")
13464
+ })), total, normalized);
13465
+ }
13329
13466
  function updateConfig(idOrSlug, input, db) {
13330
13467
  const d = db || getDatabase();
13331
13468
  const existing = getConfig(idOrSlug, d);
@@ -13407,6 +13544,7 @@ var init_configs = __esm(() => {
13407
13544
  init_types();
13408
13545
  init_database();
13409
13546
  init_snapshots();
13547
+ init_bounded_read();
13410
13548
  });
13411
13549
 
13412
13550
  // src/lib/template.ts
@@ -13847,7 +13985,10 @@ __export(exports_local, {
13847
13985
  listSnapshots: () => listSnapshots,
13848
13986
  listProfilesPage: () => listProfilesPage,
13849
13987
  listMachines: () => listMachines,
13988
+ listConfigsPage: () => listConfigsPage,
13850
13989
  listConfigs: () => listConfigs,
13990
+ listConfigSummariesPage: () => listConfigSummariesPage,
13991
+ listConfigIdentitiesPage: () => listConfigIdentitiesPage,
13851
13992
  insertFeedback: () => insertFeedback,
13852
13993
  getSnapshotByVersion: () => getSnapshotByVersion,
13853
13994
  getSnapshot: () => getSnapshot,
@@ -13876,11 +14017,11 @@ var init_local = __esm(() => {
13876
14017
 
13877
14018
  // src/data/config-store.ts
13878
14019
  import { randomUUID as randomUUID3 } from "crypto";
13879
- function parseBoundedPagePayload(value, label) {
14020
+ function parseBoundedPagePayload(value, label, expected) {
13880
14021
  const page = value;
13881
14022
  const consumed = Number(page?.cursor) + (page?.items?.length ?? 0);
13882
14023
  const complete = Boolean(page && Number.isSafeInteger(page.total) && consumed >= Number(page.total));
13883
- if (!page || !Array.isArray(page.items) || !Number.isSafeInteger(page.total) || Number(page.total) < 0 || !Number.isSafeInteger(page.limit) || Number(page.limit) < 1 || !Number.isSafeInteger(page.cursor) || Number(page.cursor) < 0 || page.items.length > Number(page.limit) || typeof page.has_more !== "boolean" || typeof page.complete !== "boolean" || page.truncated !== false || page.next_cursor !== null && !Number.isSafeInteger(page.next_cursor) || page.complete !== complete || page.has_more !== !complete || page.next_cursor !== (complete ? null : consumed)) {
14024
+ if (!page || !Array.isArray(page.items) || !Number.isSafeInteger(page.total) || Number(page.total) < 0 || !Number.isSafeInteger(page.limit) || Number(page.limit) < 1 || !Number.isSafeInteger(page.cursor) || Number(page.cursor) < 0 || page.items.length > Number(page.limit) || typeof page.has_more !== "boolean" || typeof page.complete !== "boolean" || page.truncated !== false || page.next_cursor !== null && !Number.isSafeInteger(page.next_cursor) || page.complete !== complete || page.has_more !== !complete || page.next_cursor !== (complete ? null : consumed) || expected !== undefined && Number(page.cursor) !== expected.cursor) {
13884
14025
  throw new Error(`${label} returned an invalid or truncated bounded-read envelope`);
13885
14026
  }
13886
14027
  return {
@@ -13892,7 +14033,7 @@ function parseBoundedOrLegacyPage(value, legacyItems, options, label) {
13892
14033
  if (value && typeof value === "object") {
13893
14034
  const candidate = value;
13894
14035
  if ("items" in candidate || "total" in candidate || "complete" in candidate || "truncated" in candidate || "next_cursor" in candidate) {
13895
- return parseBoundedPagePayload(value, label);
14036
+ return parseBoundedPagePayload(value, label, normalizeBoundedReadOptions(options));
13896
14037
  }
13897
14038
  }
13898
14039
  if (!Array.isArray(legacyItems)) {
@@ -13902,6 +14043,70 @@ function parseBoundedOrLegacyPage(value, legacyItems, options, label) {
13902
14043
  const page = boundedReadPage(legacyItems.slice(normalized.cursor, normalized.cursor + normalized.limit), legacyItems.length, normalized);
13903
14044
  return { ...page, source_bounded: false };
13904
14045
  }
14046
+ function projectBoundedPage(page, project) {
14047
+ return {
14048
+ items: page.items.map(project),
14049
+ total: page.total,
14050
+ limit: page.limit,
14051
+ cursor: page.cursor,
14052
+ next_cursor: page.next_cursor,
14053
+ has_more: page.has_more,
14054
+ complete: page.complete,
14055
+ truncated: page.truncated,
14056
+ source_bounded: page.source_bounded
14057
+ };
14058
+ }
14059
+ async function aggregateBoundedCollection(label, readPage, identity, options = {}) {
14060
+ let lastError = null;
14061
+ for (let attempt = 0;attempt < COLLECTION_READ_ATTEMPTS; attempt += 1) {
14062
+ try {
14063
+ const items = [];
14064
+ const seen = new Set;
14065
+ let expectedTotal = null;
14066
+ let cursor = 0;
14067
+ let previousIdentity = null;
14068
+ while (true) {
14069
+ const page = await readPage(cursor);
14070
+ if (page.cursor !== cursor) {
14071
+ throw new CollectionChangedWhilePagingError(label, `server returned cursor ${page.cursor} for requested cursor ${cursor}`);
14072
+ }
14073
+ if (expectedTotal === null)
14074
+ expectedTotal = page.total;
14075
+ else if (page.total !== expectedTotal) {
14076
+ throw new CollectionChangedWhilePagingError(label, `total changed from ${expectedTotal} to ${page.total}`);
14077
+ }
14078
+ for (const item of page.items) {
14079
+ const key = identity(item);
14080
+ if (!key)
14081
+ throw new CollectionChangedWhilePagingError(label, "an item had no stable identity");
14082
+ if (seen.has(key))
14083
+ throw new CollectionChangedWhilePagingError(label, `duplicate identity ${key}`);
14084
+ if (options.requireAscendingIdentity && page.source_bounded && previousIdentity !== null && key <= previousIdentity) {
14085
+ throw new CollectionChangedWhilePagingError(label, `identity order was not strictly increasing at ${key}`);
14086
+ }
14087
+ seen.add(key);
14088
+ previousIdentity = key;
14089
+ items.push(item);
14090
+ }
14091
+ if (page.complete) {
14092
+ if (items.length !== expectedTotal) {
14093
+ throw new CollectionChangedWhilePagingError(label, `received ${items.length} unique rows for total ${expectedTotal}`);
14094
+ }
14095
+ return items;
14096
+ }
14097
+ if (page.next_cursor === null || page.next_cursor <= cursor) {
14098
+ throw new CollectionChangedWhilePagingError(label, `cursor did not advance from ${cursor}`);
14099
+ }
14100
+ cursor = page.next_cursor;
14101
+ }
14102
+ } catch (error) {
14103
+ if (!(error instanceof CollectionChangedWhilePagingError))
14104
+ throw error;
14105
+ lastError = error;
14106
+ }
14107
+ }
14108
+ throw lastError ?? new CollectionChangedWhilePagingError(label, "read did not stabilize");
14109
+ }
13905
14110
  function isLocalOptIn(env = process.env) {
13906
14111
  return isInstructionsLocalOptIn(env);
13907
14112
  }
@@ -13946,6 +14151,15 @@ class LocalConfigStore {
13946
14151
  async listConfigs(filter) {
13947
14152
  return (await localStoreModule()).listConfigs(filter, this.db);
13948
14153
  }
14154
+ async listConfigsPage(filter = {}, options = {}) {
14155
+ return (await localStoreModule()).listConfigsPage(filter, options, this.db);
14156
+ }
14157
+ async listConfigIdentitiesPage(filter = {}, options = {}) {
14158
+ return (await localStoreModule()).listConfigIdentitiesPage(filter, options, this.db);
14159
+ }
14160
+ async listConfigSummariesPage(filter = {}, options = {}) {
14161
+ return (await localStoreModule()).listConfigSummariesPage(filter, options, this.db);
14162
+ }
13949
14163
  async getConfig(idOrSlug) {
13950
14164
  return (await localStoreModule()).getConfig(idOrSlug, this.db);
13951
14165
  }
@@ -13980,15 +14194,7 @@ class LocalConfigStore {
13980
14194
  return (await localStoreModule()).pruneSnapshots(configId, keep, this.db);
13981
14195
  }
13982
14196
  async listProfiles() {
13983
- const profiles = [];
13984
- let cursor = 0;
13985
- while (true) {
13986
- const page = await this.listProfilesPage({ limit: 100, cursor });
13987
- profiles.push(...page.items);
13988
- if (page.complete)
13989
- return profiles;
13990
- cursor = page.next_cursor;
13991
- }
14197
+ return aggregateBoundedCollection("profile list", (cursor) => this.listProfilesPage({ limit: 100, cursor }), (profile) => profile.id);
13992
14198
  }
13993
14199
  async listProfilesPage(options = {}) {
13994
14200
  return (await localStoreModule()).listProfilesPage(options, this.db);
@@ -13997,15 +14203,7 @@ class LocalConfigStore {
13997
14203
  return (await localStoreModule()).getProfile(idOrSlug, this.db);
13998
14204
  }
13999
14205
  async getProfileConfigs(idOrSlug) {
14000
- const configs = [];
14001
- let cursor = 0;
14002
- while (true) {
14003
- const page = await this.getProfileConfigsPage(idOrSlug, { limit: 100, cursor });
14004
- configs.push(...page.items);
14005
- if (page.complete)
14006
- return configs;
14007
- cursor = page.next_cursor;
14008
- }
14206
+ return aggregateBoundedCollection("profile membership", (cursor) => this.getProfileConfigsPage(idOrSlug, { limit: 100, cursor }), (config) => config.id);
14009
14207
  }
14010
14208
  async getProfileConfigsPage(idOrSlug, options = {}) {
14011
14209
  return (await localStoreModule()).getProfileConfigsPage(idOrSlug, options, this.db);
@@ -14065,6 +14263,43 @@ class LocalConfigStore {
14065
14263
  (await localStoreModule()).resetLocalDatabase();
14066
14264
  }
14067
14265
  }
14266
+ function toConfigIdentity(value) {
14267
+ const record = value && typeof value === "object" ? value : {};
14268
+ return {
14269
+ id: String(record.id ?? ""),
14270
+ name: String(record.name ?? ""),
14271
+ slug: String(record.slug ?? ""),
14272
+ kind: String(record.kind ?? ""),
14273
+ category: String(record.category ?? ""),
14274
+ agent: String(record.agent ?? ""),
14275
+ format: String(record.format ?? ""),
14276
+ is_template: Boolean(record.is_template),
14277
+ version: Number(record.version ?? 0),
14278
+ created_at: String(record.created_at ?? ""),
14279
+ updated_at: String(record.updated_at ?? ""),
14280
+ synced_at: record.synced_at == null ? null : String(record.synced_at)
14281
+ };
14282
+ }
14283
+ function toConfigSummary(value) {
14284
+ const record = value && typeof value === "object" ? value : {};
14285
+ const outputs = Array.isArray(record.outputs) ? record.outputs : [];
14286
+ return {
14287
+ id: String(record.id ?? ""),
14288
+ slug: String(record.slug ?? ""),
14289
+ name: String(record.name ?? ""),
14290
+ category: String(record.category ?? ""),
14291
+ agent: String(record.agent ?? ""),
14292
+ kind: String(record.kind ?? ""),
14293
+ format: String(record.format ?? ""),
14294
+ target_path: record.target_path == null ? null : String(record.target_path),
14295
+ output_count: Number(record.output_count ?? outputs.length),
14296
+ version: Number(record.version ?? 0),
14297
+ is_template: Boolean(record.is_template),
14298
+ ...record.updated_at !== undefined ? { updated_at: String(record.updated_at) } : {},
14299
+ ...record.description !== undefined ? { description: record.description == null ? null : String(record.description) } : {},
14300
+ ...Array.isArray(record.tags) ? { tags: record.tags.map(String) } : {}
14301
+ };
14302
+ }
14068
14303
 
14069
14304
  class CloudConfigStore {
14070
14305
  mode = "api";
@@ -14086,7 +14321,8 @@ class CloudConfigStore {
14086
14321
  throw err;
14087
14322
  }
14088
14323
  }
14089
- async listConfigs(filter = {}) {
14324
+ async listConfigsPage(filter = {}, options = {}) {
14325
+ const normalized = normalizeBoundedReadOptions(options);
14090
14326
  const params = new URLSearchParams;
14091
14327
  if (filter.category)
14092
14328
  params.set("category", filter.category);
@@ -14096,16 +14332,75 @@ class CloudConfigStore {
14096
14332
  params.set("kind", filter.kind);
14097
14333
  if (filter.search)
14098
14334
  params.set("search", filter.search);
14099
- const qs = params.toString();
14100
- const { data } = await this.request("GET", `/configs${qs ? `?${qs}` : ""}`);
14101
- let configs = data?.configs ?? [];
14335
+ for (const tag of filter.tags ?? [])
14336
+ params.append("tag", tag);
14337
+ params.set("limit", String(normalized.limit));
14338
+ params.set("cursor", String(normalized.cursor));
14339
+ const { data } = await this.request("GET", `/configs?${params.toString()}`);
14340
+ return parseBoundedOrLegacyPage(data, data?.configs, normalized, "config list");
14341
+ }
14342
+ async listConfigIdentitiesPage(filter = {}, options = {}) {
14343
+ const normalized = normalizeBoundedReadOptions(options);
14344
+ const params = new URLSearchParams;
14345
+ if (filter.category)
14346
+ params.set("category", filter.category);
14347
+ if (filter.agent)
14348
+ params.set("agent", filter.agent);
14349
+ if (filter.kind)
14350
+ params.set("kind", filter.kind);
14351
+ if (filter.search)
14352
+ params.set("search", filter.search);
14353
+ for (const tag of filter.tags ?? [])
14354
+ params.append("tag", tag);
14355
+ params.set("view", "identity");
14356
+ params.set("limit", String(normalized.limit));
14357
+ params.set("cursor", String(normalized.cursor));
14358
+ const { data } = await this.request("GET", `/configs?${params.toString()}`);
14359
+ const page = parseBoundedOrLegacyPage(data, data?.configs, normalized, "config identity list");
14360
+ if (page.items.some((item) => {
14361
+ const record = item && typeof item === "object" ? item : {};
14362
+ return ["content", "target_path", "outputs", "description", "tags"].some((field) => Object.prototype.hasOwnProperty.call(record, field));
14363
+ })) {
14364
+ throw new Error("Instructions /v1 config identity projection is unavailable; deploy the identity endpoint before using compact lists");
14365
+ }
14366
+ return projectBoundedPage(page, toConfigIdentity);
14367
+ }
14368
+ async listConfigSummariesPage(filter = {}, options = {}) {
14369
+ const normalized = normalizeBoundedReadOptions(options);
14370
+ const params = new URLSearchParams;
14371
+ if (filter.category)
14372
+ params.set("category", filter.category);
14373
+ if (filter.agent)
14374
+ params.set("agent", filter.agent);
14375
+ if (filter.kind)
14376
+ params.set("kind", filter.kind);
14377
+ if (filter.search)
14378
+ params.set("search", filter.search);
14379
+ for (const tag of filter.tags ?? [])
14380
+ params.append("tag", tag);
14381
+ params.set("view", "summary");
14382
+ params.set("limit", String(normalized.limit));
14383
+ params.set("cursor", String(normalized.cursor));
14384
+ const { data } = await this.request("GET", `/configs?${params.toString()}`);
14385
+ const page = parseBoundedOrLegacyPage(data, data?.configs, normalized, "config summary list");
14386
+ if (page.items.some((item) => {
14387
+ const record = item && typeof item === "object" ? item : {};
14388
+ return Object.prototype.hasOwnProperty.call(record, "content") || Object.prototype.hasOwnProperty.call(record, "outputs");
14389
+ })) {
14390
+ throw new Error("Instructions /v1 config summary projection is unavailable; deploy the summary endpoint before using summary lists");
14391
+ }
14392
+ return projectBoundedPage(page, toConfigSummary);
14393
+ }
14394
+ async listConfigs(filter = {}) {
14395
+ const configs = await aggregateBoundedCollection("config list", (cursor) => this.listConfigsPage(filter, { limit: 100, cursor }), (config) => config.id, { requireAscendingIdentity: true });
14396
+ let filtered = configs;
14102
14397
  if (filter.tags && filter.tags.length > 0) {
14103
- configs = configs.filter((c) => filter.tags.every((t) => c.tags.includes(t)));
14398
+ filtered = filtered.filter((config) => filter.tags.every((tag) => config.tags.includes(tag)));
14104
14399
  }
14105
14400
  if (filter.is_template !== undefined) {
14106
- configs = configs.filter((c) => c.is_template === filter.is_template);
14401
+ filtered = filtered.filter((config) => config.is_template === filter.is_template);
14107
14402
  }
14108
- return configs;
14403
+ return filtered;
14109
14404
  }
14110
14405
  async getConfig(idOrSlug) {
14111
14406
  const { status, data } = await this.request("GET", `/configs/${encodeURIComponent(idOrSlug)}`, undefined, { allow404: true });
@@ -14136,8 +14431,11 @@ class CloudConfigStore {
14136
14431
  return data ?? { total: 0 };
14137
14432
  }
14138
14433
  async listSnapshots(configId) {
14139
- const { data } = await this.request("GET", `/configs/${encodeURIComponent(configId)}/snapshots`);
14140
- return data?.snapshots ?? [];
14434
+ return aggregateBoundedCollection("snapshot list", async (cursor) => {
14435
+ const normalized = normalizeBoundedReadOptions({ limit: 100, cursor });
14436
+ const { data } = await this.request("GET", `/configs/${encodeURIComponent(configId)}/snapshots?limit=${normalized.limit}&cursor=${normalized.cursor}`);
14437
+ return parseBoundedOrLegacyPage(data, data?.snapshots, normalized, "snapshot list");
14438
+ }, (snapshot) => snapshot.id);
14141
14439
  }
14142
14440
  async getSnapshot(id) {
14143
14441
  const { status, data } = await this.request("GET", `/snapshots/${encodeURIComponent(id)}`, undefined, { allow404: true });
@@ -14160,15 +14458,7 @@ class CloudConfigStore {
14160
14458
  return data?.pruned ?? 0;
14161
14459
  }
14162
14460
  async listProfiles() {
14163
- const profiles = [];
14164
- let cursor = 0;
14165
- while (true) {
14166
- const page = await this.listProfilesPage({ limit: 100, cursor });
14167
- profiles.push(...page.items);
14168
- if (page.complete)
14169
- return profiles;
14170
- cursor = page.next_cursor;
14171
- }
14461
+ return aggregateBoundedCollection("profile list", (cursor) => this.listProfilesPage({ limit: 100, cursor }), (profile) => profile.id);
14172
14462
  }
14173
14463
  async listProfilesPage(options = {}) {
14174
14464
  const normalized = normalizeBoundedReadOptions(options);
@@ -14192,15 +14482,7 @@ class CloudConfigStore {
14192
14482
  return profile;
14193
14483
  }
14194
14484
  async getProfileConfigs(idOrSlug) {
14195
- const configs = [];
14196
- let cursor = 0;
14197
- while (true) {
14198
- const page = await this.getProfileConfigsPage(idOrSlug, { limit: 100, cursor });
14199
- configs.push(...page.items);
14200
- if (page.complete)
14201
- return configs;
14202
- cursor = page.next_cursor;
14203
- }
14485
+ return aggregateBoundedCollection("profile membership", (cursor) => this.getProfileConfigsPage(idOrSlug, { limit: 100, cursor }), (config) => config.id);
14204
14486
  }
14205
14487
  async getProfileConfigsPage(idOrSlug, options = {}) {
14206
14488
  const normalized = normalizeBoundedReadOptions(options);
@@ -14214,20 +14496,26 @@ class CloudConfigStore {
14214
14496
  return parseBoundedOrLegacyPage(data.configs, data.profile.configs, normalized, "profile membership");
14215
14497
  }
14216
14498
  async getProfileConfigBindings(idOrSlug) {
14217
- const { status, data } = await this.requestProfileRoute(idOrSlug, (profileId) => `/profiles/${encodeURIComponent(profileId)}/bindings`, (value) => Array.isArray(value?.bindings));
14218
- if (status === 404) {
14219
- const profile = await this.getProfile(idOrSlug);
14220
- const configs = await this.getProfileConfigs(idOrSlug);
14221
- return configs.map((config, sort_order) => ({
14222
- profile_id: profile.id,
14223
- config_id: config.id,
14224
- sort_order,
14225
- binding: legacyProfileConfigBinding()
14226
- }));
14227
- }
14228
- if (!data || !Array.isArray(data.bindings))
14229
- throw new ProfileNotFoundError(idOrSlug);
14230
- return data.bindings;
14499
+ let routeMissing = false;
14500
+ const bindings = await aggregateBoundedCollection("profile config bindings", async (cursor) => {
14501
+ const normalized = normalizeBoundedReadOptions({ limit: 100, cursor });
14502
+ const { status, data } = await this.requestProfileRoute(idOrSlug, (profileId) => `/profiles/${encodeURIComponent(profileId)}/bindings?limit=${normalized.limit}&cursor=${normalized.cursor}`, (value) => Boolean(value && (Array.isArray(value.bindings) || Array.isArray(value.items))));
14503
+ if (status === 404) {
14504
+ routeMissing = true;
14505
+ return { ...boundedReadPage([], 0, normalized), source_bounded: false };
14506
+ }
14507
+ return parseBoundedOrLegacyPage(data, data?.bindings, normalized, "profile config bindings");
14508
+ }, (binding) => `${binding.profile_id}\x00${binding.config_id}`);
14509
+ if (!routeMissing)
14510
+ return bindings;
14511
+ const profile = await this.getProfile(idOrSlug);
14512
+ const configs = await this.getProfileConfigs(idOrSlug);
14513
+ return configs.map((config, sort_order) => ({
14514
+ profile_id: profile.id,
14515
+ config_id: config.id,
14516
+ sort_order,
14517
+ binding: legacyProfileConfigBinding()
14518
+ }));
14231
14519
  }
14232
14520
  async requestProfileRoute(idOrSlug, pathForId, isUsable) {
14233
14521
  const attempted = new Set;
@@ -14285,14 +14573,20 @@ class CloudConfigStore {
14285
14573
  await this.request("DELETE", `/profiles/${encodeURIComponent(profileIdOrSlug)}/configs/${encodeURIComponent(configId)}`, undefined, { allow404: true });
14286
14574
  }
14287
14575
  async getProfileAssetBindings(profileIdOrSlug) {
14288
- const { status, data } = await this.requestProfileRoute(profileIdOrSlug, (profileId) => `/profiles/${encodeURIComponent(profileId)}/assets`, (value) => Array.isArray(value?.assets));
14289
- if (status === 404) {
14290
- await this.getProfile(profileIdOrSlug);
14291
- return [];
14292
- }
14293
- if (!data || !Array.isArray(data.assets))
14294
- throw new ProfileNotFoundError(profileIdOrSlug);
14295
- return data.assets;
14576
+ let routeMissing = false;
14577
+ const assets = await aggregateBoundedCollection("profile asset bindings", async (cursor) => {
14578
+ const normalized = normalizeBoundedReadOptions({ limit: 100, cursor });
14579
+ const { status, data } = await this.requestProfileRoute(profileIdOrSlug, (profileId) => `/profiles/${encodeURIComponent(profileId)}/assets?limit=${normalized.limit}&cursor=${normalized.cursor}`, (value) => Boolean(value && (Array.isArray(value.assets) || Array.isArray(value.items))));
14580
+ if (status === 404) {
14581
+ routeMissing = true;
14582
+ return { ...boundedReadPage([], 0, normalized), source_bounded: false };
14583
+ }
14584
+ return parseBoundedOrLegacyPage(data, data?.assets, normalized, "profile asset bindings");
14585
+ }, (asset) => `${asset.profile_id}\x00${asset.binding.assetKey}`);
14586
+ if (!routeMissing)
14587
+ return assets;
14588
+ await this.getProfile(profileIdOrSlug);
14589
+ return [];
14296
14590
  }
14297
14591
  async addAssetToProfile(profileIdOrSlug, sourceConfigId, binding) {
14298
14592
  const { data } = await this.request("POST", `/profiles/${encodeURIComponent(profileIdOrSlug)}/assets`, { source_config_id: sourceConfigId, binding }, { idempotent: true });
@@ -14362,8 +14656,11 @@ class CloudConfigStore {
14362
14656
  await this.request("POST", "/machines/applied", { hostname: hostname2 });
14363
14657
  }
14364
14658
  async listMachines() {
14365
- const { data } = await this.request("GET", "/machines");
14366
- return data?.machines ?? [];
14659
+ return aggregateBoundedCollection("machine list", async (cursor) => {
14660
+ const normalized = normalizeBoundedReadOptions({ limit: 100, cursor });
14661
+ const { data } = await this.request("GET", `/machines?limit=${normalized.limit}&cursor=${normalized.cursor}`);
14662
+ return parseBoundedOrLegacyPage(data, data?.machines, normalized, "machine list");
14663
+ }, (machine) => machine.id, { requireAscendingIdentity: true });
14367
14664
  }
14368
14665
  async sendFeedback(input) {
14369
14666
  await this.request("POST", "/feedback", {
@@ -14386,13 +14683,19 @@ function resolveConfigStore(env = process.env, options = {}) {
14386
14683
  const { client } = resolveInstructionsStorageClient(env, options);
14387
14684
  return new CloudConfigStore(client);
14388
14685
  }
14389
- var LOCAL_OPT_IN_ENV, localStoreModulePromise = null;
14686
+ var CollectionChangedWhilePagingError, COLLECTION_READ_ATTEMPTS = 2, LOCAL_OPT_IN_ENV, localStoreModulePromise = null;
14390
14687
  var init_config_store = __esm(() => {
14391
14688
  init_types();
14392
14689
  init_bounded_read();
14393
14690
  init_instruction_graph();
14394
14691
  init_transport_resolver();
14395
14692
  init_local_opt_in();
14693
+ CollectionChangedWhilePagingError = class CollectionChangedWhilePagingError extends Error {
14694
+ constructor(label, detail) {
14695
+ super(`${label} changed while paging: ${detail}`);
14696
+ this.name = "CollectionChangedWhilePagingError";
14697
+ }
14698
+ };
14396
14699
  LOCAL_OPT_IN_ENV = INSTRUCTIONS_LOCAL_OPT_IN_ENV;
14397
14700
  });
14398
14701
 
@@ -14415,7 +14718,7 @@ init_config_agents();
14415
14718
  init_machine();
14416
14719
  init_session_render();
14417
14720
  import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync8, realpathSync as realpathSync3, writeFileSync as writeFileSync2 } from "fs";
14418
- import { basename as basename5, dirname as dirname6, join as join12, resolve as resolve9 } from "path";
14721
+ import { basename as basename5, dirname as dirname6, isAbsolute as isAbsolute6, join as join12, relative as relative4, resolve as resolve9, sep as sep2 } from "path";
14419
14722
  import { homedir as homedir7 } from "os";
14420
14723
 
14421
14724
  // src/lib/session-render-ownership.ts
@@ -14504,6 +14807,17 @@ function expandPath(p) {
14504
14807
  }
14505
14808
  return resolve9(p);
14506
14809
  }
14810
+ function compactPathForConfigHome(p) {
14811
+ const absolute = resolve9(p);
14812
+ const configHome = resolve9(getConfigHome());
14813
+ const conventionalHome = resolve9(process.env["HOME"] || homedir7());
14814
+ if (configHome !== conventionalHome)
14815
+ return absolute;
14816
+ const rel = relative4(configHome, absolute);
14817
+ if (!rel || rel === ".." || rel.startsWith(`..${sep2}`) || isAbsolute6(rel))
14818
+ return absolute;
14819
+ return `~/${rel}`;
14820
+ }
14507
14821
  function normalizeTargetPath(p) {
14508
14822
  const expanded = expandPath(p);
14509
14823
  try {
@@ -14957,7 +15271,7 @@ import {
14957
15271
  writeFileSync as writeFileSync3
14958
15272
  } from "fs";
14959
15273
  import { homedir as homedir8 } from "os";
14960
- import { dirname as dirname8, join as join14, parse as parse4, relative as relative4, resolve as resolve10 } from "path";
15274
+ import { dirname as dirname8, join as join14, parse as parse4, relative as relative5, resolve as resolve10 } from "path";
14961
15275
  var INBOX_CONVERSATIONS_MINIMUM_VERSION = "0.5.28";
14962
15276
  var INBOX_SKILL_MARKERS = [
14963
15277
  [".claude", "skills", "inbox", "SKILL.md"],
@@ -14981,7 +15295,7 @@ function findSymlinkedAncestor(path) {
14981
15295
  const normalized = resolve10(path);
14982
15296
  const parsed = parse4(normalized);
14983
15297
  let current = parsed.root;
14984
- const rel = relative4(parsed.root, normalized);
15298
+ const rel = relative5(parsed.root, normalized);
14985
15299
  for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
14986
15300
  current = join14(current, segment);
14987
15301
  if (!existsSync11(current))
@@ -15000,16 +15314,7 @@ function assertNoSymlinkAncestors2(path) {
15000
15314
  function packagedInboxSkillPath(explicitPath) {
15001
15315
  if (explicitPath)
15002
15316
  return explicitPath;
15003
- const candidates = [
15004
- join14(import.meta.dir, "..", "..", "assets", "skills", "inbox", "SKILL.md"),
15005
- join14(import.meta.dir, "..", "assets", "skills", "inbox", "SKILL.md"),
15006
- join14(process.cwd(), "assets", "skills", "inbox", "SKILL.md")
15007
- ];
15008
- const found = candidates.find((candidate) => existsSync11(candidate));
15009
- if (!found) {
15010
- throw new Error(`packaged inbox skill contract is missing (checked ${candidates.length} package-relative locations)`);
15011
- }
15012
- return found;
15317
+ throw new Error("Bundled skill contracts are retired; use the Skills CLI to manage private skills");
15013
15318
  }
15014
15319
  function readCanonicalSkill(explicitPath) {
15015
15320
  const assetPath = packagedInboxSkillPath(explicitPath);
@@ -16028,7 +16333,7 @@ import {
16028
16333
  readdirSync as readdirSync3,
16029
16334
  statSync as statSync5
16030
16335
  } from "fs";
16031
- import { dirname as dirname10, isAbsolute as isAbsolute6, join as join17, parse as parse5, relative as relative5, resolve as resolve11 } from "path";
16336
+ import { dirname as dirname10, isAbsolute as isAbsolute7, join as join17, parse as parse5, relative as relative6, resolve as resolve11 } from "path";
16032
16337
 
16033
16338
  class SessionApplyError extends Error {
16034
16339
  constructor(message) {
@@ -16166,7 +16471,7 @@ function ensureSessionTargetHome(targetHome) {
16166
16471
  }
16167
16472
  function checkSessionRenderDrift(targetHome, manifestPath) {
16168
16473
  const safeTargetHome = assertSafeTargetHome(targetHome);
16169
- const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative5(safeTargetHome, resolve11(manifestPath)), safeTargetHome) : resolve11(safeTargetHome, ".hasna", "session-render-manifest.json");
16474
+ const resolvedManifestPath = manifestPath ? resolveManifestRelativePath(relative6(safeTargetHome, resolve11(manifestPath)), safeTargetHome) : resolve11(safeTargetHome, ".hasna", "session-render-manifest.json");
16170
16475
  const checkedAt = new Date().toISOString();
16171
16476
  const previousManifest = readPreviousManifest(resolvedManifestPath);
16172
16477
  if (!previousManifest) {
@@ -16218,11 +16523,11 @@ function restoreSessionRenderSnapshot(snapshotPath, options = {}) {
16218
16523
  const targetHome = assertSafeTargetHome(snapshot.targetHome);
16219
16524
  const resolvedSnapshotPath = resolve11(snapshotPath);
16220
16525
  const snapshotDir = getSessionRenderSnapshotDir(targetHome);
16221
- const snapshotDirRelative = relative5(snapshotDir, resolvedSnapshotPath);
16222
- const insideSnapshotDir = snapshotDirRelative !== ".." && !snapshotDirRelative.startsWith("../") && !isAbsolute6(snapshotDirRelative);
16526
+ const snapshotDirRelative = relative6(snapshotDir, resolvedSnapshotPath);
16527
+ const insideSnapshotDir = snapshotDirRelative !== ".." && !snapshotDirRelative.startsWith("../") && !isAbsolute7(snapshotDirRelative);
16223
16528
  if (!insideSnapshotDir) {
16224
- const snapshotRelativePath = relative5(targetHome, resolvedSnapshotPath);
16225
- if (snapshotRelativePath === "" || snapshotRelativePath === ".." || snapshotRelativePath.startsWith("../") || isAbsolute6(snapshotRelativePath)) {
16529
+ const snapshotRelativePath = relative6(targetHome, resolvedSnapshotPath);
16530
+ if (snapshotRelativePath === "" || snapshotRelativePath === ".." || snapshotRelativePath.startsWith("../") || isAbsolute7(snapshotRelativePath)) {
16226
16531
  throw new SessionApplyError("Session snapshot must be stored inside its session-render snapshot location.");
16227
16532
  }
16228
16533
  }
@@ -16421,7 +16726,7 @@ function readSessionRenderSnapshot(snapshotPath) {
16421
16726
  function reconstructPreRollbackLegacyV1Snapshot(snapshot, previousFiles, previousManifestFiles, targetHome, snapshotPath) {
16422
16727
  assertNoNewerSessionSnapshot(snapshotPath, snapshot.createdAt, targetHome);
16423
16728
  const manifestPath = resolve11(snapshot.manifestPath);
16424
- const manifestRelativePath = relative5(targetHome, manifestPath).replaceAll("\\", "/");
16729
+ const manifestRelativePath = relative6(targetHome, manifestPath).replaceAll("\\", "/");
16425
16730
  resolveSnapshotFilePath(manifestRelativePath, snapshot.manifestPath, targetHome);
16426
16731
  const manifestSha256 = currentSessionFileHash(manifestPath, targetHome);
16427
16732
  if (manifestSha256 === null) {
@@ -16754,8 +17059,8 @@ function isPreviouslyManaged(file, previousSha256, previousHashes, previousManif
16754
17059
  }
16755
17060
  function resolvePlannedFilePath(plan, file, targetHome) {
16756
17061
  const target = resolve11(targetHome, ...file.relativePath.split("/"));
16757
- const rel = relative5(targetHome, target);
16758
- if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute6(rel)) {
17062
+ const rel = relative6(targetHome, target);
17063
+ if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute7(rel)) {
16759
17064
  throw new SessionApplyError(`Session file escapes target home: ${file.relativePath}`);
16760
17065
  }
16761
17066
  if (resolve11(file.path) !== target) {
@@ -16766,8 +17071,8 @@ function resolvePlannedFilePath(plan, file, targetHome) {
16766
17071
  }
16767
17072
  function resolveManifestRelativePath(relativePath, targetHome) {
16768
17073
  const target = resolve11(targetHome, ...relativePath.split(/[\\/]+/));
16769
- const rel = relative5(targetHome, target);
16770
- if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute6(rel)) {
17074
+ const rel = relative6(targetHome, target);
17075
+ if (rel === "" || rel === ".." || rel.startsWith("../") || isAbsolute7(rel)) {
16771
17076
  throw new SessionApplyError(`Session manifest file escapes target home: ${relativePath}`);
16772
17077
  }
16773
17078
  assertNoSymlinkSegments2(targetHome, target);
@@ -16811,7 +17116,7 @@ function applyPlannedFile(plan, file, targetHome, resultsByPath, coordination, a
16811
17116
  function assertExpectedSessionFileHash(path, targetHome, expectedHash) {
16812
17117
  const actualHash = currentSessionFileHash(path, targetHome);
16813
17118
  if (actualHash !== expectedHash) {
16814
- throw new SessionApplyError(`Session apply path changed after planning: ${relative5(targetHome, path)}`);
17119
+ throw new SessionApplyError(`Session apply path changed after planning: ${relative6(targetHome, path)}`);
16815
17120
  }
16816
17121
  }
16817
17122
  function currentSessionFileHash(path, targetHome) {
@@ -16896,7 +17201,7 @@ function writeSessionSnapshot(plan, targetHome, manifestPath, results, previousM
16896
17201
  };
16897
17202
  }
16898
17203
  function assertSafeTargetHome(targetHome) {
16899
- if (!isAbsolute6(targetHome))
17204
+ if (!isAbsolute7(targetHome))
16900
17205
  throw new SessionApplyError(`Session target home must be absolute: ${targetHome}`);
16901
17206
  const normalized = resolve11(targetHome);
16902
17207
  if (normalized === parse5(normalized).root) {
@@ -16910,7 +17215,7 @@ function assertSafeTargetHome(targetHome) {
16910
17215
  }
16911
17216
  function assertNoSymlinkSegments2(root, target) {
16912
17217
  assertNoSymlinkAncestors3(root);
16913
- const rel = relative5(root, target);
17218
+ const rel = relative6(root, target);
16914
17219
  let current = root;
16915
17220
  for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
16916
17221
  current = join17(current, segment);
@@ -16923,7 +17228,7 @@ function assertNoSymlinkAncestors3(path) {
16923
17228
  const normalized = resolve11(path);
16924
17229
  const parsed = parse5(normalized);
16925
17230
  let current = parsed.root;
16926
- const rel = relative5(parsed.root, normalized);
17231
+ const rel = relative6(parsed.root, normalized);
16927
17232
  for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
16928
17233
  current = join17(current, segment);
16929
17234
  if (!existsSync15(current))
@@ -17268,8 +17573,7 @@ init_transforms();
17268
17573
  // src/lib/sync-dir.ts
17269
17574
  init_config_store();
17270
17575
  import { existsSync as existsSync16, readdirSync as readdirSync4, readFileSync as readFileSync15, statSync as statSync6 } from "fs";
17271
- import { join as join18, relative as relative6 } from "path";
17272
- import { homedir as homedir10 } from "os";
17576
+ import { join as join18, relative as relative7 } from "path";
17273
17577
  init_redact();
17274
17578
  var SKIP = [".db", ".db-shm", ".db-wal", ".log", ".lock", ".DS_Store", "node_modules", ".git"];
17275
17579
  function shouldSkip(p) {
@@ -17282,7 +17586,6 @@ async function syncFromDir(dir, opts = {}) {
17282
17586
  return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
17283
17587
  const files = opts.recursive !== false ? walkDir(absDir) : readdirSync4(absDir).map((f) => join18(absDir, f)).filter((f) => statSync6(f).isFile());
17284
17588
  const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
17285
- const home = homedir10();
17286
17589
  const allConfigs = await store.listConfigs();
17287
17590
  for (const file of files) {
17288
17591
  if (shouldSkip(file)) {
@@ -17295,12 +17598,12 @@ async function syncFromDir(dir, opts = {}) {
17295
17598
  result.skipped.push(file + " (too large)");
17296
17599
  continue;
17297
17600
  }
17298
- const targetPath = file.replace(home, "~");
17601
+ const targetPath = compactPathForConfigHome(file);
17299
17602
  const redacted = redactContent(content, redactFormatForTarget(targetPath, detectFormat(file)));
17300
17603
  const existing = allConfigs.find((c) => c.target_path === targetPath);
17301
17604
  if (!existing) {
17302
17605
  if (!opts.dryRun)
17303
- await store.createConfig({ name: relative6(absDir, file), category: detectCategory(file), agent: detectAgent(file), target_path: targetPath, format: detectFormat(file), content: redacted.content });
17606
+ await store.createConfig({ name: relative7(absDir, file), category: detectCategory(file), agent: detectAgent(file), target_path: targetPath, format: detectFormat(file), content: redacted.content });
17304
17607
  result.added++;
17305
17608
  } else if (existing.content !== redacted.content) {
17306
17609
  if (!opts.dryRun)
@@ -17317,9 +17620,8 @@ async function syncFromDir(dir, opts = {}) {
17317
17620
  }
17318
17621
  async function syncToDir(dir, opts = {}) {
17319
17622
  const store = opts.store ?? resolveConfigStore();
17320
- const home = homedir10();
17321
17623
  const absDir = expandPath(dir);
17322
- const normalized = dir.startsWith("~/") ? dir : absDir.replace(home, "~");
17624
+ const normalized = dir.startsWith("~/") ? dir : compactPathForConfigHome(absDir);
17323
17625
  const configs = (await store.listConfigs()).filter((c) => c.target_path && (c.target_path.startsWith(normalized) || c.target_path.startsWith(absDir)));
17324
17626
  const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
17325
17627
  for (const config of configs) {
@@ -17869,112 +18171,680 @@ function detectFormat(filePath) {
17869
18171
  return "text";
17870
18172
  }
17871
18173
  // src/lib/export.ts
18174
+ init_types();
17872
18175
  init_config_store();
17873
- import { existsSync as existsSync18, mkdirSync as mkdirSync8, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
17874
- import { join as join20, resolve as resolve12 } from "path";
18176
+ import { createHash as createHash11 } from "crypto";
18177
+ import { existsSync as existsSync18, mkdirSync as mkdirSync8, mkdtempSync, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
18178
+ import { dirname as dirname11, join as join20, resolve as resolve12 } from "path";
17875
18179
  import { tmpdir } from "os";
17876
- async function exportConfigs(outputPath, opts = {}) {
18180
+ var ARCHIVE_EXCLUSIONS = [
18181
+ {
18182
+ entity: "api_keys",
18183
+ classification: "security_state",
18184
+ reason: "API credentials are security state and must be provisioned independently, never copied in a domain archive."
18185
+ },
18186
+ {
18187
+ entity: "idempotency_receipts",
18188
+ classification: "transport_state",
18189
+ reason: "Request replay receipts are transport state and are not portable business-domain data."
18190
+ },
18191
+ {
18192
+ entity: "feedback",
18193
+ classification: "out_of_domain",
18194
+ reason: "Feedback is product telemetry, not part of the Instructions configuration domain."
18195
+ }
18196
+ ];
18197
+ function compareText(left, right) {
18198
+ return left < right ? -1 : left > right ? 1 : 0;
18199
+ }
18200
+ function sha25611(value) {
18201
+ return createHash11("sha256").update(value).digest("hex");
18202
+ }
18203
+ function canonicalize(value) {
18204
+ if (Array.isArray(value))
18205
+ return value.map(canonicalize);
18206
+ if (value && typeof value === "object") {
18207
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined).sort(([left], [right]) => compareText(left, right)).map(([key, entry]) => [key, canonicalize(entry)]));
18208
+ }
18209
+ return value;
18210
+ }
18211
+ function canonicalDomainJson(value) {
18212
+ return JSON.stringify(canonicalize(value));
18213
+ }
18214
+ function logicalAssetLocator(locator, idToSlug) {
18215
+ const match = /^config:\/\/([^@]+)@(\d+)$/.exec(locator);
18216
+ if (!match)
18217
+ return locator;
18218
+ let configId;
18219
+ try {
18220
+ configId = decodeURIComponent(match[1]);
18221
+ } catch {
18222
+ return locator;
18223
+ }
18224
+ const slug2 = idToSlug.get(configId);
18225
+ return slug2 ? `config-slug://${encodeURIComponent(slug2)}@${match[2]}` : locator;
18226
+ }
18227
+ function logicalCollections(domain, options) {
18228
+ const configs = [...domain.configs].sort((left, right) => compareText(left.slug, right.slug));
18229
+ const snapshots = domain.config_snapshots.map((snapshot) => ({
18230
+ config_slug: snapshot.config_slug,
18231
+ version: snapshot.version,
18232
+ content: snapshot.content,
18233
+ ...options.includeOperationalTimestamps ? { created_at: snapshot.created_at } : {}
18234
+ })).sort((left, right) => compareText(left.config_slug, right.config_slug) || left.version - right.version || compareText(left.content, right.content) || compareText("created_at" in left ? left.created_at ?? "" : "", "created_at" in right ? right.created_at ?? "" : ""));
18235
+ const profiles = [...domain.profiles].sort((left, right) => compareText(left.slug, right.slug));
18236
+ const configBindings = [...domain.profile_config_bindings].sort((left, right) => compareText(left.profile_slug, right.profile_slug) || left.sort_order - right.sort_order || compareText(left.config_slug, right.config_slug));
18237
+ const assetBindings = [...domain.profile_asset_bindings].sort((left, right) => compareText(left.profile_slug, right.profile_slug) || left.sort_order - right.sort_order || compareText(left.binding.assetKey, right.binding.assetKey));
18238
+ const machines = [...domain.machines].sort((left, right) => compareText(left.hostname, right.hostname));
18239
+ const idToSlug = new Map(configs.map((config) => [config.id, config.slug]));
18240
+ const configOrder = new Map;
18241
+ const assetOrder = new Map;
18242
+ return {
18243
+ configs: configs.map((config) => ({
18244
+ slug: config.slug,
18245
+ name: config.name,
18246
+ kind: config.kind,
18247
+ category: config.category,
18248
+ agent: config.agent,
18249
+ target_path: config.target_path,
18250
+ outputs: config.outputs,
18251
+ format: config.format,
18252
+ content: config.content,
18253
+ description: config.description,
18254
+ tags: [...config.tags].sort(compareText),
18255
+ is_template: config.is_template,
18256
+ version: config.version,
18257
+ ...options.includeOperationalTimestamps ? {
18258
+ created_at: config.created_at,
18259
+ updated_at: config.updated_at,
18260
+ synced_at: config.synced_at
18261
+ } : {}
18262
+ })),
18263
+ config_snapshots: snapshots,
18264
+ profiles: profiles.map((profile) => ({
18265
+ slug: profile.slug,
18266
+ name: profile.name,
18267
+ description: profile.description,
18268
+ selectors: profile.selectors,
18269
+ variables: profile.variables,
18270
+ ...options.includeOperationalTimestamps ? {
18271
+ created_at: profile.created_at,
18272
+ updated_at: profile.updated_at
18273
+ } : {}
18274
+ })),
18275
+ profile_config_bindings: configBindings.map((row) => {
18276
+ const order = configOrder.get(row.profile_slug) ?? 0;
18277
+ configOrder.set(row.profile_slug, order + 1);
18278
+ return {
18279
+ profile_slug: row.profile_slug,
18280
+ config_slug: row.config_slug,
18281
+ order,
18282
+ binding: row.binding
18283
+ };
18284
+ }),
18285
+ profile_asset_bindings: assetBindings.map((row) => {
18286
+ const order = assetOrder.get(row.profile_slug) ?? 0;
18287
+ assetOrder.set(row.profile_slug, order + 1);
18288
+ return {
18289
+ profile_slug: row.profile_slug,
18290
+ source_config_slug: row.source_config_slug,
18291
+ order,
18292
+ binding: {
18293
+ ...row.binding,
18294
+ source: {
18295
+ ...row.binding.source,
18296
+ locator: logicalAssetLocator(row.binding.source.locator, idToSlug)
18297
+ }
18298
+ }
18299
+ };
18300
+ }),
18301
+ machines: machines.map((machine) => ({
18302
+ hostname: machine.hostname,
18303
+ os: machine.os,
18304
+ arch: machine.arch,
18305
+ ...options.includeOperationalTimestamps ? {
18306
+ created_at: machine.created_at,
18307
+ last_applied_at: machine.last_applied_at
18308
+ } : {
18309
+ applied: machine.last_applied_at !== null
18310
+ }
18311
+ }))
18312
+ };
18313
+ }
18314
+ function computeIntegrity(domain, options) {
18315
+ const collections = logicalCollections(domain, options);
18316
+ const counts = {
18317
+ configs: collections.configs.length,
18318
+ config_snapshots: collections.config_snapshots.length,
18319
+ profiles: collections.profiles.length,
18320
+ profile_config_bindings: collections.profile_config_bindings.length,
18321
+ profile_asset_bindings: collections.profile_asset_bindings.length,
18322
+ machines: collections.machines.length
18323
+ };
18324
+ const hashes = {
18325
+ configs: sha25611(canonicalDomainJson(collections.configs)),
18326
+ config_snapshots: sha25611(canonicalDomainJson(collections.config_snapshots)),
18327
+ profiles: sha25611(canonicalDomainJson(collections.profiles)),
18328
+ profile_config_bindings: sha25611(canonicalDomainJson(collections.profile_config_bindings)),
18329
+ profile_asset_bindings: sha25611(canonicalDomainJson(collections.profile_asset_bindings)),
18330
+ machines: sha25611(canonicalDomainJson(collections.machines))
18331
+ };
18332
+ return {
18333
+ algorithm: "sha256",
18334
+ canonicalization: options.canonicalization,
18335
+ counts,
18336
+ hashes,
18337
+ domain_sha256: sha25611(canonicalDomainJson({ counts, hashes }))
18338
+ };
18339
+ }
18340
+ function computeDomainIntegrity(domain) {
18341
+ return computeIntegrity(domain, {
18342
+ includeOperationalTimestamps: true,
18343
+ canonicalization: "hasna.instructions.logical-json/v1"
18344
+ });
18345
+ }
18346
+ function computeRestorableDomainIntegrity(domain) {
18347
+ return computeIntegrity(domain, {
18348
+ includeOperationalTimestamps: false,
18349
+ canonicalization: "hasna.instructions.restorable-logical-json/v1"
18350
+ });
18351
+ }
18352
+ function assertArray(value, label) {
18353
+ if (!Array.isArray(value))
18354
+ throw new Error(`Invalid v2 archive: ${label} must be an array`);
18355
+ }
18356
+ function assertUnique(values, label) {
18357
+ const seen = new Set;
18358
+ for (const value of values) {
18359
+ if (!value || seen.has(value))
18360
+ throw new Error(`Invalid v2 archive: duplicate or empty ${label} ${JSON.stringify(value)}`);
18361
+ seen.add(value);
18362
+ }
18363
+ }
18364
+ function assertTimestamp(value, label, nullable = false) {
18365
+ if (nullable && value === null)
18366
+ return;
18367
+ if (typeof value !== "string" || value.length === 0 || Number.isNaN(Date.parse(value))) {
18368
+ throw new Error(`Invalid v2 archive: ${label} must be ${nullable ? "null or " : ""}an ISO timestamp`);
18369
+ }
18370
+ }
18371
+ function validateInstructionsDomainArchive(value) {
18372
+ if (!value || typeof value !== "object")
18373
+ throw new Error("Invalid v2 archive: domain payload must be an object");
18374
+ const domain = value;
18375
+ if (domain.schema !== INSTRUCTIONS_DOMAIN_ARCHIVE_SCHEMA)
18376
+ throw new Error("Invalid v2 archive: unsupported domain schema");
18377
+ assertArray(domain.configs, "configs");
18378
+ assertArray(domain.config_snapshots, "config_snapshots");
18379
+ assertArray(domain.profiles, "profiles");
18380
+ assertArray(domain.profile_config_bindings, "profile_config_bindings");
18381
+ assertArray(domain.profile_asset_bindings, "profile_asset_bindings");
18382
+ assertArray(domain.machines, "machines");
18383
+ const typed = domain;
18384
+ assertUnique(typed.configs.map((row) => row.slug), "config slug");
18385
+ assertUnique(typed.profiles.map((row) => row.slug), "profile slug");
18386
+ assertUnique(typed.machines.map((row) => row.hostname), "machine hostname");
18387
+ assertUnique(typed.config_snapshots.map((row) => row.id), "config snapshot id");
18388
+ assertUnique(typed.profile_config_bindings.map((row) => `${row.profile_slug}:${row.config_slug}`), "profile config binding");
18389
+ assertUnique(typed.profile_asset_bindings.map((row) => `${row.profile_slug}:${row.binding.assetKey}`), "profile asset binding");
18390
+ const configsBySlug = new Map(typed.configs.map((row) => [row.slug, row]));
18391
+ const configSlugs = new Set(configsBySlug.keys());
18392
+ const profileSlugs = new Set(typed.profiles.map((row) => row.slug));
18393
+ for (const config of typed.configs) {
18394
+ assertTimestamp(config.created_at, `config ${config.slug} created_at`);
18395
+ assertTimestamp(config.updated_at, `config ${config.slug} updated_at`);
18396
+ assertTimestamp(config.synced_at, `config ${config.slug} synced_at`, true);
18397
+ if (!Number.isSafeInteger(config.version) || config.version < 1)
18398
+ throw new Error(`Invalid v2 archive: config ${config.slug} has invalid version`);
18399
+ }
18400
+ for (const row of typed.config_snapshots) {
18401
+ const config = configsBySlug.get(row.config_slug);
18402
+ if (!config) {
18403
+ throw new Error(`Invalid v2 archive: snapshot references missing config ${row.config_slug}`);
18404
+ }
18405
+ if (!Number.isSafeInteger(row.version) || row.version < 1 || row.version > config.version) {
18406
+ throw new Error(`Invalid v2 archive: config ${row.config_slug} has an invalid snapshot version`);
18407
+ }
18408
+ assertTimestamp(row.created_at, `snapshot ${row.config_slug}@${row.version} created_at`);
18409
+ }
18410
+ for (const profile of typed.profiles) {
18411
+ assertTimestamp(profile.created_at, `profile ${profile.slug} created_at`);
18412
+ assertTimestamp(profile.updated_at, `profile ${profile.slug} updated_at`);
18413
+ }
18414
+ for (const row of typed.profile_config_bindings) {
18415
+ if (!profileSlugs.has(row.profile_slug) || !configSlugs.has(row.config_slug)) {
18416
+ throw new Error(`Invalid v2 archive: profile config binding has a missing logical reference`);
18417
+ }
18418
+ }
18419
+ for (const row of typed.profile_asset_bindings) {
18420
+ if (!profileSlugs.has(row.profile_slug) || !configSlugs.has(row.source_config_slug)) {
18421
+ throw new Error(`Invalid v2 archive: profile asset binding has a missing logical reference`);
18422
+ }
18423
+ }
18424
+ for (const machine of typed.machines) {
18425
+ assertTimestamp(machine.created_at, `machine ${machine.hostname} created_at`);
18426
+ assertTimestamp(machine.last_applied_at, `machine ${machine.hostname} last_applied_at`, true);
18427
+ }
18428
+ return typed;
18429
+ }
18430
+ function matchesFilter(config, filter) {
18431
+ if (!filter)
18432
+ return true;
18433
+ if (filter.category && config.category !== filter.category)
18434
+ return false;
18435
+ if (filter.agent && config.agent !== filter.agent)
18436
+ return false;
18437
+ if (filter.kind && config.kind !== filter.kind)
18438
+ return false;
18439
+ if (filter.is_template !== undefined && config.is_template !== filter.is_template)
18440
+ return false;
18441
+ if (filter.tags?.length && !filter.tags.every((tag) => config.tags.includes(tag)))
18442
+ return false;
18443
+ if (filter.search) {
18444
+ const needle = filter.search.toLocaleLowerCase();
18445
+ if (![config.name, config.description ?? "", config.content].some((value) => value.toLocaleLowerCase().includes(needle)))
18446
+ return false;
18447
+ }
18448
+ return true;
18449
+ }
18450
+ async function collectInstructionsDomain(opts = {}) {
17877
18451
  const store = opts.store ?? resolveConfigStore();
17878
- const configs = await store.listConfigs(opts.filter);
18452
+ const selectedProfile = opts.profileId ? await store.getProfile(opts.profileId) : null;
18453
+ const listedConfigs = selectedProfile ? await store.getProfileConfigs(selectedProfile.id) : await store.listConfigs(opts.filter);
18454
+ const configs = listedConfigs.filter((config) => matchesFilter(config, selectedProfile ? opts.filter : undefined));
18455
+ configs.sort((left, right) => compareText(left.slug, right.slug));
18456
+ const selectedIds = new Set(configs.map((config) => config.id));
18457
+ const profiles = (selectedProfile ? [selectedProfile] : await store.listProfiles()).sort((left, right) => compareText(left.slug, right.slug));
18458
+ const profileById = new Map(profiles.map((profile) => [profile.id, profile]));
18459
+ const configById = new Map(configs.map((config) => [config.id, config]));
18460
+ const configSnapshots = [];
18461
+ for (const config of configs) {
18462
+ for (const snapshot of await store.listSnapshots(config.id)) {
18463
+ configSnapshots.push({
18464
+ id: snapshot.id,
18465
+ config_slug: config.slug,
18466
+ content: snapshot.content,
18467
+ version: snapshot.version,
18468
+ created_at: snapshot.created_at
18469
+ });
18470
+ }
18471
+ }
18472
+ configSnapshots.sort((left, right) => compareText(left.config_slug, right.config_slug) || left.version - right.version || compareText(left.id, right.id));
18473
+ const profileConfigBindings = (await Promise.all(profiles.map(async (profile) => (await store.getProfileConfigBindings(profile.id)).filter((row) => selectedIds.has(row.config_id)).map((row) => ({
18474
+ profile_slug: profile.slug,
18475
+ config_slug: configById.get(row.config_id).slug,
18476
+ sort_order: row.sort_order,
18477
+ binding: row.binding
18478
+ }))))).flat().sort((left, right) => compareText(left.profile_slug, right.profile_slug) || left.sort_order - right.sort_order || compareText(left.config_slug, right.config_slug));
18479
+ const profileAssetBindings = (await Promise.all(profiles.map(async (profile) => (await store.getProfileAssetBindings(profile.id)).filter((row) => selectedIds.has(row.source_config_id)).map((row) => ({
18480
+ profile_slug: profile.slug,
18481
+ source_config_slug: configById.get(row.source_config_id).slug,
18482
+ sort_order: row.sort_order,
18483
+ binding: row.binding
18484
+ }))))).flat().sort((left, right) => compareText(left.profile_slug, right.profile_slug) || left.sort_order - right.sort_order || compareText(left.binding.assetKey, right.binding.assetKey));
18485
+ const archivedProfiles = profiles.filter((profile) => profileById.has(profile.id));
18486
+ const machines = (await store.listMachines()).sort((left, right) => compareText(left.hostname, right.hostname));
18487
+ return {
18488
+ schema: INSTRUCTIONS_DOMAIN_ARCHIVE_SCHEMA,
18489
+ configs,
18490
+ config_snapshots: configSnapshots,
18491
+ profiles: archivedProfiles,
18492
+ profile_config_bindings: profileConfigBindings,
18493
+ profile_asset_bindings: profileAssetBindings,
18494
+ machines
18495
+ };
18496
+ }
18497
+ async function exportConfigs(outputPath, opts = {}) {
18498
+ const domain = validateInstructionsDomainArchive(await collectInstructionsDomain(opts));
18499
+ const integrity = computeDomainIntegrity(domain);
18500
+ const payload = `${JSON.stringify(domain, null, 2)}
18501
+ `;
18502
+ const manifest = {
18503
+ schema: INSTRUCTIONS_DOMAIN_ARCHIVE_SCHEMA,
18504
+ version: "2.0.0",
18505
+ exported_at: new Date().toISOString(),
18506
+ payload: {
18507
+ path: "domain.json",
18508
+ sha256: sha25611(payload),
18509
+ size_bytes: Buffer.byteLength(payload)
18510
+ },
18511
+ integrity,
18512
+ exclusions: ARCHIVE_EXCLUSIONS
18513
+ };
17879
18514
  const absOutput = resolve12(outputPath);
17880
- const tmpDir = join20(tmpdir(), `configs-export-${Date.now()}`);
17881
- const contentsDir = join20(tmpDir, "contents");
18515
+ mkdirSync8(dirname11(absOutput), { recursive: true });
18516
+ const stagingDir = mkdtempSync(join20(tmpdir(), "instructions-domain-export-"));
17882
18517
  try {
17883
- mkdirSync8(contentsDir, { recursive: true });
17884
- const manifest = {
17885
- version: "1.0.0",
17886
- exported_at: new Date().toISOString(),
17887
- configs: configs.map(({ content: _content, ...meta }) => meta)
17888
- };
17889
- writeFileSync6(join20(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
17890
- for (const config of configs) {
17891
- const fileName = `${config.slug}.${config.format === "text" ? "txt" : config.format}`;
17892
- writeFileSync6(join20(contentsDir, fileName), config.content, "utf-8");
17893
- }
17894
- const proc = Bun.spawn(["tar", "czf", absOutput, "-C", tmpDir, "."], {
18518
+ writeFileSync6(join20(stagingDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
18519
+ `, "utf-8");
18520
+ writeFileSync6(join20(stagingDir, "domain.json"), payload, "utf-8");
18521
+ const proc = Bun.spawn(["tar", "czf", absOutput, "-C", stagingDir, "manifest.json", "domain.json"], {
17895
18522
  stdout: "pipe",
17896
18523
  stderr: "pipe"
17897
18524
  });
17898
18525
  const exitCode = await proc.exited;
17899
18526
  if (exitCode !== 0) {
17900
18527
  const stderr = await new Response(proc.stderr).text();
17901
- throw new Error(`tar failed: ${stderr}`);
18528
+ throw new Error(`tar failed: ${stderr.trim()}`);
17902
18529
  }
17903
- return { path: absOutput, count: configs.length };
18530
+ return { path: absOutput, count: domain.configs.length, counts: integrity.counts, integrity };
17904
18531
  } finally {
17905
- if (existsSync18(tmpDir)) {
17906
- rmSync4(tmpDir, { recursive: true, force: true });
17907
- }
18532
+ if (existsSync18(stagingDir))
18533
+ rmSync4(stagingDir, { recursive: true, force: true });
17908
18534
  }
17909
18535
  }
17910
18536
  // src/lib/import.ts
18537
+ init_types();
17911
18538
  init_config_store();
17912
- import { existsSync as existsSync19, mkdirSync as mkdirSync9, readFileSync as readFileSync17, rmSync as rmSync5 } from "fs";
17913
- import { join as join21, resolve as resolve13 } from "path";
17914
- import { tmpdir as tmpdir2 } from "os";
17915
- async function importConfigs(bundlePath, opts = {}) {
17916
- const store = opts.store ?? resolveConfigStore();
17917
- const conflict = opts.conflict ?? "skip";
17918
- const absPath = resolve13(bundlePath);
17919
- const tmpDir = join21(tmpdir2(), `configs-import-${Date.now()}`);
17920
- const result = { created: 0, updated: 0, skipped: 0, errors: [] };
18539
+ import { createHash as createHash12 } from "crypto";
18540
+ import { resolve as resolve13 } from "path";
18541
+ function compareText2(left, right) {
18542
+ return left < right ? -1 : left > right ? 1 : 0;
18543
+ }
18544
+ function emptyMutationCounts() {
18545
+ return { created: 0, updated: 0, skipped: 0 };
18546
+ }
18547
+ function emptyResult() {
18548
+ return {
18549
+ created: 0,
18550
+ updated: 0,
18551
+ skipped: 0,
18552
+ counts: {
18553
+ configs: emptyMutationCounts(),
18554
+ config_snapshots: { created: 0, skipped: 0 },
18555
+ profiles: emptyMutationCounts(),
18556
+ profile_config_bindings: emptyMutationCounts(),
18557
+ profile_asset_bindings: emptyMutationCounts(),
18558
+ machines: emptyMutationCounts()
18559
+ },
18560
+ errors: [],
18561
+ integrity: null
18562
+ };
18563
+ }
18564
+ function sha25612(value) {
18565
+ return createHash12("sha256").update(value).digest("hex");
18566
+ }
18567
+ function normalizeMemberName(name) {
18568
+ let normalized = name;
18569
+ while (normalized.startsWith("./"))
18570
+ normalized = normalized.slice(2);
18571
+ if (normalized.startsWith("/") || normalized.includes("\x00") || normalized.split("/").some((segment) => segment === "..")) {
18572
+ throw new Error(`Invalid archive member path: ${JSON.stringify(name)}`);
18573
+ }
18574
+ return normalized;
18575
+ }
18576
+ async function openArchive(path) {
18577
+ const list = Bun.spawn(["tar", "tzf", path], { stdout: "pipe", stderr: "pipe" });
18578
+ const [exitCode, stdout, stderr] = await Promise.all([
18579
+ list.exited,
18580
+ new Response(list.stdout).text(),
18581
+ new Response(list.stderr).text()
18582
+ ]);
18583
+ if (exitCode !== 0)
18584
+ throw new Error(`tar listing failed: ${stderr.trim()}`);
18585
+ const members = new Map;
18586
+ for (const raw of stdout.split(`
18587
+ `).filter(Boolean)) {
18588
+ const normalized = normalizeMemberName(raw);
18589
+ if (normalized.length === 0)
18590
+ continue;
18591
+ if (members.has(normalized))
18592
+ throw new Error(`Invalid archive: duplicate member ${normalized}`);
18593
+ members.set(normalized, raw);
18594
+ }
18595
+ return {
18596
+ members,
18597
+ async read(member) {
18598
+ const actual = members.get(member);
18599
+ if (!actual)
18600
+ throw new Error(`Invalid archive: missing ${member}`);
18601
+ const proc = Bun.spawn(["tar", "xOzf", path, "--", actual], { stdout: "pipe", stderr: "pipe" });
18602
+ const [code, content, memberError] = await Promise.all([
18603
+ proc.exited,
18604
+ new Response(proc.stdout).text(),
18605
+ new Response(proc.stderr).text()
18606
+ ]);
18607
+ if (code !== 0)
18608
+ throw new Error(`tar read failed for ${member}: ${memberError.trim()}`);
18609
+ return content;
18610
+ }
18611
+ };
18612
+ }
18613
+ function isNotFound(error) {
18614
+ return Boolean(error && typeof error === "object" && (error.name === "ConfigNotFoundError" || error.name === "ProfileNotFoundError" || error.status === 404));
18615
+ }
18616
+ async function findConfig(store, slug2) {
17921
18617
  try {
17922
- mkdirSync9(tmpDir, { recursive: true });
17923
- const proc = Bun.spawn(["tar", "xzf", absPath, "-C", tmpDir], {
17924
- stdout: "pipe",
17925
- stderr: "pipe"
18618
+ return await store.getConfig(slug2);
18619
+ } catch (error) {
18620
+ if (isNotFound(error))
18621
+ return null;
18622
+ throw error;
18623
+ }
18624
+ }
18625
+ function verifyCounts(actual, expected) {
18626
+ if (canonicalDomainJson(actual) !== canonicalDomainJson(expected)) {
18627
+ throw new Error("Archive integrity failure: manifest counts do not match the domain payload");
18628
+ }
18629
+ }
18630
+ async function readV2(reader, manifest) {
18631
+ if (manifest.schema !== INSTRUCTIONS_DOMAIN_ARCHIVE_SCHEMA || manifest.version !== "2.0.0" || manifest.payload?.path !== "domain.json" || manifest.integrity?.algorithm !== "sha256" || manifest.integrity?.canonicalization !== "hasna.instructions.logical-json/v1") {
18632
+ throw new Error("Invalid v2 archive manifest");
18633
+ }
18634
+ const payload = await reader.read("domain.json");
18635
+ if (Buffer.byteLength(payload) !== manifest.payload.size_bytes || sha25612(payload) !== manifest.payload.sha256) {
18636
+ throw new Error("Archive integrity failure: domain payload hash or size does not match the manifest");
18637
+ }
18638
+ const domain = validateInstructionsDomainArchive(JSON.parse(payload));
18639
+ const actual = computeDomainIntegrity(domain);
18640
+ verifyCounts(actual.counts, manifest.integrity.counts);
18641
+ if (canonicalDomainJson(actual) !== canonicalDomainJson(manifest.integrity)) {
18642
+ throw new Error("Archive integrity failure: logical domain hashes do not match the manifest");
18643
+ }
18644
+ return domain;
18645
+ }
18646
+ function configInput(config, content) {
18647
+ return {
18648
+ name: config.name,
18649
+ kind: config.kind,
18650
+ category: config.category,
18651
+ agent: config.agent,
18652
+ target_path: config.target_path,
18653
+ outputs: config.outputs,
18654
+ format: config.format,
18655
+ content,
18656
+ description: config.description ?? undefined,
18657
+ tags: config.tags,
18658
+ is_template: config.is_template
18659
+ };
18660
+ }
18661
+ async function restoreNewConfig(store, archived, domain) {
18662
+ const snapshots = domain.config_snapshots.filter((row) => row.config_slug === archived.slug).sort((left, right) => left.version - right.version || compareText2(left.content, right.content) || compareText2(left.id, right.id));
18663
+ let restored = await store.createConfig(configInput(archived, archived.content));
18664
+ for (let version = 2;version <= archived.version; version++) {
18665
+ restored = await store.updateConfig(restored.id, {
18666
+ content: archived.content,
18667
+ ...version === archived.version ? {
18668
+ name: archived.name,
18669
+ kind: archived.kind,
18670
+ category: archived.category,
18671
+ agent: archived.agent,
18672
+ target_path: archived.target_path,
18673
+ outputs: archived.outputs,
18674
+ format: archived.format,
18675
+ description: archived.description,
18676
+ tags: archived.tags,
18677
+ is_template: archived.is_template,
18678
+ synced_at: archived.synced_at
18679
+ } : {}
17926
18680
  });
17927
- const exitCode = await proc.exited;
17928
- if (exitCode !== 0) {
17929
- const stderr = await new Response(proc.stderr).text();
17930
- throw new Error(`tar extraction failed: ${stderr}`);
18681
+ }
18682
+ await store.pruneSnapshots(restored.id, 0);
18683
+ for (const snapshot of snapshots) {
18684
+ await store.createSnapshot(restored.id, snapshot.content, snapshot.version);
18685
+ }
18686
+ const restoredSnapshots = (await store.listSnapshots(restored.id)).map(({ version, content: snapshotContent }) => ({ version, content: snapshotContent })).sort((left, right) => left.version - right.version || compareText2(left.content, right.content));
18687
+ const expectedSnapshots = snapshots.map(({ version, content: snapshotContent }) => ({ version, content: snapshotContent })).sort((left, right) => left.version - right.version || compareText2(left.content, right.content));
18688
+ if (restored.version !== archived.version || restored.content !== archived.content || canonicalDomainJson(restoredSnapshots) !== canonicalDomainJson(expectedSnapshots)) {
18689
+ throw new Error(`restored config or snapshot history did not match archive for ${archived.slug}`);
18690
+ }
18691
+ return restored;
18692
+ }
18693
+ function rewriteAssetLocator(binding, archivedSourceId, restoredSourceId) {
18694
+ const match = /^config:\/\/([^@]+)@(\d+)$/.exec(binding.source.locator);
18695
+ if (!match)
18696
+ return binding;
18697
+ let locatorId;
18698
+ try {
18699
+ locatorId = decodeURIComponent(match[1]);
18700
+ } catch {
18701
+ return binding;
18702
+ }
18703
+ if (locatorId !== archivedSourceId)
18704
+ return binding;
18705
+ return {
18706
+ ...binding,
18707
+ source: {
18708
+ ...binding.source,
18709
+ locator: `config://${encodeURIComponent(restoredSourceId)}@${match[2]}`
17931
18710
  }
17932
- const manifestPath = join21(tmpDir, "manifest.json");
17933
- if (!existsSync19(manifestPath))
17934
- throw new Error("Invalid bundle: missing manifest.json");
17935
- const manifest = JSON.parse(readFileSync17(manifestPath, "utf-8"));
17936
- for (const meta of manifest.configs) {
17937
- try {
17938
- const ext = meta.format === "text" ? "txt" : meta.format;
17939
- const contentFile = join21(tmpDir, "contents", `${meta.slug}.${ext}`);
17940
- const content = existsSync19(contentFile) ? readFileSync17(contentFile, "utf-8") : "";
17941
- let existing = null;
17942
- try {
17943
- existing = await store.getConfig(meta.slug);
17944
- } catch {}
17945
- if (existing) {
17946
- if (conflict === "skip") {
17947
- result.skipped++;
17948
- } else if (conflict === "overwrite" || conflict === "version") {
17949
- await store.updateConfig(existing.id, { content, description: meta.description ?? undefined, tags: meta.tags, outputs: meta.outputs });
17950
- result.updated++;
17951
- }
17952
- } else {
17953
- await store.createConfig({
17954
- name: meta.name,
17955
- kind: meta.kind,
17956
- category: meta.category,
17957
- agent: meta.agent,
17958
- target_path: meta.target_path ?? undefined,
17959
- outputs: meta.outputs,
17960
- format: meta.format,
17961
- content,
17962
- description: meta.description ?? undefined,
17963
- tags: meta.tags,
17964
- is_template: meta.is_template
17965
- });
17966
- result.created++;
17967
- }
17968
- } catch (err) {
17969
- result.errors.push(`${meta.slug}: ${err instanceof Error ? err.message : String(err)}`);
18711
+ };
18712
+ }
18713
+ async function assertEmptyV2Destination(store) {
18714
+ const [configs, profiles, machines] = await Promise.all([
18715
+ store.listConfigs(),
18716
+ store.listProfiles(),
18717
+ store.listMachines()
18718
+ ]);
18719
+ if (configs.length > 0 || profiles.length > 0 || machines.length > 0) {
18720
+ throw new Error(`Instructions domain archive v2 recovery requires an empty destination ` + `(found configs=${configs.length}, profiles=${profiles.length}, machines=${machines.length})`);
18721
+ }
18722
+ }
18723
+ async function importV2(domain, manifest, store, conflict) {
18724
+ if (conflict !== "skip") {
18725
+ throw new Error(`Instructions domain archive v2 does not support ${conflict}; restore into an empty destination`);
18726
+ }
18727
+ await assertEmptyV2Destination(store);
18728
+ const result = emptyResult();
18729
+ result.integrity = manifest.integrity;
18730
+ const configMap = new Map;
18731
+ for (const archived of domain.configs) {
18732
+ const restored = await restoreNewConfig(store, archived, domain);
18733
+ configMap.set(archived.slug, restored);
18734
+ result.counts.configs.created++;
18735
+ result.created++;
18736
+ result.counts.config_snapshots.created += domain.config_snapshots.filter((row) => row.config_slug === archived.slug).length;
18737
+ }
18738
+ const profileMap = new Map;
18739
+ for (const archived of domain.profiles) {
18740
+ const created = await store.createProfile({
18741
+ name: archived.name,
18742
+ description: archived.description ?? undefined,
18743
+ selectors: archived.selectors,
18744
+ variables: archived.variables
18745
+ });
18746
+ profileMap.set(archived.slug, created);
18747
+ result.counts.profiles.created++;
18748
+ }
18749
+ for (const archived of domain.profiles) {
18750
+ const restoredProfile = profileMap.get(archived.slug);
18751
+ if (!restoredProfile)
18752
+ throw new Error(`post-create profile mapping missing for ${archived.slug}`);
18753
+ const archivedMembership = domain.profile_config_bindings.filter((row) => row.profile_slug === archived.slug).sort((left, right) => left.sort_order - right.sort_order || compareText2(left.config_slug, right.config_slug));
18754
+ const archivedAssets = domain.profile_asset_bindings.filter((row) => row.profile_slug === archived.slug).sort((left, right) => left.sort_order - right.sort_order || compareText2(left.binding.assetKey, right.binding.assetKey));
18755
+ for (const row of archivedMembership) {
18756
+ const config = configMap.get(row.config_slug);
18757
+ if (!config)
18758
+ throw new Error(`missing restored config ${row.config_slug}`);
18759
+ await store.addConfigToProfile(restoredProfile.id, config.id);
18760
+ await store.setProfileConfigBinding(restoredProfile.id, config.id, row.binding);
18761
+ result.counts.profile_config_bindings.created++;
18762
+ }
18763
+ for (const row of archivedAssets) {
18764
+ const source = configMap.get(row.source_config_slug);
18765
+ const archivedSource = domain.configs.find((config) => config.slug === row.source_config_slug);
18766
+ if (!source || !archivedSource)
18767
+ throw new Error(`missing restored asset source ${row.source_config_slug}`);
18768
+ await store.addAssetToProfile(restoredProfile.id, source.id, rewriteAssetLocator(row.binding, archivedSource.id, source.id));
18769
+ result.counts.profile_asset_bindings.created++;
18770
+ }
18771
+ }
18772
+ for (const archived of domain.machines) {
18773
+ const restored = await store.registerMachine(archived.hostname, archived.os ?? undefined, archived.arch ?? undefined);
18774
+ if (archived.last_applied_at !== null)
18775
+ await store.updateMachineApplied(restored.hostname);
18776
+ result.counts.machines.created++;
18777
+ }
18778
+ const restoredDomain = await collectInstructionsDomain({ store });
18779
+ const expected = computeRestorableDomainIntegrity(domain);
18780
+ const actual = computeRestorableDomainIntegrity(restoredDomain);
18781
+ if (canonicalDomainJson(actual) !== canonicalDomainJson(expected)) {
18782
+ throw new Error(`Post-restore logical integrity verification failed: expected ${expected.domain_sha256}, got ${actual.domain_sha256}`);
18783
+ }
18784
+ return result;
18785
+ }
18786
+ async function importV1(reader, manifest, store, conflict) {
18787
+ const result = emptyResult();
18788
+ if (!Array.isArray(manifest.configs))
18789
+ throw new Error("Invalid v1 bundle: configs must be an array");
18790
+ for (const meta of manifest.configs) {
18791
+ const ext = meta.format === "text" ? "txt" : meta.format;
18792
+ const contentPath = `contents/${meta.slug}.${ext}`;
18793
+ const content = reader.members.has(contentPath) ? await reader.read(contentPath) : "";
18794
+ const existing = await findConfig(store, meta.slug);
18795
+ if (existing) {
18796
+ if (conflict === "skip") {
18797
+ result.skipped++;
18798
+ result.counts.configs.skipped++;
18799
+ } else {
18800
+ await store.updateConfig(existing.id, {
18801
+ name: meta.name,
18802
+ kind: meta.kind,
18803
+ category: meta.category,
18804
+ agent: meta.agent,
18805
+ target_path: meta.target_path,
18806
+ outputs: meta.outputs,
18807
+ format: meta.format,
18808
+ content,
18809
+ description: meta.description,
18810
+ tags: meta.tags,
18811
+ is_template: meta.is_template,
18812
+ synced_at: meta.synced_at
18813
+ });
18814
+ result.updated++;
18815
+ result.counts.configs.updated++;
17970
18816
  }
18817
+ } else {
18818
+ await store.createConfig(configInput({ ...meta, content }, content));
18819
+ result.created++;
18820
+ result.counts.configs.created++;
17971
18821
  }
17972
- return result;
17973
- } finally {
17974
- if (existsSync19(tmpDir)) {
17975
- rmSync5(tmpDir, { recursive: true, force: true });
17976
- }
17977
18822
  }
18823
+ return result;
18824
+ }
18825
+ async function importConfigs(bundlePath, opts = {}) {
18826
+ const store = opts.store ?? resolveConfigStore();
18827
+ const conflict = opts.conflict ?? "skip";
18828
+ const reader = await openArchive(resolve13(bundlePath));
18829
+ const manifestText = await reader.read("manifest.json");
18830
+ let manifest;
18831
+ try {
18832
+ manifest = JSON.parse(manifestText);
18833
+ } catch {
18834
+ throw new Error("Invalid bundle: manifest.json is not valid JSON");
18835
+ }
18836
+ if (!manifest || typeof manifest !== "object" || typeof manifest.version !== "string") {
18837
+ throw new Error("Invalid bundle: manifest version is missing");
18838
+ }
18839
+ if (manifest.version === "2.0.0") {
18840
+ const v2 = manifest;
18841
+ const domain = await readV2(reader, v2);
18842
+ return importV2(domain, v2, store, conflict);
18843
+ }
18844
+ if (manifest.version.startsWith("1.")) {
18845
+ return importV1(reader, manifest, store, conflict);
18846
+ }
18847
+ throw new Error(`Unsupported Instructions archive version: ${manifest.version}`);
17978
18848
  }
17979
18849
 
17980
18850
  // src/index.ts
@@ -17983,9 +18853,9 @@ init_redact();
17983
18853
 
17984
18854
  // src/lib/package-manager-guard.ts
17985
18855
  import { execFileSync as execFileSync2 } from "child_process";
17986
- import { existsSync as existsSync20, lstatSync as lstatSync7, readdirSync as readdirSync6, readFileSync as readFileSync18 } from "fs";
17987
- import { homedir as homedir11 } from "os";
17988
- import { basename as basename7, dirname as dirname11, isAbsolute as isAbsolute7, join as join22, relative as relative7, resolve as resolve14 } from "path";
18856
+ import { existsSync as existsSync19, lstatSync as lstatSync7, readdirSync as readdirSync6, readFileSync as readFileSync17 } from "fs";
18857
+ import { homedir as homedir10 } from "os";
18858
+ import { basename as basename7, dirname as dirname12, isAbsolute as isAbsolute8, join as join21, relative as relative8, resolve as resolve14 } from "path";
17989
18859
  var SKIP_DIRS = new Set([
17990
18860
  ".git",
17991
18861
  "node_modules",
@@ -18027,7 +18897,7 @@ function scanPackageManagerSecrets(options = {}) {
18027
18897
  const findings = [];
18028
18898
  let scannedFiles = 0;
18029
18899
  for (const root of roots) {
18030
- if (!existsSync20(root))
18900
+ if (!existsSync19(root))
18031
18901
  continue;
18032
18902
  const stat = lstatSync7(root);
18033
18903
  if (stat.isFile()) {
@@ -18037,14 +18907,14 @@ function scanPackageManagerSecrets(options = {}) {
18037
18907
  if (text === null)
18038
18908
  continue;
18039
18909
  scannedFiles++;
18040
- findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root), dirname11(root)));
18910
+ findings.push(...scanFile(root, text, classifyRepoFile(root), isTrackedFile(root), dirname12(root)));
18041
18911
  continue;
18042
18912
  }
18043
18913
  if (!stat.isDirectory())
18044
18914
  continue;
18045
18915
  const tracked = trackedFiles(root);
18046
18916
  for (const file of collectRepoFiles(root)) {
18047
- const rel = toPosix(relative7(root, file));
18917
+ const rel = toPosix(relative8(root, file));
18048
18918
  const isTracked = tracked.has(rel);
18049
18919
  const text = readTextFile(file);
18050
18920
  if (text === null)
@@ -18054,10 +18924,10 @@ function scanPackageManagerSecrets(options = {}) {
18054
18924
  }
18055
18925
  }
18056
18926
  if (options.includeHome) {
18057
- const home = homedir11();
18927
+ const home = homedir10();
18058
18928
  for (const name of HOME_FILES) {
18059
- const file = join22(home, name);
18060
- if (!existsSync20(file))
18929
+ const file = join21(home, name);
18930
+ if (!existsSync19(file))
18061
18931
  continue;
18062
18932
  const text = readTextFile(file);
18063
18933
  if (text === null)
@@ -18081,12 +18951,12 @@ function collectRepoFiles(root) {
18081
18951
  if (entry.isDirectory()) {
18082
18952
  if (SKIP_DIRS.has(entry.name))
18083
18953
  continue;
18084
- visit(join22(dir, entry.name));
18954
+ visit(join21(dir, entry.name));
18085
18955
  continue;
18086
18956
  }
18087
18957
  if (!entry.isFile())
18088
18958
  continue;
18089
- const file = join22(dir, entry.name);
18959
+ const file = join21(dir, entry.name);
18090
18960
  if (shouldScanRepoFile(file))
18091
18961
  out.push(file);
18092
18962
  }
@@ -18124,7 +18994,7 @@ function readTextFile(file) {
18124
18994
  const stat = lstatSync7(file);
18125
18995
  if (!stat.isFile() || stat.size > 5000000)
18126
18996
  return null;
18127
- const buf = readFileSync18(file);
18997
+ const buf = readFileSync17(file);
18128
18998
  if (buf.includes(0))
18129
18999
  return null;
18130
19000
  return buf.toString("utf-8");
@@ -18324,11 +19194,11 @@ function trackedFiles(root) {
18324
19194
  }
18325
19195
  function isTrackedFile(file) {
18326
19196
  try {
18327
- const repoRoot = execFileSync2("git", ["-C", dirname11(file), "rev-parse", "--show-toplevel"], {
19197
+ const repoRoot = execFileSync2("git", ["-C", dirname12(file), "rev-parse", "--show-toplevel"], {
18328
19198
  encoding: "utf-8",
18329
19199
  stdio: ["ignore", "pipe", "ignore"]
18330
19200
  }).trim();
18331
- const rel = toPosix(relative7(repoRoot, file));
19201
+ const rel = toPosix(relative8(repoRoot, file));
18332
19202
  execFileSync2("git", ["-C", repoRoot, "ls-files", "--error-unmatch", "--", rel], {
18333
19203
  stdio: ["ignore", "ignore", "ignore"]
18334
19204
  });
@@ -18354,13 +19224,13 @@ function stripInlineComment(value) {
18354
19224
  return value.replace(/\s[#;].*$/, "").trim();
18355
19225
  }
18356
19226
  function displayPath(file, root) {
18357
- const home = homedir11();
19227
+ const home = homedir10();
18358
19228
  if (root === home && (file === home || file.startsWith(home + "/")))
18359
- return "~/" + toPosix(relative7(home, file));
18360
- if (isAbsolute7(root) && file.startsWith(root + "/"))
18361
- return toPosix(relative7(root, file));
19229
+ return "~/" + toPosix(relative8(home, file));
19230
+ if (isAbsolute8(root) && file.startsWith(root + "/"))
19231
+ return toPosix(relative8(root, file));
18362
19232
  if (file === home || file.startsWith(home + "/"))
18363
- return "~/" + toPosix(relative7(home, file));
19233
+ return "~/" + toPosix(relative8(home, file));
18364
19234
  return file;
18365
19235
  }
18366
19236
  function toPosix(path) {
@@ -18526,6 +19396,7 @@ export {
18526
19396
  INSTRUCTION_ACTIVATION_MODES,
18527
19397
  INSTRUCTIONS_LOCAL_OPT_IN_ENV_KEYS,
18528
19398
  INSTRUCTIONS_LOCAL_OPT_IN_ENV,
19399
+ INSTRUCTIONS_DOMAIN_ARCHIVE_SCHEMA,
18529
19400
  INBOX_CONVERSATIONS_MINIMUM_VERSION,
18530
19401
  GLOBAL_AGENT_RULES_STANDARD_SLUG,
18531
19402
  GLOBAL_AGENT_RULES_STANDARD_CONTENT,