@maudecode/kanvil 0.6.1 → 0.8.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 (134) hide show
  1. package/apps/server/dist/{auth-CFjetlB4.mjs → auth-CVnX2XIg.mjs} +3828 -3463
  2. package/apps/server/dist/client.mjs +2 -2
  3. package/apps/server/dist/{config-BQw6mPzE.mjs → config-DUIdB3c2.mjs} +1 -1
  4. package/apps/server/dist/index.mjs +165 -43
  5. package/apps/server/dist/migrate.mjs +1 -1
  6. package/apps/server/dist/recover.mjs +3 -3
  7. package/apps/server/dist/{src-BaTNDJB7.mjs → src-CC6lPIU_.mjs} +190 -35
  8. package/apps/server/drizzle/0040_upload_urls.sql +20 -0
  9. package/apps/server/drizzle/0041_project_repositories.sql +2 -0
  10. package/apps/server/drizzle/0042_public_boards.sql +37 -0
  11. package/apps/server/drizzle/0043_epics.sql +18 -0
  12. package/apps/server/drizzle/meta/0040_snapshot.json +7170 -0
  13. package/apps/server/drizzle/meta/0041_snapshot.json +7183 -0
  14. package/apps/server/drizzle/meta/0042_snapshot.json +7315 -0
  15. package/apps/server/drizzle/meta/0043_snapshot.json +7466 -0
  16. package/apps/server/drizzle/meta/_journal.json +28 -0
  17. package/apps/web/dist/assets/{CompositeItem-Dotj8JZL.js → CompositeItem-GgAhkI4m.js} +1 -1
  18. package/apps/web/dist/assets/{Trans-CE4EAAY1.js → Trans-CT9xiBED.js} +1 -1
  19. package/apps/web/dist/assets/admin-settings-C7yxWm-w.js +1 -0
  20. package/apps/web/dist/assets/{archive-restore-BMtgjFUu.js → archive-restore-UgUTJyLs.js} +1 -1
  21. package/apps/web/dist/assets/az-AZ-IkRP1MKo.js +1 -0
  22. package/apps/web/dist/assets/{az-B-DxMc__.js → az-CZ46yOUl.js} +1 -1
  23. package/apps/web/dist/assets/configuration-CU0QOaRw.js +1 -0
  24. package/apps/web/dist/assets/{corner-left-up-O4OsTW-L.js → corner-left-up-BFY7AqdS.js} +1 -1
  25. package/apps/web/dist/assets/{de-Btbkq-aX.js → de-CseN-3gF.js} +1 -1
  26. package/apps/web/dist/assets/de-DE-DCGKtpLI.js +1 -0
  27. package/apps/web/dist/assets/default-CN9zcRIy.js +56 -0
  28. package/apps/web/dist/assets/delivery-settings-xx9Bb6Ux.js +1 -0
  29. package/apps/web/dist/assets/el-GR-C5w6p2-z.js +1 -0
  30. package/apps/web/dist/assets/{el-DBVEX16k.js → el-psH0HOR3.js} +1 -1
  31. package/apps/web/dist/assets/{ellipsis-CvEPDi9V.js → ellipsis-CcIJddV1.js} +1 -1
  32. package/apps/web/dist/assets/{es-CMJoxZBk.js → es-BVvGrD23.js} +1 -1
  33. package/apps/web/dist/assets/es-ES-PjJlJCgT.js +1 -0
  34. package/apps/web/dist/assets/{forms-DbfnnPg_.js → forms-BUNl5uNV.js} +1 -1
  35. package/apps/web/dist/assets/{fr-OyEmYoRn.js → fr-C43nghuI.js} +1 -1
  36. package/apps/web/dist/assets/fr-FR-CGWH-Juq.js +1 -0
  37. package/apps/web/dist/assets/{hi-CIgXSseB.js → hi-Ds3DGtT-.js} +1 -1
  38. package/apps/web/dist/assets/hi-IN-B3bq8hDJ.js +1 -0
  39. package/apps/web/dist/assets/{highlight-B4K3yEEh.js → highlight-BPZqoBaO.js} +1 -1
  40. package/apps/web/dist/assets/id-ID-98H240ET.js +1 -0
  41. package/apps/web/dist/assets/{id-WV2oSAPB.js → id-tg9awgd8.js} +1 -1
  42. package/apps/web/dist/assets/index-CvFGdLre.js +17 -0
  43. package/apps/web/dist/assets/index-DPT5LAs1.css +2 -0
  44. package/apps/web/dist/assets/{invitations-BTB8f2Xl.js → invitations-fH1M9hYm.js} +1 -1
  45. package/apps/web/dist/assets/{it-BWSrRqus.js → it-DRwaWQyE.js} +1 -1
  46. package/apps/web/dist/assets/it-IT-CNS9cL7I.js +1 -0
  47. package/apps/web/dist/assets/{item-HmFuRZet.js → item-BbaYy50i.js} +1 -1
  48. package/apps/web/dist/assets/ja-JP-BFTqtCdV.js +1 -0
  49. package/apps/web/dist/assets/{ja-BtOHWvUJ.js → ja-jj4Ubayf.js} +1 -1
  50. package/apps/web/dist/assets/{ko-DAryK0HN.js → ko-Dn2pRrar.js} +1 -1
  51. package/apps/web/dist/assets/ko-KR-C58VNccr.js +1 -0
  52. package/apps/web/dist/assets/{label-settings-ssfEX2uR.js → label-settings-CbBHSL0c.js} +1 -1
  53. package/apps/web/dist/assets/{markdown-blocks-B1kO6iku.js → markdown-blocks-B6mE_S6n.js} +1 -1
  54. package/apps/web/dist/assets/{member-directory-BA4ZT1qU.js → member-directory-CoGg7WkW.js} +1 -1
  55. package/apps/web/dist/assets/{mk-DBbiGUk5.js → mk-DQUnXaPE.js} +1 -1
  56. package/apps/web/dist/assets/mk-MK-Bf5RDo6j.js +1 -0
  57. package/apps/web/dist/assets/{nl-D4V-8WdF.js → nl-C_Ijir3O.js} +1 -1
  58. package/apps/web/dist/assets/nl-NL-D1WFJyOY.js +1 -0
  59. package/apps/web/dist/assets/{pl-C--QrxO0.js → pl-DgWXgYL-.js} +1 -1
  60. package/apps/web/dist/assets/pl-PL--jAcwyAK.js +1 -0
  61. package/apps/web/dist/assets/project-lifecycle-BHWCMq7o.js +1 -0
  62. package/apps/web/dist/assets/project-settings-CBG5CZX1.js +1 -0
  63. package/apps/web/dist/assets/pt-BR-CJQfuq9b.js +1 -0
  64. package/apps/web/dist/assets/{pt-BR-DvFVKpmO.js → pt-BR-N-Xvp9fC.js} +1 -1
  65. package/apps/web/dist/assets/public-project-DmTr9q4c.js +1 -0
  66. package/apps/web/dist/assets/{rotate-ccw-CB36Zu3L.js → rotate-ccw-BFYpBtuj.js} +1 -1
  67. package/apps/web/dist/assets/{ru-UmbPUOka.js → ru-Ctq-5FYl.js} +1 -1
  68. package/apps/web/dist/assets/ru-RU-BtvJ8Glt.js +1 -0
  69. package/apps/web/dist/assets/{search-Cvetr1OK.js → search-C2QaoLwI.js} +6 -6
  70. package/apps/web/dist/assets/{send-CJDct4Oj.js → send-qRPqbT2m.js} +1 -1
  71. package/apps/web/dist/assets/settings-BNPajW83.js +1 -0
  72. package/apps/web/dist/assets/{sliders-horizontal-dmqWngxy.js → sliders-horizontal-S16oyCiS.js} +1 -1
  73. package/apps/web/dist/assets/{switch-BtScTpP9.js → switch-C44Ouoqh.js} +1 -1
  74. package/apps/web/dist/assets/{table-byFokzo-.js → table-esnQpDtb.js} +1 -1
  75. package/apps/web/dist/assets/ticket-detail-BVyScTlg.js +2 -0
  76. package/apps/web/dist/assets/ticket-editor-BjuSeeiA.js +2 -0
  77. package/apps/web/dist/assets/ticket-list-BCvUvYju.js +6 -0
  78. package/apps/web/dist/assets/ticket-list-controls-BiC5CIwM.js +1 -0
  79. package/apps/web/dist/assets/{tr-CNpgQ4ka.js → tr-CYT8BT69.js} +1 -1
  80. package/apps/web/dist/assets/tr-TR-Bahy-EKO.js +1 -0
  81. package/apps/web/dist/assets/uk-UA-DDVgxWqX.js +1 -0
  82. package/apps/web/dist/assets/{uk-MQoTXdpk.js → uk-y7QuyfrN.js} +1 -1
  83. package/apps/web/dist/assets/{upload-D1x_ib3B.js → upload-e7CmLHhp.js} +1 -1
  84. package/apps/web/dist/assets/{useQueries-BnlNCoy4.js → useQueries-U3MOyy3t.js} +1 -1
  85. package/apps/web/dist/assets/{vi-CEw4gTWc.js → vi-Bz2g5X1k.js} +1 -1
  86. package/apps/web/dist/assets/vi-VN-CuHCYxNQ.js +1 -0
  87. package/apps/web/dist/assets/{visual-editor-CpdMRn3e.js → visual-editor-UmVaDXWu.js} +1 -1
  88. package/apps/web/dist/assets/workspace-settings-BiWJqW0m.js +1 -0
  89. package/apps/web/dist/assets/zh-CN-C94sYOI_.js +1 -0
  90. package/apps/web/dist/assets/{zh-CN-ChzoXkfr.js → zh-CN-y_sluDUm.js} +1 -1
  91. package/apps/web/dist/assets/zh-TW-BoRQfGQL.js +1 -0
  92. package/apps/web/dist/assets/{zh-TW-bWBKARrK.js → zh-TW-DIEfy239.js} +1 -1
  93. package/apps/web/dist/index.html +8 -8
  94. package/docs/api.md +63 -5
  95. package/docs/cli.md +1 -1
  96. package/docs/mobile.md +1 -1
  97. package/docs/plan.md +1 -0
  98. package/docs/releasing.md +1 -1
  99. package/package.json +1 -1
  100. package/apps/web/dist/assets/admin-settings-CO7nnD9T.js +0 -1
  101. package/apps/web/dist/assets/az-AZ-R8_HFWJr.js +0 -1
  102. package/apps/web/dist/assets/configuration-Bnt72HRu.js +0 -1
  103. package/apps/web/dist/assets/de-DE-BHgMj19Y.js +0 -1
  104. package/apps/web/dist/assets/default-DqsN66dq.js +0 -56
  105. package/apps/web/dist/assets/delivery-settings-Cji0N0m0.js +0 -1
  106. package/apps/web/dist/assets/el-GR-CQFGM5Sr.js +0 -1
  107. package/apps/web/dist/assets/es-ES-DZkcRVA4.js +0 -1
  108. package/apps/web/dist/assets/fr-FR-CRPAezXU.js +0 -1
  109. package/apps/web/dist/assets/hi-IN-2YFE4UQk.js +0 -1
  110. package/apps/web/dist/assets/id-ID-B-J0_QIW.js +0 -1
  111. package/apps/web/dist/assets/index-BAnwOrh1.css +0 -2
  112. package/apps/web/dist/assets/index-nltuyQtq.js +0 -17
  113. package/apps/web/dist/assets/it-IT-CJ0HYggO.js +0 -1
  114. package/apps/web/dist/assets/ja-JP-DDQS7D7b.js +0 -1
  115. package/apps/web/dist/assets/ko-KR-CkpoewEa.js +0 -1
  116. package/apps/web/dist/assets/mk-MK-C0RhS6XR.js +0 -1
  117. package/apps/web/dist/assets/nl-NL-BSKqlzcL.js +0 -1
  118. package/apps/web/dist/assets/pl-PL-CHz38SiU.js +0 -1
  119. package/apps/web/dist/assets/project-lifecycle-COZb9FqM.js +0 -1
  120. package/apps/web/dist/assets/project-settings-DwmTm9KA.js +0 -1
  121. package/apps/web/dist/assets/pt-BR-BotWHRiO.js +0 -1
  122. package/apps/web/dist/assets/public-project-B3hEkeCF.js +0 -1
  123. package/apps/web/dist/assets/ru-RU-DtOwNna6.js +0 -1
  124. package/apps/web/dist/assets/settings-3IXm9lJ-.js +0 -1
  125. package/apps/web/dist/assets/ticket-detail-C3YA3Hzc.js +0 -2
  126. package/apps/web/dist/assets/ticket-editor-CZnZQMG1.js +0 -2
  127. package/apps/web/dist/assets/ticket-list-BXUorfmj.js +0 -6
  128. package/apps/web/dist/assets/ticket-list-controls-BxGKCZMo.js +0 -1
  129. package/apps/web/dist/assets/tr-TR-CdG2alU2.js +0 -1
  130. package/apps/web/dist/assets/uk-UA-Ceu5gorw.js +0 -1
  131. package/apps/web/dist/assets/vi-VN-CxshgV4W.js +0 -1
  132. package/apps/web/dist/assets/workspace-settings-BickBtZx.js +0 -1
  133. package/apps/web/dist/assets/zh-CN-D7Yrjhd3.js +0 -1
  134. package/apps/web/dist/assets/zh-TW-DyF38kqx.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-BaTNDJB7.mjs";
1
+ import { Dt as streamErrorSchema, G as notificationIdsInput, J as notificationReadInput, K as notificationPreferencesInput, O as eventSchema, Q as operations, St as snapshotSchema, T as errorSchema, X as okSchema, Y as notificationsSchema, ct as publicOperations, et as parseOutput, o as checkpointSchema, q as notificationPreferencesSchema, t as ApiError, tt as parsePublicOutput } from "./src-CC6lPIU_.mjs";
2
2
  import { z } from "zod";
3
3
  import { parseArgs } from "node:util";
4
4
  import { constants } from "node:fs";
@@ -653,7 +653,7 @@ profile list | profile remove <name> --yes
653
653
  --input <json|-> Operation input as a JSON object; - reads stdin
654
654
  --title <text> Ticket title
655
655
  --body <markdown> Comment body
656
- --expected-version <n> Required version for update, delete, acquire and rescheduling recovery
656
+ --expected-version <n> Required version for update, delete and rescheduling recovery; optional for acquire
657
657
  --changes <json> Ticket update changes as a JSON object
658
658
  --request-id <uuid> Reuse for a deliberate retry across invocations
659
659
  --json Compact JSON output (default: indented JSON)
@@ -1,4 +1,4 @@
1
- import { Bt as searchSettingsSchema, Pt as brokerSettingsSchema, Rt as s3SettingsSchema, m as defaultClaimMaxLeaseSeconds, o as claimLeaseSecondsSchema } from "./src-BaTNDJB7.mjs";
1
+ import { Gt as searchSettingsSchema, Ut as s3SettingsSchema, g as defaultClaimMaxLeaseSeconds, s as claimLeaseSecondsSchema, zt as brokerSettingsSchema } from "./src-CC6lPIU_.mjs";
2
2
  import pg from "pg";
3
3
  import { drizzle } from "drizzle-orm/node-postgres";
4
4
  import { relations } from "drizzle-orm";
@@ -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-BQw6mPzE.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-BaTNDJB7.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-DUIdB3c2.mjs";
2
+ import { B as isPublicOperation, Bt as instanceUpdateSchema, G as notificationIdsInput, Gt as searchSettingsSchema, H as memberSchema, Ht as photoResultSchema, J as notificationReadInput, K as notificationPreferencesInput, Lt as adminInstanceSchema, M as hasAdminRole, N as importLimits, Ot as ticketExportFormat, P as importProviderDefinitions, Q as operations, R as isOperation, T as errorSchema, Tt as stateLabels, Ut as s3SettingsSchema, Vt as photoInputSchema, X as okSchema, _t as roles, ct as publicOperations, l as closedStates, lt as pushDeviceInput, q as notificationPreferencesSchema, t as ApiError, u as credentialInput, v as defaultWorkflow, vt as scopes, z as isPresetColor, zt as brokerSettingsSchema } from "./src-CC6lPIU_.mjs";
3
3
  import { t as migrateDatabase } from "./migrate-C9f4ozB4.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-CFjetlB4.mjs";
4
+ import { $ as pushDevices, A as createWriteLimiter, B as createPollingJob, C as receiveRepositoryWebhook, D as createHttpSender, E as DeliveryError, F as createBlobStorage, G as eventClock, H as attachments, I as decodeImage, J as members, K as events, L as imageResponse, M as requireSession, N as createTicketSearch, O as receiveUpload, P as createTicketIndex, Q as projects, R as stagedUploadCutoff, S as readCalendarFeed, T as deliverBatch, U as blobObjects, V as attachmentContent, W as deliveryCheckpoints, X as notifications, Y as notificationPreferences, Z as profilePhotos, _ as one, a as deliverySecrets, at as workspaces, b as requireRow, c as ownerBlocker, d as createDomain, et as stagedUploads, f as errorEnvelope, g as notify, h as json, i as createInstance, it as workspaceMembers, j as requireInstanceAdmin, k as createAccess, l as appendEvents, m as intakeOnly, n as emailCodesAvailable, nt as subscriptions, o as otherActiveAdministrator, ot as matchesOIDCBinding, p as fail, r as signInProviders, rt as tickets, s as ownedWorkspaces, t as createAuth, tt as storageBackends, u as assertVersion, v as reachable, w as defaultSenders, x as processImports, y as recipientColumns, z as createObjectStore } from "./auth-CVnX2XIg.mjs";
5
5
  import { serve } from "@hono/node-server";
6
6
  import pg from "pg";
7
7
  import { drizzle } from "drizzle-orm/node-postgres";
@@ -359,7 +359,7 @@ function createCleanup() {
359
359
  }
360
360
  //#endregion
361
361
  //#region src/version.ts
362
- const applicationVersion = "0.6.1";
362
+ const applicationVersion = "0.8.0";
363
363
  //#endregion
364
364
  //#region src/realtime.ts
365
365
  async function createRealtime(config) {
@@ -3121,49 +3121,143 @@ const defaultAdapters = {
3121
3121
  };
3122
3122
  //#endregion
3123
3123
  //#region src/mcp.ts
3124
+ const operationNames = Object.keys(operations).filter(isOperation);
3125
+ const coreNames = operationNames.filter((name) => operations[name].mcp === "core");
3126
+ const instructions = "Ticket keys look like KAN-12, and a key's prefix or the project's linked git remote works as projectId. To work a ticket: claim_ticket, keep its status current with update_ticket, add_comment on what you did, add_evidence, then set status done and release_claim. Leave tickets with automationReady false to people. The core tools are listed. Reach every other operation with find_operation, then read_operation for reads or run_operation for writes. In replies, a missing key means null or empty; lists return summary rows unless view is full.";
3127
+ const defaultView = "summary";
3128
+ const toolInput = (name) => {
3129
+ const input = operations[name].input;
3130
+ return "view" in input.shape ? input.safeExtend({ view: z.enum([defaultView, "full"]).default(defaultView) }) : input;
3131
+ };
3132
+ const echoedFields = (name) => {
3133
+ const { shape } = toolInput(name);
3134
+ const { changes } = shape;
3135
+ return ["description", "acceptanceCriteria"].filter((field) => field in shape || changes instanceof z.ZodObject && field in changes.shape);
3136
+ };
3137
+ const isEmpty = (value) => value === null || typeof value === "object" && Object.keys(value).length === 0;
3138
+ const compact = (value, echoed) => {
3139
+ if (Array.isArray(value)) return value.map((item) => compact(item, echoed));
3140
+ if (typeof value !== "object" || value === null) return value;
3141
+ const dropped = "acceptanceCriteria" in value ? echoed : [];
3142
+ return Object.fromEntries(Object.entries(value).filter(([key]) => !dropped.includes(key)).map(([key, field]) => [key, compact(field, echoed)]).filter(([, field]) => !isEmpty(field)));
3143
+ };
3144
+ const known = (name) => {
3145
+ if (!isOperation(name)) throw new ApiError(404, "not_found", `No operation is named ${name}`);
3146
+ return name;
3147
+ };
3148
+ const refuseUnknownKeys = (name, input) => {
3149
+ const { shape } = toolInput(name);
3150
+ const unknown = Object.keys(input).filter((key) => !Object.hasOwn(shape, key));
3151
+ if (unknown.length > 0) throw new ApiError(400, "invalid_request", `${name} does not take ${unknown.join(", ")}. Valid keys: ${Object.keys(shape).join(", ") || "none"}.`);
3152
+ };
3153
+ const summary = (name) => ({
3154
+ name,
3155
+ description: operations[name].description,
3156
+ access: operations[name].method === "GET" ? "read" : "write"
3157
+ });
3158
+ function findOperation({ query = "", name }) {
3159
+ if (name !== void 0) {
3160
+ const operation = known(name), spec = operations[operation];
3161
+ return {
3162
+ ...summary(operation),
3163
+ method: spec.method,
3164
+ scope: spec.scope,
3165
+ inputSchema: z.toJSONSchema(toolInput(operation), { io: "input" })
3166
+ };
3167
+ }
3168
+ const terms = query.toLowerCase().split(/\s+/);
3169
+ return { operations: operationNames.map(summary).filter((row) => terms.every((term) => `${row.name} ${row.description}`.toLowerCase().includes(term))) };
3170
+ }
3171
+ const reply = async (name, work) => {
3172
+ try {
3173
+ return {
3174
+ content: [{
3175
+ type: "text",
3176
+ text: `${name} completed.`
3177
+ }],
3178
+ structuredContent: { result: await work() }
3179
+ };
3180
+ } catch (error) {
3181
+ return {
3182
+ isError: true,
3183
+ content: [{
3184
+ type: "text",
3185
+ text: JSON.stringify(errorEnvelope(error).body)
3186
+ }]
3187
+ };
3188
+ }
3189
+ };
3190
+ const runnerInput = {
3191
+ name: z.string().max(80).describe("Operation name from find_operation."),
3192
+ input: z.record(z.string(), z.unknown()).default({}).describe("The operation's input.")
3193
+ };
3124
3194
  function createMcp(auth, domain, access, config, limitWrite) {
3125
- const serve = (request, identity, limited = false) => createMcpHandler(() => {
3126
- const server = new McpServer({
3127
- name: "Kanvil",
3128
- version: applicationVersion
3195
+ const serve = (request, identity, limited = false) => {
3196
+ const execute = async (name, input) => {
3197
+ refuseUnknownKeys(name, input);
3198
+ if (limited && operations[name].method !== "GET") limitWrite(identity.ownerId);
3199
+ const value = "view" in operations[name].input.shape ? {
3200
+ view: defaultView,
3201
+ ...input
3202
+ } : input;
3203
+ return compact(await domain.execute(name, value, identity), echoedFields(name));
3204
+ };
3205
+ const runner = (write) => (args) => reply(args.name, () => {
3206
+ const name = known(args.name);
3207
+ if (operations[name].method !== "GET" !== write) throw new ApiError(400, "invalid_request", `${name} ${write ? "only reads; use read_operation" : "writes; use run_operation"}`);
3208
+ return execute(name, args.input);
3129
3209
  });
3130
- for (const name of Object.keys(operations).filter(isOperation)) {
3131
- const spec = operations[name];
3132
- const inputSchema = spec.input.shape;
3133
- server.registerTool(name, {
3134
- description: spec.description,
3135
- inputSchema,
3136
- outputSchema: { result: spec.output },
3210
+ return createMcpHandler(() => {
3211
+ const server = new McpServer({
3212
+ name: "Kanvil",
3213
+ version: applicationVersion
3214
+ }, { instructions });
3215
+ for (const name of coreNames) {
3216
+ const spec = operations[name];
3217
+ server.registerTool(name, {
3218
+ description: spec.description,
3219
+ inputSchema: toolInput(name).loose(),
3220
+ annotations: {
3221
+ readOnlyHint: spec.method === "GET",
3222
+ destructiveHint: false,
3223
+ idempotentHint: spec.method === "GET",
3224
+ openWorldHint: false
3225
+ }
3226
+ }, (input) => reply(name, () => execute(name, input)));
3227
+ }
3228
+ server.registerTool("find_operation", {
3229
+ description: "Find an operation that is not a listed tool. `query` matches names and descriptions and marks each match as read or write; `name` returns one operation with its input JSON Schema, method, and scope.",
3230
+ inputSchema: {
3231
+ query: z.string().max(200).optional(),
3232
+ name: z.string().max(80).optional()
3233
+ },
3137
3234
  annotations: {
3138
- readOnlyHint: spec.method === "GET",
3139
- destructiveHint: /^(delete|remove|recover)_/.test(name),
3140
- idempotentHint: spec.method === "GET",
3235
+ readOnlyHint: true,
3236
+ idempotentHint: true,
3141
3237
  openWorldHint: false
3142
3238
  }
3143
- }, async (input) => {
3144
- try {
3145
- if (limited && spec.method !== "GET") limitWrite(identity.ownerId);
3146
- const result = await domain.execute(name, input, identity);
3147
- return {
3148
- content: [{
3149
- type: "text",
3150
- text: `${name} completed.`
3151
- }],
3152
- structuredContent: { result }
3153
- };
3154
- } catch (error) {
3155
- return {
3156
- isError: true,
3157
- content: [{
3158
- type: "text",
3159
- text: JSON.stringify(errorEnvelope(error).body)
3160
- }]
3161
- };
3239
+ }, (input) => reply("find_operation", () => findOperation(input)));
3240
+ server.registerTool("read_operation", {
3241
+ description: "Run a read operation found with find_operation.",
3242
+ inputSchema: runnerInput,
3243
+ annotations: {
3244
+ readOnlyHint: true,
3245
+ idempotentHint: true,
3246
+ openWorldHint: false
3162
3247
  }
3163
- });
3164
- }
3165
- return server;
3166
- }, { legacy: "stateless" }).fetch(request);
3248
+ }, runner(false));
3249
+ server.registerTool("run_operation", {
3250
+ description: "Run a write operation found with find_operation.",
3251
+ inputSchema: runnerInput,
3252
+ annotations: {
3253
+ readOnlyHint: false,
3254
+ destructiveHint: true,
3255
+ openWorldHint: false
3256
+ }
3257
+ }, runner(true));
3258
+ return server;
3259
+ }, { legacy: "stateless" }).fetch(request);
3260
+ };
3167
3261
  const oauth = requireMcpAuth(auth, async (request, claims) => {
3168
3262
  if (typeof claims.sub !== "string") throw new ApiError(401, "unauthorized", "A user subject is required");
3169
3263
  const granted = typeof claims.scope === "string" ? claims.scope.split(" ") : [];
@@ -3319,13 +3413,14 @@ async function initializeApplication(database, config, adapters, cleanup) {
3319
3413
  } }, { status: 400 });
3320
3414
  } });
3321
3415
  app.use("*", secureHeaders());
3322
- app.use("*", bodyLimit({
3416
+ const limitBody = bodyLimit({
3323
3417
  maxSize: 15e6,
3324
3418
  onError: () => Response.json({ error: {
3325
3419
  code: "payload_too_large",
3326
3420
  message: "The request exceeds the upload limit"
3327
3421
  } }, { status: 413 })
3328
- }));
3422
+ });
3423
+ app.use("*", (c, next) => c.req.path.startsWith("/api/uploads/") ? next() : limitBody(c, next));
3329
3424
  app.onError((error) => {
3330
3425
  if (error instanceof APIError && error.statusCode >= 400 && error.statusCode < 500) return Response.json({ error: {
3331
3426
  code: typeof error.body?.code === "string" ? error.body.code : "authentication_error",
@@ -3421,6 +3516,33 @@ async function initializeApplication(database, config, adapters, cleanup) {
3421
3516
  "Referrer-Policy": "no-referrer"
3422
3517
  } });
3423
3518
  });
3519
+ app.openAPIRegistry.registerPath({
3520
+ method: "put",
3521
+ path: "/api/uploads/{token}",
3522
+ request: {
3523
+ params: z$1.object({ token: z$1.string() }),
3524
+ body: { content: { "application/octet-stream": { schema: z$1.string() } } }
3525
+ },
3526
+ responses: {
3527
+ 200: { description: "The staged upload, or the attachment when the URL names a ticket" },
3528
+ 400: { description: "The body or its Content-Length is not the declared size" },
3529
+ 404: { description: "Unknown upload URL" },
3530
+ 410: { description: "The upload URL was already used or expired" }
3531
+ }
3532
+ });
3533
+ app.put("/api/uploads/:token", async (c) => Response.json(await receiveUpload(domain, c.req.param("token"), c.req.header("content-length"), async (limit) => {
3534
+ const chunks = [], reader = c.req.raw.body?.getReader();
3535
+ if (!reader) return Buffer.alloc(0);
3536
+ let total = 0;
3537
+ while (total <= limit) {
3538
+ const { done, value } = await reader.read();
3539
+ if (done) return Buffer.concat(chunks);
3540
+ chunks.push(value);
3541
+ total += value.length;
3542
+ }
3543
+ await reader.cancel();
3544
+ return Buffer.concat(chunks);
3545
+ })));
3424
3546
  app.openAPIRegistry.registerPath({
3425
3547
  method: "post",
3426
3548
  path: "/api/repository-hooks/{connectionId}",
@@ -1,4 +1,4 @@
1
- import { n as createDatabase, t as readConfig } from "./config-BQw6mPzE.mjs";
1
+ import { n as createDatabase, t as readConfig } from "./config-DUIdB3c2.mjs";
2
2
  import { t as migrateDatabase } from "./migrate-C9f4ozB4.mjs";
3
3
  //#region src/cli/migrate.ts
4
4
  const database = createDatabase(readConfig());
@@ -1,6 +1,6 @@
1
- import { d as user, n as createDatabase, r as account, t as readConfig, u as session } from "./config-BQw6mPzE.mjs";
2
- import { A as hasAdminRole } from "./src-BaTNDJB7.mjs";
3
- import { K as instanceSettings, p as fail, t as createAuth } from "./auth-CFjetlB4.mjs";
1
+ import { d as user, n as createDatabase, r as account, t as readConfig, u as session } from "./config-DUIdB3c2.mjs";
2
+ import { M as hasAdminRole } from "./src-CC6lPIU_.mjs";
3
+ import { p as fail, q as instanceSettings, t as createAuth } from "./auth-CVnX2XIg.mjs";
4
4
  import { and, eq, isNull, sql } from "drizzle-orm";
5
5
  import { z } from "zod";
6
6
  import { parseArgs } from "node:util";