@maudecode/kanvil 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/apps/server/.env.example +5 -0
  2. package/apps/server/dist/{auth-CnAQzKyq.mjs → auth-Cs_whzgk.mjs} +382 -127
  3. package/apps/server/dist/client.mjs +4 -3
  4. package/apps/server/dist/{config-DOT1SDQO.mjs → config-DKuNQY5b.mjs} +15 -7
  5. package/apps/server/dist/index.mjs +105 -57
  6. package/apps/server/dist/migrate.mjs +2 -2
  7. package/apps/server/dist/recover.mjs +4 -4
  8. package/apps/server/dist/{src-VxR5jZiK.mjs → src-DptOUNRd.mjs} +67 -23
  9. package/apps/web/dist/assets/{CompositeItem-B5Qlh5m0.js → CompositeItem-CXqewtEL.js} +1 -1
  10. package/apps/web/dist/assets/{Trans-DxqBaUXP.js → Trans-B1rRlWca.js} +1 -1
  11. package/apps/web/dist/assets/{admin-settings-Bp7V2t1V.js → admin-settings-DE3MfBFE.js} +1 -1
  12. package/apps/web/dist/assets/{archive-restore-wsAzhqi-.js → archive-restore-76sStk8K.js} +1 -1
  13. package/apps/web/dist/assets/az-AZ-bFxpMMZN.js +1 -0
  14. package/apps/web/dist/assets/{az-C4KDjOOw.js → az-B7OgHoaE.js} +1 -1
  15. package/apps/web/dist/assets/{corner-left-up-arA2ipmM.js → corner-left-up-PD-73Nae.js} +1 -1
  16. package/apps/web/dist/assets/{de-Z599ZW7b.js → de-B-bv38Q0.js} +1 -1
  17. package/apps/web/dist/assets/de-DE-BeXI30Ll.js +1 -0
  18. package/apps/web/dist/assets/default-bQFGTwIp.js +56 -0
  19. package/apps/web/dist/assets/{delivery-settings-D5NKPlhN.js → delivery-settings-BvMeOsCS.js} +1 -1
  20. package/apps/web/dist/assets/{el-xsvq992p.js → el-BQHQHQgX.js} +1 -1
  21. package/apps/web/dist/assets/el-GR-SgRHlWhY.js +1 -0
  22. package/apps/web/dist/assets/{ellipsis-UOkL14vq.js → ellipsis-GF1HwTDb.js} +1 -1
  23. package/apps/web/dist/assets/es-ES-BF1J5vap.js +1 -0
  24. package/apps/web/dist/assets/{es-D3e9L5_i.js → es-f5Xr8ldr.js} +1 -1
  25. package/apps/web/dist/assets/{forms-BbWAl0BZ.js → forms-BlEL8C_P.js} +1 -1
  26. package/apps/web/dist/assets/{fr-CY35Bcpp.js → fr-DSR0htnl.js} +1 -1
  27. package/apps/web/dist/assets/fr-FR-D_sldwf6.js +1 -0
  28. package/apps/web/dist/assets/{hi-Cqfv-E5P.js → hi-BaAJEWuW.js} +1 -1
  29. package/apps/web/dist/assets/hi-IN-CfOw4TWf.js +1 -0
  30. package/apps/web/dist/assets/{highlight-Ds7xUUQv.js → highlight-DXT2xgt8.js} +1 -1
  31. package/apps/web/dist/assets/id-ID-CLKtHO7Q.js +1 -0
  32. package/apps/web/dist/assets/{id-CFBOVDUC.js → id-kxogWcip.js} +1 -1
  33. package/apps/web/dist/assets/{index-2V6MWx63.js → index-CYVrs22O.js} +5 -5
  34. package/apps/web/dist/assets/{invitations-BPk5fwwU.js → invitations-BPik3J_G.js} +1 -1
  35. package/apps/web/dist/assets/{it-DNLponcC.js → it-C_qk40U6.js} +1 -1
  36. package/apps/web/dist/assets/it-IT-eVZNW5qX.js +1 -0
  37. package/apps/web/dist/assets/{item-DED0Ikqi.js → item-TCHuZNK6.js} +1 -1
  38. package/apps/web/dist/assets/{ja-ZXGlS4WA.js → ja-Dn5h64V9.js} +1 -1
  39. package/apps/web/dist/assets/ja-JP-D96njn-P.js +1 -0
  40. package/apps/web/dist/assets/{ko-CSoj1hQM.js → ko-CjaoEPuG.js} +1 -1
  41. package/apps/web/dist/assets/ko-KR-Bash_ieA.js +1 -0
  42. package/apps/web/dist/assets/{label-settings-D12QOkDs.js → label-settings-DZTorNDm.js} +1 -1
  43. package/apps/web/dist/assets/{markdown-blocks-DbsqN1T2.js → markdown-blocks-BQLO-H_7.js} +1 -1
  44. package/apps/web/dist/assets/{member-directory-BRhEIYZ6.js → member-directory-Dx_jQYfI.js} +1 -1
  45. package/apps/web/dist/assets/mk-MK-BWQWGIRx.js +1 -0
  46. package/apps/web/dist/assets/{mk-D77vceoz.js → mk-jLeGD50T.js} +1 -1
  47. package/apps/web/dist/assets/{nl-Bew8-xmX.js → nl-AVtC5ygT.js} +1 -1
  48. package/apps/web/dist/assets/nl-NL-D1tvo0Gd.js +1 -0
  49. package/apps/web/dist/assets/{pl-CEBl_oa9.js → pl-Dup1h_Y1.js} +1 -1
  50. package/apps/web/dist/assets/pl-PL-DNgGUEcQ.js +1 -0
  51. package/apps/web/dist/assets/{project-lifecycle-DGHAmEB2.js → project-lifecycle-n1ZZbYfu.js} +1 -1
  52. package/apps/web/dist/assets/{project-settings-DY8Ossd-.js → project-settings-B_Ih5kiz.js} +1 -1
  53. package/apps/web/dist/assets/pt-BR-CZsUZQ6P.js +1 -0
  54. package/apps/web/dist/assets/{pt-BR-BDTQeKcp.js → pt-BR-Cm04EWM-.js} +1 -1
  55. package/apps/web/dist/assets/{public-project-BeXla8TL.js → public-project-BivDjU_a.js} +1 -1
  56. package/apps/web/dist/assets/{rotate-ccw-DGM890Fc.js → rotate-ccw-CSaKhZYt.js} +1 -1
  57. package/apps/web/dist/assets/{ru-BeH0eXQq.js → ru-IeycXsyE.js} +1 -1
  58. package/apps/web/dist/assets/ru-RU-4ew92YE_.js +1 -0
  59. package/apps/web/dist/assets/{search-B3-w3oCd.js → search-Ble6WSEU.js} +3 -3
  60. package/apps/web/dist/assets/{send-B9baaCnt.js → send-BLMZ8gQ0.js} +1 -1
  61. package/apps/web/dist/assets/settings-CH48qQK2.js +1 -0
  62. package/apps/web/dist/assets/{sliders-horizontal-yKgghk8h.js → sliders-horizontal-Db6kRvuw.js} +1 -1
  63. package/apps/web/dist/assets/{switch-ByOrazEW.js → switch-BeX-0Fwq.js} +1 -1
  64. package/apps/web/dist/assets/{table-Cm6NQXfq.js → table-UJp5S9yy.js} +1 -1
  65. package/apps/web/dist/assets/ticket-detail-C24opfNW.js +2 -0
  66. package/apps/web/dist/assets/{ticket-editor-CLDBUk0n.js → ticket-editor--d_bG9C1.js} +2 -2
  67. package/apps/web/dist/assets/{ticket-list-BC9acNl4.js → ticket-list-DRbZWYHf.js} +1 -1
  68. package/apps/web/dist/assets/{ticket-list-controls-CRQk5ghe.js → ticket-list-controls-fYBottFC.js} +1 -1
  69. package/apps/web/dist/assets/{tr-BNApk6Hc.js → tr-DtT_dKUK.js} +1 -1
  70. package/apps/web/dist/assets/tr-TR-CM381XCw.js +1 -0
  71. package/apps/web/dist/assets/{uk-CjShsgpN.js → uk-CsbeHSS6.js} +1 -1
  72. package/apps/web/dist/assets/uk-UA-AeOiWCVo.js +1 -0
  73. package/apps/web/dist/assets/{upload-BzLq4CMS.js → upload-DUEg04Nj.js} +1 -1
  74. package/apps/web/dist/assets/{useQueries-D64vLhM6.js → useQueries-5lW2C5Sb.js} +1 -1
  75. package/apps/web/dist/assets/vi-VN-td08xXv9.js +1 -0
  76. package/apps/web/dist/assets/{vi-s5Hfv1Y-.js → vi-rkf1BOLx.js} +1 -1
  77. package/apps/web/dist/assets/{visual-editor-KE96mwCg.js → visual-editor-DkFLjouD.js} +1 -1
  78. package/apps/web/dist/assets/{workspace-settings-C-Phmi9i.js → workspace-settings-Bcsp-VoJ.js} +1 -1
  79. package/apps/web/dist/assets/zh-CN-D3dhEYLS.js +1 -0
  80. package/apps/web/dist/assets/{zh-CN-BBnpHJ9R.js → zh-CN-Xo-QId5w.js} +1 -1
  81. package/apps/web/dist/assets/zh-TW-CfhOEFo8.js +1 -0
  82. package/apps/web/dist/assets/{zh-TW-sSWkqSLN.js → zh-TW-DEnTqjbn.js} +1 -1
  83. package/apps/web/dist/index.html +7 -7
  84. package/docs/api.md +27 -13
  85. package/docs/cli.md +9 -1
  86. package/docs/releasing.md +16 -2
  87. package/docs/self-hosting.md +8 -0
  88. package/package.json +1 -1
  89. package/apps/web/dist/assets/az-AZ-DlsPlUZi.js +0 -1
  90. package/apps/web/dist/assets/de-DE-DLCaqUUe.js +0 -1
  91. package/apps/web/dist/assets/default-6QIAyMq2.js +0 -56
  92. package/apps/web/dist/assets/el-GR-P2XcwrSU.js +0 -1
  93. package/apps/web/dist/assets/es-ES-CJoX8f3w.js +0 -1
  94. package/apps/web/dist/assets/fr-FR-yLkQdy6a.js +0 -1
  95. package/apps/web/dist/assets/hi-IN-y8dqej7z.js +0 -1
  96. package/apps/web/dist/assets/id-ID-DSvh8mGH.js +0 -1
  97. package/apps/web/dist/assets/it-IT-BUcfUqNo.js +0 -1
  98. package/apps/web/dist/assets/ja-JP-C-jLP_Ay.js +0 -1
  99. package/apps/web/dist/assets/ko-KR-C5Ozdm2E.js +0 -1
  100. package/apps/web/dist/assets/mk-MK-BdM_SsE8.js +0 -1
  101. package/apps/web/dist/assets/nl-NL-CwjB5sva.js +0 -1
  102. package/apps/web/dist/assets/pl-PL-6-PMhTBd.js +0 -1
  103. package/apps/web/dist/assets/pt-BR-TD_wt6Hr.js +0 -1
  104. package/apps/web/dist/assets/ru-RU-Bk1a6Oln.js +0 -1
  105. package/apps/web/dist/assets/settings-BKZfEkVk.js +0 -1
  106. package/apps/web/dist/assets/ticket-detail-Cd3_e2jz.js +0 -2
  107. package/apps/web/dist/assets/tr-TR-CUfBb7K9.js +0 -1
  108. package/apps/web/dist/assets/uk-UA-BDaXBsDP.js +0 -1
  109. package/apps/web/dist/assets/vi-VN-Bgb-L6mE.js +0 -1
  110. package/apps/web/dist/assets/zh-CN-BypkvBe6.js +0 -1
  111. package/apps/web/dist/assets/zh-TW-CgMsT_XS.js +0 -1
@@ -1,4 +1,4 @@
1
- import { C as errorSchema, Ct as streamErrorSchema, E as eventSchema, G as notificationReadInput, H as notificationIdsInput, K as notificationsSchema, Q as parsePublicOutput, U as notificationPreferencesInput, W as notificationPreferencesSchema, Y as operations, Z as parseOutput, a as checkpointSchema, at as publicOperations, q as okSchema, t as ApiError, yt as snapshotSchema } from "./src-VxR5jZiK.mjs";
1
+ import { C as errorSchema, Ct as streamErrorSchema, E as eventSchema, G as notificationReadInput, H as notificationIdsInput, K as notificationsSchema, Q as parsePublicOutput, U as notificationPreferencesInput, W as notificationPreferencesSchema, Y as operations, Z as parseOutput, a as checkpointSchema, at as publicOperations, q as okSchema, t as ApiError, yt as snapshotSchema } from "./src-DptOUNRd.mjs";
2
2
  import { z } from "zod";
3
3
  import { parseArgs } from "node:util";
4
4
  import { constants } from "node:fs";
@@ -639,7 +639,8 @@ const commands = {
639
639
  "comment add": "add_comment",
640
640
  "claim acquire": "claim_ticket",
641
641
  "claim renew": "renew_claim",
642
- "claim release": "release_claim"
642
+ "claim release": "release_claim",
643
+ "claim recover": "recover_claim"
643
644
  };
644
645
  const clientHelp = `Usage: kanvil <namespace> <action> [ticket-key] [options]
645
646
  ${Object.keys(commands).join("\n")}
@@ -651,7 +652,7 @@ profile list | profile remove <name> --yes
651
652
  --input <json|-> Operation input as a JSON object; - reads stdin
652
653
  --title <text> Ticket title
653
654
  --body <markdown> Comment body
654
- --expected-version <n> Required version for update, delete and acquire
655
+ --expected-version <n> Required version for update, delete, acquire and rescheduling recovery
655
656
  --changes <json> Ticket update changes as a JSON object
656
657
  --request-id <uuid> Reuse for a deliberate retry across invocations
657
658
  --json Compact JSON output (default: indented JSON)
@@ -1,4 +1,4 @@
1
- import { Lt as s3SettingsSchema, Nt as brokerSettingsSchema, m as defaultClaimMaxLeaseSeconds, o as claimLeaseSecondsSchema, zt as searchSettingsSchema } from "./src-VxR5jZiK.mjs";
1
+ import { Bt as searchSettingsSchema, Pt as brokerSettingsSchema, Rt as s3SettingsSchema, m as defaultClaimMaxLeaseSeconds, o as claimLeaseSecondsSchema } from "./src-DptOUNRd.mjs";
2
2
  import pg from "pg";
3
3
  import { drizzle } from "drizzle-orm/node-postgres";
4
4
  import { relations } from "drizzle-orm";
@@ -341,12 +341,17 @@ const oauthConsentRelations = relations(oauthConsent, ({ one }) => ({
341
341
  }));
342
342
  //#endregion
343
343
  //#region src/database.ts
344
- function createDatabase(url) {
345
- const pool = new pg.Pool({
346
- connectionString: url,
347
- max: 10,
348
- idleTimeoutMillis: 3e4
349
- });
344
+ function poolOptions(config, role) {
345
+ return {
346
+ connectionString: config.DATABASE_URL,
347
+ application_name: role === "domain" ? "kanvil" : "kanvil-integration",
348
+ max: role === "domain" ? config.KANVIL_DATABASE_POOL_MAX : config.KANVIL_INTEGRATION_POOL_MAX,
349
+ idleTimeoutMillis: 3e4,
350
+ connectionTimeoutMillis: config.KANVIL_DATABASE_ACQUIRE_TIMEOUT_MS
351
+ };
352
+ }
353
+ function createDatabase(config, role = "domain") {
354
+ const pool = new pg.Pool(poolOptions(config, role));
350
355
  pool.on("error", () => {
351
356
  console.error("A PostgreSQL idle connection was lost; the pool will reconnect.");
352
357
  });
@@ -360,6 +365,9 @@ function createDatabase(url) {
360
365
  const optionalEnv = (schema) => z.preprocess((value) => value === "" ? void 0 : value, schema.optional());
361
366
  const envSchema = z.object({
362
367
  DATABASE_URL: z.url(),
368
+ KANVIL_DATABASE_POOL_MAX: z.coerce.number().int().min(2).max(1e3).default(10),
369
+ KANVIL_INTEGRATION_POOL_MAX: z.coerce.number().int().min(1).max(1e3).default(4),
370
+ KANVIL_DATABASE_ACQUIRE_TIMEOUT_MS: z.coerce.number().int().min(100).max(6e5).default(3e4),
363
371
  KANVIL_STORAGE_PROVIDER: optionalEnv(z.enum(["local", "s3"])),
364
372
  S3_ENDPOINT: optionalEnv(s3SettingsSchema.shape.endpoint),
365
373
  S3_REGION: s3SettingsSchema.shape.region,
@@ -1,7 +1,7 @@
1
- import { a as auth_schema_exports, d as user$1, i as apikey, n as createDatabase, r as account, t as readConfig, u as session } from "./config-DOT1SDQO.mjs";
2
- import { A as hasAdminRole, B as memberSchema, C as errorSchema, Ft as photoInputSchema, G as notificationReadInput, H as notificationIdsInput, I as isOperation, It as photoResultSchema, L as isPresetColor, Lt as s3SettingsSchema, M as importProviderDefinitions, Nt as brokerSettingsSchema, Pt as instanceUpdateSchema, R as isPublicOperation, U as notificationPreferencesInput, W as notificationPreferencesSchema, Y as operations, at as publicOperations, c as credentialInput, g as defaultWorkflow, ht as scopes, j as importLimits, jt as adminInstanceSchema, mt as roles, ot as pushDeviceInput, q as okSchema, s as closedStates, t as ApiError, wt as ticketExportFormat, xt as stateLabels, zt as searchSettingsSchema } from "./src-VxR5jZiK.mjs";
1
+ import { a as auth_schema_exports, d as user$1, i as apikey, n as createDatabase, r as account, t as readConfig, u as session } from "./config-DKuNQY5b.mjs";
2
+ import { A as hasAdminRole, B as memberSchema, Bt as searchSettingsSchema, C as errorSchema, Ft as instanceUpdateSchema, G as notificationReadInput, H as notificationIdsInput, I as isOperation, It as photoInputSchema, L as isPresetColor, Lt as photoResultSchema, M as importProviderDefinitions, Mt as adminInstanceSchema, Pt as brokerSettingsSchema, R as isPublicOperation, Rt as s3SettingsSchema, U as notificationPreferencesInput, W as notificationPreferencesSchema, Y as operations, at as publicOperations, c as credentialInput, g as defaultWorkflow, ht as scopes, j as importLimits, mt as roles, ot as pushDeviceInput, q as okSchema, s as closedStates, t as ApiError, wt as ticketExportFormat, xt as stateLabels } from "./src-DptOUNRd.mjs";
3
3
  import { t as migrateDatabase } from "./migrate-C9f4ozB4.mjs";
4
- import { $ as subscriptions, A as createTicketSearch, B as blobObjects, C as DeliveryError, D as requireInstanceAdmin, E as createWriteLimiter, F as stagedUploadCutoff, G as members, H as eventClock, I as createObjectStore, J as profilePhotos, K as notificationPreferences, L as createPollingJob, M as createBlobStorage, N as decodeImage, O as requireSession, P as imageResponse, Q as storageBackends, R as attachmentContent, S as deliverBatch, T as createAccess, U as events, V as deliveryCheckpoints, X as pushDevices, Y as projects, Z as stagedUploads, _ as recipientColumns, a as deliverySecrets, b as receiveRepositoryWebhook, c as ownerBlocker, d as createDomain, et as tickets, f as fail, g as reachable, h as one, i as createInstance, j as createTicketIndex, k as readCalendarFeed, l as appendEvents, m as notify, n as emailCodesAvailable, nt as workspaces, o as otherActiveAdministrator, p as json, q as notifications, r as signInProviders, rt as matchesOIDCBinding, s as ownedWorkspaces, t as createAuth, tt as workspaceMembers, u as assertVersion, v as requireRow, w as createHttpSender, x as defaultSenders, y as processImports, z as attachments } from "./auth-CnAQzKyq.mjs";
4
+ import { $ as stagedUploads, A as requireSession, B as attachmentContent, C as defaultSenders, D as createAccess, E as createHttpSender, F as decodeImage, G as events, H as blobObjects, I as imageResponse, J as notificationPreferences, L as stagedUploadCutoff, M as createTicketSearch, N as createTicketIndex, O as createWriteLimiter, P as createBlobStorage, Q as pushDevices, R as createObjectStore, S as receiveRepositoryWebhook, T as DeliveryError, U as deliveryCheckpoints, V as attachments, W as eventClock, X as profilePhotos, Y as notifications, Z as projects, _ as one, a as deliverySecrets, at as matchesOIDCBinding, b as requireRow, c as ownerBlocker, d as createDomain, et as storageBackends, f as errorEnvelope, g as notify, h as json, i as createInstance, it as workspaces, j as readCalendarFeed, k as requireInstanceAdmin, l as appendEvents, m as intakeOnly, n as emailCodesAvailable, nt as tickets, o as otherActiveAdministrator, p as fail, q as members, r as signInProviders, rt as workspaceMembers, s as ownedWorkspaces, t as createAuth, tt as subscriptions, u as assertVersion, v as reachable, w as deliverBatch, x as processImports, y as recipientColumns, z as createPollingJob } from "./auth-Cs_whzgk.mjs";
5
5
  import { serve } from "@hono/node-server";
6
6
  import pg from "pg";
7
7
  import { drizzle } from "drizzle-orm/node-postgres";
@@ -324,8 +324,42 @@ function createAccountAdministration(database, auth, config) {
324
324
  };
325
325
  }
326
326
  //#endregion
327
+ //#region src/cleanup.ts
328
+ function createCleanup() {
329
+ const resources = [];
330
+ let closing;
331
+ function close() {
332
+ return closing ??= Promise.resolve().then(async () => {
333
+ const failures = [];
334
+ for (const resource of resources.toReversed()) try {
335
+ await resource.close();
336
+ } catch {
337
+ failures.push(/* @__PURE__ */ new Error(`Could not close ${resource.name}.`));
338
+ }
339
+ if (failures.length) throw new AggregateError(failures, "Resource cleanup failed.");
340
+ });
341
+ }
342
+ return {
343
+ add(name, release) {
344
+ resources.push({
345
+ name,
346
+ close: release
347
+ });
348
+ },
349
+ close,
350
+ async fail(error) {
351
+ try {
352
+ await close();
353
+ } catch (cleanupError) {
354
+ console.error("Startup cleanup failed.", cleanupError);
355
+ }
356
+ throw error;
357
+ }
358
+ };
359
+ }
360
+ //#endregion
327
361
  //#region src/version.ts
328
- const applicationVersion = "0.4.3";
362
+ const applicationVersion = "0.5.0";
329
363
  //#endregion
330
364
  //#region src/realtime.ts
331
365
  async function createRealtime(config) {
@@ -334,7 +368,10 @@ async function createRealtime(config) {
334
368
  let client, closed = false, retry;
335
369
  async function connect() {
336
370
  if (closed) return;
337
- const connection = new pg.Client({ connectionString: config.DATABASE_URL });
371
+ const connection = new pg.Client({
372
+ connectionString: config.DATABASE_URL,
373
+ application_name: "kanvil-realtime"
374
+ });
338
375
  client = connection;
339
376
  const disconnected = () => {
340
377
  if (client !== connection || closed) return;
@@ -497,8 +534,25 @@ function brokerEndpoint(settings) {
497
534
  return `${url.protocol}//${url.host}${url.pathname}`;
498
535
  }
499
536
  async function createEventDelivery(config, instance, database, factory = createWakeupBus) {
500
- const postgres = await createRealtime(config), emitter = new EventEmitter();
537
+ const cleanup = createCleanup();
538
+ try {
539
+ const postgres = await createRealtime(config);
540
+ cleanup.add("PostgreSQL realtime client", () => postgres.close());
541
+ return await initializeEventDelivery(instance, database, factory, postgres, cleanup);
542
+ } catch (error) {
543
+ return cleanup.fail(error);
544
+ }
545
+ }
546
+ async function initializeEventDelivery(instance, database, factory, postgres, cleanup) {
547
+ const emitter = new EventEmitter();
501
548
  emitter.setMaxListeners(0);
549
+ cleanup.add("realtime listeners", () => {
550
+ try {
551
+ emitter.emit("close");
552
+ } finally {
553
+ emitter.removeAllListeners();
554
+ }
555
+ });
502
556
  const identity = (await instance.read()).identity;
503
557
  let bus, fingerprint = "", destination = "", closed = false, pending, dirty = false, error = null;
504
558
  const wake = () => emitter.emit("change");
@@ -580,11 +634,23 @@ async function createEventDelivery(config, instance, database, factory = createW
580
634
  if (!bus?.ready() || error) wake();
581
635
  trigger();
582
636
  });
637
+ cleanup.add("broker connection", async () => {
638
+ await bus?.close();
639
+ });
640
+ cleanup.add("broker worker", async () => {
641
+ closed = true;
642
+ await pending;
643
+ });
644
+ cleanup.add("PostgreSQL subscription", unsubscribe);
583
645
  const timer = setInterval(() => {
584
646
  if (error) wake();
585
647
  trigger();
586
648
  }, 1e3);
587
649
  timer.unref();
650
+ cleanup.add("broker timer", () => {
651
+ closed = true;
652
+ clearInterval(timer);
653
+ });
588
654
  trigger();
589
655
  return {
590
656
  subscribe(listener) {
@@ -631,16 +697,7 @@ async function createEventDelivery(config, instance, database, factory = createW
631
697
  await probe.close().catch(() => {});
632
698
  }
633
699
  },
634
- async close() {
635
- closed = true;
636
- clearInterval(timer);
637
- unsubscribe();
638
- await pending;
639
- await bus?.close().catch(() => {});
640
- emitter.emit("close");
641
- emitter.removeAllListeners();
642
- await postgres.close();
643
- }
700
+ close: cleanup.close
644
701
  };
645
702
  }
646
703
  //#endregion
@@ -3095,12 +3152,11 @@ function createMcp(auth, domain, access, config, limitWrite) {
3095
3152
  structuredContent: { result }
3096
3153
  };
3097
3154
  } catch (error) {
3098
- if (!(error instanceof ApiError) && !(error instanceof z.ZodError)) throw error;
3099
3155
  return {
3100
3156
  isError: true,
3101
3157
  content: [{
3102
3158
  type: "text",
3103
- text: error instanceof ApiError ? `${error.code}: ${error.message}` : "validation_error: Invalid tool arguments"
3159
+ text: JSON.stringify(errorEnvelope(error).body)
3104
3160
  }]
3105
3161
  };
3106
3162
  }
@@ -3205,11 +3261,28 @@ const toPreferences = (row) => notificationPreferencesSchema.parse(row ? {
3205
3261
  } : {});
3206
3262
  const digest = (value) => createHash("sha256").update(value).digest();
3207
3263
  async function createApplication(database, config, overrides = {}) {
3264
+ const cleanup = createCleanup();
3208
3265
  const adapters = {
3209
3266
  ...defaultAdapters,
3210
3267
  ...overrides
3211
3268
  };
3212
- const integrationDatabase = createDatabase(config.DATABASE_URL);
3269
+ const jobs = (name, interval, run) => {
3270
+ const job = adapters.jobs(name, interval, run);
3271
+ cleanup.add(name, () => job.close());
3272
+ return job;
3273
+ };
3274
+ try {
3275
+ return await initializeApplication(database, config, {
3276
+ ...adapters,
3277
+ jobs
3278
+ }, cleanup);
3279
+ } catch (error) {
3280
+ return cleanup.fail(error);
3281
+ }
3282
+ }
3283
+ async function initializeApplication(database, config, adapters, cleanup) {
3284
+ const integrationDatabase = createDatabase(config, "integration");
3285
+ cleanup.add("integration database", () => integrationDatabase.pool.end());
3213
3286
  const instance = createInstance(integrationDatabase, config);
3214
3287
  await instance.read();
3215
3288
  const blobs = createBlobStorage(integrationDatabase, instance, adapters.objectStore, adapters.jobs), search = createTicketSearch(integrationDatabase, instance, adapters.ticketIndex, adapters.jobs);
@@ -3223,12 +3296,14 @@ async function createApplication(database, config, overrides = {}) {
3223
3296
  sources: adapters.importSources(config),
3224
3297
  claimMaxLeaseSeconds: config.KANVIL_CLAIM_MAX_LEASE_SECONDS
3225
3298
  }), access = createAccess(auth, domain, config, database), realtime = await adapters.realtime(config, instance, integrationDatabase);
3299
+ cleanup.add("realtime", () => realtime.close());
3226
3300
  const unsubscribeJobs = realtime.subscribe(() => {
3227
3301
  blobs.sync().catch(() => {});
3228
3302
  search.sync().catch(() => {});
3229
3303
  });
3230
- const outbox = adapters.jobs("outbox", 5e3, () => deliverBatch(database, config, senders, secrets, repositories));
3231
- const reminders = adapters.jobs("due-reminders", 6e4, () => sendDueReminders(database));
3304
+ cleanup.add("job subscription", unsubscribeJobs);
3305
+ adapters.jobs("outbox", 5e3, () => deliverBatch(database, config, senders, secrets, repositories));
3306
+ adapters.jobs("due-reminders", 6e4, () => sendDueReminders(database));
3232
3307
  const imports = adapters.jobs("imports", 2e3, async () => {
3233
3308
  if (await processImports(domain)) imports.trigger().catch(() => {});
3234
3309
  });
@@ -3256,35 +3331,16 @@ async function createApplication(database, config, overrides = {}) {
3256
3331
  code: typeof error.body?.code === "string" ? error.body.code : "authentication_error",
3257
3332
  message: typeof error.body?.message === "string" ? error.body.message : "Authentication request failed"
3258
3333
  } }, { status: error.statusCode });
3259
- if (error instanceof ApiError) {
3260
- const retry = error.status === 429 && error.details && typeof error.details === "object" && "retryAfterSeconds" in error.details ? String(error.details.retryAfterSeconds) : void 0;
3261
- return Response.json({ error: {
3262
- status: error.status,
3263
- code: error.code,
3264
- message: error.message,
3265
- details: error.details
3266
- } }, {
3267
- status: error.status,
3268
- headers: retry ? { "Retry-After": retry } : {}
3269
- });
3270
- }
3271
- if (error instanceof z$1.ZodError) return Response.json({ error: {
3272
- code: "validation_error",
3273
- message: "Invalid request",
3274
- details: error.issues.map((issue) => ({
3275
- path: issue.path.join("."),
3276
- message: issue.message
3277
- }))
3278
- } }, { status: 400 });
3279
3334
  if (error instanceof SyntaxError || error instanceof HTTPException) return Response.json({ error: {
3280
3335
  code: "invalid_request",
3281
3336
  message: "The request could not be parsed"
3282
3337
  } }, { status: error instanceof HTTPException ? error.status : 400 });
3283
- console.error("Kanvil request failed", { name: error.name });
3284
- return Response.json({ error: {
3285
- code: "internal_error",
3286
- message: "The request could not be completed"
3287
- } }, { status: 500 });
3338
+ const retry = error instanceof ApiError && error.status === 429 && error.details && typeof error.details === "object" && "retryAfterSeconds" in error.details ? String(error.details.retryAfterSeconds) : void 0;
3339
+ const envelope = errorEnvelope(error);
3340
+ return Response.json(envelope.body, {
3341
+ status: envelope.status,
3342
+ headers: retry ? { "Retry-After": retry } : {}
3343
+ });
3288
3344
  });
3289
3345
  app.notFound(() => Response.json({ error: {
3290
3346
  code: "not_found",
@@ -3383,6 +3439,7 @@ async function createApplication(database, config, overrides = {}) {
3383
3439
  app.use("/api/v1/*", async (c, next) => {
3384
3440
  access.checkOrigin(c.req.raw);
3385
3441
  const actor = await access.authenticate(c.req.raw);
3442
+ if (intakeOnly(actor) && !(c.req.method === "POST" && /^\/api\/v1\/projects\/[^/]+\/tickets$/.test(c.req.path))) fail(403, "insufficient_scope", "Intake credentials can only create tickets");
3386
3443
  c.set("actor", actor);
3387
3444
  if (!actor.machine && ![
3388
3445
  "GET",
@@ -3757,22 +3814,13 @@ async function createApplication(database, config, overrides = {}) {
3757
3814
  blobs,
3758
3815
  search,
3759
3816
  realtime,
3760
- close: async () => {
3761
- await imports.close();
3762
- unsubscribeJobs();
3763
- await realtime.close();
3764
- await search.close();
3765
- await blobs.close();
3766
- await integrationDatabase.pool.end();
3767
- await outbox.close();
3768
- await reminders.close();
3769
- }
3817
+ close: cleanup.close
3770
3818
  };
3771
3819
  }
3772
3820
  //#endregion
3773
3821
  //#region src/index.ts
3774
3822
  const config = readConfig();
3775
- const database = createDatabase(config.DATABASE_URL);
3823
+ const database = createDatabase(config);
3776
3824
  await migrateDatabase(database);
3777
3825
  const application = await createApplication(database, config);
3778
3826
  const server = serve({
@@ -1,7 +1,7 @@
1
- import { n as createDatabase, t as readConfig } from "./config-DOT1SDQO.mjs";
1
+ import { n as createDatabase, t as readConfig } from "./config-DKuNQY5b.mjs";
2
2
  import { t as migrateDatabase } from "./migrate-C9f4ozB4.mjs";
3
3
  //#region src/cli/migrate.ts
4
- const database = createDatabase(readConfig().DATABASE_URL);
4
+ const database = createDatabase(readConfig());
5
5
  try {
6
6
  await migrateDatabase(database);
7
7
  console.info("Kanvil database is current.");
@@ -1,6 +1,6 @@
1
- import { d as user, n as createDatabase, r as account, t as readConfig, u as session } from "./config-DOT1SDQO.mjs";
2
- import { A as hasAdminRole } from "./src-VxR5jZiK.mjs";
3
- import { W as instanceSettings, f as fail, t as createAuth } from "./auth-CnAQzKyq.mjs";
1
+ import { d as user, n as createDatabase, r as account, t as readConfig, u as session } from "./config-DKuNQY5b.mjs";
2
+ import { A as hasAdminRole } from "./src-DptOUNRd.mjs";
3
+ import { K as instanceSettings, p as fail, t as createAuth } from "./auth-Cs_whzgk.mjs";
4
4
  import { and, eq, isNull, sql } from "drizzle-orm";
5
5
  import { z } from "zod";
6
6
  import { parseArgs } from "node:util";
@@ -56,7 +56,7 @@ const password = process.stdin.isTTY ? await passwordPrompt({
56
56
  validate: (value) => localPasswordSchema.safeParse(value).success || "Use 12 to 128 characters"
57
57
  }) : readFileSync(0, "utf8").replace(/\r?\n$/, "");
58
58
  const config = readConfig();
59
- const database = createDatabase(config.DATABASE_URL);
59
+ const database = createDatabase(config);
60
60
  try {
61
61
  await recoverLocalPassword(database, config, values.email, password, values["enable-password-sign-in"]);
62
62
  if (values["enable-password-sign-in"]) console.info("Instance password sign-in enabled.");
@@ -1548,6 +1548,10 @@ var en_US_default = {
1548
1548
  custom: "Custom permissions"
1549
1549
  },
1550
1550
  permission: {
1551
+ intake: {
1552
+ name: "Submit reports",
1553
+ description: "Create quarantined tickets only. Cannot read or edit tickets. Use alone."
1554
+ },
1551
1555
  read: {
1552
1556
  name: "Read tickets",
1553
1557
  description: "View projects, tickets, comments, and attachments."
@@ -2844,7 +2848,8 @@ const scopes = [
2844
2848
  "read",
2845
2849
  "write",
2846
2850
  "claim",
2847
- "admin"
2851
+ "admin",
2852
+ "intake"
2848
2853
  ];
2849
2854
  const attachmentLimit = 1e7;
2850
2855
  const stagedUploadLifetime = 864e5;
@@ -3037,10 +3042,13 @@ function rejectConflicts(filter, context) {
3037
3042
  });
3038
3043
  }
3039
3044
  }
3040
- const ticketSelection = z.array(z.object({
3045
+ const selectedTicket = z.object({
3041
3046
  ticketId: id,
3042
3047
  expectedVersion: version
3043
- })).min(1).max(100).refine((values) => new Set(values.map((v) => v.ticketId)).size === values.length, "Tickets must be unique");
3048
+ });
3049
+ const selectionOf = (item) => z.array(item).min(1).max(100).refine((values) => new Set(values.map((v) => v.ticketId)).size === values.length, "Tickets must be unique");
3050
+ const ticketSelection = selectionOf(selectedTicket);
3051
+ const claimedTicketSelection = selectionOf(selectedTicket.extend({ claimId: z.uuid().optional() }));
3044
3052
  const ticketFilter = z.object({
3045
3053
  q: z.string().trim().max(200).optional(),
3046
3054
  ...filterShape,
@@ -3257,6 +3265,26 @@ const ticketSchema = z.object({
3257
3265
  createdAt: timestamp,
3258
3266
  updatedAt: timestamp
3259
3267
  });
3268
+ const ticketSummarySchema = ticketSchema.pick({
3269
+ id: true,
3270
+ key: true,
3271
+ number: true,
3272
+ projectId: true,
3273
+ title: true,
3274
+ status: true,
3275
+ stateId: true,
3276
+ kind: true,
3277
+ difficulty: true,
3278
+ priority: true,
3279
+ labels: true,
3280
+ scopes: true,
3281
+ archived: true,
3282
+ automationReady: true,
3283
+ updatedAt: true,
3284
+ version: true
3285
+ });
3286
+ const ticketView = { view: z.enum(["summary", "full"]).default("full") };
3287
+ const ticketRow = z.union([ticketSchema, ticketSummarySchema]);
3260
3288
  const snapshotSchema = z.object({
3261
3289
  project: projectSchema,
3262
3290
  tickets: z.array(ticketSchema),
@@ -3292,8 +3320,13 @@ const relationSchema = z.object({
3292
3320
  ]),
3293
3321
  sourceKey: z.string().optional(),
3294
3322
  sourceTitle: z.string().optional(),
3323
+ sourceProjectId: z.uuid().optional(),
3324
+ sourceProjectKey: z.string().optional(),
3295
3325
  targetKey: z.string().optional(),
3296
- targetTitle: z.string().optional()
3326
+ targetTitle: z.string().optional(),
3327
+ targetProjectId: z.uuid().optional(),
3328
+ targetProjectKey: z.string().optional(),
3329
+ restricted: z.boolean().optional()
3297
3330
  });
3298
3331
  const relationDirections = z.enum(["outgoing", "incoming"]);
3299
3332
  const relationLabel = (type, outgoing) => t(`common.relation.${type}.${outgoing ? "outgoing" : "incoming"}`);
@@ -3440,14 +3473,15 @@ const searchResultSchema = z.object({
3440
3473
  type: z.enum(searchResultTypes),
3441
3474
  id: z.string(),
3442
3475
  project: projectRef,
3443
- ticket: z.object({
3476
+ ticket: z.union([ticketSummarySchema, z.object({
3444
3477
  id: z.uuid(),
3445
3478
  key: z.string(),
3446
3479
  title: z.string(),
3447
3480
  status: z.enum(states)
3448
- }).nullable(),
3481
+ })]).nullable(),
3449
3482
  title: z.string(),
3450
3483
  excerpt: z.string(),
3484
+ snippet: z.string().optional(),
3451
3485
  actorId: z.string().nullable(),
3452
3486
  at: timestamp
3453
3487
  });
@@ -3789,7 +3823,7 @@ const credentialProjectsSchema = z.object({ projectIds: z.array(z.uuid()).max(1e
3789
3823
  const credentialInput = z.object({
3790
3824
  name: z.string().trim().min(1).max(100),
3791
3825
  projectIds: credentialProjectsSchema.shape.projectIds.refine((ids) => ids === null || ids.length > 0),
3792
- scopes: z.array(z.enum(scopes)).min(1),
3826
+ scopes: z.array(z.enum(scopes)).min(1).refine((values) => !values.includes("intake") || values.every((value) => value === "intake"), "Intake cannot be combined with other scopes"),
3793
3827
  expiresIn: z.number().int().min(3600).max(31536e3).nullable().default(7776e3)
3794
3828
  }).strict();
3795
3829
  z.object({
@@ -4364,8 +4398,9 @@ const operations = {
4364
4398
  relatedTo: id.optional(),
4365
4399
  stateId: z.uuid().optional(),
4366
4400
  scheduledFrom: timestamp.optional(),
4367
- scheduledTo: timestamp.optional()
4368
- }).superRefine(rejectConflicts), page(ticketSchema), "GET", "/projects/{projectId}/tickets", "read", "Search tickets with server-side filters, stable ordering, and pagination. Each property filter takes one value or a repeated list (any of them); its `Not` twin excludes values."),
4401
+ scheduledTo: timestamp.optional(),
4402
+ ...ticketView
4403
+ }).superRefine(rejectConflicts), page(ticketRow), "GET", "/projects/{projectId}/tickets", "read", "Search tickets with server-side filters, stable ordering, and pagination. Each property filter takes one value or a repeated list (any of them); its `Not` twin excludes values. Double-quoted segments of `q` match as exact phrases. Pass `view: \"summary\"` when scanning results: rows then omit the description and acceptance criteria."),
4369
4404
  list_ticket_labels: op(projectInput.extend({ archived: ticketFilter.shape.archived }), z.array(z.object({
4370
4405
  name: z.string(),
4371
4406
  count: z.number().int().nonnegative()
@@ -4489,6 +4524,11 @@ const operations = {
4489
4524
  changes: ticketChanges,
4490
4525
  claimId: z.uuid().optional()
4491
4526
  }).strict(), ticketSchema, "PATCH", "/projects/{projectId}/tickets/{ticketId}", "write", "Update a ticket with optimistic concurrency. Worker completion requires its active claim. `changes.customFields` sets only the named fields; null clears one."),
4527
+ update_ticket_labels: op(ticketInput.extend({
4528
+ ...mutation,
4529
+ expectedVersion: version,
4530
+ labels: tags
4531
+ }).strict(), ticketSchema, "PATCH", "/projects/{projectId}/tickets/{ticketId}/labels", "admin", "Replace one ticket's labels with optimistic concurrency, for label administration and triage. Takes no worker claim and leaves any claim, ownership requirement, readiness, and every other field unchanged; ordinary edits use update_ticket. Reuse requestId when retrying this write."),
4492
4532
  move_ticket: op(ticketInput.extend({
4493
4533
  ...mutation,
4494
4534
  expectedVersion: version,
@@ -4509,16 +4549,16 @@ const operations = {
4509
4549
  destinationProjectId: id,
4510
4550
  status: z.enum(states).optional(),
4511
4551
  stateId: z.uuid().optional()
4512
- }).strict(), ticketSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/relocate", "write", "Move a ticket to another project of the same workspace. It keeps its ID, comments, attachments, evidence, links, and followers, takes the destination's next number as its new key, and its previous key keeps resolving. A claim, a parent or children, relationships, an assignee without destination access, custom field values, or a destination field that is required without a default block the move with `relocation_blocked`; the ticket takes the destination's custom field defaults. Activity before the move stays with the original project. Reuse requestId when retrying this write."),
4552
+ }).strict(), ticketSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/relocate", "write", "Move a ticket to another project of the same workspace. It keeps its ID, comments, attachments, evidence, links, and followers, takes the destination's next number as its new key, and its previous key keeps resolving. A claim (or, for a worker, a ticket that was claimed and has no claim now), a parent or children, relationships, an assignee without destination access, custom field values, or a destination field that is required without a default block the move with `relocation_blocked`; the ticket takes the destination's custom field defaults. Activity before the move stays with the original project. Reuse requestId when retrying this write."),
4513
4553
  resolve_ticket: op(z.object({ ticketId: id }).strict(), z.object({
4514
4554
  ticket: ticketSchema,
4515
4555
  project: projectRef
4516
4556
  }), "GET", "/tickets/resolve", "read", "Find a ticket by ID, current key, or any earlier key from a prefix change or a move, across every accessible project. An unknown or inaccessible ticket reads as not found."),
4517
4557
  bulk_update_tickets: op(projectInput.extend({
4518
4558
  ...mutation,
4519
- tickets: ticketSelection,
4559
+ tickets: claimedTicketSelection,
4520
4560
  changes: ticketChanges
4521
- }).strict(), z.array(ticketSchema), "POST", "/projects/{projectId}/tickets/bulk", "write", "Apply an atomic bulk edit using each ticket's current version."),
4561
+ }).strict(), z.array(ticketSchema), "POST", "/projects/{projectId}/tickets/bulk", "write", "Apply an atomic bulk edit using each ticket's current version. A worker passes each claimed ticket's active `claimId`."),
4522
4562
  delete_ticket: op(ticketInput.extend({
4523
4563
  ...mutation,
4524
4564
  expectedVersion: version
@@ -4530,25 +4570,28 @@ const operations = {
4530
4570
  search_tickets: op(z.object({
4531
4571
  q: z.string().trim().min(1).max(200),
4532
4572
  workspaceId: z.uuid().optional(),
4533
- limit: z.coerce.number().int().min(1).max(50).default(20)
4573
+ limit: z.coerce.number().int().min(1).max(50).default(20),
4574
+ ...ticketView
4534
4575
  }).strict(), z.object({ items: z.array(z.object({
4535
- ticket: ticketSchema,
4536
- project: projectRef
4537
- })) }), "GET", "/tickets/search", "read", "Search unarchived tickets by key or text across every accessible project, optionally within one workspace."),
4576
+ ticket: ticketRow,
4577
+ project: projectRef,
4578
+ snippet: z.string().optional()
4579
+ })) }), "GET", "/tickets/search", "read", "Search unarchived tickets by key or text across every accessible project, optionally within one workspace. Results order by exact key match, then relevance, then recency. Double-quoted segments of `q` match as exact phrases. Pass `view: \"summary\"` when scanning results: each ticket is then a compact row and the result carries a `snippet` of the matching text."),
4538
4580
  search_workspace: op(z.object({
4539
4581
  q: z.string().trim().min(1).max(200),
4540
4582
  workspaceId: z.uuid().optional(),
4541
4583
  type: z.enum(searchResultTypes).optional(),
4542
4584
  ...filterShape,
4543
4585
  limit: z.coerce.number().int().min(1).max(50).default(20),
4544
- offset: z.coerce.number().int().min(0).max(1e3).default(0)
4586
+ offset: z.coerce.number().int().min(0).max(1e3).default(0),
4587
+ ...ticketView
4545
4588
  }).strict().superRefine(rejectConflicts), z.object({
4546
4589
  items: z.array(searchResultSchema),
4547
4590
  total: z.number().int().nonnegative(),
4548
4591
  counts: z.record(z.enum(searchResultTypes), z.number().int().nonnegative()),
4549
4592
  engine: z.enum(["postgres", "index"]),
4550
4593
  indexing: z.boolean()
4551
- }), "GET", "/search", "read", "Search accessible tickets, projects, comments, and activity by text or ticket key, optionally within one workspace or one result type. Property filters restrict results to matching tickets. Results order by exact key match, then type, then recency."),
4594
+ }), "GET", "/search", "read", "Search accessible tickets, projects, comments, and activity by text or ticket key, optionally within one workspace or one result type. Property filters restrict results to matching tickets. Results order by exact key match, then type, then ticket relevance, then recency. Double-quoted segments of `q` match ticket text as exact phrases. Pass `view: \"summary\"` when scanning results: ticket results then carry their summary row and each result a `snippet` of the matching text."),
4552
4595
  list_my_work: op(z.object({
4553
4596
  workspaceId: z.uuid().optional(),
4554
4597
  q: ticketFilter.shape.q,
@@ -4561,17 +4604,17 @@ const operations = {
4561
4604
  ticket: ticketSchema,
4562
4605
  readiness: readinessSchema
4563
4606
  })), "GET", "/projects/{projectId}/eligibility", "read", "Inspect eligibility and explicit blocking reasons for open tickets."),
4564
- list_relations: op(ticketInput, z.array(relationSchema), "GET", "/projects/{projectId}/tickets/{ticketId}/relations", "read", "List incoming and outgoing ticket relationships."),
4607
+ list_relations: op(ticketInput, z.array(relationSchema), "GET", "/projects/{projectId}/tickets/{ticketId}/relations", "read", "List incoming and outgoing ticket relationships with each side's key, title, and project. A ticket in a project the caller cannot read appears by ID and key alone, with `restricted: true`."),
4565
4608
  add_relation: op(ticketInput.extend({
4566
4609
  ...mutation,
4567
- targetId: z.uuid(),
4610
+ targetId: id,
4568
4611
  type: relationSchema.shape.type,
4569
4612
  direction: relationDirections.optional()
4570
- }).strict(), relationSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/relations", "write", "Relate tickets while rejecting dependency cycles."),
4613
+ }).strict(), relationSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/relations", "write", "Relate a ticket to another by ID or key, in this project or any project the caller can read, while rejecting dependency cycles across projects. Blocking or marking as duplicate a ticket of another project also needs write there. Both projects' feeds record the change."),
4571
4614
  remove_relation: op(projectInput.extend({
4572
4615
  ...mutation,
4573
4616
  relationId: z.uuid()
4574
- }).strict(), okSchema, "DELETE", "/projects/{projectId}/relations/{relationId}", "write", "Remove a ticket relationship."),
4617
+ }).strict(), okSchema, "DELETE", "/projects/{projectId}/relations/{relationId}", "write", "Remove a ticket relationship that names a ticket of this project."),
4575
4618
  list_comments: op(ticketInput.extend(pagination), page(commentSchema), "GET", "/projects/{projectId}/tickets/{ticketId}/comments", "read", "Read ticket discussion."),
4576
4619
  add_comment: op(ticketInput.extend({
4577
4620
  ...mutation,
@@ -4927,6 +4970,7 @@ function isOperation(name) {
4927
4970
  return Object.hasOwn(operations, name);
4928
4971
  }
4929
4972
  const errorSchema = z.object({ error: z.object({
4973
+ status: z.number().int().optional(),
4930
4974
  code: z.string(),
4931
4975
  message: z.string(),
4932
4976
  details: z.unknown().optional()
@@ -4952,4 +4996,4 @@ function parseOutput(name, value) {
4952
4996
  return operations[name].output.parse(value);
4953
4997
  }
4954
4998
  //#endregion
4955
- export { permissionLabels as $, hasAdminRole as A, workspaceSchema as At, memberSchema as B, errorSchema as C, streamErrorSchema as Ct, filterKey as D, ticketPredicates as Dt, eventSchema as E, ticketImportSchema as Et, isHexColor as F, photoInputSchema as Ft, notificationReadInput as G, notificationIdsInput as H, isOperation as I, photoResultSchema as It, operationPermission as J, notificationsSchema as K, isPresetColor as L, s3SettingsSchema as Lt, importProviderDefinitions as M, authenticationPolicySchema as Mt, importSecretFields as N, brokerSettingsSchema as Nt, filterList as O, ticketSchema as Ot, invitationStatuses as P, instanceUpdateSchema as Pt, parsePublicOutput as Q, isPublicOperation as R, searchAuthentication as Rt, difficulties as S, states as St, eventLabels as T, ticketFilter as Tt, notificationPreferencesInput as U, mentionScheme as V, notificationPreferencesSchema as W, parseCustomFieldFilter as X, operations as Y, parseOutput as Z, deletedAccountName as _, seededPermissions as _t, checkpointSchema as a, publicOperations as at, destinationEventKeys as b, stagedUploadLifetime as bt, credentialInput as c, repositoryProviderDefinitions as ct, customFieldSchema as d, resourceProvider as dt, permissionScope as et, customFieldValue as f, roleCategory as ft, defaultWorkflow as g, searchResultTypes as gt, defaultOrder as h, scopes as ht, channelRouteAllows as i, projectSchema as it, importLimits as j, adminInstanceSchema as jt, formatEvent as k, workflowOrder as kt, credentialProjectsSchema as l, repositoryProviders as lt, defaultClaimMaxLeaseSeconds as m, roles as mt, attachmentLimit as n, priorities as nt, claimLeaseSecondsSchema as o, pushDeviceInput as ot, customFieldValueSchema as p, roleLabels as pt, okSchema as q, channelKinds as r, priorityLabels as rt, closedStates as s, remoteKinds as st, ApiError as t, permissions as tt, customFieldFilterTokens as u, repositorySecretFields as ut, deliverySecretFields as v, signInMethodLabels as vt, eventCategories as w, ticketExportFormat as wt, destinationKinds as x, stateLabels as xt, deliverySettingsSchema as y, snapshotSchema as yt, isSeededRole as z, searchSettingsSchema as zt };
4999
+ export { permissionLabels as $, hasAdminRole as A, workflowOrder as At, memberSchema as B, searchSettingsSchema as Bt, errorSchema as C, streamErrorSchema as Ct, filterKey as D, ticketPredicates as Dt, eventSchema as E, ticketImportSchema as Et, isHexColor as F, instanceUpdateSchema as Ft, notificationReadInput as G, notificationIdsInput as H, isOperation as I, photoInputSchema as It, operationPermission as J, notificationsSchema as K, isPresetColor as L, photoResultSchema as Lt, importProviderDefinitions as M, adminInstanceSchema as Mt, importSecretFields as N, authenticationPolicySchema as Nt, filterList as O, ticketSchema as Ot, invitationStatuses as P, brokerSettingsSchema as Pt, parsePublicOutput as Q, isPublicOperation as R, s3SettingsSchema as Rt, difficulties as S, states as St, eventLabels as T, ticketFilter as Tt, notificationPreferencesInput as U, mentionScheme as V, notificationPreferencesSchema as W, parseCustomFieldFilter as X, operations as Y, parseOutput as Z, deletedAccountName as _, seededPermissions as _t, checkpointSchema as a, publicOperations as at, destinationEventKeys as b, stagedUploadLifetime as bt, credentialInput as c, repositoryProviderDefinitions as ct, customFieldSchema as d, resourceProvider as dt, permissionScope as et, customFieldValue as f, roleCategory as ft, defaultWorkflow as g, searchResultTypes as gt, defaultOrder as h, scopes as ht, channelRouteAllows as i, projectSchema as it, importLimits as j, workspaceSchema as jt, formatEvent as k, ticketSummarySchema as kt, credentialProjectsSchema as l, repositoryProviders as lt, defaultClaimMaxLeaseSeconds as m, roles as mt, attachmentLimit as n, priorities as nt, claimLeaseSecondsSchema as o, pushDeviceInput as ot, customFieldValueSchema as p, roleLabels as pt, okSchema as q, channelKinds as r, priorityLabels as rt, closedStates as s, remoteKinds as st, ApiError as t, permissions as tt, customFieldFilterTokens as u, repositorySecretFields as ut, deliverySecretFields as v, signInMethodLabels as vt, eventCategories as w, ticketExportFormat as wt, destinationKinds as x, stateLabels as xt, deliverySettingsSchema as y, snapshotSchema as yt, isSeededRole as z, searchAuthentication as zt };
@@ -1 +1 @@
1
- import{Ga as e,_a as t,ba as n,ya as r}from"./search-B3-w3oCd.js";import{i}from"./forms-BbWAl0BZ.js";var a={name:`chevrons-up-down`,size:24,node:[[`path`,{d:`m7 15 5 5 5-5`,key:`1hf1tw`}],[`path`,{d:`m7 9 5-5 5 5`,key:`sgt6xg`}]]};a.node;var o=e(a),s={name:`pencil`,size:24,node:[[`path`,{d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z`,key:`1a8usu`}],[`path`,{d:`m15 5 4 4`,key:`1mk7zo`}]]};s.node;var c=e(s),l={name:`trash`,size:24,node:[[`path`,{d:`M10 11v6`,key:`nco0om`}],[`path`,{d:`M14 11v6`,key:`outv1u`}],[`path`,{d:`M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6`,key:`miytrc`}],[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`,key:`e791ji`}]],aliases:[`trash-2`]};l.node;var u=e(l);function d(e){let{render:a,className:o,style:s,state:c=n,props:l=r,refs:u=r,metadata:d,stateAttributesMapping:f,tag:p=`div`,...m}=e,{compositeProps:h,compositeRef:g}=i({metadata:d});return t(p,e,{state:c,ref:[g,...u],props:[h,...l,m],stateAttributesMapping:f})}export{o as i,u as n,c as r,d as t};
1
+ import{Ga as e,_a as t,ba as n,ya as r}from"./search-Ble6WSEU.js";import{i}from"./forms-BlEL8C_P.js";var a={name:`chevrons-up-down`,size:24,node:[[`path`,{d:`m7 15 5 5 5-5`,key:`1hf1tw`}],[`path`,{d:`m7 9 5-5 5 5`,key:`sgt6xg`}]]};a.node;var o=e(a),s={name:`pencil`,size:24,node:[[`path`,{d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z`,key:`1a8usu`}],[`path`,{d:`m15 5 4 4`,key:`1mk7zo`}]]};s.node;var c=e(s),l={name:`trash`,size:24,node:[[`path`,{d:`M10 11v6`,key:`nco0om`}],[`path`,{d:`M14 11v6`,key:`outv1u`}],[`path`,{d:`M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6`,key:`miytrc`}],[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`,key:`e791ji`}]],aliases:[`trash-2`]};l.node;var u=e(l);function d(e){let{render:a,className:o,style:s,state:c=n,props:l=r,refs:u=r,metadata:d,stateAttributesMapping:f,tag:p=`div`,...m}=e,{compositeProps:h,compositeRef:g}=i({metadata:d});return t(p,e,{state:c,ref:[g,...u],props:[h,...l,m],stateAttributesMapping:f})}export{o as i,u as n,c as r,d as t};