@maudecode/kanvil 0.5.0 → 0.6.1
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-Cs_whzgk.mjs → auth-CFjetlB4.mjs} +63 -15
- package/apps/server/dist/client.mjs +10 -9
- package/apps/server/dist/{config-DKuNQY5b.mjs → config-BQw6mPzE.mjs} +1 -1
- package/apps/server/dist/index.mjs +10 -5
- package/apps/server/dist/migrate.mjs +1 -1
- package/apps/server/dist/recover.mjs +3 -3
- package/apps/server/dist/{src-DptOUNRd.mjs → src-BaTNDJB7.mjs} +93 -5
- package/apps/server/drizzle/0039_intake_credential.sql +2 -0
- package/apps/server/drizzle/meta/0039_snapshot.json +7020 -0
- package/apps/server/drizzle/meta/_journal.json +7 -0
- package/apps/web/dist/assets/{CompositeItem-CXqewtEL.js → CompositeItem-Dotj8JZL.js} +1 -1
- package/apps/web/dist/assets/{Trans-B1rRlWca.js → Trans-CE4EAAY1.js} +1 -1
- package/apps/web/dist/assets/admin-settings-CO7nnD9T.js +1 -0
- package/apps/web/dist/assets/{archive-restore-76sStk8K.js → archive-restore-BMtgjFUu.js} +1 -1
- package/apps/web/dist/assets/az-AZ-R8_HFWJr.js +1 -0
- package/apps/web/dist/assets/{az-B7OgHoaE.js → az-B-DxMc__.js} +1 -1
- package/apps/web/dist/assets/configuration-Bnt72HRu.js +1 -0
- package/apps/web/dist/assets/{corner-left-up-PD-73Nae.js → corner-left-up-O4OsTW-L.js} +1 -1
- package/apps/web/dist/assets/{de-B-bv38Q0.js → de-Btbkq-aX.js} +1 -1
- package/apps/web/dist/assets/de-DE-BHgMj19Y.js +1 -0
- package/apps/web/dist/assets/default-DqsN66dq.js +56 -0
- package/apps/web/dist/assets/delivery-settings-Cji0N0m0.js +1 -0
- package/apps/web/dist/assets/{el-BQHQHQgX.js → el-DBVEX16k.js} +1 -1
- package/apps/web/dist/assets/el-GR-CQFGM5Sr.js +1 -0
- package/apps/web/dist/assets/{ellipsis-GF1HwTDb.js → ellipsis-CvEPDi9V.js} +1 -1
- package/apps/web/dist/assets/{es-f5Xr8ldr.js → es-CMJoxZBk.js} +1 -1
- package/apps/web/dist/assets/es-ES-DZkcRVA4.js +1 -0
- package/apps/web/dist/assets/{forms-BlEL8C_P.js → forms-DbfnnPg_.js} +1 -1
- package/apps/web/dist/assets/fr-FR-CRPAezXU.js +1 -0
- package/apps/web/dist/assets/{fr-DSR0htnl.js → fr-OyEmYoRn.js} +1 -1
- package/apps/web/dist/assets/{hi-BaAJEWuW.js → hi-CIgXSseB.js} +1 -1
- package/apps/web/dist/assets/hi-IN-2YFE4UQk.js +1 -0
- package/apps/web/dist/assets/{highlight-DXT2xgt8.js → highlight-B4K3yEEh.js} +1 -1
- package/apps/web/dist/assets/id-ID-B-J0_QIW.js +1 -0
- package/apps/web/dist/assets/{id-kxogWcip.js → id-WV2oSAPB.js} +1 -1
- package/apps/web/dist/assets/index-BAnwOrh1.css +2 -0
- package/apps/web/dist/assets/index-nltuyQtq.js +17 -0
- package/apps/web/dist/assets/invitations-BTB8f2Xl.js +1 -0
- package/apps/web/dist/assets/{it-C_qk40U6.js → it-BWSrRqus.js} +1 -1
- package/apps/web/dist/assets/it-IT-CJ0HYggO.js +1 -0
- package/apps/web/dist/assets/item-HmFuRZet.js +1 -0
- package/apps/web/dist/assets/{ja-Dn5h64V9.js → ja-BtOHWvUJ.js} +1 -1
- package/apps/web/dist/assets/ja-JP-DDQS7D7b.js +1 -0
- package/apps/web/dist/assets/{ko-CjaoEPuG.js → ko-DAryK0HN.js} +1 -1
- package/apps/web/dist/assets/ko-KR-CkpoewEa.js +1 -0
- package/apps/web/dist/assets/label-settings-ssfEX2uR.js +1 -0
- package/apps/web/dist/assets/{markdown-blocks-BQLO-H_7.js → markdown-blocks-B1kO6iku.js} +1 -1
- package/apps/web/dist/assets/member-directory-BA4ZT1qU.js +1 -0
- package/apps/web/dist/assets/{mk-jLeGD50T.js → mk-DBbiGUk5.js} +1 -1
- package/apps/web/dist/assets/mk-MK-C0RhS6XR.js +1 -0
- package/apps/web/dist/assets/{nl-AVtC5ygT.js → nl-D4V-8WdF.js} +1 -1
- package/apps/web/dist/assets/nl-NL-BSKqlzcL.js +1 -0
- package/apps/web/dist/assets/{pl-Dup1h_Y1.js → pl-C--QrxO0.js} +1 -1
- package/apps/web/dist/assets/pl-PL-CHz38SiU.js +1 -0
- package/apps/web/dist/assets/project-lifecycle-COZb9FqM.js +1 -0
- package/apps/web/dist/assets/project-settings-DwmTm9KA.js +1 -0
- package/apps/web/dist/assets/pt-BR-BotWHRiO.js +1 -0
- package/apps/web/dist/assets/{pt-BR-Cm04EWM-.js → pt-BR-DvFVKpmO.js} +1 -1
- package/apps/web/dist/assets/{public-project-BivDjU_a.js → public-project-B3hEkeCF.js} +1 -1
- package/apps/web/dist/assets/{rotate-ccw-CSaKhZYt.js → rotate-ccw-CB36Zu3L.js} +1 -1
- package/apps/web/dist/assets/ru-RU-DtOwNna6.js +1 -0
- package/apps/web/dist/assets/{ru-IeycXsyE.js → ru-UmbPUOka.js} +1 -1
- package/apps/web/dist/assets/search-Cvetr1OK.js +85 -0
- package/apps/web/dist/assets/{send-BLMZ8gQ0.js → send-CJDct4Oj.js} +1 -1
- package/apps/web/dist/assets/settings-3IXm9lJ-.js +1 -0
- package/apps/web/dist/assets/{sliders-horizontal-Db6kRvuw.js → sliders-horizontal-dmqWngxy.js} +1 -1
- package/apps/web/dist/assets/switch-BtScTpP9.js +1 -0
- package/apps/web/dist/assets/{table-UJp5S9yy.js → table-byFokzo-.js} +1 -1
- package/apps/web/dist/assets/ticket-detail-C3YA3Hzc.js +2 -0
- package/apps/web/dist/assets/ticket-editor-CZnZQMG1.js +2 -0
- package/apps/web/dist/assets/ticket-list-BXUorfmj.js +6 -0
- package/apps/web/dist/assets/ticket-list-controls-BxGKCZMo.js +1 -0
- package/apps/web/dist/assets/{tr-DtT_dKUK.js → tr-CNpgQ4ka.js} +1 -1
- package/apps/web/dist/assets/tr-TR-CdG2alU2.js +1 -0
- package/apps/web/dist/assets/{uk-CsbeHSS6.js → uk-MQoTXdpk.js} +1 -1
- package/apps/web/dist/assets/uk-UA-Ceu5gorw.js +1 -0
- package/apps/web/dist/assets/{upload-DUEg04Nj.js → upload-D1x_ib3B.js} +1 -1
- package/apps/web/dist/assets/useQueries-BnlNCoy4.js +1 -0
- package/apps/web/dist/assets/{vi-rkf1BOLx.js → vi-CEw4gTWc.js} +1 -1
- package/apps/web/dist/assets/vi-VN-CxshgV4W.js +1 -0
- package/apps/web/dist/assets/visual-editor-CpdMRn3e.js +208 -0
- package/apps/web/dist/assets/workspace-settings-BickBtZx.js +1 -0
- package/apps/web/dist/assets/{zh-CN-Xo-QId5w.js → zh-CN-ChzoXkfr.js} +1 -1
- package/apps/web/dist/assets/zh-CN-D7Yrjhd3.js +1 -0
- package/apps/web/dist/assets/zh-TW-DyF38kqx.js +1 -0
- package/apps/web/dist/assets/{zh-TW-DEnTqjbn.js → zh-TW-bWBKARrK.js} +1 -1
- package/apps/web/dist/index.html +9 -9
- package/docs/api.md +5 -3
- package/docs/cli.md +1 -1
- package/docs/mobile.md +67 -4
- package/docs/releasing.md +6 -4
- package/package.json +1 -1
- package/apps/web/dist/assets/admin-settings-DE3MfBFE.js +0 -1
- package/apps/web/dist/assets/az-AZ-bFxpMMZN.js +0 -1
- package/apps/web/dist/assets/de-DE-BeXI30Ll.js +0 -1
- package/apps/web/dist/assets/default-bQFGTwIp.js +0 -56
- package/apps/web/dist/assets/delivery-settings-BvMeOsCS.js +0 -1
- package/apps/web/dist/assets/el-GR-SgRHlWhY.js +0 -1
- package/apps/web/dist/assets/es-ES-BF1J5vap.js +0 -1
- package/apps/web/dist/assets/fr-FR-D_sldwf6.js +0 -1
- package/apps/web/dist/assets/hi-IN-CfOw4TWf.js +0 -1
- package/apps/web/dist/assets/id-ID-CLKtHO7Q.js +0 -1
- package/apps/web/dist/assets/index-CYVrs22O.js +0 -17
- package/apps/web/dist/assets/index-DyQNFS69.css +0 -2
- package/apps/web/dist/assets/invitations-BPik3J_G.js +0 -1
- package/apps/web/dist/assets/it-IT-eVZNW5qX.js +0 -1
- package/apps/web/dist/assets/item-TCHuZNK6.js +0 -1
- package/apps/web/dist/assets/ja-JP-D96njn-P.js +0 -1
- package/apps/web/dist/assets/ko-KR-Bash_ieA.js +0 -1
- package/apps/web/dist/assets/label-settings-DZTorNDm.js +0 -1
- package/apps/web/dist/assets/member-directory-Dx_jQYfI.js +0 -1
- package/apps/web/dist/assets/mk-MK-BWQWGIRx.js +0 -1
- package/apps/web/dist/assets/nl-NL-D1tvo0Gd.js +0 -1
- package/apps/web/dist/assets/pl-PL-DNgGUEcQ.js +0 -1
- package/apps/web/dist/assets/project-lifecycle-n1ZZbYfu.js +0 -1
- package/apps/web/dist/assets/project-settings-B_Ih5kiz.js +0 -1
- package/apps/web/dist/assets/pt-BR-CZsUZQ6P.js +0 -1
- package/apps/web/dist/assets/ru-RU-4ew92YE_.js +0 -1
- package/apps/web/dist/assets/search-Ble6WSEU.js +0 -85
- package/apps/web/dist/assets/settings-CH48qQK2.js +0 -1
- package/apps/web/dist/assets/switch-BeX-0Fwq.js +0 -1
- package/apps/web/dist/assets/ticket-detail-C24opfNW.js +0 -2
- package/apps/web/dist/assets/ticket-editor--d_bG9C1.js +0 -2
- package/apps/web/dist/assets/ticket-list-DRbZWYHf.js +0 -5
- package/apps/web/dist/assets/ticket-list-controls-fYBottFC.js +0 -1
- package/apps/web/dist/assets/tr-TR-CM381XCw.js +0 -1
- package/apps/web/dist/assets/uk-UA-AeOiWCVo.js +0 -1
- package/apps/web/dist/assets/useQueries-5lW2C5Sb.js +0 -1
- package/apps/web/dist/assets/vi-VN-td08xXv9.js +0 -1
- package/apps/web/dist/assets/visual-editor-DkFLjouD.js +0 -208
- package/apps/web/dist/assets/workspace-settings-Bcsp-VoJ.js +0 -1
- package/apps/web/dist/assets/zh-CN-D3dhEYLS.js +0 -1
- package/apps/web/dist/assets/zh-TW-CfhOEFo8.js +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as auth_schema_exports, c as oauthConsent, d as user, i as apikey, l as oauthRefreshToken, o as oauthAccessToken, r as account, s as oauthClient, u as session } from "./config-
|
|
2
|
-
import { $ as permissionLabels, A as hasAdminRole, At as workflowOrder, Bt as searchSettingsSchema, D as filterKey, Dt as ticketPredicates, E as eventSchema, Et as ticketImportSchema, F as isHexColor, Ft as instanceUpdateSchema, J as operationPermission, N as importSecretFields, Nt as authenticationPolicySchema, O as filterList, Ot as ticketSchema, P as invitationStatuses, Pt as brokerSettingsSchema, Q as parsePublicOutput, Rt as s3SettingsSchema, S as difficulties, St as states, T as eventLabels, Tt as ticketFilter, V as mentionScheme, X as parseCustomFieldFilter, Y as operations, Z as parseOutput, _ as deletedAccountName, _t as seededPermissions, at as publicOperations, b as destinationEventKeys, bt as stagedUploadLifetime, ct as repositoryProviderDefinitions, d as customFieldSchema, dt as resourceProvider, et as permissionScope, f as customFieldValue, ft as roleCategory, g as defaultWorkflow, gt as searchResultTypes, h as defaultOrder, ht as scopes, i as channelRouteAllows, it as projectSchema, jt as workspaceSchema, k as formatEvent, kt as ticketSummarySchema, l as credentialProjectsSchema, lt as repositoryProviders, m as defaultClaimMaxLeaseSeconds, mt as roles, n as attachmentLimit, nt as priorities, p as customFieldValueSchema, pt as roleLabels, r as channelKinds, rt as priorityLabels, s as closedStates, st as remoteKinds, t as ApiError, tt as permissions, u as customFieldFilterTokens, ut as repositorySecretFields, v as deliverySecretFields, vt as signInMethodLabels, w as eventCategories, wt as ticketExportFormat, x as destinationKinds, xt as stateLabels, y as deliverySettingsSchema, yt as snapshotSchema, z as isSeededRole, zt as searchAuthentication } from "./src-
|
|
1
|
+
import { a as auth_schema_exports, c as oauthConsent, d as user, i as apikey, l as oauthRefreshToken, o as oauthAccessToken, r as account, s as oauthClient, u as session } from "./config-BQw6mPzE.mjs";
|
|
2
|
+
import { $ as permissionLabels, A as hasAdminRole, At as workflowOrder, Bt as searchSettingsSchema, D as filterKey, Dt as ticketPredicates, E as eventSchema, Et as ticketImportSchema, F as isHexColor, Ft as instanceUpdateSchema, J as operationPermission, N as importSecretFields, Nt as authenticationPolicySchema, O as filterList, Ot as ticketSchema, P as invitationStatuses, Pt as brokerSettingsSchema, Q as parsePublicOutput, Rt as s3SettingsSchema, S as difficulties, St as states, T as eventLabels, Tt as ticketFilter, V as mentionScheme, X as parseCustomFieldFilter, Y as operations, Z as parseOutput, _ as deletedAccountName, _t as seededPermissions, at as publicOperations, b as destinationEventKeys, bt as stagedUploadLifetime, ct as repositoryProviderDefinitions, d as customFieldSchema, dt as resourceProvider, et as permissionScope, f as customFieldValue, ft as roleCategory, g as defaultWorkflow, gt as searchResultTypes, h as defaultOrder, ht as scopes, i as channelRouteAllows, it as projectSchema, jt as workspaceSchema, k as formatEvent, kt as ticketSummarySchema, l as credentialProjectsSchema, lt as repositoryProviders, m as defaultClaimMaxLeaseSeconds, mt as roles, n as attachmentLimit, nt as priorities, p as customFieldValueSchema, pt as roleLabels, r as channelKinds, rt as priorityLabels, s as closedStates, st as remoteKinds, t as ApiError, tt as permissions, u as customFieldFilterTokens, ut as repositorySecretFields, v as deliverySecretFields, vt as signInMethodLabels, w as eventCategories, wt as ticketExportFormat, x as destinationKinds, xt as stateLabels, y as deliverySettingsSchema, yt as snapshotSchema, z as isSeededRole, zt as searchAuthentication } from "./src-BaTNDJB7.mjs";
|
|
3
3
|
import { and, asc, count, desc, eq, getTableColumns, gt, ilike, inArray, isNotNull, isNull, lt, lte, ne, notInArray, or, sql } from "drizzle-orm";
|
|
4
4
|
import { alias, bigint, boolean, check, customType, foreignKey, index, integer, jsonb, pgTable, primaryKey, text, timestamp, unionAll, unique, uniqueIndex, uuid } from "drizzle-orm/pg-core";
|
|
5
5
|
import { z } from "zod";
|
|
@@ -218,6 +218,7 @@ const tickets = pgTable("tickets", {
|
|
|
218
218
|
automationReady: boolean("automation_ready").notNull().default(false),
|
|
219
219
|
claimRequired: boolean("claim_required").notNull().default(false),
|
|
220
220
|
holdReason: text("hold_reason"),
|
|
221
|
+
intakeCredentialId: text("intake_credential_id"),
|
|
221
222
|
startAt: timestamp("start_at", { withTimezone: true }),
|
|
222
223
|
dueAt: timestamp("due_at", { withTimezone: true }),
|
|
223
224
|
archived: boolean("archived").notNull().default(false),
|
|
@@ -233,6 +234,7 @@ const tickets = pgTable("tickets", {
|
|
|
233
234
|
index("ticket_parent").on(table.parentId),
|
|
234
235
|
index("ticket_project_position").on(table.projectId, table.position),
|
|
235
236
|
index("ticket_state").on(table.stateId),
|
|
237
|
+
index("ticket_intake_credential").on(table.intakeCredentialId).where(sql`${table.intakeCredentialId} IS NOT NULL`),
|
|
236
238
|
foreignKey({
|
|
237
239
|
name: "ticket_workflow_state",
|
|
238
240
|
columns: [
|
|
@@ -711,8 +713,7 @@ const createObjectStore = (configuration) => {
|
|
|
711
713
|
if (!create) throw new Error("Unsupported object storage provider");
|
|
712
714
|
return create(configuration.config);
|
|
713
715
|
};
|
|
714
|
-
|
|
715
|
-
//#region src/blob-storage.ts
|
|
716
|
+
const intakeAttachmentLifetime = 2592e6;
|
|
716
717
|
const stagedUploadCutoff = () => new Date(Date.now() - stagedUploadLifetime);
|
|
717
718
|
const digest$1 = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
718
719
|
function decodeBlobData(value, limit) {
|
|
@@ -811,6 +812,31 @@ function createBlobStorage(database, instance, factory = createObjectStore, sche
|
|
|
811
812
|
});
|
|
812
813
|
}
|
|
813
814
|
async function collectBatch() {
|
|
815
|
+
await database.db.transaction(async (tx) => {
|
|
816
|
+
await tx.execute(sql`SELECT pg_advisory_xact_lock(7361080)`);
|
|
817
|
+
const expired = await tx.select({
|
|
818
|
+
attachment: attachments,
|
|
819
|
+
key: tickets.key,
|
|
820
|
+
title: tickets.title
|
|
821
|
+
}).from(attachments).innerJoin(tickets, eq(tickets.id, attachments.ticketId)).where(and(isNotNull(tickets.intakeCredentialId), eq(tickets.holdReason, intakeHoldReason), lt(attachments.createdAt, /* @__PURE__ */ new Date(Date.now() - intakeAttachmentLifetime)))).orderBy(asc(attachments.createdAt), asc(attachments.id)).limit(100);
|
|
822
|
+
if (!expired.length) return;
|
|
823
|
+
await tx.delete(attachments).where(inArray(attachments.id, expired.map(({ attachment }) => attachment.id)));
|
|
824
|
+
await appendEvents(tx, expired.map(({ attachment, key, title }) => ({
|
|
825
|
+
projectId: attachment.projectId,
|
|
826
|
+
ticketId: attachment.ticketId,
|
|
827
|
+
actorId: null,
|
|
828
|
+
type: "attachment.deleted",
|
|
829
|
+
data: {
|
|
830
|
+
attachmentId: attachment.id,
|
|
831
|
+
key,
|
|
832
|
+
title,
|
|
833
|
+
source: {
|
|
834
|
+
kind: "system",
|
|
835
|
+
id: "blob-cleanup"
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
})));
|
|
839
|
+
});
|
|
814
840
|
return database.db.transaction(async (tx) => {
|
|
815
841
|
await tx.delete(stagedUploads).where(lt(stagedUploads.createdAt, stagedUploadCutoff()));
|
|
816
842
|
const rows = await tx.select({ object: blobObjects }).from(blobObjects).leftJoin(attachments, eq(attachments.objectId, blobObjects.id)).leftJoin(profilePhotos, eq(profilePhotos.objectId, blobObjects.id)).leftJoin(stagedUploads, eq(stagedUploads.id, blobObjects.id)).leftJoin(projects, or(eq(projects.iconObjectId, blobObjects.id), eq(projects.backgroundObjectId, blobObjects.id))).where(and(isNull(attachments.id), isNull(profilePhotos.userId), isNull(stagedUploads.id), isNull(projects.id), lt(blobObjects.createdAt, /* @__PURE__ */ new Date(Date.now() - 864e5)))).limit(20).for("update", {
|
|
@@ -2453,7 +2479,8 @@ async function createTicketRow(ctx, input) {
|
|
|
2453
2479
|
customFields: intakeOnly(ctx.actor) ? {} : applyCustomFieldValues(await projectCustomFields(ctx), null, customFields),
|
|
2454
2480
|
...await allocateTicket(ctx),
|
|
2455
2481
|
id,
|
|
2456
|
-
createdBy: ctx.actor.id
|
|
2482
|
+
createdBy: ctx.actor.id,
|
|
2483
|
+
intakeCredentialId: intakeOnly(ctx.actor) ? ctx.actor.ownerId : null
|
|
2457
2484
|
}).returning());
|
|
2458
2485
|
await subscribe(ctx, row.projectId, row.id, [ctx.actor.id, row.assigneeId]);
|
|
2459
2486
|
return {
|
|
@@ -3036,11 +3063,17 @@ const ticketHandlers = {
|
|
|
3036
3063
|
},
|
|
3037
3064
|
async recover_claim(ctx, input) {
|
|
3038
3065
|
const ticket = await getTicket(ctx, input.ticketId);
|
|
3039
|
-
const current = one(await ctx.db.select().from(claims).where(and(eq(claims.id, input.claimId), eq(claims.ticketId, ticket.id))));
|
|
3040
|
-
if (
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3066
|
+
const current = input.claimId === void 0 ? void 0 : one(await ctx.db.select().from(claims).where(and(eq(claims.id, input.claimId), eq(claims.ticketId, ticket.id))));
|
|
3067
|
+
if (!current) {
|
|
3068
|
+
const held = (await ctx.db.select({ id: claims.id }).from(claims).where(eq(claims.ticketId, ticket.id)).limit(1))[0];
|
|
3069
|
+
if (held) fail(409, "claim_conflict", "This ticket has a claim; recover it by its claim ID", { claimId: held.id });
|
|
3070
|
+
if (!ticket.claimRequired) fail(409, "claim_not_required", "This ticket does not require ownership to recover");
|
|
3071
|
+
} else {
|
|
3072
|
+
if (input.cancelActive && !can(ctx, "project.manage")) fail(403, "forbidden", "Cancelling an active claim requires project management access and admin scope");
|
|
3073
|
+
if (current.expiresAt > /* @__PURE__ */ new Date() && !input.cancelActive) fail(409, "claim_not_expired", "The current claim has not expired");
|
|
3074
|
+
}
|
|
3075
|
+
if (input.expectedVersion !== void 0 && (input.reschedule || !current)) assertVersion(ticket, input.expectedVersion, "ticket", "recovering ownership");
|
|
3076
|
+
if (input.reschedule) {
|
|
3044
3077
|
const row = one(await ctx.db.update(tickets).set({
|
|
3045
3078
|
...await resolveState(ctx, { status: "todo" }, ticket),
|
|
3046
3079
|
version: ticket.version + 1,
|
|
@@ -3061,12 +3094,12 @@ const ticketHandlers = {
|
|
|
3061
3094
|
changedFields: ["claimRequired"]
|
|
3062
3095
|
});
|
|
3063
3096
|
}
|
|
3064
|
-
await ctx.db.delete(claims).where(eq(claims.id, current.id));
|
|
3097
|
+
if (current) await ctx.db.delete(claims).where(eq(claims.id, current.id));
|
|
3065
3098
|
await publish(ctx, "claim.recovered", ticket.id, {
|
|
3066
|
-
claimId: current
|
|
3099
|
+
claimId: current?.id ?? null,
|
|
3067
3100
|
reason: input.reason,
|
|
3068
3101
|
confirmedStopped: true,
|
|
3069
|
-
...input.cancelActive && { cancelActive: true }
|
|
3102
|
+
...current && input.cancelActive && { cancelActive: true }
|
|
3070
3103
|
});
|
|
3071
3104
|
return { ok: true };
|
|
3072
3105
|
}
|
|
@@ -3489,6 +3522,14 @@ const collaborationHandlers = {
|
|
|
3489
3522
|
async upload_attachment(ctx, input) {
|
|
3490
3523
|
const ticket = await getTicket(ctx, input.ticketId);
|
|
3491
3524
|
const bytes = decodeBlobData(input.data, attachmentLimit);
|
|
3525
|
+
if (intakeOnly(ctx.actor)) {
|
|
3526
|
+
const stored = one(await ctx.db.select({
|
|
3527
|
+
files: sql`count(*) filter (where ${attachments.ticketId} = ${ticket.id})`.mapWith(Number),
|
|
3528
|
+
bytes: sql`coalesce(sum(${attachments.size}), 0)`.mapWith(Number)
|
|
3529
|
+
}).from(attachments).innerJoin(tickets, eq(tickets.id, attachments.ticketId)).where(and(eq(tickets.intakeCredentialId, ctx.actor.ownerId), eq(tickets.holdReason, intakeHoldReason))));
|
|
3530
|
+
if (stored.files >= 10) fail(409, "attachment_limit", `An intake credential can attach at most 10 files to a ticket`);
|
|
3531
|
+
if (stored.bytes + bytes.length > 1e9) fail(409, "attachment_limit", "This intake credential's untriaged tickets hold too many attachment bytes");
|
|
3532
|
+
}
|
|
3492
3533
|
const id = crypto.randomUUID();
|
|
3493
3534
|
await ctx.blobs.put(ctx.db, id, bytes, input.contentType);
|
|
3494
3535
|
const row = one(await ctx.db.insert(attachments).values({
|
|
@@ -6963,7 +7004,12 @@ const complete = (status) => closedStates.some((closed) => closed === status);
|
|
|
6963
7004
|
const intakeHoldReason = "Untrusted intake: human triage required";
|
|
6964
7005
|
const intakeQuarantined = (ticket) => ticket.holdReason === intakeHoldReason;
|
|
6965
7006
|
const intakeOnly = (actor) => actor.scopes.length > 0 && actor.scopes.every((scope) => scope === "intake");
|
|
6966
|
-
const
|
|
7007
|
+
const intakeOperations = /* @__PURE__ */ new Set([
|
|
7008
|
+
"create_ticket",
|
|
7009
|
+
"upload_attachment",
|
|
7010
|
+
"list_attachments"
|
|
7011
|
+
]);
|
|
7012
|
+
const hasScope = (actor, scope, operation) => intakeOnly(actor) ? !!operation && intakeOperations.has(operation) : actor.scopes.includes("admin") || actor.scopes.includes(scope) || scope === "read" && actor.scopes.some((value) => value !== "intake");
|
|
6967
7013
|
function requireScope(actor, scope, operation) {
|
|
6968
7014
|
if (!hasScope(actor, scope, operation)) fail(403, "insufficient_scope", `This credential requires ${scope} permission`);
|
|
6969
7015
|
}
|
|
@@ -7040,7 +7086,9 @@ async function context(db, actor, scope, projectId, workspaceId, permission = sc
|
|
|
7040
7086
|
};
|
|
7041
7087
|
}
|
|
7042
7088
|
async function getTicket(ctx, identifier) {
|
|
7043
|
-
|
|
7089
|
+
const ticket = one(await ctx.db.select().from(tickets).where(and(eq(tickets.projectId, project(ctx).id), ticketMatch(identifier))).limit(1));
|
|
7090
|
+
if (intakeOnly(ctx.actor) && !(ticket.intakeCredentialId === ctx.actor.ownerId && intakeQuarantined(ticket))) fail(404, "not_found", "The requested record was not found");
|
|
7091
|
+
return ticket;
|
|
7044
7092
|
}
|
|
7045
7093
|
const insertBatch = 1e3;
|
|
7046
7094
|
async function insertAll(rows, insert) {
|
|
@@ -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-
|
|
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";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
import { constants } from "node:fs";
|
|
@@ -403,7 +403,7 @@ function createClient(options) {
|
|
|
403
403
|
return request(url, init);
|
|
404
404
|
}
|
|
405
405
|
};
|
|
406
|
-
const send = async (spec, input, base) => {
|
|
406
|
+
const send = async (spec, input, base, signal) => {
|
|
407
407
|
const values = { ...spec.input.parse(input) };
|
|
408
408
|
const path = spec.path.replace(/\{(\w+)\}/g, (_, key) => {
|
|
409
409
|
const value = values[key];
|
|
@@ -416,7 +416,8 @@ function createClient(options) {
|
|
|
416
416
|
const init = {
|
|
417
417
|
method: spec.method,
|
|
418
418
|
credentials: options.credentials ?? "include",
|
|
419
|
-
headers
|
|
419
|
+
headers,
|
|
420
|
+
...signal ? { signal } : {}
|
|
420
421
|
};
|
|
421
422
|
if (spec.method === "GET") {
|
|
422
423
|
for (const [key, value] of Object.entries(values)) for (const item of Array.isArray(value) ? value : [value]) if (typeof item === "string" || typeof item === "number" || typeof item === "boolean") url.searchParams.append(key, String(item));
|
|
@@ -437,11 +438,11 @@ function createClient(options) {
|
|
|
437
438
|
credentials: options.credentials ?? "include"
|
|
438
439
|
})));
|
|
439
440
|
},
|
|
440
|
-
async call(name, input) {
|
|
441
|
-
return parseOutput(name, await send(operations[name], input, "/api/v1"));
|
|
441
|
+
async call(name, input, signal) {
|
|
442
|
+
return parseOutput(name, await send(operations[name], input, "/api/v1", signal));
|
|
442
443
|
},
|
|
443
|
-
async callPublic(name, input) {
|
|
444
|
-
return parsePublicOutput(name, await send(publicOperations[name], input, "/api/public"));
|
|
444
|
+
async callPublic(name, input, signal) {
|
|
445
|
+
return parsePublicOutput(name, await send(publicOperations[name], input, "/api/public", signal));
|
|
445
446
|
},
|
|
446
447
|
async subscribe(subscription) {
|
|
447
448
|
let cursor = subscription.cursor, backoff = 1e3;
|
|
@@ -599,7 +600,7 @@ function createClient(options) {
|
|
|
599
600
|
name,
|
|
600
601
|
input
|
|
601
602
|
],
|
|
602
|
-
queryFn: () => api.call(name, input)
|
|
603
|
+
queryFn: ({ signal } = {}) => api.call(name, input, signal)
|
|
603
604
|
};
|
|
604
605
|
},
|
|
605
606
|
publicQuery(name, input) {
|
|
@@ -610,7 +611,7 @@ function createClient(options) {
|
|
|
610
611
|
name,
|
|
611
612
|
input
|
|
612
613
|
],
|
|
613
|
-
queryFn: () => api.callPublic(name, input)
|
|
614
|
+
queryFn: ({ signal } = {}) => api.callPublic(name, input, signal)
|
|
614
615
|
};
|
|
615
616
|
},
|
|
616
617
|
action(name) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Bt as searchSettingsSchema, Pt as brokerSettingsSchema, Rt as s3SettingsSchema, m as defaultClaimMaxLeaseSeconds, o as claimLeaseSecondsSchema } from "./src-
|
|
1
|
+
import { Bt as searchSettingsSchema, Pt as brokerSettingsSchema, Rt as s3SettingsSchema, m as defaultClaimMaxLeaseSeconds, o as claimLeaseSecondsSchema } from "./src-BaTNDJB7.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-
|
|
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-
|
|
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";
|
|
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-
|
|
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";
|
|
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.
|
|
362
|
+
const applicationVersion = "0.6.1";
|
|
363
363
|
//#endregion
|
|
364
364
|
//#region src/realtime.ts
|
|
365
365
|
async function createRealtime(config) {
|
|
@@ -3436,10 +3436,15 @@ async function initializeApplication(database, config, adapters, cleanup) {
|
|
|
3436
3436
|
const result = await receiveRepositoryWebhook(domain, repositories, secrets, c.req.param("connectionId"), c.req.raw.headers, await c.req.text());
|
|
3437
3437
|
return c.json(result, result.outcome === "duplicate" ? 200 : 202);
|
|
3438
3438
|
});
|
|
3439
|
+
const intakeRoutes = [
|
|
3440
|
+
["POST", /^\/api\/v1\/projects\/[^/]+\/tickets$/],
|
|
3441
|
+
["POST", /^\/api\/v1\/projects\/[^/]+\/tickets\/[^/]+\/attachments$/],
|
|
3442
|
+
["GET", /^\/api\/v1\/projects\/[^/]+\/tickets\/[^/]+\/attachments$/]
|
|
3443
|
+
];
|
|
3439
3444
|
app.use("/api/v1/*", async (c, next) => {
|
|
3440
3445
|
access.checkOrigin(c.req.raw);
|
|
3441
3446
|
const actor = await access.authenticate(c.req.raw);
|
|
3442
|
-
if (intakeOnly(actor) && !(c.req.method ===
|
|
3447
|
+
if (intakeOnly(actor) && !intakeRoutes.some(([method, path]) => c.req.method === method && path.test(c.req.path))) fail(403, "insufficient_scope", "Intake credentials can only create tickets and attach files to them");
|
|
3443
3448
|
c.set("actor", actor);
|
|
3444
3449
|
if (!actor.machine && ![
|
|
3445
3450
|
"GET",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as createDatabase, t as readConfig } from "./config-
|
|
1
|
+
import { n as createDatabase, t as readConfig } from "./config-BQw6mPzE.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 { A as hasAdminRole } from "./src-
|
|
3
|
-
import { K as instanceSettings, p as fail, t as createAuth } from "./auth-
|
|
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";
|
|
4
4
|
import { and, eq, isNull, sql } from "drizzle-orm";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { parseArgs } from "node:util";
|
|
@@ -1,5 +1,79 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import i18next from "i18next";
|
|
3
|
+
//#region ../../packages/contracts/src/icons.ts
|
|
4
|
+
const ticketSymbols = {
|
|
5
|
+
status: {
|
|
6
|
+
backlog: {
|
|
7
|
+
icon: "circle-dashed",
|
|
8
|
+
tone: "muted"
|
|
9
|
+
},
|
|
10
|
+
todo: {
|
|
11
|
+
icon: "circle",
|
|
12
|
+
tone: "muted"
|
|
13
|
+
},
|
|
14
|
+
in_progress: {
|
|
15
|
+
icon: "circle-dot",
|
|
16
|
+
tone: "accent"
|
|
17
|
+
},
|
|
18
|
+
in_review: {
|
|
19
|
+
icon: "clock",
|
|
20
|
+
tone: "warning"
|
|
21
|
+
},
|
|
22
|
+
done: {
|
|
23
|
+
icon: "circle-check",
|
|
24
|
+
tone: "success"
|
|
25
|
+
},
|
|
26
|
+
cancelled: {
|
|
27
|
+
icon: "circle-x",
|
|
28
|
+
tone: "muted"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
priority: {
|
|
32
|
+
urgent: {
|
|
33
|
+
icon: "octagon-alert",
|
|
34
|
+
tone: "danger"
|
|
35
|
+
},
|
|
36
|
+
high: {
|
|
37
|
+
icon: "arrow-up",
|
|
38
|
+
tone: "warning"
|
|
39
|
+
},
|
|
40
|
+
medium: {
|
|
41
|
+
icon: "equal",
|
|
42
|
+
tone: "foreground"
|
|
43
|
+
},
|
|
44
|
+
low: {
|
|
45
|
+
icon: "arrow-down",
|
|
46
|
+
tone: "accent"
|
|
47
|
+
},
|
|
48
|
+
none: {
|
|
49
|
+
icon: "minus",
|
|
50
|
+
tone: "muted"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
kind: {
|
|
54
|
+
feature: {
|
|
55
|
+
icon: "sparkles",
|
|
56
|
+
tone: "muted"
|
|
57
|
+
},
|
|
58
|
+
bug: {
|
|
59
|
+
icon: "bug",
|
|
60
|
+
tone: "muted"
|
|
61
|
+
},
|
|
62
|
+
chore: {
|
|
63
|
+
icon: "wrench",
|
|
64
|
+
tone: "muted"
|
|
65
|
+
},
|
|
66
|
+
research: {
|
|
67
|
+
icon: "flask-conical",
|
|
68
|
+
tone: "muted"
|
|
69
|
+
},
|
|
70
|
+
documentation: {
|
|
71
|
+
icon: "book-open",
|
|
72
|
+
tone: "muted"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
//#endregion
|
|
3
77
|
//#region ../../packages/contracts/src/instance.ts
|
|
4
78
|
const endpoint$1 = z.url({ protocol: /^https?$/ }).refine((value) => !/^https?:\/\/[^/]*@/.test(value) && !/[?#]/.test(value), "Use an HTTP(S) endpoint without embedded credentials");
|
|
5
79
|
const s3SettingsSchema = z.object({
|
|
@@ -930,6 +1004,7 @@ var en_US_default = {
|
|
|
930
1004
|
},
|
|
931
1005
|
ticket: {
|
|
932
1006
|
list: {
|
|
1007
|
+
manageViews: "Manage views",
|
|
933
1008
|
selectSection: "Select {{section}} tickets",
|
|
934
1009
|
expandSection: "Expand {{section}} tickets",
|
|
935
1010
|
collapseSection: "Collapse {{section}} tickets",
|
|
@@ -1058,6 +1133,7 @@ var en_US_default = {
|
|
|
1058
1133
|
noDue: "No due date"
|
|
1059
1134
|
},
|
|
1060
1135
|
filter: {
|
|
1136
|
+
add: "Add filter",
|
|
1061
1137
|
me: "Me",
|
|
1062
1138
|
searchTickets: "Search tickets",
|
|
1063
1139
|
searchTicketsPlaceholder: "Search tickets…",
|
|
@@ -1550,7 +1626,7 @@ var en_US_default = {
|
|
|
1550
1626
|
permission: {
|
|
1551
1627
|
intake: {
|
|
1552
1628
|
name: "Submit reports",
|
|
1553
|
-
description: "Create quarantined tickets only. Cannot read or edit tickets. Use alone."
|
|
1629
|
+
description: "Create quarantined tickets and attach files to them only. Cannot read or edit tickets. Use alone."
|
|
1554
1630
|
},
|
|
1555
1631
|
read: {
|
|
1556
1632
|
name: "Read tickets",
|
|
@@ -2497,6 +2573,9 @@ var en_US_default = {
|
|
|
2497
2573
|
}
|
|
2498
2574
|
},
|
|
2499
2575
|
native: {
|
|
2576
|
+
"navigation.projectButton": "{{project}} ⌄",
|
|
2577
|
+
"navigation.recentProjects": "Recent projects",
|
|
2578
|
+
"navigation.switchProject": "Switch workspace or project",
|
|
2500
2579
|
labelValue: "{{label}}: {{value}}",
|
|
2501
2580
|
ui: {
|
|
2502
2581
|
dismiss: "Dismiss",
|
|
@@ -2545,6 +2624,10 @@ var en_US_default = {
|
|
|
2545
2624
|
create: "Create project"
|
|
2546
2625
|
},
|
|
2547
2626
|
tickets: {
|
|
2627
|
+
options: "List options",
|
|
2628
|
+
expandAll: "Expand all sections",
|
|
2629
|
+
collapseAll: "Collapse all sections",
|
|
2630
|
+
select: "Select tickets",
|
|
2548
2631
|
search: "Search tickets",
|
|
2549
2632
|
searchPlaceholder: "Title, description, or key",
|
|
2550
2633
|
allStatuses: "All statuses",
|
|
@@ -2671,6 +2754,7 @@ var en_US_default = {
|
|
|
2671
2754
|
next: "Next activity"
|
|
2672
2755
|
},
|
|
2673
2756
|
inbox: {
|
|
2757
|
+
read: "Read",
|
|
2674
2758
|
markAllRead: "Mark all read",
|
|
2675
2759
|
empty: "No notifications"
|
|
2676
2760
|
},
|
|
@@ -2855,6 +2939,7 @@ const attachmentLimit = 1e7;
|
|
|
2855
2939
|
const stagedUploadLifetime = 864e5;
|
|
2856
2940
|
const stateLabels = labelTable(states, (value) => t(`common.status.${value}`));
|
|
2857
2941
|
const priorityLabels = labelTable(priorities, (value) => t(`common.priority.${value}`));
|
|
2942
|
+
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;
|
|
2858
2943
|
const id = z.string().min(1).max(80);
|
|
2859
2944
|
const timestamp = z.iso.datetime();
|
|
2860
2945
|
const version = z.number().int().positive();
|
|
@@ -4635,7 +4720,7 @@ const operations = {
|
|
|
4635
4720
|
upload_attachment: op(ticketInput.extend({
|
|
4636
4721
|
...mutation,
|
|
4637
4722
|
...fileFields
|
|
4638
|
-
}).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."),
|
|
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."),
|
|
4639
4724
|
stage_upload: op(projectInput.extend({
|
|
4640
4725
|
...mutation,
|
|
4641
4726
|
...fileFields
|
|
@@ -4644,7 +4729,7 @@ const operations = {
|
|
|
4644
4729
|
...mutation,
|
|
4645
4730
|
uploadId: z.uuid()
|
|
4646
4731
|
}).strict(), okSchema, "DELETE", "/projects/{projectId}/uploads/{uploadId}", "write", "Delete one of your unclaimed staged uploads and its bytes."),
|
|
4647
|
-
list_attachments: op(ticketInput, z.array(attachmentSchema), "GET", "/projects/{projectId}/tickets/{ticketId}/attachments", "read", "List authorized attachment metadata."),
|
|
4732
|
+
list_attachments: op(ticketInput, z.array(attachmentSchema), "GET", "/projects/{projectId}/tickets/{ticketId}/attachments", "read", "List authorized attachment metadata. An intake credential can list them on a quarantined ticket it created."),
|
|
4648
4733
|
delete_attachment: op(projectInput.extend({
|
|
4649
4734
|
...mutation,
|
|
4650
4735
|
attachmentId: z.uuid()
|
|
@@ -4714,7 +4799,7 @@ const operations = {
|
|
|
4714
4799
|
}).strict(), okSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/claim/release", "claim", "Release the caller's claim."),
|
|
4715
4800
|
recover_claim: op(ticketInput.extend({
|
|
4716
4801
|
...mutation,
|
|
4717
|
-
claimId: z.uuid(),
|
|
4802
|
+
claimId: z.uuid().optional(),
|
|
4718
4803
|
reason: text.min(10).max(2e3),
|
|
4719
4804
|
confirmedStopped: z.literal(true),
|
|
4720
4805
|
cancelActive: z.boolean().optional(),
|
|
@@ -4723,7 +4808,10 @@ const operations = {
|
|
|
4723
4808
|
}).strict().refine((input) => !input.reschedule || input.expectedVersion !== void 0, {
|
|
4724
4809
|
message: "expectedVersion is required to reschedule recovered work",
|
|
4725
4810
|
path: ["expectedVersion"]
|
|
4726
|
-
})
|
|
4811
|
+
}).refine((input) => input.claimId !== void 0 || input.expectedVersion !== void 0, {
|
|
4812
|
+
message: "expectedVersion is required to recover ownership without a claim",
|
|
4813
|
+
path: ["expectedVersion"]
|
|
4814
|
+
}), okSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/claim/recover", "claim", "Recover an expired claim after confirming the worker stopped. Optionally reschedule it to To do at its expectedVersion. Project managers with admin scope may explicitly cancel an active claim. Omit claimId, with the ticket's expectedVersion, to lift the ownership requirement of a ticket that has no claim left, such as one released outside To do."),
|
|
4727
4815
|
set_subscription: op(ticketInput.extend({
|
|
4728
4816
|
...mutation,
|
|
4729
4817
|
enabled: z.boolean()
|