@hasna/hooks 0.9.5 → 0.9.6

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.
package/bin/serve.js CHANGED
@@ -174,6 +174,7 @@ var HOOKS = [
174
174
  },
175
175
  {
176
176
  name: "workspace-repos-guard",
177
+ timeoutSeconds: 10,
177
178
  displayName: "Workspace Repos Guard",
178
179
  description: "Blocks writes and deletes that break the protected repo-checkout structure (~/.hasna/repos/clones canonical, plus legacy ~/workspace/repos during the migration window): writes to a protected root, top-level entry creation, non-org folders, and any delete under a protected root",
179
180
  version: "0.1.0",
@@ -192,7 +193,7 @@ var HOOKS = [
192
193
  matcher: "^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$",
193
194
  tags: ["rm", "delete", "trash", "recoverable", "guard", "safety", "bash"],
194
195
  rewritesInput: true,
195
- timeoutSeconds: 5
196
+ timeoutSeconds: 10
196
197
  },
197
198
  {
198
199
  name: "checktests",
@@ -6112,155 +6113,356 @@ function secureEqual(a, b) {
6112
6113
  }
6113
6114
 
6114
6115
  // src/openapi.ts
6115
- var openApiDocument = {
6116
- openapi: "3.1.0",
6117
- info: {
6118
- title: "Hooks Registry API",
6119
- version: "0.1.0",
6120
- description: "Local registry HTTP API for @hasna/hooks: catalog, lock, and artifact reads are open; publishing (PUT /api/v1/hooks) requires the API key (authorization: Bearer or x-api-key)."
6121
- },
6122
- servers: [{ url: "/" }],
6123
- paths: {
6124
- "/health": {
6125
- get: {
6126
- summary: "Liveness probe",
6127
- operationId: "getHealth",
6128
- responses: {
6129
- "200": {
6130
- description: "Service is alive",
6131
- content: {
6132
- "application/json": {
6133
- schema: { type: "object", additionalProperties: true }
6116
+ function buildOpenApiDocument(version) {
6117
+ return {
6118
+ openapi: "3.1.0",
6119
+ info: {
6120
+ title: "Hooks Registry API",
6121
+ version,
6122
+ description: "Local registry HTTP API for @hasna/hooks: catalog, lock, and artifact reads are open; publishing (PUT /api/v1/hooks) requires the API key (authorization: Bearer or x-api-key)."
6123
+ },
6124
+ servers: [{ url: "/" }],
6125
+ paths: {
6126
+ "/health": {
6127
+ get: {
6128
+ summary: "Liveness probe",
6129
+ operationId: "getHealth",
6130
+ responses: {
6131
+ "200": {
6132
+ description: "Service is alive",
6133
+ content: {
6134
+ "application/json": {
6135
+ schema: { $ref: "#/components/schemas/HooksHealth" }
6136
+ }
6134
6137
  }
6135
6138
  }
6136
6139
  }
6137
6140
  }
6138
- }
6139
- },
6140
- "/ready": {
6141
- get: {
6142
- summary: "Readiness probe \u2014 the hooks.lock store is readable",
6143
- operationId: "getReady",
6144
- responses: {
6145
- "200": { description: "Lock store is readable" },
6146
- "503": { description: "Lock store is not readable" }
6141
+ },
6142
+ "/ready": {
6143
+ get: {
6144
+ summary: "Readiness probe \u2014 the hooks.lock store is readable",
6145
+ operationId: "getReady",
6146
+ responses: {
6147
+ "200": {
6148
+ description: "Lock store is readable",
6149
+ content: {
6150
+ "application/json": {
6151
+ schema: { $ref: "#/components/schemas/HooksHealth" }
6152
+ }
6153
+ }
6154
+ },
6155
+ "503": {
6156
+ description: "Lock store is not readable",
6157
+ content: {
6158
+ "application/json": {
6159
+ schema: { $ref: "#/components/schemas/HooksHealth" }
6160
+ }
6161
+ }
6162
+ }
6163
+ }
6147
6164
  }
6148
- }
6149
- },
6150
- "/version": {
6151
- get: {
6152
- summary: "Package version",
6153
- operationId: "getVersion",
6154
- responses: {
6155
- "200": {
6156
- description: "The installed @hasna/hooks version",
6157
- content: {
6158
- "application/json": {
6159
- schema: { type: "object", properties: { version: { type: "string" } } }
6165
+ },
6166
+ "/version": {
6167
+ get: {
6168
+ summary: "Package version",
6169
+ operationId: "getVersion",
6170
+ responses: {
6171
+ "200": {
6172
+ description: "The installed @hasna/hooks version",
6173
+ content: {
6174
+ "application/json": {
6175
+ schema: { $ref: "#/components/schemas/HooksVersion" }
6176
+ }
6160
6177
  }
6161
6178
  }
6162
6179
  }
6163
6180
  }
6164
- }
6165
- },
6166
- "/openapi.json": {
6167
- get: {
6168
- summary: "This OpenAPI document",
6169
- operationId: "getOpenApi",
6170
- responses: { "200": { description: "OpenAPI 3.1 document" } }
6171
- }
6172
- },
6173
- "/api/v1/catalog": {
6174
- get: {
6175
- summary: "List the full hook catalog with versions and sha256",
6176
- operationId: "getCatalog",
6177
- responses: {
6178
- "200": {
6179
- description: "Catalog entries",
6180
- content: {
6181
- "application/json": {
6182
- schema: {
6183
- type: "object",
6184
- properties: {
6185
- hooks: {
6186
- type: "array",
6187
- items: {
6188
- type: "object",
6189
- properties: {
6190
- name: { type: "string" },
6191
- version: { type: "string" },
6192
- sha256: { type: "string" },
6193
- events: { type: "array", items: { type: "string" } }
6194
- }
6195
- }
6196
- }
6197
- }
6181
+ },
6182
+ "/openapi.json": {
6183
+ get: {
6184
+ summary: "This OpenAPI document",
6185
+ operationId: "getOpenApi",
6186
+ responses: {
6187
+ "200": {
6188
+ description: "OpenAPI 3.1 document",
6189
+ content: {
6190
+ "application/json": {
6191
+ schema: { type: "object", additionalProperties: true }
6198
6192
  }
6199
6193
  }
6200
6194
  }
6201
6195
  }
6202
6196
  }
6203
- }
6204
- },
6205
- "/api/v1/lock": {
6206
- get: {
6207
- summary: "Read the published hooks.lock with per-hook versions",
6208
- operationId: "getLock",
6209
- responses: { "200": { description: "The lock file" } }
6210
- }
6211
- },
6212
- "/api/v1/hooks/{name}/{version}": {
6213
- get: {
6214
- summary: "Fetch one hook's manifest and script",
6215
- operationId: "getHookArtifact",
6216
- parameters: [
6217
- { name: "name", in: "path", required: true, schema: { type: "string" } },
6218
- { name: "version", in: "path", required: true, schema: { type: "string" } }
6219
- ],
6220
- responses: {
6221
- "200": {
6222
- description: "Hook artifact; x-hook-sha256 header carries the script hash"
6197
+ },
6198
+ "/api/v1/catalog": {
6199
+ get: {
6200
+ summary: "List the full hook catalog with versions and sha256",
6201
+ operationId: "getCatalog",
6202
+ responses: {
6203
+ "200": {
6204
+ description: "Catalog entries",
6205
+ content: {
6206
+ "application/json": {
6207
+ schema: { $ref: "#/components/schemas/HooksCatalog" }
6208
+ }
6209
+ }
6210
+ }
6211
+ }
6212
+ }
6213
+ },
6214
+ "/api/v1/lock": {
6215
+ get: {
6216
+ summary: "Read the published hooks.lock with per-hook versions",
6217
+ operationId: "getLock",
6218
+ responses: {
6219
+ "200": {
6220
+ description: "The lock file",
6221
+ content: {
6222
+ "application/json": {
6223
+ schema: { $ref: "#/components/schemas/HooksLock" }
6224
+ }
6225
+ }
6226
+ }
6227
+ }
6228
+ }
6229
+ },
6230
+ "/api/v1/hooks/{name}/{version}": {
6231
+ get: {
6232
+ summary: "Fetch one hook's manifest and script",
6233
+ operationId: "getHookArtifact",
6234
+ parameters: [
6235
+ { name: "name", in: "path", required: true, schema: { type: "string" } },
6236
+ { name: "version", in: "path", required: true, schema: { type: "string" } }
6237
+ ],
6238
+ responses: {
6239
+ "200": {
6240
+ description: "Hook artifact; x-hook-sha256 header carries the script hash",
6241
+ headers: {
6242
+ "x-hook-sha256": {
6243
+ description: "sha256 of the served script, the same value the lock pins",
6244
+ schema: { type: "string" }
6245
+ }
6246
+ },
6247
+ content: {
6248
+ "application/json": {
6249
+ schema: { $ref: "#/components/schemas/HooksArtifact" }
6250
+ }
6251
+ }
6252
+ },
6253
+ "400": {
6254
+ description: "Invalid URL encoding or semver version",
6255
+ content: {
6256
+ "application/json": {
6257
+ schema: { $ref: "#/components/schemas/HooksError" }
6258
+ }
6259
+ }
6260
+ },
6261
+ "404": {
6262
+ description: "Hook not found locally",
6263
+ content: {
6264
+ "application/json": {
6265
+ schema: { $ref: "#/components/schemas/HooksError" }
6266
+ }
6267
+ }
6268
+ }
6269
+ }
6270
+ }
6271
+ },
6272
+ "/api/v1/hooks": {
6273
+ put: {
6274
+ summary: "Publish/retrust a hook (requires the API key)",
6275
+ operationId: "putHook",
6276
+ requestBody: {
6277
+ required: true,
6278
+ content: {
6279
+ "application/json": {
6280
+ schema: { $ref: "#/components/schemas/HooksPublishRequest" }
6281
+ }
6282
+ }
6283
+ },
6284
+ responses: {
6285
+ "200": {
6286
+ description: "Hook retrusted and pin updated",
6287
+ content: {
6288
+ "application/json": {
6289
+ schema: { $ref: "#/components/schemas/HooksPublishResponse" }
6290
+ }
6291
+ }
6292
+ },
6293
+ "400": {
6294
+ description: "Invalid JSON body",
6295
+ content: {
6296
+ "application/json": {
6297
+ schema: { $ref: "#/components/schemas/HooksError" }
6298
+ }
6299
+ }
6300
+ },
6301
+ "401": {
6302
+ description: "Missing or invalid API key",
6303
+ content: {
6304
+ "application/json": {
6305
+ schema: { $ref: "#/components/schemas/HooksError" }
6306
+ }
6307
+ }
6308
+ },
6309
+ "404": {
6310
+ description: "Hook not found in local store",
6311
+ content: {
6312
+ "application/json": {
6313
+ schema: { $ref: "#/components/schemas/HooksError" }
6314
+ }
6315
+ }
6316
+ },
6317
+ "409": {
6318
+ description: "Version mismatch with the local store",
6319
+ content: {
6320
+ "application/json": {
6321
+ schema: { $ref: "#/components/schemas/HooksError" }
6322
+ }
6323
+ }
6324
+ }
6223
6325
  },
6224
- "400": { description: "Invalid URL encoding or semver version" },
6225
- "404": { description: "Hook not found locally" }
6326
+ security: [{ apiKey: [] }]
6226
6327
  }
6227
6328
  }
6228
6329
  },
6229
- "/api/v1/hooks": {
6230
- put: {
6231
- summary: "Publish/retrust a hook (requires the API key)",
6232
- operationId: "putHook",
6233
- requestBody: {
6234
- required: true,
6235
- content: {
6236
- "application/json": {
6237
- schema: {
6238
- type: "object",
6239
- properties: { name: { type: "string" }, version: { type: "string" } }
6240
- }
6330
+ components: {
6331
+ securitySchemes: {
6332
+ apiKey: {
6333
+ type: "http",
6334
+ scheme: "bearer",
6335
+ description: "The registry API key \u2014 resolved by the client through the @hasna/contracts chain (HASNA_HOOKS_API_KEY, the Keychain item hasna.credentials.hooks.api-key, or ~/.hasna/hooks/config/credentials); the server compares the inbound bearer/x-api-key value."
6336
+ }
6337
+ },
6338
+ schemas: {
6339
+ HooksHealth: {
6340
+ type: "object",
6341
+ additionalProperties: false,
6342
+ required: ["status", "name"],
6343
+ properties: {
6344
+ status: { type: "string" },
6345
+ name: { type: "string" }
6346
+ }
6347
+ },
6348
+ HooksVersion: {
6349
+ type: "object",
6350
+ additionalProperties: false,
6351
+ required: ["version"],
6352
+ properties: { version: { type: "string" } }
6353
+ },
6354
+ HooksCatalogEntry: {
6355
+ type: "object",
6356
+ additionalProperties: false,
6357
+ required: ["name", "version", "sha256", "events", "description", "source", "versions"],
6358
+ properties: {
6359
+ name: { type: "string" },
6360
+ version: { type: "string" },
6361
+ sha256: { type: "string" },
6362
+ events: { type: "array", items: { type: "string" } },
6363
+ description: { type: "string" },
6364
+ source: {
6365
+ type: "string",
6366
+ description: "bundled | custom | custom-overrides-bundled"
6367
+ },
6368
+ versions: { type: "array", items: { type: "string" } }
6369
+ }
6370
+ },
6371
+ HooksCatalog: {
6372
+ type: "object",
6373
+ additionalProperties: false,
6374
+ required: ["hooks"],
6375
+ properties: {
6376
+ hooks: { type: "array", items: { $ref: "#/components/schemas/HooksCatalogEntry" } }
6377
+ }
6378
+ },
6379
+ HooksLockEntry: {
6380
+ type: "object",
6381
+ additionalProperties: false,
6382
+ required: ["version", "sha256", "source"],
6383
+ properties: {
6384
+ version: { type: "string" },
6385
+ sha256: { type: "string" },
6386
+ source: { type: "string" },
6387
+ pinned: {
6388
+ type: "boolean",
6389
+ description: "True for an explicit install/update pin, absent for sync-maintained pins."
6390
+ },
6391
+ versions: { type: "array", items: { type: "string" } }
6392
+ }
6393
+ },
6394
+ HooksLock: {
6395
+ type: "object",
6396
+ additionalProperties: false,
6397
+ required: ["hooks"],
6398
+ properties: {
6399
+ hooks: {
6400
+ type: "object",
6401
+ additionalProperties: { $ref: "#/components/schemas/HooksLockEntry" }
6241
6402
  }
6242
6403
  }
6243
6404
  },
6244
- responses: {
6245
- "200": { description: "Hook retrusted and pin updated" },
6246
- "401": { description: "Missing or invalid API key" },
6247
- "404": { description: "Hook not found in local store" },
6248
- "409": { description: "Version mismatch with the local store" }
6405
+ HooksManifest: {
6406
+ type: "object",
6407
+ additionalProperties: false,
6408
+ required: ["name", "version", "events", "script"],
6409
+ properties: {
6410
+ name: { type: "string" },
6411
+ version: { type: "string" },
6412
+ description: { type: "string" },
6413
+ events: { type: "array", items: { type: "string" } },
6414
+ script: { type: "string" },
6415
+ script_kind: { type: "string", enum: ["inline", "file"] },
6416
+ args: { type: "array", items: { type: "string" } },
6417
+ timeout_ms: { type: "number" }
6418
+ }
6249
6419
  },
6250
- security: [{ apiKey: [] }]
6251
- }
6252
- }
6253
- },
6254
- components: {
6255
- securitySchemes: {
6256
- apiKey: {
6257
- type: "http",
6258
- scheme: "bearer",
6259
- description: "The registry API key \u2014 resolved by the client through the @hasna/contracts chain (HASNA_HOOKS_API_KEY, the Keychain item hasna.credentials.hooks.api-key, or ~/.hasna/hooks/config/credentials); the server compares the inbound bearer/x-api-key value."
6420
+ HooksArtifact: {
6421
+ type: "object",
6422
+ additionalProperties: false,
6423
+ required: ["manifest", "script"],
6424
+ properties: {
6425
+ manifest: { $ref: "#/components/schemas/HooksManifest" },
6426
+ script: { type: "string" }
6427
+ }
6428
+ },
6429
+ HooksError: {
6430
+ type: "object",
6431
+ additionalProperties: false,
6432
+ required: ["error"],
6433
+ properties: { error: { type: "string" } }
6434
+ },
6435
+ HooksPublishRequest: {
6436
+ type: "object",
6437
+ additionalProperties: false,
6438
+ required: ["name"],
6439
+ properties: {
6440
+ name: { type: "string" },
6441
+ version: { type: "string" }
6442
+ }
6443
+ },
6444
+ HooksPublishResponse: {
6445
+ type: "object",
6446
+ additionalProperties: false,
6447
+ required: ["ok", "hook"],
6448
+ properties: {
6449
+ ok: { type: "boolean" },
6450
+ hook: {
6451
+ type: "object",
6452
+ additionalProperties: false,
6453
+ required: ["name", "version", "sha256"],
6454
+ properties: {
6455
+ name: { type: "string" },
6456
+ version: { type: "string" },
6457
+ sha256: { type: "string" }
6458
+ }
6459
+ }
6460
+ }
6461
+ }
6260
6462
  }
6261
6463
  }
6262
- }
6263
- };
6464
+ };
6465
+ }
6264
6466
 
6265
6467
  // src/serve.ts
6266
6468
  var DEFAULT_SERVE_PORT = 39428;
@@ -6358,7 +6560,7 @@ function handleServeRequest(req, apiKey) {
6358
6560
  return Promise.resolve(json({ version: packageVersion() }));
6359
6561
  }
6360
6562
  if (url.pathname === "/openapi.json" && req.method === "GET") {
6361
- return Promise.resolve(json(openApiDocument));
6563
+ return Promise.resolve(json(buildOpenApiDocument(packageVersion())));
6362
6564
  }
6363
6565
  if (url.pathname === "/api/v1/catalog" && req.method === "GET") {
6364
6566
  return buildCatalog().then((catalog) => json({ hooks: catalog }));