@hasna/instructions 0.7.0 → 0.7.2

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 (74) hide show
  1. package/README.md +52 -11
  2. package/dist/chunks/{apply-7svah2qm.js → apply-ps6jjt6k.js} +3 -3
  3. package/dist/chunks/{apply-jp13sa1e.js → apply-wj7wh5km.js} +3 -3
  4. package/dist/chunks/{index-1cnyjnd7.js → index-18j7x3xw.js} +2 -2
  5. package/dist/chunks/{index-3djthcm6.js → index-27az1qb1.js} +28 -1
  6. package/dist/chunks/{index-fwbs6tma.js → index-ape24zc0.js} +2 -2
  7. package/dist/chunks/{index-bzyxvwcd.js → index-ek15201v.js} +2 -2
  8. package/dist/chunks/{index-arrrx0ed.js → index-mvckkf15.js} +39 -20
  9. package/dist/chunks/{index-7m4j8a33.js → index-n6nh6f5r.js} +235 -20
  10. package/dist/chunks/{index-4pfq42t5.js → index-pq18fdbj.js} +146 -32
  11. package/dist/chunks/{index-n3fjspr0.js → index-preyvj8n.js} +28 -1
  12. package/dist/chunks/{index-z3ma4aqk.js → index-y8410ve0.js} +3 -3
  13. package/dist/chunks/{local-5wvv5xv3.js → local-1c1qxzvx.js} +155 -4
  14. package/dist/chunks/{local-zxa5dpf4.js → local-hc67bynj.js} +155 -4
  15. package/dist/chunks/{s3-backup-n2z4fwyh.js → s3-backup-hxe3zpfz.js} +1 -1
  16. package/dist/chunks/{s3-object-store-90tgbh6k.js → s3-object-store-n0s82swt.js} +1 -1
  17. package/dist/chunks/{sync-r8t7zsp3.js → sync-12m3vs6g.js} +4 -4
  18. package/dist/chunks/{sync-2075deve.js → sync-azd9t4p8.js} +4 -4
  19. package/dist/chunks/{template-wgnzdn7h.js → template-e4qrxs8p.js} +1 -1
  20. package/dist/chunks/{template-fsfxpe4p.js → template-nhn124zd.js} +1 -1
  21. package/dist/cli/index.js +860 -181
  22. package/dist/data/config-store.d.ts +22 -6
  23. package/dist/data/config-store.d.ts.map +1 -1
  24. package/dist/db/configs.d.ts +4 -1
  25. package/dist/db/configs.d.ts.map +1 -1
  26. package/dist/db/local.d.ts +1 -1
  27. package/dist/db/local.d.ts.map +1 -1
  28. package/dist/index.d.ts +5 -3
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +940 -134
  31. package/dist/lib/asset-plan.d.ts.map +1 -1
  32. package/dist/lib/codewith-shared-todos-storage-standard.d.ts.map +1 -1
  33. package/dist/lib/compact-output.d.ts +2 -18
  34. package/dist/lib/compact-output.d.ts.map +1 -1
  35. package/dist/lib/dangerous-operation-guard-standard.d.ts.map +1 -1
  36. package/dist/lib/export.d.ts.map +1 -1
  37. package/dist/lib/global-agent-rules-standard.d.ts.map +1 -1
  38. package/dist/lib/global-source-coverage.d.ts +12 -7
  39. package/dist/lib/global-source-coverage.d.ts.map +1 -1
  40. package/dist/lib/import.d.ts.map +1 -1
  41. package/dist/lib/instruction-graph.d.ts +1 -1
  42. package/dist/lib/instruction-graph.d.ts.map +1 -1
  43. package/dist/lib/instruction-source-policy.d.ts +6 -0
  44. package/dist/lib/instruction-source-policy.d.ts.map +1 -0
  45. package/dist/lib/platform-profiles.d.ts.map +1 -1
  46. package/dist/lib/project-dashboard-standard.d.ts.map +1 -1
  47. package/dist/lib/session-apply.d.ts +24 -0
  48. package/dist/lib/session-apply.d.ts.map +1 -1
  49. package/dist/lib/session-refresh.d.ts +30 -0
  50. package/dist/lib/session-refresh.d.ts.map +1 -0
  51. package/dist/lib/session-render.d.ts +34 -1
  52. package/dist/lib/session-render.d.ts.map +1 -1
  53. package/dist/lib/transforms.d.ts +1 -0
  54. package/dist/lib/transforms.d.ts.map +1 -1
  55. package/dist/mcp/index.js +23 -22
  56. package/dist/mcp/server.d.ts.map +1 -1
  57. package/dist/sdk/index.js +46 -3
  58. package/dist/sdk/v1-client.d.ts +10 -3
  59. package/dist/sdk/v1-client.d.ts.map +1 -1
  60. package/dist/sdk/v1.generated.d.ts +52 -2
  61. package/dist/sdk/v1.generated.d.ts.map +1 -1
  62. package/dist/server/index.d.ts +1 -1
  63. package/dist/server/index.d.ts.map +1 -1
  64. package/dist/server/index.js +215 -15
  65. package/dist/server/openapi.d.ts +240 -3
  66. package/dist/server/openapi.d.ts.map +1 -1
  67. package/dist/server/v1.d.ts.map +1 -1
  68. package/dist/storage/cloud-store.d.ts +2 -15
  69. package/dist/storage/cloud-store.d.ts.map +1 -1
  70. package/dist/storage/index.js +39 -20
  71. package/dist/storage/s3-object-store.d.ts.map +1 -1
  72. package/dist/types/index.d.ts +49 -2
  73. package/dist/types/index.d.ts.map +1 -1
  74. package/package.json +3 -3
@@ -17,12 +17,18 @@ var __export = (target, all) => {
17
17
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
18
18
 
19
19
  // src/types/index.ts
20
- var CONFIG_AGENTS, 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;
20
+ function validateExpectedConfigVersion(value) {
21
+ if (value !== undefined && (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1)) {
22
+ throw new InvalidExpectedVersionError;
23
+ }
24
+ }
25
+ var CONFIG_AGENTS, 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, InvalidExpectedVersionError, ConfigVersionConflictError, ConfigNotFoundError, ProfileNotFoundError;
21
26
  var init_types = __esm(() => {
22
27
  CONFIG_AGENTS = [
23
28
  "claude",
24
29
  "codex",
25
30
  "opencode",
31
+ "sumi",
26
32
  "cursor",
27
33
  "codewith",
28
34
  "aicopilot",
@@ -45,6 +51,25 @@ var init_types = __esm(() => {
45
51
  ASSET_SCOPES = ["global", "project", "session"];
46
52
  ASSET_UNINSTALL_POLICIES = ["remove-managed", "retain"];
47
53
  ASSET_ROLLBACK_POLICIES = ["snapshot", "installer-receipt", "none"];
54
+ InvalidExpectedVersionError = class InvalidExpectedVersionError extends Error {
55
+ code = "INVALID_EXPECTED_VERSION";
56
+ constructor() {
57
+ super("expected_version must be a positive safe integer");
58
+ this.name = "InvalidExpectedVersionError";
59
+ }
60
+ };
61
+ ConfigVersionConflictError = class ConfigVersionConflictError extends Error {
62
+ config_id;
63
+ expected_version;
64
+ code = "CONFIG_VERSION_CONFLICT";
65
+ status = 409;
66
+ constructor(config_id, expected_version) {
67
+ super(`Config version conflict: ${config_id} no longer has expected version ${expected_version}`);
68
+ this.config_id = config_id;
69
+ this.expected_version = expected_version;
70
+ this.name = "ConfigVersionConflictError";
71
+ }
72
+ };
48
73
  ConfigNotFoundError = class ConfigNotFoundError extends Error {
49
74
  constructor(id) {
50
75
  super(`Config not found: ${id}`);
@@ -104,7 +129,6 @@ function boundedReadPage(items, total, options = {}) {
104
129
  };
105
130
  }
106
131
  var init_bounded_read = () => {};
107
-
108
132
  // ../../node_modules/.bun/@hasna+contracts@1.0.2+e8014c875821e0be/node_modules/@hasna/contracts/dist/client/transport.js
109
133
  import { createRequire } from "module";
110
134
  var MAX_CREDENTIAL_FILE_BYTES, INSPECT_CUSTOM, CREDENTIAL_SEAL, AMBIENT_ENVIRONMENT, SECRETS_PACKAGE_SPECIFIER, requireSecretsSdk, IDEMPOTENT_METHODS, AUTHORITY_OVERRIDE_HEADERS;
@@ -174,6 +198,7 @@ var init_config_store = __esm(() => {
174
198
  // src/lib/global-agent-rules-standard.ts
175
199
  var AGENT_OPERATING_RULES_SOURCE_SET_ID = "hasna-global-agent-rules-standard", AGENT_OPERATING_RULES_SOURCE_ID = "hasna-agent-operating-rules", AGENT_OPERATING_RULES_VERSION = "1.1.26", AGENT_OPERATING_RULES_SOURCE_SET_VERSION = "2026-08-11", AGENT_OPERATING_RULES_PAYLOAD_SHA256 = "486844a3d869e3dabc2f33fd66479d4e2fb0a0d1864a3d8a3dd05669eb3f1b77", AGENT_OPERATING_RULES_UPSTREAM_FILE_SHA256 = "b8e89cdb49e207e5b497ac51384d67022b94fe5645cc9273db60384eb2c2fb32", SCOPED_OPERATIONAL_CONTROL_POLICY_REFERENCE = "hasna-agent-operating-rules/scoped-operational-control/v1", AGENT_OPERATING_RULES_UPSTREAM, SCOPED_OPERATIONAL_CONTROL_POLICY, AGENT_OPERATING_RULES_PROVENANCE, GLOBAL_AGENT_RULES_STANDARD_CONTENT;
176
200
  var init_global_agent_rules_standard = __esm(() => {
201
+ init_types();
177
202
  init_config_store();
178
203
  AGENT_OPERATING_RULES_UPSTREAM = {
179
204
  repository: "hasnaxyz/iapp-identities",
@@ -264,6 +289,7 @@ var init_global_agent_rules_standard = __esm(() => {
264
289
  // src/lib/codewith-shared-todos-storage-standard.ts
265
290
  var CODEWITH_SHARED_TODOS_STORAGE_POLICY_REFERENCE = "codewith/shared-todos-storage/v1", CODEWITH_SHARED_TODOS_STORAGE_STANDARD_CONTENT;
266
291
  var init_codewith_shared_todos_storage_standard = __esm(() => {
292
+ init_types();
267
293
  init_config_store();
268
294
  CODEWITH_SHARED_TODOS_STORAGE_STANDARD_CONTENT = `# Codewith Shared Todos Storage Standard
269
295
 
@@ -4717,6 +4743,7 @@ var init_asset_plan = __esm(() => {
4717
4743
  codex: "cli",
4718
4744
  cursor: "ide",
4719
4745
  opencode: "cli",
4746
+ sumi: "cli",
4720
4747
  codewith: "cli",
4721
4748
  qwen: "cli",
4722
4749
  aicopilot: "cli",
@@ -4842,6 +4869,15 @@ var init_session_render = __esm(() => {
4842
4869
  description: "OpenCode AGENTS.md plus opencode.json instructions pointing at managed fragments.",
4843
4870
  providerSurface: "legacy-dual"
4844
4871
  },
4872
+ sumi: {
4873
+ tool: "sumi",
4874
+ mode: "flattened-markdown",
4875
+ indexFile: "AGENTS.md",
4876
+ managedDir: SESSION_RENDER_INSTRUCTIONS_MANAGED_DIR,
4877
+ envVar: "SUMI_CONFIG_DIR",
4878
+ nativeImports: false,
4879
+ description: "Sumi native AGENTS.md in the explicit resolved config directory or project root."
4880
+ },
4845
4881
  aicopilot: {
4846
4882
  tool: "aicopilot",
4847
4883
  mode: "flattened-markdown",
@@ -5063,6 +5099,7 @@ var init_instruction_graph = __esm(() => {
5063
5099
  init_types();
5064
5100
  init_session_render();
5065
5101
  init_asset_plan();
5102
+ init_transforms();
5066
5103
  COMMON_FALLBACKS = ["fail", "flatten", "promote-always", "omit"];
5067
5104
  DEFAULT_PROVIDER_CAPABILITIES = Object.freeze({
5068
5105
  claude: capability("claude", ">=1.0.0", "native-import", "CLAUDE.md @ imports plus path-gated rules", {
@@ -5074,6 +5111,7 @@ var init_instruction_graph = __esm(() => {
5074
5111
  codex: capability("codex", ">=0.1.0", "flattened", "AGENTS.md", { asset_surface: "cli" }),
5075
5112
  cursor: capability("cursor", ">=1.0.0", "cursor-rule", ".cursor/rules/*.mdc", { activation_modes: ["always", "glob"], conditional_artifacts: true, asset_surface: "ide" }),
5076
5113
  opencode: capability("opencode", ">=1.0.0", "managed-fragment", "opencode.json instructions", { provider_variant: "v1-instructions", session_surface: "opencode-config-instructions" }),
5114
+ sumi: capability("sumi", ">=0.2.22 <0.3.0", "flattened", "AGENTS.md"),
5077
5115
  codewith: capability("codewith", ">=0.1.0", "flattened", "CODEWITH.md"),
5078
5116
  qwen: capability("qwen", ">=0.1.0", "flattened", "QWEN.md"),
5079
5117
  aicopilot: capability("aicopilot", ">=0.1.0", "flattened", "AICOPILOT.md"),
@@ -8013,6 +8051,10 @@ function configFilterSql(filter) {
8013
8051
  const parameter = `$${params.length}`;
8014
8052
  conditions.push(`(name ILIKE ${parameter} OR description ILIKE ${parameter} OR content ILIKE ${parameter})`);
8015
8053
  }
8054
+ if (filter.tags?.length) {
8055
+ for (const tag of filter.tags)
8056
+ add("tags @> $?::jsonb", JSON.stringify([tag]));
8057
+ }
8016
8058
  return {
8017
8059
  where: conditions.length ? `WHERE ${conditions.join(" AND ")}` : "",
8018
8060
  params
@@ -8025,6 +8067,31 @@ async function listConfigsPage(client, filter = {}, options = {}) {
8025
8067
  const rows = await client.many(`SELECT * FROM configs ${where} ORDER BY id LIMIT $${params.length + 1} OFFSET $${params.length + 2}`, [...params, normalized.limit, normalized.cursor]);
8026
8068
  return boundedReadPage(rows.map(rowToConfig), Number(count?.total ?? 0), normalized);
8027
8069
  }
8070
+ async function listConfigSummariesPage(client, filter = {}, options = {}) {
8071
+ const normalized = normalizeBoundedReadOptions(options);
8072
+ const { where, params } = configFilterSql(filter);
8073
+ const count = await client.get(`SELECT COUNT(*) AS total FROM configs ${where}`, params);
8074
+ const rows = await client.many(`SELECT id, name, slug, kind, category, agent, target_path, format,
8075
+ jsonb_array_length(outputs) AS output_count, description, tags,
8076
+ is_template, version, updated_at
8077
+ FROM configs ${where} ORDER BY id LIMIT $${params.length + 1} OFFSET $${params.length + 2}`, [...params, normalized.limit, normalized.cursor]);
8078
+ return boundedReadPage(rows.map((row) => ({
8079
+ id: row.id,
8080
+ slug: row.slug,
8081
+ name: row.name,
8082
+ category: row.category,
8083
+ agent: row.agent,
8084
+ kind: row.kind,
8085
+ format: row.format,
8086
+ target_path: row.target_path,
8087
+ output_count: Number(row.output_count ?? 0),
8088
+ version: row.version,
8089
+ is_template: Boolean(row.is_template),
8090
+ updated_at: toIso2(row.updated_at),
8091
+ description: row.description,
8092
+ tags: Array.isArray(row.tags) ? row.tags.map(String) : []
8093
+ })), Number(count?.total ?? 0), normalized);
8094
+ }
8028
8095
  async function listConfigIdentitiesPage(client, filterOrOptions = {}, maybeOptions) {
8029
8096
  const filterKeys = ["category", "agent", "kind", "is_template", "search", "tags"];
8030
8097
  const isFilterOnly = maybeOptions === undefined && filterKeys.some((key) => (key in filterOrOptions));
@@ -8082,6 +8149,7 @@ async function createConfig(client, input) {
8082
8149
  return getConfig(client, id);
8083
8150
  }
8084
8151
  async function updateConfig(client, idOrSlug, input) {
8152
+ validateExpectedConfigVersion(input.expected_version);
8085
8153
  const existing = await getConfig(client, idOrSlug);
8086
8154
  const sets = ["updated_at = now()", "version = version + 1"];
8087
8155
  const params = [];
@@ -8119,13 +8187,27 @@ async function updateConfig(client, idOrSlug, input) {
8119
8187
  const configIdParam = params.length;
8120
8188
  params.push(randomUUID());
8121
8189
  const snapshotIdParam = params.length;
8122
- await client.execute(`WITH updated_config AS (
8123
- UPDATE configs SET ${sets.join(", ")} WHERE id = $${configIdParam}
8124
- RETURNING id, content, version
8190
+ let condition = "";
8191
+ if (input.expected_version !== undefined) {
8192
+ params.push(input.expected_version);
8193
+ condition = ` AND version::bigint = $${params.length}::bigint`;
8194
+ }
8195
+ const updated = await client.get(`WITH updated_config AS (
8196
+ UPDATE configs SET ${sets.join(", ")} WHERE id = $${configIdParam}${condition}
8197
+ RETURNING *
8198
+ ), inserted_snapshot AS (
8199
+ INSERT INTO config_snapshots (id, config_id, content, version, created_at)
8200
+ SELECT $${snapshotIdParam}, id, content, version, now() FROM updated_config
8201
+ RETURNING config_id
8125
8202
  )
8126
- INSERT INTO config_snapshots (id, config_id, content, version, created_at)
8127
- SELECT $${snapshotIdParam}, id, content, version, now() FROM updated_config`, params);
8128
- return getConfig(client, existing.id);
8203
+ SELECT updated_config.* FROM updated_config
8204
+ JOIN inserted_snapshot ON inserted_snapshot.config_id = updated_config.id`, params);
8205
+ if (!updated) {
8206
+ if (input.expected_version !== undefined)
8207
+ throw new ConfigVersionConflictError(existing.id, input.expected_version);
8208
+ throw new ConfigNotFoundError(existing.id);
8209
+ }
8210
+ return rowToConfig(updated);
8129
8211
  }
8130
8212
  async function deleteConfig(client, idOrSlug) {
8131
8213
  const existing = await getConfig(client, idOrSlug);
@@ -8717,16 +8799,25 @@ async function handleV1Request(req, url, context = {}) {
8717
8799
  ...url.searchParams.get("category") ? { category: url.searchParams.get("category") } : {},
8718
8800
  ...url.searchParams.get("agent") ? { agent: url.searchParams.get("agent") } : {},
8719
8801
  ...url.searchParams.get("kind") ? { kind: url.searchParams.get("kind") } : {},
8720
- ...search ? { search } : {}
8802
+ ...search ? { search } : {},
8803
+ ...url.searchParams.getAll("tag").length ? { tags: url.searchParams.getAll("tag") } : {}
8721
8804
  };
8722
8805
  const options = {
8723
8806
  limit: url.searchParams.get("limit") ?? undefined,
8724
8807
  cursor: url.searchParams.get("cursor") ?? undefined
8725
8808
  };
8726
- if (url.searchParams.get("view") === "identity") {
8809
+ const view = url.searchParams.get("view");
8810
+ if (view !== null && view !== "identity" && view !== "summary") {
8811
+ return errorResponse(400, "unsupported config view", { code: "INVALID_CONFIG_VIEW" });
8812
+ }
8813
+ if (view === "identity") {
8727
8814
  const page2 = await listConfigIdentitiesPage(client, filter, options);
8728
8815
  return json(pagePayload("configs", page2));
8729
8816
  }
8817
+ if (view === "summary") {
8818
+ const page2 = await listConfigSummariesPage(client, filter, options);
8819
+ return json({ ...page2, count: page2.items.length });
8820
+ }
8730
8821
  const page = await listConfigsPage(client, filter, options);
8731
8822
  return json(pagePayload("configs", page));
8732
8823
  }
@@ -8772,6 +8863,18 @@ async function handleV1Request(req, url, context = {}) {
8772
8863
  }
8773
8864
  return errorResponse(405, `method ${method} not allowed on /v1/configs/:id/snapshots`);
8774
8865
  }
8866
+ if (action === "conditional-update") {
8867
+ if (method !== "POST")
8868
+ return errorResponse(405, `method ${method} not allowed on /v1/configs/:id/conditional-update`);
8869
+ const body = await readJson(req);
8870
+ if (!body)
8871
+ return errorResponse(400, "invalid JSON body");
8872
+ if (body.expected_version === undefined)
8873
+ throw new InvalidExpectedVersionError;
8874
+ validateExpectedConfigVersion(body.expected_version);
8875
+ const config = await updateConfig(client, id, body);
8876
+ return json({ config });
8877
+ }
8775
8878
  if (action)
8776
8879
  return errorResponse(404, `unknown config action: ${action}`);
8777
8880
  if (method === "GET") {
@@ -8975,6 +9078,11 @@ async function handleV1Request(req, url, context = {}) {
8975
9078
  } catch (e) {
8976
9079
  if (e instanceof HttpInputError)
8977
9080
  return errorResponse(e.status, e.message, { code: e.code });
9081
+ if (e instanceof InvalidExpectedVersionError)
9082
+ return errorResponse(400, e.message, { code: e.code });
9083
+ if (e instanceof ConfigVersionConflictError) {
9084
+ return errorResponse(409, e.message, { code: e.code, config_id: e.config_id, expected_version: e.expected_version });
9085
+ }
8978
9086
  if (e instanceof StoreValidationError)
8979
9087
  return errorResponse(400, e.message, { code: e.code });
8980
9088
  if (e instanceof IdempotencyConflictError)
@@ -9010,6 +9118,27 @@ var configSchema = {
9010
9118
  synced_at: { type: "string", nullable: true }
9011
9119
  }
9012
9120
  };
9121
+ var configSummarySchema = {
9122
+ type: "object",
9123
+ additionalProperties: false,
9124
+ required: ["id", "name", "slug", "kind", "category", "agent", "target_path", "format", "output_count", "description", "tags", "is_template", "version", "updated_at"],
9125
+ properties: {
9126
+ id: { type: "string" },
9127
+ name: { type: "string" },
9128
+ slug: { type: "string" },
9129
+ kind: { type: "string" },
9130
+ category: { type: "string" },
9131
+ agent: { type: "string" },
9132
+ target_path: { type: "string", nullable: true },
9133
+ format: { type: "string" },
9134
+ output_count: { type: "number" },
9135
+ description: { type: "string", nullable: true },
9136
+ tags: { type: "array", items: { type: "string" } },
9137
+ is_template: { type: "boolean" },
9138
+ version: { type: "number" },
9139
+ updated_at: { type: "string" }
9140
+ }
9141
+ };
9013
9142
  var profileSchema = {
9014
9143
  type: "object",
9015
9144
  properties: {
@@ -9025,6 +9154,7 @@ var profileSchema = {
9025
9154
  };
9026
9155
  var configIdentitySchema = {
9027
9156
  type: "object",
9157
+ additionalProperties: false,
9028
9158
  required: ["id", "name", "slug", "kind", "category", "agent", "format", "is_template", "version", "created_at", "updated_at", "synced_at"],
9029
9159
  properties: {
9030
9160
  id: { type: "string" },
@@ -9077,6 +9207,12 @@ var snapshotSchema = {
9077
9207
  };
9078
9208
  var limitParameter = { name: "limit", in: "query", schema: { type: "integer", minimum: 1, maximum: 100 } };
9079
9209
  var cursorParameter = { name: "cursor", in: "query", schema: { type: "integer", minimum: 0, maximum: 1e5 } };
9210
+ var configViewParameter = {
9211
+ name: "view",
9212
+ in: "query",
9213
+ description: "Use summary for a content-free list projection, or identity for the smallest metadata-only projection.",
9214
+ schema: { type: "string", enum: ["summary", "identity"] }
9215
+ };
9080
9216
  var identityViewParameter = {
9081
9217
  name: "view",
9082
9218
  in: "query",
@@ -9120,6 +9256,7 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
9120
9256
  },
9121
9257
  schemas: {
9122
9258
  Config: configSchema,
9259
+ ConfigSummary: configSummarySchema,
9123
9260
  ConfigIdentity: configIdentitySchema,
9124
9261
  Profile: profileSchema,
9125
9262
  ProfileIdentity: profileIdentitySchema,
@@ -9144,6 +9281,26 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
9144
9281
  UpdateConfigInput: {
9145
9282
  type: "object",
9146
9283
  properties: {
9284
+ expected_version: { type: "integer", minimum: 1, description: "Atomically require this current version; a mismatch returns 409 without changing the config or snapshots." },
9285
+ name: { type: "string" },
9286
+ kind: { type: "string" },
9287
+ category: { type: "string" },
9288
+ agent: { type: "string" },
9289
+ target_path: { type: "string", nullable: true },
9290
+ outputs: { type: "array", items: { type: "object" } },
9291
+ format: { type: "string" },
9292
+ content: { type: "string" },
9293
+ description: { type: "string", nullable: true },
9294
+ tags: { type: "array", items: { type: "string" } },
9295
+ is_template: { type: "boolean" },
9296
+ synced_at: { type: "string", nullable: true }
9297
+ }
9298
+ },
9299
+ ConditionalUpdateConfigInput: {
9300
+ type: "object",
9301
+ required: ["expected_version"],
9302
+ properties: {
9303
+ expected_version: { type: "integer", minimum: 1, description: "Atomically require this current version; a mismatch returns 409 without changing the config or snapshots." },
9147
9304
  name: { type: "string" },
9148
9305
  kind: { type: "string" },
9149
9306
  category: { type: "string" },
@@ -9347,6 +9504,23 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
9347
9504
  source_bounded: { type: "boolean" }
9348
9505
  }
9349
9506
  },
9507
+ BoundedConfigSummaryPage: {
9508
+ type: "object",
9509
+ required: ["items", "total", "limit", "cursor", "next_cursor", "has_more", "complete", "truncated", "source_bounded"],
9510
+ properties: {
9511
+ configs: { type: "array", items: { $ref: "#/components/schemas/ConfigSummary" } },
9512
+ items: { type: "array", items: { $ref: "#/components/schemas/ConfigSummary" } },
9513
+ count: { type: "number" },
9514
+ total: { type: "number" },
9515
+ limit: { type: "number" },
9516
+ cursor: { type: "number" },
9517
+ next_cursor: { type: "number", nullable: true },
9518
+ has_more: { type: "boolean" },
9519
+ complete: { type: "boolean" },
9520
+ truncated: { type: "boolean", const: false },
9521
+ source_bounded: { type: "boolean" }
9522
+ }
9523
+ },
9350
9524
  BoundedConfigIdentityPage: {
9351
9525
  type: "object",
9352
9526
  required: ["items", "total", "limit", "cursor", "next_cursor", "has_more", "complete", "truncated", "source_bounded"],
@@ -9485,15 +9659,17 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
9485
9659
  { name: "search", in: "query", schema: { type: "string", maxLength: 512 } },
9486
9660
  limitParameter,
9487
9661
  cursorParameter,
9488
- identityViewParameter
9662
+ { name: "tag", in: "query", schema: { type: "array", items: { type: "string" } } },
9663
+ configViewParameter
9489
9664
  ],
9490
9665
  responses: {
9491
9666
  "200": {
9492
9667
  content: {
9493
9668
  "application/json": {
9494
9669
  schema: {
9495
- oneOf: [
9670
+ anyOf: [
9496
9671
  { $ref: "#/components/schemas/BoundedConfigPage" },
9672
+ { $ref: "#/components/schemas/BoundedConfigSummaryPage" },
9497
9673
  { $ref: "#/components/schemas/BoundedConfigIdentityPage" }
9498
9674
  ]
9499
9675
  }
@@ -9546,6 +9722,8 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
9546
9722
  content: { "application/json": { schema: { $ref: "#/components/schemas/UpdateConfigInput" } } }
9547
9723
  },
9548
9724
  responses: {
9725
+ "400": { description: "Invalid expected_version; must be a positive safe integer." },
9726
+ "409": { description: "CONFIG_VERSION_CONFLICT: expected_version no longer matches; config and snapshots are unchanged." },
9549
9727
  "200": {
9550
9728
  content: {
9551
9729
  "application/json": {
@@ -9564,6 +9742,8 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
9564
9742
  content: { "application/json": { schema: { $ref: "#/components/schemas/UpdateConfigInput" } } }
9565
9743
  },
9566
9744
  responses: {
9745
+ "400": { description: "Invalid expected_version; must be a positive safe integer." },
9746
+ "409": { description: "CONFIG_VERSION_CONFLICT: expected_version no longer matches; config and snapshots are unchanged." },
9567
9747
  "200": {
9568
9748
  content: {
9569
9749
  "application/json": {
@@ -9588,6 +9768,26 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
9588
9768
  }
9589
9769
  }
9590
9770
  },
9771
+ "/v1/configs/{id}/conditional-update": {
9772
+ post: {
9773
+ operationId: "conditionalUpdateConfig",
9774
+ summary: "Atomically update a config only if its current version matches",
9775
+ description: "Requires expected_version. Older servers return 404 without changing the config. Clients must never retry through an unconditional update route.",
9776
+ parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }],
9777
+ requestBody: {
9778
+ required: true,
9779
+ content: { "application/json": { schema: { $ref: "#/components/schemas/ConditionalUpdateConfigInput" } } }
9780
+ },
9781
+ responses: {
9782
+ "200": {
9783
+ content: { "application/json": { schema: { type: "object", properties: { config: { $ref: "#/components/schemas/Config" } } } } }
9784
+ },
9785
+ "400": { description: "expected_version is required and must be a positive safe integer." },
9786
+ "404": { description: "Config not found, or conditional updates are unsupported by this server." },
9787
+ "409": { description: "CONFIG_VERSION_CONFLICT: config and snapshots are unchanged." }
9788
+ }
9789
+ }
9790
+ },
9591
9791
  "/v1/configs/{id}/snapshots": {
9592
9792
  get: {
9593
9793
  operationId: "listSnapshots",
@@ -10034,14 +10234,14 @@ app.all("/v1/*", async (c) => {
10034
10234
  });
10035
10235
  var HOST = process.env["HOST"] ?? process.env["INSTRUCTIONS_HOST"] ?? "localhost";
10036
10236
  var serverOptions = { port: PORT, hostname: HOST, fetch: app.fetch };
10237
+ var server;
10037
10238
  if (import.meta.main) {
10038
10239
  console.log(`instructions-serve listening on http://${HOST}:${PORT} (backend: ${serviceBackend()})`);
10039
- Bun.serve(serverOptions);
10240
+ server = Bun.serve(serverOptions);
10040
10241
  }
10041
- var server_default = app;
10042
10242
  export {
10043
10243
  serviceBackend,
10044
10244
  serverOptions,
10045
- server_default as default,
10245
+ server,
10046
10246
  app
10047
10247
  };