@maudecode/kanvil 0.6.1 → 0.7.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.
- package/apps/server/dist/{auth-CFjetlB4.mjs → auth-D43XyHdK.mjs} +227 -71
- package/apps/server/dist/client.mjs +2 -2
- package/apps/server/dist/{config-BQw6mPzE.mjs → config-CQVdVhXv.mjs} +1 -1
- package/apps/server/dist/index.mjs +161 -44
- package/apps/server/dist/migrate.mjs +1 -1
- package/apps/server/dist/recover.mjs +3 -3
- package/apps/server/dist/{src-BaTNDJB7.mjs → src-D70GmZ5R.mjs} +83 -26
- package/apps/server/drizzle/0040_upload_urls.sql +20 -0
- package/apps/server/drizzle/0041_project_repositories.sql +2 -0
- package/apps/server/drizzle/meta/0040_snapshot.json +7170 -0
- package/apps/server/drizzle/meta/0041_snapshot.json +7183 -0
- package/apps/server/drizzle/meta/_journal.json +14 -0
- package/apps/web/dist/assets/{CompositeItem-Dotj8JZL.js → CompositeItem-DLTRsada.js} +1 -1
- package/apps/web/dist/assets/{Trans-CE4EAAY1.js → Trans-DqX-qhvQ.js} +1 -1
- package/apps/web/dist/assets/admin-settings-BQzdZII5.js +1 -0
- package/apps/web/dist/assets/{archive-restore-BMtgjFUu.js → archive-restore-BO1qng5Y.js} +1 -1
- package/apps/web/dist/assets/az-AZ-SXqDBPA2.js +1 -0
- package/apps/web/dist/assets/{az-B-DxMc__.js → az-CEW7-a7J.js} +1 -1
- package/apps/web/dist/assets/configuration-D0zvwrXt.js +1 -0
- package/apps/web/dist/assets/{corner-left-up-O4OsTW-L.js → corner-left-up-Be7Z9Uhf.js} +1 -1
- package/apps/web/dist/assets/{de-Btbkq-aX.js → de-B-oGVMYL.js} +1 -1
- package/apps/web/dist/assets/de-DE-CB7aQ6b-.js +1 -0
- package/apps/web/dist/assets/default-CCI1vZxa.js +56 -0
- package/apps/web/dist/assets/delivery-settings-BrwSrFIN.js +1 -0
- package/apps/web/dist/assets/{el-DBVEX16k.js → el-Cp69kezM.js} +1 -1
- package/apps/web/dist/assets/el-GR-D1ntYXIt.js +1 -0
- package/apps/web/dist/assets/{ellipsis-CvEPDi9V.js → ellipsis-B0AE8ax3.js} +1 -1
- package/apps/web/dist/assets/{es-CMJoxZBk.js → es-CwOMVSTx.js} +1 -1
- package/apps/web/dist/assets/es-ES-CllsaxcB.js +1 -0
- package/apps/web/dist/assets/{forms-DbfnnPg_.js → forms-CkVpTx29.js} +1 -1
- package/apps/web/dist/assets/{fr-OyEmYoRn.js → fr-Cy4co9ic.js} +1 -1
- package/apps/web/dist/assets/fr-FR-DxTw6LgI.js +1 -0
- package/apps/web/dist/assets/{hi-CIgXSseB.js → hi-DbO9TKwg.js} +1 -1
- package/apps/web/dist/assets/hi-IN-CaQga-0l.js +1 -0
- package/apps/web/dist/assets/{highlight-B4K3yEEh.js → highlight-zMg1BK-r.js} +1 -1
- package/apps/web/dist/assets/{id-WV2oSAPB.js → id-B6NKsmCm.js} +1 -1
- package/apps/web/dist/assets/id-ID-DBfwAAqU.js +1 -0
- package/apps/web/dist/assets/index-CZuFd4LM.js +17 -0
- package/apps/web/dist/assets/index-DPT5LAs1.css +2 -0
- package/apps/web/dist/assets/{invitations-BTB8f2Xl.js → invitations-Dp40BfHj.js} +1 -1
- package/apps/web/dist/assets/it-IT-BckuH9Nl.js +1 -0
- package/apps/web/dist/assets/{it-BWSrRqus.js → it-ilGYCq2T.js} +1 -1
- package/apps/web/dist/assets/{item-HmFuRZet.js → item-DQR89bKm.js} +1 -1
- package/apps/web/dist/assets/{ja-BtOHWvUJ.js → ja-D-hJRTgP.js} +1 -1
- package/apps/web/dist/assets/ja-JP-BAGgJKZ4.js +1 -0
- package/apps/web/dist/assets/ko-KR-srIEsTP5.js +1 -0
- package/apps/web/dist/assets/{ko-DAryK0HN.js → ko-ajkkoJ_d.js} +1 -1
- package/apps/web/dist/assets/{label-settings-ssfEX2uR.js → label-settings-ChnKkA97.js} +1 -1
- package/apps/web/dist/assets/{markdown-blocks-B1kO6iku.js → markdown-blocks-CpzrwyQG.js} +1 -1
- package/apps/web/dist/assets/{member-directory-BA4ZT1qU.js → member-directory-XK0ZEGUq.js} +1 -1
- package/apps/web/dist/assets/{mk-DBbiGUk5.js → mk-C_sMyEHX.js} +1 -1
- package/apps/web/dist/assets/mk-MK-BTa1NMAF.js +1 -0
- package/apps/web/dist/assets/{nl-D4V-8WdF.js → nl-D9-P4UPR.js} +1 -1
- package/apps/web/dist/assets/nl-NL-CQduhDqs.js +1 -0
- package/apps/web/dist/assets/{pl-C--QrxO0.js → pl-CiQM7fSB.js} +1 -1
- package/apps/web/dist/assets/pl-PL-BdZMf7Ni.js +1 -0
- package/apps/web/dist/assets/project-lifecycle-VYecJuiP.js +1 -0
- package/apps/web/dist/assets/project-settings-Bs6YEpo9.js +1 -0
- package/apps/web/dist/assets/{pt-BR-DvFVKpmO.js → pt-BR-CP7dhuqF.js} +1 -1
- package/apps/web/dist/assets/pt-BR-DZEXJNHg.js +1 -0
- package/apps/web/dist/assets/public-project-DUfWDao9.js +1 -0
- package/apps/web/dist/assets/{rotate-ccw-CB36Zu3L.js → rotate-ccw-Cxh9ec2s.js} +1 -1
- package/apps/web/dist/assets/{ru-UmbPUOka.js → ru-DxJ6vF4E.js} +1 -1
- package/apps/web/dist/assets/ru-RU-CZKEe7P8.js +1 -0
- package/apps/web/dist/assets/{search-Cvetr1OK.js → search-DfbsqO7M.js} +6 -6
- package/apps/web/dist/assets/{send-CJDct4Oj.js → send-DrJl0BKm.js} +1 -1
- package/apps/web/dist/assets/settings-BN8uIiBG.js +1 -0
- package/apps/web/dist/assets/{sliders-horizontal-dmqWngxy.js → sliders-horizontal-DM47MLe7.js} +1 -1
- package/apps/web/dist/assets/{switch-BtScTpP9.js → switch-CJTVGtE9.js} +1 -1
- package/apps/web/dist/assets/{table-byFokzo-.js → table-BLLjFPWD.js} +1 -1
- package/apps/web/dist/assets/{ticket-detail-C3YA3Hzc.js → ticket-detail-BTy_3JeU.js} +2 -2
- package/apps/web/dist/assets/ticket-editor-Bb_lyxnJ.js +2 -0
- package/apps/web/dist/assets/{ticket-list-BXUorfmj.js → ticket-list-Botp5qf5.js} +5 -5
- package/apps/web/dist/assets/ticket-list-controls-Dmkno4pm.js +1 -0
- package/apps/web/dist/assets/{tr-CNpgQ4ka.js → tr-9IkdRLIk.js} +1 -1
- package/apps/web/dist/assets/tr-TR-Y8wBLO8I.js +1 -0
- package/apps/web/dist/assets/{uk-MQoTXdpk.js → uk-Btampsdw.js} +1 -1
- package/apps/web/dist/assets/uk-UA-CVpasGUS.js +1 -0
- package/apps/web/dist/assets/{upload-D1x_ib3B.js → upload-HARy4VC_.js} +1 -1
- package/apps/web/dist/assets/{useQueries-BnlNCoy4.js → useQueries-CTeEF--U.js} +1 -1
- package/apps/web/dist/assets/{vi-CEw4gTWc.js → vi-BVZzOn_n.js} +1 -1
- package/apps/web/dist/assets/vi-VN-BMy-iJD-.js +1 -0
- package/apps/web/dist/assets/{visual-editor-CpdMRn3e.js → visual-editor-DKBPjqV-.js} +1 -1
- package/apps/web/dist/assets/{workspace-settings-BickBtZx.js → workspace-settings-DqzBNw60.js} +1 -1
- package/apps/web/dist/assets/{zh-CN-ChzoXkfr.js → zh-CN-BoUvF2do.js} +1 -1
- package/apps/web/dist/assets/zh-CN-Cds_v0R8.js +1 -0
- package/apps/web/dist/assets/{zh-TW-bWBKARrK.js → zh-TW-CEIDGTWe.js} +1 -1
- package/apps/web/dist/assets/zh-TW-DCCYakXz.js +1 -0
- package/apps/web/dist/index.html +8 -8
- package/docs/api.md +33 -3
- package/docs/cli.md +1 -1
- package/package.json +1 -1
- package/apps/web/dist/assets/admin-settings-CO7nnD9T.js +0 -1
- package/apps/web/dist/assets/az-AZ-R8_HFWJr.js +0 -1
- package/apps/web/dist/assets/configuration-Bnt72HRu.js +0 -1
- package/apps/web/dist/assets/de-DE-BHgMj19Y.js +0 -1
- package/apps/web/dist/assets/default-DqsN66dq.js +0 -56
- package/apps/web/dist/assets/delivery-settings-Cji0N0m0.js +0 -1
- package/apps/web/dist/assets/el-GR-CQFGM5Sr.js +0 -1
- package/apps/web/dist/assets/es-ES-DZkcRVA4.js +0 -1
- package/apps/web/dist/assets/fr-FR-CRPAezXU.js +0 -1
- package/apps/web/dist/assets/hi-IN-2YFE4UQk.js +0 -1
- package/apps/web/dist/assets/id-ID-B-J0_QIW.js +0 -1
- package/apps/web/dist/assets/index-BAnwOrh1.css +0 -2
- package/apps/web/dist/assets/index-nltuyQtq.js +0 -17
- package/apps/web/dist/assets/it-IT-CJ0HYggO.js +0 -1
- package/apps/web/dist/assets/ja-JP-DDQS7D7b.js +0 -1
- package/apps/web/dist/assets/ko-KR-CkpoewEa.js +0 -1
- package/apps/web/dist/assets/mk-MK-C0RhS6XR.js +0 -1
- package/apps/web/dist/assets/nl-NL-BSKqlzcL.js +0 -1
- package/apps/web/dist/assets/pl-PL-CHz38SiU.js +0 -1
- package/apps/web/dist/assets/project-lifecycle-COZb9FqM.js +0 -1
- package/apps/web/dist/assets/project-settings-DwmTm9KA.js +0 -1
- package/apps/web/dist/assets/pt-BR-BotWHRiO.js +0 -1
- package/apps/web/dist/assets/public-project-B3hEkeCF.js +0 -1
- package/apps/web/dist/assets/ru-RU-DtOwNna6.js +0 -1
- package/apps/web/dist/assets/settings-3IXm9lJ-.js +0 -1
- package/apps/web/dist/assets/ticket-editor-CZnZQMG1.js +0 -2
- package/apps/web/dist/assets/ticket-list-controls-BxGKCZMo.js +0 -1
- package/apps/web/dist/assets/tr-TR-CdG2alU2.js +0 -1
- package/apps/web/dist/assets/uk-UA-Ceu5gorw.js +0 -1
- package/apps/web/dist/assets/vi-VN-CxshgV4W.js +0 -1
- package/apps/web/dist/assets/zh-CN-D7Yrjhd3.js +0 -1
- package/apps/web/dist/assets/zh-TW-DyF38kqx.js +0 -1
|
@@ -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-
|
|
2
|
-
import {
|
|
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-CQVdVhXv.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-D70GmZ5R.mjs";
|
|
3
3
|
import { t as migrateDatabase } from "./migrate-C9f4ozB4.mjs";
|
|
4
|
-
import { $ as
|
|
4
|
+
import { $ as pushDevices, A as requireInstanceAdmin, B as createPollingJob, C as defaultSenders, D as receiveUpload, E as createHttpSender, F as createBlobStorage, G as eventClock, H as attachments, I as decodeImage, J as members, K as events, L as imageResponse, M as readCalendarFeed, N as createTicketSearch, O as createAccess, P as createTicketIndex, Q as projects, R as stagedUploadCutoff, S as receiveRepositoryWebhook, T as DeliveryError, 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 requireSession, k as createWriteLimiter, 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 deliverBatch, x as processImports, y as recipientColumns, z as createObjectStore } from "./auth-D43XyHdK.mjs";
|
|
5
5
|
import { serve } from "@hono/node-server";
|
|
6
6
|
import pg from "pg";
|
|
7
7
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
@@ -9,8 +9,8 @@ import { and, asc, count, desc, eq, gt, ilike, inArray, isNotNull, isNull, notIn
|
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
12
|
-
import { Expo } from "expo-server-sdk";
|
|
13
12
|
import { isDeepStrictEqual } from "node:util";
|
|
13
|
+
import { Expo } from "expo-server-sdk";
|
|
14
14
|
import { EventEmitter } from "node:events";
|
|
15
15
|
import { connect } from "amqplib";
|
|
16
16
|
import { OpenAPIHono, createRoute, z as z$1 } from "@hono/zod-openapi";
|
|
@@ -359,7 +359,7 @@ function createCleanup() {
|
|
|
359
359
|
}
|
|
360
360
|
//#endregion
|
|
361
361
|
//#region src/version.ts
|
|
362
|
-
const applicationVersion = "0.
|
|
362
|
+
const applicationVersion = "0.7.0";
|
|
363
363
|
//#endregion
|
|
364
364
|
//#region src/realtime.ts
|
|
365
365
|
async function createRealtime(config) {
|
|
@@ -3121,49 +3121,138 @@ 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 summary = (name) => ({
|
|
3149
|
+
name,
|
|
3150
|
+
description: operations[name].description,
|
|
3151
|
+
access: operations[name].method === "GET" ? "read" : "write"
|
|
3152
|
+
});
|
|
3153
|
+
function findOperation({ query = "", name }) {
|
|
3154
|
+
if (name !== void 0) {
|
|
3155
|
+
const operation = known(name), spec = operations[operation];
|
|
3156
|
+
return {
|
|
3157
|
+
...summary(operation),
|
|
3158
|
+
method: spec.method,
|
|
3159
|
+
scope: spec.scope,
|
|
3160
|
+
inputSchema: z.toJSONSchema(toolInput(operation), { io: "input" })
|
|
3161
|
+
};
|
|
3162
|
+
}
|
|
3163
|
+
const terms = query.toLowerCase().split(/\s+/);
|
|
3164
|
+
return { operations: operationNames.map(summary).filter((row) => terms.every((term) => `${row.name} ${row.description}`.toLowerCase().includes(term))) };
|
|
3165
|
+
}
|
|
3166
|
+
const reply = async (name, work) => {
|
|
3167
|
+
try {
|
|
3168
|
+
return {
|
|
3169
|
+
content: [{
|
|
3170
|
+
type: "text",
|
|
3171
|
+
text: `${name} completed.`
|
|
3172
|
+
}],
|
|
3173
|
+
structuredContent: { result: await work() }
|
|
3174
|
+
};
|
|
3175
|
+
} catch (error) {
|
|
3176
|
+
return {
|
|
3177
|
+
isError: true,
|
|
3178
|
+
content: [{
|
|
3179
|
+
type: "text",
|
|
3180
|
+
text: JSON.stringify(errorEnvelope(error).body)
|
|
3181
|
+
}]
|
|
3182
|
+
};
|
|
3183
|
+
}
|
|
3184
|
+
};
|
|
3185
|
+
const runnerInput = {
|
|
3186
|
+
name: z.string().max(80).describe("Operation name from find_operation."),
|
|
3187
|
+
input: z.record(z.string(), z.unknown()).default({}).describe("The operation's input.")
|
|
3188
|
+
};
|
|
3124
3189
|
function createMcp(auth, domain, access, config, limitWrite) {
|
|
3125
|
-
const serve = (request, identity, limited = false) =>
|
|
3126
|
-
const
|
|
3127
|
-
name
|
|
3128
|
-
|
|
3190
|
+
const serve = (request, identity, limited = false) => {
|
|
3191
|
+
const execute = async (name, input) => {
|
|
3192
|
+
if (limited && operations[name].method !== "GET") limitWrite(identity.ownerId);
|
|
3193
|
+
const value = "view" in operations[name].input.shape ? {
|
|
3194
|
+
view: defaultView,
|
|
3195
|
+
...input
|
|
3196
|
+
} : input;
|
|
3197
|
+
return compact(await domain.execute(name, value, identity), echoedFields(name));
|
|
3198
|
+
};
|
|
3199
|
+
const runner = (write) => (args) => reply(args.name, () => {
|
|
3200
|
+
const name = known(args.name);
|
|
3201
|
+
if (operations[name].method !== "GET" !== write) throw new ApiError(400, "invalid_request", `${name} ${write ? "only reads; use read_operation" : "writes; use run_operation"}`);
|
|
3202
|
+
return execute(name, args.input);
|
|
3129
3203
|
});
|
|
3130
|
-
|
|
3131
|
-
const
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3204
|
+
return createMcpHandler(() => {
|
|
3205
|
+
const server = new McpServer({
|
|
3206
|
+
name: "Kanvil",
|
|
3207
|
+
version: applicationVersion
|
|
3208
|
+
}, { instructions });
|
|
3209
|
+
for (const name of coreNames) {
|
|
3210
|
+
const spec = operations[name];
|
|
3211
|
+
const inputSchema = toolInput(name).shape;
|
|
3212
|
+
server.registerTool(name, {
|
|
3213
|
+
description: spec.description,
|
|
3214
|
+
inputSchema,
|
|
3215
|
+
annotations: {
|
|
3216
|
+
readOnlyHint: spec.method === "GET",
|
|
3217
|
+
destructiveHint: false,
|
|
3218
|
+
idempotentHint: spec.method === "GET",
|
|
3219
|
+
openWorldHint: false
|
|
3220
|
+
}
|
|
3221
|
+
}, (input) => reply(name, () => execute(name, input)));
|
|
3222
|
+
}
|
|
3223
|
+
server.registerTool("find_operation", {
|
|
3224
|
+
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.",
|
|
3225
|
+
inputSchema: {
|
|
3226
|
+
query: z.string().max(200).optional(),
|
|
3227
|
+
name: z.string().max(80).optional()
|
|
3228
|
+
},
|
|
3137
3229
|
annotations: {
|
|
3138
|
-
readOnlyHint:
|
|
3139
|
-
|
|
3140
|
-
idempotentHint: spec.method === "GET",
|
|
3230
|
+
readOnlyHint: true,
|
|
3231
|
+
idempotentHint: true,
|
|
3141
3232
|
openWorldHint: false
|
|
3142
3233
|
}
|
|
3143
|
-
},
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
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
|
-
};
|
|
3234
|
+
}, (input) => reply("find_operation", () => findOperation(input)));
|
|
3235
|
+
server.registerTool("read_operation", {
|
|
3236
|
+
description: "Run a read operation found with find_operation.",
|
|
3237
|
+
inputSchema: runnerInput,
|
|
3238
|
+
annotations: {
|
|
3239
|
+
readOnlyHint: true,
|
|
3240
|
+
idempotentHint: true,
|
|
3241
|
+
openWorldHint: false
|
|
3162
3242
|
}
|
|
3163
|
-
});
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3243
|
+
}, runner(false));
|
|
3244
|
+
server.registerTool("run_operation", {
|
|
3245
|
+
description: "Run a write operation found with find_operation.",
|
|
3246
|
+
inputSchema: runnerInput,
|
|
3247
|
+
annotations: {
|
|
3248
|
+
readOnlyHint: false,
|
|
3249
|
+
destructiveHint: true,
|
|
3250
|
+
openWorldHint: false
|
|
3251
|
+
}
|
|
3252
|
+
}, runner(true));
|
|
3253
|
+
return server;
|
|
3254
|
+
}, { legacy: "stateless" }).fetch(request);
|
|
3255
|
+
};
|
|
3167
3256
|
const oauth = requireMcpAuth(auth, async (request, claims) => {
|
|
3168
3257
|
if (typeof claims.sub !== "string") throw new ApiError(401, "unauthorized", "A user subject is required");
|
|
3169
3258
|
const granted = typeof claims.scope === "string" ? claims.scope.split(" ") : [];
|
|
@@ -3319,13 +3408,14 @@ async function initializeApplication(database, config, adapters, cleanup) {
|
|
|
3319
3408
|
} }, { status: 400 });
|
|
3320
3409
|
} });
|
|
3321
3410
|
app.use("*", secureHeaders());
|
|
3322
|
-
|
|
3411
|
+
const limitBody = bodyLimit({
|
|
3323
3412
|
maxSize: 15e6,
|
|
3324
3413
|
onError: () => Response.json({ error: {
|
|
3325
3414
|
code: "payload_too_large",
|
|
3326
3415
|
message: "The request exceeds the upload limit"
|
|
3327
3416
|
} }, { status: 413 })
|
|
3328
|
-
})
|
|
3417
|
+
});
|
|
3418
|
+
app.use("*", (c, next) => c.req.path.startsWith("/api/uploads/") ? next() : limitBody(c, next));
|
|
3329
3419
|
app.onError((error) => {
|
|
3330
3420
|
if (error instanceof APIError && error.statusCode >= 400 && error.statusCode < 500) return Response.json({ error: {
|
|
3331
3421
|
code: typeof error.body?.code === "string" ? error.body.code : "authentication_error",
|
|
@@ -3421,6 +3511,33 @@ async function initializeApplication(database, config, adapters, cleanup) {
|
|
|
3421
3511
|
"Referrer-Policy": "no-referrer"
|
|
3422
3512
|
} });
|
|
3423
3513
|
});
|
|
3514
|
+
app.openAPIRegistry.registerPath({
|
|
3515
|
+
method: "put",
|
|
3516
|
+
path: "/api/uploads/{token}",
|
|
3517
|
+
request: {
|
|
3518
|
+
params: z$1.object({ token: z$1.string() }),
|
|
3519
|
+
body: { content: { "application/octet-stream": { schema: z$1.string() } } }
|
|
3520
|
+
},
|
|
3521
|
+
responses: {
|
|
3522
|
+
200: { description: "The staged upload, or the attachment when the URL names a ticket" },
|
|
3523
|
+
400: { description: "The body or its Content-Length is not the declared size" },
|
|
3524
|
+
404: { description: "Unknown upload URL" },
|
|
3525
|
+
410: { description: "The upload URL was already used or expired" }
|
|
3526
|
+
}
|
|
3527
|
+
});
|
|
3528
|
+
app.put("/api/uploads/:token", async (c) => Response.json(await receiveUpload(domain, c.req.param("token"), c.req.header("content-length"), async (limit) => {
|
|
3529
|
+
const chunks = [], reader = c.req.raw.body?.getReader();
|
|
3530
|
+
if (!reader) return Buffer.alloc(0);
|
|
3531
|
+
let total = 0;
|
|
3532
|
+
while (total <= limit) {
|
|
3533
|
+
const { done, value } = await reader.read();
|
|
3534
|
+
if (done) return Buffer.concat(chunks);
|
|
3535
|
+
chunks.push(value);
|
|
3536
|
+
total += value.length;
|
|
3537
|
+
}
|
|
3538
|
+
await reader.cancel();
|
|
3539
|
+
return Buffer.concat(chunks);
|
|
3540
|
+
})));
|
|
3424
3541
|
app.openAPIRegistry.registerPath({
|
|
3425
3542
|
method: "post",
|
|
3426
3543
|
path: "/api/repository-hooks/{connectionId}",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as createDatabase, t as readConfig } from "./config-
|
|
1
|
+
import { n as createDatabase, t as readConfig } from "./config-CQVdVhXv.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-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { d as user, n as createDatabase, r as account, t as readConfig, u as session } from "./config-CQVdVhXv.mjs";
|
|
2
|
+
import { M as hasAdminRole } from "./src-D70GmZ5R.mjs";
|
|
3
|
+
import { p as fail, q as instanceSettings, t as createAuth } from "./auth-D43XyHdK.mjs";
|
|
4
4
|
import { and, eq, isNull, sql } from "drizzle-orm";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { parseArgs } from "node:util";
|
|
@@ -290,10 +290,15 @@ var en_US_default = {
|
|
|
290
290
|
priority: "Priority",
|
|
291
291
|
kind: "Kind",
|
|
292
292
|
difficulty: "Difficulty",
|
|
293
|
+
automation: "Agent readiness",
|
|
293
294
|
label: "Label",
|
|
294
295
|
assigneeId: "Assignee",
|
|
295
296
|
customField: "Field"
|
|
296
297
|
},
|
|
298
|
+
automationFilter: {
|
|
299
|
+
ready: "Ready for agents",
|
|
300
|
+
manual: "Needs a person"
|
|
301
|
+
},
|
|
297
302
|
sort: {
|
|
298
303
|
priority: "Priority",
|
|
299
304
|
updated: "Recently updated",
|
|
@@ -535,6 +540,7 @@ var en_US_default = {
|
|
|
535
540
|
time_entry_conflict: "This actor already has a running entry on the ticket.",
|
|
536
541
|
claim_conflict: "Another worker holds this ticket.",
|
|
537
542
|
idempotency_conflict: "This request was already used for another change.",
|
|
543
|
+
duplicate_repository: "Another project in this workspace links this repository.",
|
|
538
544
|
project_archived: "Restore the archived project before changing it.",
|
|
539
545
|
rate_limited: "Too many changes in a short time. Wait and retry.",
|
|
540
546
|
snapshot_required: "The live feed fell behind. Reload to continue.",
|
|
@@ -1162,6 +1168,7 @@ var en_US_default = {
|
|
|
1162
1168
|
priority: "All priorities",
|
|
1163
1169
|
kind: "All kinds",
|
|
1164
1170
|
difficulty: "All difficulties",
|
|
1171
|
+
automation: "Any readiness",
|
|
1165
1172
|
label: "All labels",
|
|
1166
1173
|
assigneeId: "Anyone",
|
|
1167
1174
|
customField: "Any field value"
|
|
@@ -1171,6 +1178,7 @@ var en_US_default = {
|
|
|
1171
1178
|
priority: "Search priorities",
|
|
1172
1179
|
kind: "Search kinds",
|
|
1173
1180
|
difficulty: "Search difficulties",
|
|
1181
|
+
automation: "Search readiness",
|
|
1174
1182
|
label: "Search labels",
|
|
1175
1183
|
assigneeId: "Search people",
|
|
1176
1184
|
customField: "Search fields"
|
|
@@ -1703,6 +1711,9 @@ var en_US_default = {
|
|
|
1703
1711
|
changePrefixDescription: "Every ticket in {{name}} takes the new prefix and keeps its number.",
|
|
1704
1712
|
changing: "Changing…",
|
|
1705
1713
|
prefixChanged: "Tickets now use {{key}}",
|
|
1714
|
+
repositories: "Repositories",
|
|
1715
|
+
repositoriesDescription: "Agents can pass one of these git remotes as the project ID.",
|
|
1716
|
+
repositoriesInvalid: "Enter up to 20 git remotes, such as git@github.com:owner/repo.git.",
|
|
1706
1717
|
publicLink: "Public link",
|
|
1707
1718
|
publicDisclosure: "Anyone with the link can read every unarchived ticket's title, description, acceptance criteria, status, priority, kind, difficulty, labels, scopes, dates, and links to other tickets in this project. People, comments, activity, attachments, evidence, and claims stay private.",
|
|
1708
1719
|
publicOn: "Public link on",
|
|
@@ -2937,6 +2948,7 @@ const scopes = [
|
|
|
2937
2948
|
];
|
|
2938
2949
|
const attachmentLimit = 1e7;
|
|
2939
2950
|
const stagedUploadLifetime = 864e5;
|
|
2951
|
+
const uploadUrlLifetime = 9e5;
|
|
2940
2952
|
const stateLabels = labelTable(states, (value) => t(`common.status.${value}`));
|
|
2941
2953
|
const priorityLabels = labelTable(priorities, (value) => t(`common.priority.${value}`));
|
|
2942
2954
|
ticketSymbols.status.backlog.tone, ticketSymbols.status.todo.tone, ticketSymbols.status.in_progress.tone, ticketSymbols.status.in_review.tone, ticketSymbols.status.done.tone, ticketSymbols.status.cancelled.tone;
|
|
@@ -3053,9 +3065,30 @@ const placement = {
|
|
|
3053
3065
|
after: id.optional()
|
|
3054
3066
|
};
|
|
3055
3067
|
const oneNeighbor = (input) => input.before === void 0 || input.after === void 0;
|
|
3056
|
-
const projectInput = z.object({ projectId:
|
|
3068
|
+
const projectInput = z.object({ projectId: z.string().min(1).max(300) });
|
|
3057
3069
|
const ticketInput = projectInput.extend({ ticketId: id });
|
|
3058
3070
|
const projectKey = z.string().trim().regex(/^[A-Z][A-Z0-9]{1,9}$/);
|
|
3071
|
+
const remoteHost = "(?<host>[a-z0-9][a-z0-9.-]*)";
|
|
3072
|
+
const remotePath = "(?<path>[^?#]+)";
|
|
3073
|
+
const remoteForms = [
|
|
3074
|
+
new RegExp(`^(?<scheme>https|ssh)://(?:[^/]*@)?${remoteHost}(?::(?<port>\\d+))?/${remotePath}$`),
|
|
3075
|
+
new RegExp(`^[^/:@]+@${remoteHost}:${remotePath}$`),
|
|
3076
|
+
new RegExp(`^${remoteHost}(?::(?<port>\\d+))?/${remotePath}$`)
|
|
3077
|
+
];
|
|
3078
|
+
const defaultRemotePorts = {
|
|
3079
|
+
https: "443",
|
|
3080
|
+
ssh: "22"
|
|
3081
|
+
};
|
|
3082
|
+
function normalizeRemote(value) {
|
|
3083
|
+
const remote = value.trim().toLowerCase();
|
|
3084
|
+
if (/\s/.test(remote)) return null;
|
|
3085
|
+
const { scheme, host, port, path } = remoteForms.map((form) => form.exec(remote)).find(Boolean)?.groups ?? {};
|
|
3086
|
+
const segments = path?.replace(/\/+$/, "").replace(/\.git$/, "").split("/").filter(Boolean);
|
|
3087
|
+
if (!host || !segments?.length) return null;
|
|
3088
|
+
return `${host}${port && port !== defaultRemotePorts[scheme ?? ""] ? `:${port}` : ""}/${segments.join("/")}`;
|
|
3089
|
+
}
|
|
3090
|
+
const repositoryRemote = z.string().max(300).refine((value) => normalizeRemote(value) !== null, "Enter a git remote URL").overwrite((value) => normalizeRemote(value) ?? value);
|
|
3091
|
+
const projectRepositories = z.array(repositoryRemote).max(20).overwrite((remotes) => [...new Set(remotes)]);
|
|
3059
3092
|
const reason = z.object({
|
|
3060
3093
|
code: z.string(),
|
|
3061
3094
|
message: z.string(),
|
|
@@ -3072,15 +3105,19 @@ const filterFieldNames = [
|
|
|
3072
3105
|
"priority",
|
|
3073
3106
|
"kind",
|
|
3074
3107
|
"difficulty",
|
|
3108
|
+
"automation",
|
|
3075
3109
|
"label",
|
|
3076
3110
|
"assigneeId",
|
|
3077
3111
|
"customField"
|
|
3078
3112
|
];
|
|
3113
|
+
const automationFilters = ["ready", "manual"];
|
|
3114
|
+
labelTable(automationFilters, (value) => t(`common.automationFilter.${value}`));
|
|
3079
3115
|
const filterFields = {
|
|
3080
3116
|
status: z.enum(states),
|
|
3081
3117
|
priority: z.enum(priorities),
|
|
3082
3118
|
kind: z.enum(kinds),
|
|
3083
3119
|
difficulty: z.enum(difficulties),
|
|
3120
|
+
automation: z.enum(automationFilters),
|
|
3084
3121
|
label: z.string().max(40),
|
|
3085
3122
|
assigneeId: z.union([id, z.literal("none")]),
|
|
3086
3123
|
customField: z.string().regex(/^[0-9a-f-]{36}:(?:none|true|[0-9a-f-]{36})$/)
|
|
@@ -3098,6 +3135,8 @@ const filterShape = {
|
|
|
3098
3135
|
kindNot: filterValues(filterFields.kind),
|
|
3099
3136
|
difficulty: filterValues(filterFields.difficulty),
|
|
3100
3137
|
difficultyNot: filterValues(filterFields.difficulty),
|
|
3138
|
+
automation: filterValues(filterFields.automation),
|
|
3139
|
+
automationNot: filterValues(filterFields.automation),
|
|
3101
3140
|
label: filterValues(filterFields.label),
|
|
3102
3141
|
labelNot: filterValues(filterFields.label),
|
|
3103
3142
|
assigneeId: filterValues(filterFields.assigneeId),
|
|
@@ -3167,6 +3206,7 @@ const ticketFilter = z.object({
|
|
|
3167
3206
|
]).default("priority"),
|
|
3168
3207
|
order: z.enum(["asc", "desc"]).optional()
|
|
3169
3208
|
}).strict().superRefine(rejectConflicts);
|
|
3209
|
+
const listSort = ticketFilter.shape.sort.unwrap().optional();
|
|
3170
3210
|
labelTable(ticketFilter.shape.sort.unwrap().options, (sort) => t(`common.sort.${sort}`));
|
|
3171
3211
|
const defaultOrder = {
|
|
3172
3212
|
priority: "asc",
|
|
@@ -3275,6 +3315,7 @@ const projectSchema = z.object({
|
|
|
3275
3315
|
key: z.string(),
|
|
3276
3316
|
name: z.string(),
|
|
3277
3317
|
description: z.string(),
|
|
3318
|
+
repositories: z.array(z.string()).default([]),
|
|
3278
3319
|
version,
|
|
3279
3320
|
archivedAt: timestamp.nullable().default(null),
|
|
3280
3321
|
createdAt: timestamp,
|
|
@@ -3445,6 +3486,11 @@ const attachmentSchema = z.object({
|
|
|
3445
3486
|
createdAt: timestamp
|
|
3446
3487
|
});
|
|
3447
3488
|
const stagedUploadSchema = attachmentSchema.omit({ ticketId: true }).extend({ expiresAt: timestamp });
|
|
3489
|
+
const uploadUrlSchema = z.object({
|
|
3490
|
+
uploadId: z.uuid(),
|
|
3491
|
+
url: z.url(),
|
|
3492
|
+
expiresAt: timestamp
|
|
3493
|
+
});
|
|
3448
3494
|
const httpUrl = z.url().regex(/^https?:\/\//i, "Use an HTTP or HTTPS URL");
|
|
3449
3495
|
const evidenceFields = z.object({
|
|
3450
3496
|
kind: z.enum([
|
|
@@ -4210,21 +4256,25 @@ const page = (item) => z.object({
|
|
|
4210
4256
|
items: z.array(item),
|
|
4211
4257
|
total: z.number().int().nonnegative()
|
|
4212
4258
|
});
|
|
4213
|
-
const
|
|
4259
|
+
const fileMetadata = {
|
|
4214
4260
|
name: text.max(255).refine((value) => {
|
|
4215
4261
|
for (const character of value) if (character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127) return false;
|
|
4216
4262
|
return true;
|
|
4217
4263
|
}, "Use a filename without control characters"),
|
|
4218
|
-
contentType: z.string().max(120).regex(/^[a-z0-9][a-z0-9.+-]*\/[a-z0-9][a-z0-9.+-]*$/i).default("application/octet-stream")
|
|
4264
|
+
contentType: z.string().max(120).regex(/^[a-z0-9][a-z0-9.+-]*\/[a-z0-9][a-z0-9.+-]*$/i).default("application/octet-stream")
|
|
4265
|
+
};
|
|
4266
|
+
const fileFields = {
|
|
4267
|
+
...fileMetadata,
|
|
4219
4268
|
data: z.string().max(Math.ceil(attachmentLimit / 3) * 4)
|
|
4220
4269
|
};
|
|
4221
|
-
const op = (input, output, method, path, scope, description) => ({
|
|
4270
|
+
const op = (input, output, method, path, scope, description, options = {}) => ({
|
|
4222
4271
|
input,
|
|
4223
4272
|
output,
|
|
4224
4273
|
method,
|
|
4225
4274
|
path,
|
|
4226
4275
|
scope,
|
|
4227
|
-
description
|
|
4276
|
+
description,
|
|
4277
|
+
...options
|
|
4228
4278
|
});
|
|
4229
4279
|
const operations = {
|
|
4230
4280
|
list_workspaces: op(z.object({}), z.array(workspaceSchema), "GET", "/workspaces", "read", "List accessible workspaces and the current membership role."),
|
|
@@ -4414,8 +4464,9 @@ const operations = {
|
|
|
4414
4464
|
publicReadEnabled: z.boolean().optional(),
|
|
4415
4465
|
name: text.max(120),
|
|
4416
4466
|
description: z.string().max(5e3),
|
|
4417
|
-
key: projectKey.optional()
|
|
4418
|
-
|
|
4467
|
+
key: projectKey.optional(),
|
|
4468
|
+
repositories: projectRepositories.optional()
|
|
4469
|
+
}).strict(), projectSchema, "PATCH", "/projects/{projectId}", "admin", "Update project details. A new `key` re-keys every ticket of the project; the previous prefix stays reserved for the project, and the project and every key issued under it keep resolving. `publicReadEnabled` shares or stops sharing the project's read-only public view (see the public operations). `repositories` replaces the project's linked git remotes (at most 20), and any `projectId` then accepts one of them; within a workspace only one unarchived project can hold a remote."),
|
|
4419
4470
|
move_project: op(projectInput.extend({
|
|
4420
4471
|
...mutation,
|
|
4421
4472
|
expectedVersion: version,
|
|
@@ -4479,13 +4530,14 @@ const operations = {
|
|
|
4479
4530
|
}).strict(), okSchema, "POST", "/projects/{projectId}/members", "admin", "Add or update an existing user's membership with a seeded role or a custom role of the project's workspace, or remove it with a null role. Needs `member.manage`; the role, and any role the person holds now, must grant nothing the caller lacks."),
|
|
4480
4531
|
list_tickets: op(projectInput.extend({
|
|
4481
4532
|
...ticketFilter.shape,
|
|
4533
|
+
sort: listSort,
|
|
4482
4534
|
...pagination,
|
|
4483
4535
|
relatedTo: id.optional(),
|
|
4484
4536
|
stateId: z.uuid().optional(),
|
|
4485
4537
|
scheduledFrom: timestamp.optional(),
|
|
4486
4538
|
scheduledTo: timestamp.optional(),
|
|
4487
4539
|
...ticketView
|
|
4488
|
-
}).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.
|
|
4540
|
+
}).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. `q` keeps only tickets whose key, title, description, acceptance criteria, labels, or scopes contain its text as written, or whose key or ID it names exactly; double-quoted segments are exact phrases that must each appear on word boundaries, so `\"IME\"` does not match `time` while unquoted `IME` does. With `q` and no `sort`, results order by relevance as in search_tickets; otherwise by `sort` (default `priority`). With `view: \"summary\"`, rows omit the description and acceptance criteria."),
|
|
4489
4541
|
list_ticket_labels: op(projectInput.extend({ archived: ticketFilter.shape.archived }), z.array(z.object({
|
|
4490
4542
|
name: z.string(),
|
|
4491
4543
|
count: z.number().int().nonnegative()
|
|
@@ -4579,7 +4631,7 @@ const operations = {
|
|
|
4579
4631
|
claim: claimSchema.nullable(),
|
|
4580
4632
|
subscribed: z.boolean(),
|
|
4581
4633
|
canReleaseClaim: z.boolean()
|
|
4582
|
-
}), "GET", "/projects/{projectId}/tickets/{ticketId}", "read", "Read a ticket by ID or key, with readiness and ownership."),
|
|
4634
|
+
}), "GET", "/projects/{projectId}/tickets/{ticketId}", "read", "Read a ticket by ID or key, with readiness and ownership.", { mcp: "core" }),
|
|
4583
4635
|
create_ticket: op(projectInput.extend({
|
|
4584
4636
|
...mutation,
|
|
4585
4637
|
title: ticketFields.shape.title,
|
|
@@ -4601,14 +4653,14 @@ const operations = {
|
|
|
4601
4653
|
archived: z.boolean().default(false),
|
|
4602
4654
|
uploads: z.array(z.uuid()).max(50).default([]),
|
|
4603
4655
|
customFields: customFieldValues.default({})
|
|
4604
|
-
}).strict(), ticketSchema, "POST", "/projects/{projectId}/tickets", "write", "Create a ticket. `customFields` maps field IDs from list_custom_fields to values; omitted fields take their default, and a required field must end with a value. `uploads` lists your staged inline uploads in this project; they become its attachments in the same write, or nothing is created. Reuse requestId when retrying this write."),
|
|
4656
|
+
}).strict(), ticketSchema, "POST", "/projects/{projectId}/tickets", "write", "Create a ticket. `customFields` maps field IDs from list_custom_fields to values; omitted fields take their default, and a required field must end with a value. `uploads` lists your staged inline uploads in this project; they become its attachments in the same write, or nothing is created. Reuse requestId when retrying this write.", { mcp: "core" }),
|
|
4605
4657
|
duplicate_ticket: op(ticketInput.extend(mutation).strict(), ticketSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/duplicate", "write", "Copy a ticket into a new To do ticket in the same project, linked to the original by a clones relationship. Copies requirements, properties, labels, scopes, and parent; resets agent readiness and never copies claims, comments, evidence, attachments, or children. Reuse requestId when retrying this write."),
|
|
4606
4658
|
update_ticket: op(ticketInput.extend({
|
|
4607
4659
|
...mutation,
|
|
4608
4660
|
expectedVersion: version,
|
|
4609
4661
|
changes: ticketChanges,
|
|
4610
4662
|
claimId: z.uuid().optional()
|
|
4611
|
-
}).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."),
|
|
4663
|
+
}).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.", { mcp: "core" }),
|
|
4612
4664
|
update_ticket_labels: op(ticketInput.extend({
|
|
4613
4665
|
...mutation,
|
|
4614
4666
|
expectedVersion: version,
|
|
@@ -4638,7 +4690,7 @@ const operations = {
|
|
|
4638
4690
|
resolve_ticket: op(z.object({ ticketId: id }).strict(), z.object({
|
|
4639
4691
|
ticket: ticketSchema,
|
|
4640
4692
|
project: projectRef
|
|
4641
|
-
}), "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."),
|
|
4693
|
+
}), "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.", { mcp: "core" }),
|
|
4642
4694
|
bulk_update_tickets: op(projectInput.extend({
|
|
4643
4695
|
...mutation,
|
|
4644
4696
|
tickets: claimedTicketSelection,
|
|
@@ -4661,7 +4713,7 @@ const operations = {
|
|
|
4661
4713
|
ticket: ticketRow,
|
|
4662
4714
|
project: projectRef,
|
|
4663
4715
|
snippet: z.string().optional()
|
|
4664
|
-
})) }), "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
|
|
4716
|
+
})) }), "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 on word boundaries, so `\"IME\"` does not match `time`. With `view: \"summary\"`, each ticket is a compact row and the result carries a `snippet` of the matching text.", { mcp: "core" }),
|
|
4665
4717
|
search_workspace: op(z.object({
|
|
4666
4718
|
q: z.string().trim().min(1).max(200),
|
|
4667
4719
|
workspaceId: z.uuid().optional(),
|
|
@@ -4676,19 +4728,19 @@ const operations = {
|
|
|
4676
4728
|
counts: z.record(z.enum(searchResultTypes), z.number().int().nonnegative()),
|
|
4677
4729
|
engine: z.enum(["postgres", "index"]),
|
|
4678
4730
|
indexing: z.boolean()
|
|
4679
|
-
}), "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.
|
|
4731
|
+
}), "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 on word boundaries. With `view: \"summary\"`, ticket results carry their summary row and each result a `snippet` of the matching text."),
|
|
4680
4732
|
list_my_work: op(z.object({
|
|
4681
4733
|
workspaceId: z.uuid().optional(),
|
|
4682
4734
|
q: ticketFilter.shape.q,
|
|
4683
|
-
sort:
|
|
4735
|
+
sort: listSort,
|
|
4684
4736
|
order: ticketFilter.shape.order,
|
|
4685
4737
|
activity: z.coerce.number().int().min(0).max(50).default(0),
|
|
4686
4738
|
...pagination
|
|
4687
|
-
}).strict(), page(ticketSchema).extend({ activity: z.array(eventSchema).default([]) }), "GET", "/my-work", "read", "List open, unarchived tickets assigned to the current identity across every accessible project, optionally within one workspace, with the most recent accessible activity when `activity` is set."),
|
|
4739
|
+
}).strict(), page(ticketSchema).extend({ activity: z.array(eventSchema).default([]) }), "GET", "/my-work", "read", "List open, unarchived tickets assigned to the current identity across every accessible project, optionally within one workspace, with the most recent accessible activity when `activity` is set. `q` and `sort` work as in list_tickets.", { mcp: "core" }),
|
|
4688
4740
|
list_eligible_tickets: op(projectInput.extend(pagination), page(z.object({
|
|
4689
4741
|
ticket: ticketSchema,
|
|
4690
4742
|
readiness: readinessSchema
|
|
4691
|
-
})), "GET", "/projects/{projectId}/eligibility", "read", "Inspect eligibility and explicit blocking reasons for open tickets."),
|
|
4743
|
+
})), "GET", "/projects/{projectId}/eligibility", "read", "Inspect eligibility and explicit blocking reasons for open tickets.", { mcp: "core" }),
|
|
4692
4744
|
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`."),
|
|
4693
4745
|
add_relation: op(ticketInput.extend({
|
|
4694
4746
|
...mutation,
|
|
@@ -4704,7 +4756,7 @@ const operations = {
|
|
|
4704
4756
|
add_comment: op(ticketInput.extend({
|
|
4705
4757
|
...mutation,
|
|
4706
4758
|
body: text.max(5e4)
|
|
4707
|
-
}).strict(), commentSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/comments", "write", "Add a comment with authenticated attribution. Mention a project member with `[@Name](mention:<user id>)` from list_members; members who can read the project are notified."),
|
|
4759
|
+
}).strict(), commentSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/comments", "write", "Add a comment with authenticated attribution. Mention a project member with `[@Name](mention:<user id>)` from list_members; members who can read the project are notified.", { mcp: "core" }),
|
|
4708
4760
|
update_comment: op(projectInput.extend({
|
|
4709
4761
|
...mutation,
|
|
4710
4762
|
commentId: z.uuid(),
|
|
@@ -4720,11 +4772,16 @@ const operations = {
|
|
|
4720
4772
|
upload_attachment: op(ticketInput.extend({
|
|
4721
4773
|
...mutation,
|
|
4722
4774
|
...fileFields
|
|
4723
|
-
}).strict(), attachmentSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/attachments", "write", "Upload up to 10 MB of base64-encoded bytes. The original filename is metadata, never a filesystem path. An intake credential can attach up to 10 files to a quarantined ticket it created, within 1 GB across its quarantined tickets."),
|
|
4775
|
+
}).strict(), attachmentSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/attachments", "write", "Upload up to 10 MB of base64-encoded bytes. The original filename is metadata, never a filesystem path. An intake credential can attach up to 10 files to a quarantined ticket it created, within 1 GB across its quarantined tickets. For a file on disk, use create_upload_url instead of inlining it."),
|
|
4724
4776
|
stage_upload: op(projectInput.extend({
|
|
4725
4777
|
...mutation,
|
|
4726
4778
|
...fileFields
|
|
4727
|
-
}).strict(), stagedUploadSchema, "POST", "/projects/{projectId}/uploads", "write", "Stage up to 10 MB of base64-encoded bytes for a ticket that does not exist yet. Only you can read it, at its attachment content address, until create_ticket claims it; unclaimed uploads expire after 24 hours."),
|
|
4779
|
+
}).strict(), stagedUploadSchema, "POST", "/projects/{projectId}/uploads", "write", "Stage up to 10 MB of base64-encoded bytes for a ticket that does not exist yet. Only you can read it, at its attachment content address, until create_ticket claims it; unclaimed uploads expire after 24 hours. For a file on disk, use create_upload_url instead of inlining it."),
|
|
4780
|
+
create_upload_url: op(projectInput.extend({
|
|
4781
|
+
...fileMetadata,
|
|
4782
|
+
ticketId: id.optional(),
|
|
4783
|
+
size: z.number().int().nonnegative().max(attachmentLimit)
|
|
4784
|
+
}).strict(), uploadUrlSchema, "POST", "/projects/{projectId}/upload-urls", "write", "Get a single-use URL for one file of up to 10 MB, so its bytes never pass through the tool call. PUT exactly `size` bytes to `url` within 15 minutes, for example `curl --upload-file <path> <url>`; the URL needs no other credential and reads nothing. With `ticketId` the file becomes that ticket's attachment; without it, it is a staged upload that create_ticket `uploads` claims by `uploadId`."),
|
|
4728
4785
|
discard_upload: op(projectInput.extend({
|
|
4729
4786
|
...mutation,
|
|
4730
4787
|
uploadId: z.uuid()
|
|
@@ -4739,7 +4796,7 @@ const operations = {
|
|
|
4739
4796
|
...mutation,
|
|
4740
4797
|
...evidenceFields.shape,
|
|
4741
4798
|
claimId: z.uuid().optional()
|
|
4742
|
-
}).strict(), evidenceSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/evidence", "write", "Attach evidence with its commit and provenance."),
|
|
4799
|
+
}).strict(), evidenceSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/evidence", "write", "Attach evidence with its commit and provenance.", { mcp: "core" }),
|
|
4743
4800
|
list_time_entries: op(ticketInput.extend(pagination), page(timeEntrySchema), "GET", "/projects/{projectId}/tickets/{ticketId}/time-entries", "read", "List observed time entries, newest first. Running entries have null endAt and durationMs."),
|
|
4744
4801
|
get_time_entry: op(ticketInput.extend({ timeEntryId: z.uuid() }), timeEntrySchema, "GET", "/projects/{projectId}/tickets/{ticketId}/time-entries/{timeEntryId}", "read", "Read one attributed time entry within its current ticket and project."),
|
|
4745
4802
|
create_time_entry: op(ticketInput.extend({
|
|
@@ -4785,18 +4842,18 @@ const operations = {
|
|
|
4785
4842
|
claim_ticket: op(ticketInput.extend({
|
|
4786
4843
|
...mutation,
|
|
4787
4844
|
workerId: text.max(200),
|
|
4788
|
-
expectedVersion: version,
|
|
4845
|
+
expectedVersion: version.optional(),
|
|
4789
4846
|
leaseSeconds: claimLeaseSecondsSchema.optional()
|
|
4790
|
-
}).strict(), claimSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/claim", "claim", "Atomically claim ready work. Expired claims require explicit recovery."),
|
|
4847
|
+
}).strict(), claimSchema.extend({ ticket: ticketSchema }), "POST", "/projects/{projectId}/tickets/{ticketId}/claim", "claim", "Atomically claim ready work and return the claim with the claimed ticket. expectedVersion is optional; readiness always applies. Expired claims require explicit recovery.", { mcp: "core" }),
|
|
4791
4848
|
renew_claim: op(ticketInput.extend({
|
|
4792
4849
|
...mutation,
|
|
4793
4850
|
claimId: z.uuid(),
|
|
4794
4851
|
leaseSeconds: claimLeaseSecondsSchema.optional()
|
|
4795
|
-
}).strict(), claimSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/claim/renew", "claim", "Renew the caller's unexpired claim."),
|
|
4852
|
+
}).strict(), claimSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/claim/renew", "claim", "Renew the caller's unexpired claim.", { mcp: "core" }),
|
|
4796
4853
|
release_claim: op(ticketInput.extend({
|
|
4797
4854
|
...mutation,
|
|
4798
4855
|
claimId: z.uuid()
|
|
4799
|
-
}).strict(), okSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/claim/release", "claim", "Release the caller's claim."),
|
|
4856
|
+
}).strict(), okSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/claim/release", "claim", "Release the caller's claim.", { mcp: "core" }),
|
|
4800
4857
|
recover_claim: op(ticketInput.extend({
|
|
4801
4858
|
...mutation,
|
|
4802
4859
|
claimId: z.uuid().optional(),
|
|
@@ -5084,4 +5141,4 @@ function parseOutput(name, value) {
|
|
|
5084
5141
|
return operations[name].output.parse(value);
|
|
5085
5142
|
}
|
|
5086
5143
|
//#endregion
|
|
5087
|
-
export {
|
|
5144
|
+
export { parseCustomFieldFilter as $, filterList as A, ticketImportSchema as At, isPublicOperation as B, instanceUpdateSchema as Bt, destinationKinds as C, stagedUploadLifetime as Ct, eventLabels as D, streamErrorSchema as Dt, eventCategories as E, states as Et, importSecretFields as F, workflowOrder as Ft, notificationIdsInput as G, searchSettingsSchema as Gt, memberSchema as H, photoResultSchema as Ht, invitationStatuses as I, workspaceSchema as It, notificationReadInput as J, notificationPreferencesInput as K, isHexColor as L, adminInstanceSchema as Lt, hasAdminRole as M, ticketSchema as Mt, importLimits as N, ticketSummarySchema as Nt, eventSchema as O, ticketExportFormat as Ot, importProviderDefinitions as P, uploadUrlLifetime as Pt, operations as Q, isOperation as R, authenticationPolicySchema as Rt, destinationEventKeys as S, snapshotSchema as St, errorSchema as T, stateLabels as Tt, mentionScheme as U, s3SettingsSchema as Ut, isSeededRole as V, photoInputSchema as Vt, normalizeRemote as W, searchAuthentication as Wt, okSchema as X, notificationsSchema as Y, operationPermission as Z, defaultOrder as _, roles as _t, channelRouteAllows as a, priorities as at, deliverySecretFields as b, seededPermissions as bt, claimSchema as c, publicOperations as ct, credentialProjectsSchema as d, repositoryProviderDefinitions as dt, parseOutput as et, customFieldFilterTokens as f, repositoryProviders as ft, defaultClaimMaxLeaseSeconds as g, roleLabels as gt, customFieldValueSchema as h, roleCategory as ht, channelKinds as i, permissions as it, formatEvent as j, ticketPredicates as jt, filterKey as k, ticketFilter as kt, closedStates as l, pushDeviceInput as lt, customFieldValue as m, resourceProvider as mt, attachmentLimit as n, permissionLabels as nt, checkpointSchema as o, priorityLabels as ot, customFieldSchema as p, repositorySecretFields as pt, notificationPreferencesSchema as q, attachmentSchema as r, permissionScope as rt, claimLeaseSecondsSchema as s, projectSchema as st, ApiError as t, parsePublicOutput as tt, credentialInput as u, remoteKinds as ut, defaultWorkflow as v, scopes as vt, difficulties as w, stagedUploadSchema as wt, deliverySettingsSchema as x, signInMethodLabels as xt, deletedAccountName as y, searchResultTypes as yt, isPresetColor as z, brokerSettingsSchema as zt };
|