@maudecode/kanvil 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/server/.env.example +5 -0
- package/apps/server/dist/{auth-CnAQzKyq.mjs → auth-Cs_whzgk.mjs} +382 -127
- package/apps/server/dist/client.mjs +4 -3
- package/apps/server/dist/{config-DOT1SDQO.mjs → config-DKuNQY5b.mjs} +15 -7
- package/apps/server/dist/index.mjs +105 -57
- package/apps/server/dist/migrate.mjs +2 -2
- package/apps/server/dist/recover.mjs +4 -4
- package/apps/server/dist/{src-VxR5jZiK.mjs → src-DptOUNRd.mjs} +67 -23
- package/apps/web/dist/assets/{CompositeItem-B5Qlh5m0.js → CompositeItem-CXqewtEL.js} +1 -1
- package/apps/web/dist/assets/{Trans-DxqBaUXP.js → Trans-B1rRlWca.js} +1 -1
- package/apps/web/dist/assets/{admin-settings-Bp7V2t1V.js → admin-settings-DE3MfBFE.js} +1 -1
- package/apps/web/dist/assets/{archive-restore-wsAzhqi-.js → archive-restore-76sStk8K.js} +1 -1
- package/apps/web/dist/assets/az-AZ-bFxpMMZN.js +1 -0
- package/apps/web/dist/assets/{az-C4KDjOOw.js → az-B7OgHoaE.js} +1 -1
- package/apps/web/dist/assets/{corner-left-up-arA2ipmM.js → corner-left-up-PD-73Nae.js} +1 -1
- package/apps/web/dist/assets/{de-Z599ZW7b.js → de-B-bv38Q0.js} +1 -1
- package/apps/web/dist/assets/de-DE-BeXI30Ll.js +1 -0
- package/apps/web/dist/assets/default-bQFGTwIp.js +56 -0
- package/apps/web/dist/assets/{delivery-settings-D5NKPlhN.js → delivery-settings-BvMeOsCS.js} +1 -1
- package/apps/web/dist/assets/{el-xsvq992p.js → el-BQHQHQgX.js} +1 -1
- package/apps/web/dist/assets/el-GR-SgRHlWhY.js +1 -0
- package/apps/web/dist/assets/{ellipsis-UOkL14vq.js → ellipsis-GF1HwTDb.js} +1 -1
- package/apps/web/dist/assets/es-ES-BF1J5vap.js +1 -0
- package/apps/web/dist/assets/{es-D3e9L5_i.js → es-f5Xr8ldr.js} +1 -1
- package/apps/web/dist/assets/{forms-BbWAl0BZ.js → forms-BlEL8C_P.js} +1 -1
- package/apps/web/dist/assets/{fr-CY35Bcpp.js → fr-DSR0htnl.js} +1 -1
- package/apps/web/dist/assets/fr-FR-D_sldwf6.js +1 -0
- package/apps/web/dist/assets/{hi-Cqfv-E5P.js → hi-BaAJEWuW.js} +1 -1
- package/apps/web/dist/assets/hi-IN-CfOw4TWf.js +1 -0
- package/apps/web/dist/assets/{highlight-Ds7xUUQv.js → highlight-DXT2xgt8.js} +1 -1
- package/apps/web/dist/assets/id-ID-CLKtHO7Q.js +1 -0
- package/apps/web/dist/assets/{id-CFBOVDUC.js → id-kxogWcip.js} +1 -1
- package/apps/web/dist/assets/{index-2V6MWx63.js → index-CYVrs22O.js} +5 -5
- package/apps/web/dist/assets/{invitations-BPk5fwwU.js → invitations-BPik3J_G.js} +1 -1
- package/apps/web/dist/assets/{it-DNLponcC.js → it-C_qk40U6.js} +1 -1
- package/apps/web/dist/assets/it-IT-eVZNW5qX.js +1 -0
- package/apps/web/dist/assets/{item-DED0Ikqi.js → item-TCHuZNK6.js} +1 -1
- package/apps/web/dist/assets/{ja-ZXGlS4WA.js → ja-Dn5h64V9.js} +1 -1
- package/apps/web/dist/assets/ja-JP-D96njn-P.js +1 -0
- package/apps/web/dist/assets/{ko-CSoj1hQM.js → ko-CjaoEPuG.js} +1 -1
- package/apps/web/dist/assets/ko-KR-Bash_ieA.js +1 -0
- package/apps/web/dist/assets/{label-settings-D12QOkDs.js → label-settings-DZTorNDm.js} +1 -1
- package/apps/web/dist/assets/{markdown-blocks-DbsqN1T2.js → markdown-blocks-BQLO-H_7.js} +1 -1
- package/apps/web/dist/assets/{member-directory-BRhEIYZ6.js → member-directory-Dx_jQYfI.js} +1 -1
- package/apps/web/dist/assets/mk-MK-BWQWGIRx.js +1 -0
- package/apps/web/dist/assets/{mk-D77vceoz.js → mk-jLeGD50T.js} +1 -1
- package/apps/web/dist/assets/{nl-Bew8-xmX.js → nl-AVtC5ygT.js} +1 -1
- package/apps/web/dist/assets/nl-NL-D1tvo0Gd.js +1 -0
- package/apps/web/dist/assets/{pl-CEBl_oa9.js → pl-Dup1h_Y1.js} +1 -1
- package/apps/web/dist/assets/pl-PL-DNgGUEcQ.js +1 -0
- package/apps/web/dist/assets/{project-lifecycle-DGHAmEB2.js → project-lifecycle-n1ZZbYfu.js} +1 -1
- package/apps/web/dist/assets/{project-settings-DY8Ossd-.js → project-settings-B_Ih5kiz.js} +1 -1
- package/apps/web/dist/assets/pt-BR-CZsUZQ6P.js +1 -0
- package/apps/web/dist/assets/{pt-BR-BDTQeKcp.js → pt-BR-Cm04EWM-.js} +1 -1
- package/apps/web/dist/assets/{public-project-BeXla8TL.js → public-project-BivDjU_a.js} +1 -1
- package/apps/web/dist/assets/{rotate-ccw-DGM890Fc.js → rotate-ccw-CSaKhZYt.js} +1 -1
- package/apps/web/dist/assets/{ru-BeH0eXQq.js → ru-IeycXsyE.js} +1 -1
- package/apps/web/dist/assets/ru-RU-4ew92YE_.js +1 -0
- package/apps/web/dist/assets/{search-B3-w3oCd.js → search-Ble6WSEU.js} +3 -3
- package/apps/web/dist/assets/{send-B9baaCnt.js → send-BLMZ8gQ0.js} +1 -1
- package/apps/web/dist/assets/settings-CH48qQK2.js +1 -0
- package/apps/web/dist/assets/{sliders-horizontal-yKgghk8h.js → sliders-horizontal-Db6kRvuw.js} +1 -1
- package/apps/web/dist/assets/{switch-ByOrazEW.js → switch-BeX-0Fwq.js} +1 -1
- package/apps/web/dist/assets/{table-Cm6NQXfq.js → table-UJp5S9yy.js} +1 -1
- package/apps/web/dist/assets/ticket-detail-C24opfNW.js +2 -0
- package/apps/web/dist/assets/{ticket-editor-CLDBUk0n.js → ticket-editor--d_bG9C1.js} +2 -2
- package/apps/web/dist/assets/{ticket-list-BC9acNl4.js → ticket-list-DRbZWYHf.js} +1 -1
- package/apps/web/dist/assets/{ticket-list-controls-CRQk5ghe.js → ticket-list-controls-fYBottFC.js} +1 -1
- package/apps/web/dist/assets/{tr-BNApk6Hc.js → tr-DtT_dKUK.js} +1 -1
- package/apps/web/dist/assets/tr-TR-CM381XCw.js +1 -0
- package/apps/web/dist/assets/{uk-CjShsgpN.js → uk-CsbeHSS6.js} +1 -1
- package/apps/web/dist/assets/uk-UA-AeOiWCVo.js +1 -0
- package/apps/web/dist/assets/{upload-BzLq4CMS.js → upload-DUEg04Nj.js} +1 -1
- package/apps/web/dist/assets/{useQueries-D64vLhM6.js → useQueries-5lW2C5Sb.js} +1 -1
- package/apps/web/dist/assets/vi-VN-td08xXv9.js +1 -0
- package/apps/web/dist/assets/{vi-s5Hfv1Y-.js → vi-rkf1BOLx.js} +1 -1
- package/apps/web/dist/assets/{visual-editor-KE96mwCg.js → visual-editor-DkFLjouD.js} +1 -1
- package/apps/web/dist/assets/{workspace-settings-C-Phmi9i.js → workspace-settings-Bcsp-VoJ.js} +1 -1
- package/apps/web/dist/assets/zh-CN-D3dhEYLS.js +1 -0
- package/apps/web/dist/assets/{zh-CN-BBnpHJ9R.js → zh-CN-Xo-QId5w.js} +1 -1
- package/apps/web/dist/assets/zh-TW-CfhOEFo8.js +1 -0
- package/apps/web/dist/assets/{zh-TW-sSWkqSLN.js → zh-TW-DEnTqjbn.js} +1 -1
- package/apps/web/dist/index.html +7 -7
- package/docs/api.md +27 -13
- package/docs/cli.md +9 -1
- package/docs/releasing.md +16 -2
- package/docs/self-hosting.md +8 -0
- package/package.json +1 -1
- package/apps/web/dist/assets/az-AZ-DlsPlUZi.js +0 -1
- package/apps/web/dist/assets/de-DE-DLCaqUUe.js +0 -1
- package/apps/web/dist/assets/default-6QIAyMq2.js +0 -56
- package/apps/web/dist/assets/el-GR-P2XcwrSU.js +0 -1
- package/apps/web/dist/assets/es-ES-CJoX8f3w.js +0 -1
- package/apps/web/dist/assets/fr-FR-yLkQdy6a.js +0 -1
- package/apps/web/dist/assets/hi-IN-y8dqej7z.js +0 -1
- package/apps/web/dist/assets/id-ID-DSvh8mGH.js +0 -1
- package/apps/web/dist/assets/it-IT-BUcfUqNo.js +0 -1
- package/apps/web/dist/assets/ja-JP-C-jLP_Ay.js +0 -1
- package/apps/web/dist/assets/ko-KR-C5Ozdm2E.js +0 -1
- package/apps/web/dist/assets/mk-MK-BdM_SsE8.js +0 -1
- package/apps/web/dist/assets/nl-NL-CwjB5sva.js +0 -1
- package/apps/web/dist/assets/pl-PL-6-PMhTBd.js +0 -1
- package/apps/web/dist/assets/pt-BR-TD_wt6Hr.js +0 -1
- package/apps/web/dist/assets/ru-RU-Bk1a6Oln.js +0 -1
- package/apps/web/dist/assets/settings-BKZfEkVk.js +0 -1
- package/apps/web/dist/assets/ticket-detail-Cd3_e2jz.js +0 -2
- package/apps/web/dist/assets/tr-TR-CUfBb7K9.js +0 -1
- package/apps/web/dist/assets/uk-UA-BDaXBsDP.js +0 -1
- package/apps/web/dist/assets/vi-VN-Bgb-L6mE.js +0 -1
- package/apps/web/dist/assets/zh-CN-BypkvBe6.js +0 -1
- package/apps/web/dist/assets/zh-TW-CgMsT_XS.js +0 -1
package/docs/api.md
CHANGED
|
@@ -8,7 +8,13 @@ Runnable MCP and durable realtime examples, plus the worker ownership sequence,
|
|
|
8
8
|
|
|
9
9
|
Browser and native clients use Better Auth sessions with local login or configured OIDC. Public registration is closed. Initial owner setup requires `KANVIL_SETUP_TOKEN` and is available only before any account exists.
|
|
10
10
|
|
|
11
|
-
Unattended consumers use revocable API credentials in `Authorization: Bearer <key>`. Create credentials through an authenticated account at `POST /api/v1/credentials`, with a name, `projectIds`, `read`, `write`, `claim`, or `
|
|
11
|
+
Unattended consumers use revocable API credentials in `Authorization: Bearer <key>`. Create credentials through an authenticated account at `POST /api/v1/credentials`, with a name, `projectIds`, `read`, `write`, `claim`, `admin`, or `intake` scopes, and an optional `expiresIn` in seconds (one hour to one year). An omitted `expiresIn` expires the key after 90 days; `null` creates a key that never expires. Pass a nonempty `projectIds` array for a project-scoped key, or explicit `projectIds: null` for a global key. Omitted or empty project lists are invalid. Global keys follow the owning account's current project and workspace access, including future grants; existing project-scoped keys retain their allowlists. Both key types obey operation scopes and current account roles, and neither can perform actions that require a signed-in session, including credential management and instance administration. A scope never grants more access than the owning account has. The secret is returned only on creation. Delete a credential at `/api/v1/credentials/{id}` to revoke it.
|
|
12
|
+
|
|
13
|
+
The `intake` scope is create-only and cannot be combined with another scope (`400 validation_error`). It allows only `create_ticket` over HTTP or MCP in projects the credential and its owning account can write. It grants no read access, including ticket lookup, search, snapshots, events, or attachment downloads, and cannot update, delete, move, comment on, or attach files to tickets. Other authenticated operations return `403 insufficient_scope`.
|
|
14
|
+
|
|
15
|
+
Intake creation returns only the new ticket's normal payload. The server forces `automationReady: false`, `holdReason: "Untrusted intake: human triage required"`, the first To do state, no assignee or parent, `archived: false`, no dates, empty custom fields (including defaults and required fields), and no uploads. It preserves the supplied title, description, requirements, kind, difficulty, priority, labels, and scopes, and adds the `intake` label. The 30-label limit includes that label. Reusing the same `requestId` returns the original ticket without creating another. A human session or a credential with ordinary write access can triage it later and enable automation. Eligibility inspection includes its explicit not-ready reasons until triage clears the hold and enables automation.
|
|
16
|
+
|
|
17
|
+
Repository issue sync uses the server-forced intake hold as a quarantine marker: neither export planning nor event-driven export includes the ticket until a trusted writer clears that hold. Already queued issue-sync deliveries recheck the hold before contacting the repository. Ordinary tickets without automation readiness still follow their configured sync rules.
|
|
12
18
|
|
|
13
19
|
## Writes and ownership
|
|
14
20
|
|
|
@@ -16,9 +22,9 @@ Use the ticket's current `version` as `expectedVersion` for updates. A stale ver
|
|
|
16
22
|
|
|
17
23
|
Reuse the same UUID `requestId` when retrying a write. Reusing it with a different operation or payload returns `409 idempotency_conflict`. Bulk edits either all commit or all roll back, and publish one `ticket.updated` event per ticket with consecutive IDs in a single batch.
|
|
18
24
|
|
|
19
|
-
Signed-in sessions may make `KANVIL_SESSION_WRITE_LIMIT` write requests per minute (120 by default) to `/api/v1`, and MCP OAuth clients get the same budget for write tools; exceeding it returns `429 rate_limited` with a `Retry-After` header. This counter is per application process. API keys have a separate limit of 600 requests per minute per key across HTTP and MCP. An instance operator can set `KANVIL_API_KEY_RATE_LIMIT_ENABLED=false` and restart the server to disable that limit for existing and new keys. The default is `true`; restoring it enforces the limit again. Session, MCP OAuth, and sign-in limits are unchanged, as are key expiration, revocation, scopes, and account permissions.
|
|
25
|
+
Signed-in sessions may make `KANVIL_SESSION_WRITE_LIMIT` write requests per minute (120 by default) to `/api/v1`, and MCP OAuth clients get the same budget for write tools; exceeding it returns `429 rate_limited` with a `Retry-After` header. This counter is per application process. API keys have a separate limit of 600 requests per minute per key across HTTP and MCP. An instance operator can set `KANVIL_API_KEY_RATE_LIMIT_ENABLED=false` and restart the server to disable that limit for existing and new keys. The default is `true`; restoring it enforces the limit again. Session, MCP OAuth, and sign-in limits are unchanged, as are key expiration, revocation, scopes, and account permissions. Authentication rereads each key and skips timestamp-only bookkeeping writes. When a key is unlimited, authentication leaves its stored `last_request` and `updated_at` unchanged; enabled rate limits and usage quotas still consume their allowance synchronously and atomically. A request that consumed a key's last use is admitted even when a concurrent request finds the key exhausted and deletes it first.
|
|
20
26
|
|
|
21
|
-
Claims require an eligible ticket and current version. Successful claiming moves it to In progress. Renew an unexpired claim using its ID and the same credential. Once a ticket has been claimed, machine ticket updates and evidence submissions
|
|
27
|
+
Claims require an eligible ticket and current version. Successful claiming moves it to In progress. Renew an unexpired claim using its ID and the same credential. Once a ticket has been claimed, machine ticket updates and evidence submissions require a current matching claim ID, including after release. Recovery without `reschedule` lifts that requirement and increments the ticket version, publishing `ticket.updated` with `changedFields: ["claimRequired"]`, so a ticket left outside To do (for example, In review after its work merged) accepts ordinary worker writes again; a write that still names the recovered claim ID returns `409 claim_conflict`. Submit completion and evidence before releasing ownership. Human sessions can edit and reschedule work without a machine claim. Release unexpired ownership after the worker stops or completes. The ticket response exposes `canReleaseClaim` for the exact credential or client identity; sharing an account does not confer ownership. An expired claim remains blocking until a caller with `claim` scope and `ticket.write` permission confirms the old worker stopped and explicitly calls `recover_claim` with its ID, a reason, and `confirmedStopped: true`. Administrator access is not required for expired claims. If a worker has stopped while its claim is still live, for example after revoking its credential, a caller with `project.manage` and `admin` scope can explicitly pass `cancelActive: true` to cancel it before expiry. This still requires `confirmedStopped: true` and a reason, and the recovery event records the cancellation. Without that flag, live claims remain protected. Recovery does not automatically reschedule work or grant a replacement claim. To resume without a human session, explicitly pass `reschedule: true` and the ticket's current `expectedVersion` to `recover_claim`; this returns it to To do and increments its version atomically with recovery. Then call `get_ticket` and `claim_ticket` with the new version for a fresh claim ID. A missing version returns `400 validation_error`, and a stale one returns `409 version_conflict`, leaving the claim and status unchanged. Initial claims and renewals accept `leaseSeconds` of at least 30, bounded by `KANVIL_CLAIM_MAX_LEASE_SECONDS`, which defaults to 86400 (24 hours). The default lease remains 300 seconds, or the configured maximum if smaller. A request above the configured maximum returns `400 validation_error`; renew before expiry to keep ownership.
|
|
22
28
|
|
|
23
29
|
Evidence records identify the submitting account and supplied commit. A reported passing outcome is an assertion by that identity, not independent verification by Kanvil.
|
|
24
30
|
|
|
@@ -36,15 +42,19 @@ The property filters `status`, `priority`, `kind`, `difficulty`, `label`, `assig
|
|
|
36
42
|
|
|
37
43
|
`list_my_work` (`GET /api/v1/my-work`) pages through the open, unarchived tickets assigned to the caller across every project they can read, optionally narrowed to one `workspaceId`. It accepts `q`, `sort`, and `order` like `list_tickets`, plus `limit` and `offset`; `total` counts every match so clients can fetch the remaining pages. Set `activity` (up to 50) to also receive that many of the most recent events from the same accessible projects, newest first. Revoked memberships and project-bound credentials narrow the tickets, totals, and activity alike.
|
|
38
44
|
|
|
39
|
-
`search_tickets` (`GET /api/v1/tickets/search?q=...`) finds unarchived tickets by exact key or text across every project the caller can read, optionally limited to one `workspaceId`, returning each ticket with its project `id`, `key`, and `name`. Exact key matches sort first.
|
|
45
|
+
`search_tickets` (`GET /api/v1/tickets/search?q=...`) finds unarchived tickets by exact key or text across every project the caller can read, optionally limited to one `workspaceId`, returning each ticket with its project `id`, `key`, and `name`. Exact key matches sort first, then the most relevant matches (the external index's score, or on PostgreSQL title matches before matches only in other text), then the most recently updated.
|
|
46
|
+
|
|
47
|
+
Ticket text queries in `list_tickets`, `list_my_work`, `search_tickets`, and `search_workspace` treat each double-quoted segment of `q` as an exact phrase: `q="live activity" acknowledge` needs the phrase `live activity` and the term `acknowledge`. Terms outside quotes keep fuzzy matching on the external index, and on PostgreSQL they must appear together as written. An unpaired quote is an ordinary character. Repeated phrases count once, and only the first 8 distinct phrases count.
|
|
40
48
|
|
|
41
|
-
`
|
|
49
|
+
`list_tickets`, `search_tickets`, and `search_workspace` take `view`: `full` (the default) or `summary`. A summary row has only `id`, `key`, `number`, `projectId`, `title`, `status`, `stateId`, `kind`, `difficulty`, `priority`, `labels`, `scopes`, `archived`, `automationReady`, `updatedAt`, and `version`, so agents can scan many results without every description and acceptance criteria. In summary view each `search_tickets` result and `search_workspace` result also carries `snippet`: up to 240 characters of its body text (a ticket's description and acceptance criteria) around the first literal match of a phrase or term, or empty when only the title, key, or a fuzzy term matched; a `search_workspace` result's `ticket` is its summary row.
|
|
42
50
|
|
|
43
|
-
|
|
51
|
+
`search_workspace` (`GET /api/v1/search?q=...`) searches projects, unarchived tickets, comments on unarchived tickets, and activity across every project the caller can read, optionally within one `workspaceId`. Each result has a `type` (`project`, `ticket`, `comment`, or `activity`), a stable `id` (the project, ticket, or comment ID, or the event ID), its `project`, its `ticket` (`id`, `key`, `title`, `status`, or `null`), a display `title`, an `excerpt` around the first literal match, `actorId`, and `at`. Activity matches its event label, its ticket key, or the name of an active (not disabled) actor. Results order by exact key match, then type in the order above, then ticket relevance as in `search_tickets`, then most recent first. `limit` (1–50, default 20) and `offset` (up to 1000) page through them; `type` narrows the page to one kind while `counts` still reports every kind and `total` counts the page's kind. The property filters above restrict results to matching tickets and their comments and activity, and leave out projects. Ticket text uses the external index when one is configured and current (`engine: "index"`); otherwise PostgreSQL answers (`engine: "postgres"`), and `indexing: true` says an index is configured but behind or unavailable. Projects, comments, and activity always come from PostgreSQL, and every result is filtered by current project access before counts and excerpts are computed.
|
|
52
|
+
|
|
53
|
+
Use `list_tickets` with `parentId` set to a parent UUID or ticket key to page through child tickets. Set `archived: "include"` to include archived children. Set `relatedTo` to a ticket UUID or key to page through the tickets linked to it by any relationship, in either direction. `list_relations` also returns the current source/target keys, full titles, and projects, so consumers can show readable dependency context without separate lookups. `relatedTo` lists only the linked tickets in the current project; `list_relations` includes links to other projects.
|
|
44
54
|
|
|
45
55
|
`duplicate_ticket` (`POST /api/v1/projects/{projectId}/tickets/{ticketId}/duplicate`) creates a new ticket in the same project with the next key, the original title followed by ` (copy)` (shortened to fit 500 characters), and the caller as `createdBy`. It copies the description, acceptance criteria, priority, kind, difficulty, due date, labels, scopes, and parent, and the assignee while they are still a project member. The copy starts in To do, unarchived, with `automationReady` false and no hold reason; the original's claims, comments, evidence, attachments, relationships, and children are not copied. A `clones` relationship links the copy to the original. One transaction writes the ticket, the relationship, a `ticket.created` event carrying `duplicateOf` (the original ID), and `relation.added` events for both tickets. It requires `write`; reuse `requestId` to retry without creating a second copy.
|
|
46
56
|
|
|
47
|
-
`bulk_update_tickets` (`POST /api/v1/projects/{projectId}/tickets/bulk`) applies one set of `changes` to up to 100 tickets, and `bulk_delete_tickets` (`POST /api/v1/projects/{projectId}/tickets/bulk-delete`) deletes up to 100. Both take `tickets` as `{ ticketId, expectedVersion }` pairs (UUID or key)
|
|
57
|
+
`bulk_update_tickets` (`POST /api/v1/projects/{projectId}/tickets/bulk`) applies one set of `changes` to up to 100 tickets, and `bulk_delete_tickets` (`POST /api/v1/projects/{projectId}/tickets/bulk-delete`) deletes up to 100. Both take `tickets` as `{ ticketId, expectedVersion }` pairs (UUID or key); a bulk edit item also takes an optional `claimId`, which a worker credential passes for each ticket it has claimed, exactly as on `update_ticket`. Both are all or nothing: one missing ticket, stale version, missing or mismatched worker claim (for an edit), or active claim (for deletion) rejects the whole request with `not_found`, `version_conflict`, or `claim_conflict` (for deletion, its `details.ticketId` names the claimed ticket) before anything changes. Naming one ticket twice, for example by key and by UUID, is a `validation_error`. Deletion requires `admin`, like `delete_ticket`; children outside the selection lose their parent with a `ticket.updated` event, followed by one `ticket.deleted` event per ticket in the same transaction. Reuse `requestId` to retry either write.
|
|
48
58
|
|
|
49
59
|
`list_ticket_labels` (`GET /api/v1/projects/{projectId}/labels`) returns distinct label names and ticket counts for the whole authorized project. Its `archived` option matches ticket listing. The `label` ticket filter matches exact labels; combine it with other filters or saved views. `sort: "labels"` orders by the first label alphabetically, ignoring case and label-array order, followed by ticket number. Tickets without labels come last.
|
|
50
60
|
|
|
@@ -52,6 +62,8 @@ Use `list_tickets` with `parentId` set to a parent UUID or ticket key to page th
|
|
|
52
62
|
|
|
53
63
|
Project administrators can `create_project_label` (`POST .../labels`), `update_project_label` (`PATCH .../labels/{labelId}`), and `delete_project_label` (`DELETE .../labels/{labelId}`) through HTTP or MCP. Creation takes `name` and optional `color`; updates take `name`, `color`, and `expectedVersion`; deletion takes `expectedVersion`. Renames update every affected ticket, including archived tickets, and saved-view label filters. Deletion removes the label from tickets and removes its filter from saved views, retaining other filters. These changes require label-administration permission and commit atomically, increment affected ticket versions, and emit normal `ticket.updated` events plus `label.created`, `label.updated`, or `label.deleted`. Recoloring emits a label event without changing ticket versions. Realtime consumers can refresh the label catalog on label events. Label-definition renames and deletions propagate only labels without requiring or changing affected tickets' worker claims, including completed tickets whose ownership requirement remains recorded. Ordinary ticket and bulk edits still enforce worker ownership; automation readiness and other ticket fields remain unchanged.
|
|
54
64
|
|
|
65
|
+
To relabel one ticket, project administrators use `update_ticket_labels` (`PATCH /api/v1/projects/{projectId}/tickets/{ticketId}/labels`) with `expectedVersion`, the replacement `labels` (an empty array clears them), and an optional `requestId`. It requires `admin` and takes nothing else: status, readiness, hold reason, scopes, and content stay with `update_ticket`. It neither requires nor changes a worker claim, so it also works on a completed ticket whose ownership requirement remains recorded and on a ticket another worker holds, live or expired; the claim, its expiry, and readiness are unchanged. New label names register like any ticket write, the version increments, and one `ticket.updated` event lists `labels` as the changed field. `update_ticket` and `bulk_update_tickets` keep enforcing worker ownership, even for a label-only change.
|
|
66
|
+
|
|
55
67
|
Workspace administrators manage shared labels with `list_workspace_labels` (`GET /api/v1/workspaces/{workspaceId}/labels`), `create_workspace_label` (`POST .../labels`), `update_workspace_label` (`PATCH .../labels/{labelId}`), and `delete_workspace_label` (`DELETE .../labels/{labelId}`); any workspace member can list them. Inputs match the project operations. List counts include only tickets in projects the caller can read, so private projects never show up in them. A rename or deletion updates tickets, saved-view filters, and calendar feed filters in every project of the workspace in one transaction with label-administration permission and version checks. Worker claims remain unchanged and do not block this label-only propagation; any failed permission or version check rolls the whole change back. The label event goes to each project of the workspace with `scope: "workspace"` and `workspaceId`.
|
|
56
68
|
|
|
57
69
|
Within a workspace a name belongs to the workspace or to projects, never both, so a ticket label always resolves to one color. Creating or renaming a project label onto a workspace label's name fails with `409 duplicate_label`. Creating or renaming a workspace label onto a name that projects already define fails with `409 label_conflict`, whose `details.projects` lists their keys. Retry with `adopt: true` to replace those project definitions: their tickets keep the label and show the workspace color, and a ticket that carries both names after an adopting rename keeps one.
|
|
@@ -64,9 +76,9 @@ A key such as `ABC-12` is the project prefix and the ticket's number in that pro
|
|
|
64
76
|
|
|
65
77
|
Prefixes were permanent before this release; they are now editable and permanently reserved instead. A project administrator changes one with `update_project` and a new `key` (same format as at creation) at the project's `expectedVersion`. Every ticket of the project takes the new prefix with its number unchanged, new tickets keep counting, and the old prefix and every key issued under it keep resolving, so existing links and stored keys keep working. Ticket versions do not change. A prefix that any other project has or had fails with `409 duplicate_prefix`; a project can return to one of its own. The write publishes `project.updated` with `key` and `previousKey`, on which a configured external search index reindexes every ticket of the project. Clients that cache keys should refresh project tickets on that event.
|
|
66
78
|
|
|
67
|
-
`check_ticket_relocation` (`GET /api/v1/projects/{projectId}/tickets/{ticketId}/relocation?destinationProjectId=...`) previews a move to another project of the same workspace. It returns the `destination` (`id`, `key`, `name`), its workflow `states`, the default `stateId` (the destination's first state of the ticket's current status), and `blockers`. Each blocker has a `code` (`claimed`, `parent`, `children`, `relations`, `assignee`, `custom_fields`, or `required_fields`), a `message`, and for hierarchy and relationship blockers the related `ticketId`. Kanvil never severs a claim, a parent link, children, or relationships to make a move succeed, never drops an assignee who cannot access the destination, and never discards custom field values, which belong to the source project's fields; resolve them first. A destination field that is required and has no default also blocks the move, since the moved ticket takes the destination's custom field defaults.
|
|
79
|
+
`check_ticket_relocation` (`GET /api/v1/projects/{projectId}/tickets/{ticketId}/relocation?destinationProjectId=...`) previews a move to another project of the same workspace. It returns the `destination` (`id`, `key`, `name`), its workflow `states`, the default `stateId` (the destination's first state of the ticket's current status), and `blockers`. Each blocker has a `code` (`claimed`, `claim_conflict`, `parent`, `children`, `relations`, `assignee`, `custom_fields`, or `required_fields`), a `message`, and for hierarchy and relationship blockers the related `ticketId`. `claim_conflict` reaches only machine callers: a ticket that has been claimed keeps requiring ownership for worker writes after its claim is released or recovered with `reschedule`, so a worker cannot move it without a claim. A person can still move it, or return it to To do so a worker can claim it again. Kanvil never severs a claim, a parent link, children, or relationships to make a move succeed, never drops an assignee who cannot access the destination, and never discards custom field values, which belong to the source project's fields; resolve them first. A destination field that is required and has no default also blocks the move, since the moved ticket takes the destination's custom field defaults.
|
|
68
80
|
|
|
69
|
-
`relocate_ticket` (`POST /api/v1/projects/{projectId}/tickets/{ticketId}/relocate`) moves the ticket with its `expectedVersion`, `destinationProjectId` (ID or key), and optionally `status`, `stateId`, or both, resolved in the destination like a ticket update's. Both projects require `write`, so a project-bound credential can move tickets only between projects it is bound to; a destination the caller cannot reach reads as `404 not_found`, an archived destination is a `409 project_archived`, and a different workspace or the same project is a `400 validation_error`. An archived source refuses the move like any other write. Any blocker fails the move with `409 relocation_blocked` and `details.blockers`. Machine callers follow the claim rules of `update_ticket
|
|
81
|
+
`relocate_ticket` (`POST /api/v1/projects/{projectId}/tickets/{ticketId}/relocate`) moves the ticket with its `expectedVersion`, `destinationProjectId` (ID or key), and optionally `status`, `stateId`, or both, resolved in the destination like a ticket update's. Both projects require `write`, so a project-bound credential can move tickets only between projects it is bound to; a destination the caller cannot reach reads as `404 not_found`, an archived destination is a `409 project_archived`, and a different workspace or the same project is a `400 validation_error`. An archived source refuses the move like any other write. Any blocker fails the move with `409 relocation_blocked` and `details.blockers`. Machine callers follow the claim rules of `update_ticket`, reported as the `claimed` or `claim_conflict` blocker so the preview predicts the result. The ticket keeps its ID, content, comments, attachments, evidence, links, and followers, takes the destination's next number as its new key, bumps its version, and its labels register in the destination. Every key it had in the source, under each prefix the source has used, keeps resolving to it in the destination and through `resolve_ticket`, never in the source. Retrying with the same `requestId` returns the moved ticket only while the caller can still write to both projects. Files embedded before the move keep loading from their original `/attachments/{id}/content` address, authorized in the ticket's current project.
|
|
70
82
|
|
|
71
83
|
One transaction publishes `ticket.moved_away` to the source with `ticketId: null` and `data` holding only the ticket's `ticketId`, old `key`, and `title`, so source readers drop it without learning the destination (the source's outbound destinations receive it as an update), then `ticket.moved` to the destination with the ticket's ID, `from` (the old key), the new `key`, and `version`. Followers who can read the destination are notified once. Activity recorded before the move stays with the source project and no longer refers to the ticket there: search results for it carry `ticket: null`, ticket filters skip it, and queued source notifications treat the ticket as gone.
|
|
72
84
|
|
|
@@ -98,17 +110,19 @@ Entries follow ticket moves with unchanged IDs, attribution, and versions. Every
|
|
|
98
110
|
|
|
99
111
|
Each project has named workflow states. Every state maps to one of the six canonical statuses (`backlog`, `todo`, `in_progress`, `in_review`, `done`, `cancelled`), its category. A ticket stores one state, `stateId`. Its `status` is that state's category, kept equal by the database, so every rule that reads `status` reads the category: open and closed counts (`done` and `cancelled` close work), readiness (a ticket must be in a To do state), finished dependencies, filters, `sort: "status"` (category, then the state's place), claims, notifications, and calendar feeds. New projects and projects upgraded from earlier versions start with one state per category, whose `slug` is the category name.
|
|
100
112
|
|
|
101
|
-
`list_workflow_states` (`GET /api/v1/projects/{projectId}/workflow`) returns the states in display order (by category, then `position`) with `id`, `slug`, `name`, `category`, `color` (a label color; `default` uses the category's tint), `position`, `version`, and `count` (tickets in the state, archived included). Project administrators `create_workflow_state` (`POST …/workflow`) with `name`, `category`, and optional `color`; the state joins the end of its category, and its `slug` comes from the first name and never changes. `update_workflow_state` (`PATCH …/workflow/{stateId}`) takes `expectedVersion` and any of `name`, `color`, `category`, and `position` (the state's new place within its category). A category change moves every ticket in the state to that status, increments their versions,
|
|
113
|
+
`list_workflow_states` (`GET /api/v1/projects/{projectId}/workflow`) returns the states in display order (by category, then `position`) with `id`, `slug`, `name`, `category`, `color` (a label color; `default` uses the category's tint), `position`, `version`, and `count` (tickets in the state, archived included). Project administrators `create_workflow_state` (`POST …/workflow`) with `name`, `category`, and optional `color`; the state joins the end of its category, and its `slug` comes from the first name and never changes. `update_workflow_state` (`PATCH …/workflow/{stateId}`) takes `expectedVersion` and any of `name`, `color`, `category`, and `position` (the state's new place within its category). A category change moves every ticket in the state to that status, increments their versions, and publishes `ticket.updated` with `changedFields: ["status"]` for each. `delete_workflow_state` (`DELETE …/workflow/{stateId}`) takes `expectedVersion` and `reassignTo`, another state of the project; its tickets, archived included, move there through the versioned ticket write before the state is removed. Both migrations follow the definition ownership rule below. State names are unique within a project ignoring case (`409 duplicate_state`), and every category keeps at least one state: recategorizing or deleting its last one returns `409 last_state`. A project holds at most 50 states. These writes publish `workflow.created`, `workflow.updated`, or `workflow.deleted` with the `stateId`.
|
|
102
114
|
|
|
103
115
|
Ticket writes take `status`, `stateId`, or both. A `stateId` must name a state of the ticket's project, and a `status` written with it must equal that state's category; otherwise the write fails with `400 validation_error` instead of choosing a state. A `status` alone keeps the ticket's state when the category is unchanged and otherwise moves it to that category's first state, so clients that predate workflows keep working. Creation without either starts in the first To do state, duplication copies into it, and claiming moves the ticket to the first In progress state. A change to either field reports both `status` and `stateId` in `changedFields`. `list_tickets` takes `stateId` to list one state's tickets; the `status` filter matches categories. Clients reading a server that predates workflows get `stateId: null`. Clients built from this contract ignore ticket fields they do not know, so later additions do not break them.
|
|
104
116
|
|
|
105
|
-
`add_relation` accepts `blocks`, `related`, `duplicate`, `causes`, `clones`, `implements`, or `reviews`. `direction` defaults to `outgoing` for existing clients. Use `incoming` to express the inverse from the current ticket: for example, `{ticketId: "<current>", targetId: "<blocker>", type: "blocks", direction: "incoming"}` means the current ticket is blocked by the selected ticket. The
|
|
117
|
+
`add_relation` accepts `blocks`, `related`, `duplicate`, `causes`, `clones`, `implements`, or `reviews`. `direction` defaults to `outgoing` for existing clients. Use `incoming` to express the inverse from the current ticket: for example, `{ticketId: "<current>", targetId: "<blocker>", type: "blocks", direction: "incoming"}` means the current ticket is blocked by the selected ticket. `targetId` is a ticket ID or key in this project or in any other project the caller can read, in this workspace or another; a target the caller cannot read, including one outside a project-bound API key's projects, returns `404 not_found` and creates nothing. The caller needs `write` on the current ticket's project. An edge that constrains a ticket in another project, as the blocked side of `blocks` or the source of `duplicate`, also needs `write` there, to add it or to remove it, and fails with `403 forbidden` otherwise; reading that project is enough to depend on its tickets or to add an informational link. The server stores a single canonical edge and rejects dependency cycles in either direction, following blockers and parents across every project. Related links are symmetric. Only blocking dependencies add readiness blockers, whichever project the blocker is in; duplicate sources remain ineligible for agent work. Other link types are informational. Adding or removing an edge writes `relation.added` or `relation.removed` for each ticket in that ticket's own project, so realtime subscribers and followers of either project see it. `remove_relation` works from either ticket's project, under the same `write` rule for a constrained ticket elsewhere. Deleting a ticket, or its whole project, removes its relationships and writes `relation.removed` for each surviving ticket in another project, in that ticket's project. `list_relations` returns each side's `sourceKey`/`targetKey`, title, `sourceProjectId`/`targetProjectId`, and `sourceProjectKey`/`targetProjectKey`, plus `restricted`. When the other ticket is in a project the caller cannot read, `restricted` is `true` and that side carries only its ID and key, with no title or project. Relocating a ticket and moving a project to another workspace stay blocked by its relationships, whichever projects they reach. Parent/child hierarchy continues to use `parentId` rather than a second relationship edge.
|
|
106
118
|
|
|
107
119
|
### Custom fields
|
|
108
120
|
|
|
109
121
|
A project defines typed ticket fields; tickets carry their values in `customFields`, an object keyed by field ID. `list_custom_fields` (`GET /api/v1/projects/{projectId}/custom-fields`) returns the definitions in display order, each with `id`, `name`, `type`, `options`, `required`, `defaultValue`, `version`, and `count` (tickets holding a value, archived included). Types and their values: `text` (up to 1,000 characters, trimmed), `number`, `date` (a calendar day as `YYYY-MM-DD`, the same in every time zone), `checkbox` (`true`), `select` (one option ID), and `multiselect` (a list of option IDs). An empty value is no value: blank text, an unchecked box, and an empty option list are stored as absent.
|
|
110
122
|
|
|
111
|
-
Project administrators manage definitions: `create_custom_field` (`POST .../custom-fields`) takes `name`, `type`, optional `options` (select types only, each `{ id?, name, color }` with a label color), `required`, and `defaultValue`; `update_custom_field` (`PATCH .../custom-fields/{fieldId}`) takes `expectedVersion` and any of `name`, the complete `options` list in display order, `required`, and `defaultValue` (`null` clears it; omitting it keeps it). The type never changes. Option IDs stay stable through renames and reorders; a new option may bring its own UUID. Removing an option that tickets or the default still use fails with `400 validation_error` (path `reassign.<option id>`, naming how many tickets use it) unless `reassign` maps it to a kept option or to `null`, which clears it; the affected tickets, the default, and saved-view filters then move in the same transaction. `move_custom_field` (`POST .../custom-fields/{fieldId}/move`) places a field `before` or `after` another, and `delete_custom_field` (`DELETE .../custom-fields/{fieldId}`) removes the field, its values, and its saved-view filters. Names are unique per project, ignoring case (`409 duplicate_field`); a project holds at most 50 fields and a field 100 options. A checkbox cannot be required. Each write checks `expectedVersion` (`409 version_conflict`), accepts `requestId`, and publishes `custom_field.created`, `custom_field.updated`, or `custom_field.deleted`; tickets whose values change get `ticket.updated` events with `changedFields: ["customFields"]
|
|
123
|
+
Project administrators manage definitions: `create_custom_field` (`POST .../custom-fields`) takes `name`, `type`, optional `options` (select types only, each `{ id?, name, color }` with a label color), `required`, and `defaultValue`; `update_custom_field` (`PATCH .../custom-fields/{fieldId}`) takes `expectedVersion` and any of `name`, the complete `options` list in display order, `required`, and `defaultValue` (`null` clears it; omitting it keeps it). The type never changes. Option IDs stay stable through renames and reorders; a new option may bring its own UUID. Removing an option that tickets or the default still use fails with `400 validation_error` (path `reassign.<option id>`, naming how many tickets use it) unless `reassign` maps it to a kept option or to `null`, which clears it; the affected tickets, the default, and saved-view filters then move in the same transaction. `move_custom_field` (`POST .../custom-fields/{fieldId}/move`) places a field `before` or `after` another, and `delete_custom_field` (`DELETE .../custom-fields/{fieldId}`) removes the field, its values, and its saved-view filters. Names are unique per project, ignoring case (`409 duplicate_field`); a project holds at most 50 fields and a field 100 options. A checkbox cannot be required. Each write checks `expectedVersion` (`409 version_conflict`), accepts `requestId`, and publishes `custom_field.created`, `custom_field.updated`, or `custom_field.deleted`; tickets whose values change get `ticket.updated` events with `changedFields: ["customFields"]`.
|
|
124
|
+
|
|
125
|
+
Definition ownership rule: workflow recategorization and deletion, removed-option reassignment, and custom field deletion rewrite their tickets as project administration (`project.manage` with the `admin` scope), not as worker writes. A machine credential needs no claim for a ticket that has none, even one whose completed worker work left it requiring ownership; that requirement stays recorded, and ordinary ticket, bulk, evidence, and resource writes still need a matching claim. Any claim on an affected ticket, live or expired and whoever holds it, rejects the whole change with `409 claim_conflict` and changes nothing; Kanvil never cancels or recovers the claim for you. Release it, or recover an expired one, first.
|
|
112
126
|
|
|
113
127
|
`create_ticket` takes `customFields`: an omitted field takes its default, and a required field must end with a value. `update_ticket` and `bulk_update_tickets` take `changes.customFields`, which sets only the named fields and leaves the others alone, so a bulk edit of one field keeps each ticket's other values; `null` clears a field, except a required one. Values are checked against the ticket's own project: a field of another project, a value of the wrong type, or a removed option is a `400 validation_error` with the path `customFields.<field id>`. Required applies to new tickets and to edits of that field; tickets created before a field became required keep no value until someone sets one. `duplicate_ticket` copies the values.
|
|
114
128
|
|
|
@@ -269,7 +283,7 @@ While the connection is paused nothing syncs; issue changes reported meanwhile a
|
|
|
269
283
|
|
|
270
284
|
## MCP
|
|
271
285
|
|
|
272
|
-
The authenticated Streamable HTTP endpoint is `/mcp`. Tool inputs use the shared HTTP contracts, including expected versions, request IDs, and claim IDs. Successful structured results have a `result` property.
|
|
286
|
+
The authenticated Streamable HTTP endpoint is `/mcp`. Tool inputs use the shared HTTP contracts, including expected versions, request IDs, and claim IDs. Successful structured results have a `result` property. A rejected call returns an error result (`isError: true`) whose text content is the same JSON envelope an HTTP response carries, `{"error": {"status", "code", "message", "details"}}`, so claim readiness reasons, lease maximums, version conflicts, and validation paths read the same on both transports. Unexpected failures return only `internal_error`.
|
|
273
287
|
|
|
274
288
|
Clients supporting custom headers can connect using a scoped API credential. Interactive clients can discover OAuth authorization through the endpoint's protected-resource metadata, then sign in and consent through Kanvil. Resource scopes are `kanvil:read`, `kanvil:write`, `kanvil:claim`, and `kanvil:admin`. The web app supplies the login and consent pages. Configure OIDC callback URLs as `/api/auth/callback/oidc` and explicitly link an existing local account from Settings before using OIDC sign-in. Protocol compatibility is tested using the official SDK, including discovery, tool listing, and writes.
|
|
275
289
|
|
package/docs/cli.md
CHANGED
|
@@ -52,10 +52,18 @@ kanvil task update APP-12 --profile work --expected-version 3 \
|
|
|
52
52
|
kanvil claim acquire APP-12 --profile work --input '{"expectedVersion":4,"workerId":"worker-1"}'
|
|
53
53
|
kanvil claim renew APP-12 --profile work --input '{"claimId":"CLAIM_UUID","leaseSeconds":300}'
|
|
54
54
|
kanvil claim release APP-12 --profile work --input '{"claimId":"CLAIM_UUID"}'
|
|
55
|
+
kanvil claim recover APP-12 --profile work --expected-version 5 --input - <<'JSON'
|
|
56
|
+
{
|
|
57
|
+
"claimId": "EXPIRED_CLAIM_UUID",
|
|
58
|
+
"reason": "Confirmed the old worker has stopped",
|
|
59
|
+
"confirmedStopped": true,
|
|
60
|
+
"reschedule": true
|
|
61
|
+
}
|
|
62
|
+
JSON
|
|
55
63
|
kanvil task delete APP-12 --profile work --expected-version 7 --yes
|
|
56
64
|
```
|
|
57
65
|
|
|
58
|
-
Use the version returned by a read or write. The CLI never fetches a newer version to bypass a conflict. Claims retain the server's readiness, ownership and lease rules; include `claimId` in an update when the operation requires it. Deletion requires an explicit version and `--yes`; noninteractive invocations receive no confirmation prompt.
|
|
66
|
+
Use the version returned by a read or write. The CLI never fetches a newer version to bypass a conflict. Claims retain the server's readiness, ownership and lease rules; include `claimId` in an update when the operation requires it. Once a lease expires, renew and release refuse it; after confirming the old worker stopped, any writer with `claim` scope can run `claim recover` with the claim ID, a reason and `confirmedStopped: true`. Recovery never reschedules or reclaims on its own: pass `reschedule: true` with the ticket's current version to return it to To do, then read the ticket and `claim acquire` at the new version. `cancelActive: true` cancels a live claim only for project managers whose credential has `admin` scope. See [writes and ownership](api.md#writes-and-ownership) for the full policy. Deletion requires an explicit version and `--yes`; noninteractive invocations receive no confirmation prompt.
|
|
59
67
|
|
|
60
68
|
The typed client retries a transport failure once for reads and receipted writes, using the identical request body, request ID and expected version. HTTP errors, including conflicts, are never retried. For a retry across CLI invocations, supply the original `--request-id UUID` (or `requestId` in JSON) and unchanged input. If both transport attempts fail, the outcome may be unknown: reconcile by reading or replaying that same request ID; do not submit a new write identity blindly. Requests have a finite transport timeout.
|
|
61
69
|
|
package/docs/releasing.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
-
PRs and `main` pushes run `.github/workflows/ci.yml`. Independent suites and browser/native/Docker matrices run in parallel; only jobs consuming build artifacts wait for the build. The always-running `Checks` status requires every suite to succeed. Releases start on a `vX.Y.Z` tag and reuse that workflow against the tagged commit. Publishing jobs have registry write permissions; PR checks have read-only repository access.
|
|
3
|
+
PRs and `main` pushes run `.github/workflows/ci.yml`. `main` pushes also publish an [experimental build](#experimental-builds). Independent suites and browser/native/Docker matrices run in parallel; only jobs consuming build artifacts wait for the build. The always-running `Checks` status requires every suite to succeed. Releases start on a `vX.Y.Z` tag and reuse that workflow against the tagged commit. Publishing jobs have registry write permissions; PR checks have read-only repository access.
|
|
4
4
|
|
|
5
5
|
The public npm application package is `@maudecode/kanvil`; the workspace root and internal packages remain private. The release tag supplies the version for both npm and Docker: `v1.2.3` publishes `@maudecode/kanvil@1.2.3` and `ghcr.io/maudecode/kanvil:1.2.3`. The server build embeds that same version for Administration, public instance info, OpenAPI, and MCP. Installed artifacts need no Git checkout or release environment variables. No `package.json` version bump is needed. PR and local builds use the development version in `package.json`; custom Docker builds pass `--build-arg KANVIL_VERSION=X.Y.Z` to stamp the server and image label together. Native mobile app/store versions remain separate.
|
|
6
6
|
|
|
@@ -11,10 +11,24 @@ The public npm application package is `@maudecode/kanvil`; the workspace root an
|
|
|
11
11
|
3. Check the `Release` run. It rejects malformed or prerelease tags, `v0.0.0`, and commits outside `main` history. The full CI workflow must pass on that commit.
|
|
12
12
|
4. Verify `npm view @maudecode/kanvil@X.Y.Z dist.integrity` and `docker buildx imagetools inspect ghcr.io/maudecode/kanvil:X.Y.Z`. The image includes Linux amd64 and arm64. Confirm anonymous Docker pulls work, and check the GitHub Release for the tag and its automatically generated notes.
|
|
13
13
|
|
|
14
|
-
The workflow publishes the exact npm tarball installed by CI to the `latest` dist-tag, while separate amd64/arm64 publishers push the exact image archives tested by CI. Docker builds and restoration tests run on matching native runners; release publishing does not rebuild or emulate either image. A final Docker manifest job joins those immutable digests under `ghcr.io/maudecode/kanvil:X.Y.Z` and `:latest`. Stable releases only; prerelease versions are rejected. Do not tag an older version to move `latest` backward.
|
|
14
|
+
The workflow publishes the exact npm tarball installed by CI to the `latest` dist-tag, while separate amd64/arm64 publishers push the exact image archives tested by CI. Docker builds and restoration tests run on matching native runners; release publishing does not rebuild or emulate either image. A final Docker manifest job joins those immutable digests under `ghcr.io/maudecode/kanvil:X.Y.Z` and `:latest`. Stable and experimental builds share this publisher, `.github/workflows/publish-images.yml`. Stable releases only; prerelease versions are rejected. Do not tag an older version to move `latest` backward.
|
|
15
15
|
|
|
16
16
|
If either registry publication fails, rerun the failed release jobs. npm and Docker publication are independent after validation. The npm step accepts an existing version only when its SHA-512 integrity matches the tested tarball; different bytes fail and require a new version. Registry publication is not atomic across npm and GHCR. Never move a published tag or reuse a version for changed bytes. Avoid rerunning an old release after a newer release, since Docker's `latest` tag would move backward.
|
|
17
17
|
|
|
18
18
|
After npm publication and the Docker manifest job both succeed, the workflow creates a GitHub Release for the existing tag with an automatically generated title and release notes. Only this final job has repository contents write permission. Reruns preserve an existing release and its notes.
|
|
19
19
|
|
|
20
|
+
## Experimental builds
|
|
21
|
+
|
|
22
|
+
Every push to `main` also publishes an experimental build once CI passes on that exact commit. `.github/workflows/experimental.yml` runs when a CI run finishes. It publishes only a successful `push` run of this repository's `main` whose commit is still on `main`; pull requests, forks, other branches, release tags, and failed or cancelled runs publish nothing. Each `main` commit runs CI in its own concurrency group, so a newer push never cancels or replaces an older commit's verification or publication.
|
|
23
|
+
|
|
24
|
+
An experimental build of commit `SHA` (the full 40-character commit) has the version `0.0.0-experimental.gSHA` in Administration, public instance info, OpenAPI, MCP, and the image's `org.opencontainers.image.version` label; `org.opencontainers.image.revision` is `SHA`. CI builds and tests it with that identity (`KANVIL_RELEASE_CHANNEL=experimental` in `scripts/release-version.ts`), and publishing reuses the exact native images CI tested:
|
|
25
|
+
|
|
26
|
+
- `ghcr.io/maudecode/kanvil:experimental-SHA-amd64` and `-arm64`, then the multi-platform `ghcr.io/maudecode/kanvil:experimental-SHA`. These names are immutable.
|
|
27
|
+
- A Git tag and GitHub prerelease named `experimental-SHA`, pinned to `SHA`, created only after both architectures and the multi-platform manifest are published. It is never marked latest; its notes give the source commit, image, and manifest digest after the generated change list.
|
|
28
|
+
- `ghcr.io/maudecode/kanvil:experimental`, the opt-in rolling alias. It moves to the newest announced experimental build on `main`'s first-parent history and never to an older commit, so builds that finish out of order and reruns of old runs cannot move it backward. Only this step is serialized.
|
|
29
|
+
|
|
30
|
+
Experimental tags start with a letter, so tools that read numeric Docker tags as versions, such as Renovate, never treat them as upgrades from a stable release; CI's `Renovate tag selection` job checks this with Renovate itself. Experimental builds never publish npm packages or move npm dist-tags, `latest`, version tags, or the latest GitHub release.
|
|
31
|
+
|
|
32
|
+
Reruns are safe. `scripts/release-publish.ts` keeps an existing image, manifest, tag, or release that names the same commit and version, and fails on one that names anything else rather than replacing it. If one architecture fails to publish, rerun the failed jobs of the `Experimental` run; they reuse the images CI tested and the architecture already published. If only the alias move fails, rerun it, or let the next `main` commit's run move the alias; it always selects the newest published build.
|
|
33
|
+
|
|
20
34
|
Tag creation and package publication are separate from deploying to any running instance. Operators choose when to upgrade using [self-hosting](self-hosting.md).
|
package/docs/self-hosting.md
CHANGED
|
@@ -49,6 +49,12 @@ docker compose up -d --wait
|
|
|
49
49
|
|
|
50
50
|
The image supports Linux amd64 and arm64. Replace `latest` with a release version such as `0.1.0`, or pin `ghcr.io/maudecode/kanvil@sha256:YOUR_DIGEST` for an immutable deployment.
|
|
51
51
|
|
|
52
|
+
To run unreleased code from `main`, opt in explicitly. `ghcr.io/maudecode/kanvil:experimental` follows the newest `main` commit that passed CI; `ghcr.io/maudecode/kanvil:experimental-FULL_COMMIT_SHA` names one such commit and never changes. Experimental builds are not releases: they can carry unfinished features and migrations that a later stable release does not undo, so back up first and do not move an instance from experimental back to an older stable version. Stable tags and `latest` never point to an experimental build, and Renovate's default Docker versioning does not offer experimental tags to stable consumers:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
KANVIL_IMAGE=ghcr.io/maudecode/kanvil:experimental-0123456789abcdef0123456789abcdef01234567
|
|
56
|
+
```
|
|
57
|
+
|
|
52
58
|
The app binds to loopback port 5180; change the host port with `KANVIL_PORT`. Set up the HTTPS reverse proxy below before opening the site. The application runs as the `node` user with a read-only root filesystem and temporary `/tmp`. Its health check verifies database connectivity. PostgreSQL storage is kept in the `postgres-data` volume. `docker compose down` retains that volume; removing volumes deletes instance data.
|
|
53
59
|
|
|
54
60
|
For source builds, use the repository's Node/pnpm versions and run:
|
|
@@ -97,6 +103,8 @@ API keys have a separate limit of 600 requests per minute per key across HTTP an
|
|
|
97
103
|
|
|
98
104
|
Set `KANVIL_CLAIM_MAX_LEASE_SECONDS` in `.env` to change the maximum initial or renewed worker claim lease. The default is `86400` (24 hours); valid values are integers from `30` to `2147483647` seconds. Restart the server after changing it; with Compose, run `docker compose up -d app`. Omitting `leaseSeconds` requests 300 seconds, capped by this setting. Lowering the setting does not shorten existing claims, but their next renewal must use the new maximum. Expired claims can be explicitly recovered by callers with claim scope and ticket write permission after confirming the old worker stopped, without administrator intervention. A worker can pass `reschedule: true` and the current `expectedVersion` to recovery, then acquire a new claim, without needing a human to reset the ticket. Revoking a credential does not itself clear its claim; after stopping the worker, a project manager with admin scope can explicitly pass `cancelActive: true` to cancel live ownership immediately, including alongside version-checked rescheduling.
|
|
99
105
|
|
|
106
|
+
Each application process opens at most `KANVIL_DATABASE_POOL_MAX` (default `10`, minimum `2`) PostgreSQL connections for API, MCP, and authentication work, `KANVIL_INTEGRATION_POOL_MAX` (default `4`) for integration receipts, instance settings, background sync, and account guards, and one realtime `LISTEN` connection: 15 by default. Connections open on demand and close after 30 seconds idle. A request that waits longer than `KANVIL_DATABASE_ACQUIRE_TIMEOUT_MS` (default `30000`) for a free connection fails instead of queueing forever. Keep the sum for every process below PostgreSQL's `max_connections` with room for backups and administration; `pg_stat_activity` lists the connections as `kanvil`, `kanvil-integration`, and `kanvil-realtime`. Restart the server after changing these settings.
|
|
107
|
+
|
|
100
108
|
## PocketID / OIDC
|
|
101
109
|
|
|
102
110
|
Create a confidential OIDC client in PocketID, enable PKCE, and register:
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var e={common:{status:{backlog:`Gözləmə siyahısı`,todo:`Ediləcək`,in_progress:`İcrada`,in_review:`Yoxlamada`,done:`Hazırdır`,cancelled:`Ləğv edilib`},priority:{urgent:`Təcili`,high:`Yüksək`,medium:`Orta`,low:`Aşağı`,none:`Prioritet yoxdur`},kind:{feature:`Funksiya`,bug:`Xəta`,chore:`Rutin iş`,research:`Araşdırma`,documentation:`Sənədləşmə`},difficulty:{low:`Aşağı`,med:`Orta`,high:`Yüksək`,none:`Çətinlik yoxdur`},role:{reader:`Yalnız oxuma`,writer:`Redaktə edə bilər`,admin:`Administrator`},permission:{ticket:{write:{label:`Tiketləri redaktə etmək`,description:`Tiketləri yaratmaq, redaktə etmək, köçürmək, götürmək və şərh yazmaq; layihə yaratmaq`},delete:{label:`Tiketləri silmək`,description:`Tiketləri həmişəlik silmək`}},project:{manage:{label:`Layihələri idarə etmək`,description:`Layihə məlumatları, iş axını, etiketlər, sahələr, inteqrasiyalar və həyat dövrü`}},member:{manage:{label:`Üzvləri idarə etmək`,description:`Üzvləri əlavə etmək, dəyişmək və çıxarmaq`}},invitation:{manage:{label:`Dəvətləri idarə etmək`,description:`İnsanları e-poçtla dəvət etmək`}},sharing:{manage:{label:`Paylaşımı idarə etmək`,description:`İctimai yalnız oxuma keçidləri və təqvim lentləri`}},workspace:{manage:{label:`İş sahəsini idarə etmək`,description:`İş sahəsi məlumatları, ümumi etiketlər və rollar`}}},customFieldType:{text:`Mətn`,number:`Rəqəm`,date:`Tarix`,checkbox:`Qeyd qutusu`,select:`Seçim`,multiselect:`Çoxlu seçim`},customField:{removedOption:`Silinmiş seçim`,checked:`Bəli`},filterField:{status:`Status`,priority:`Prioritet`,kind:`Növ`,difficulty:`Çətinlik`,label:`Etiket`,assigneeId:`İcraçı`,customField:`Sahə`},sort:{priority:`Prioritet`,updated:`Son yenilənənlər`,created:`Son yaradılanlar`,title:`Başlıq`,labels:`Etiketlər`,due:`Son tarix`,start:`Başlama tarixi`,number:`Tiket nömrəsi`,status:`Status`,difficulty:`Çətinlik`,kind:`Növ`,assignee:`İcraçı`,manual:`Əl ilə`},due:{overdue:`Gecikmiş`,today:`Bu gün bitir`,week:`Bu həftə bitir`,scheduled:`Son tarixi var`,none:`Son tarix yoxdur`},relation:{blocks:{outgoing:`Bloklayır`,incoming:`Tərəfindən bloklanır`},related:{outgoing:`Əlaqəlidir`,incoming:`Əlaqəlidir`},duplicate:{outgoing:`Təkrarlayır`,incoming:`Tərəfindən təkrarlanır`},causes:{outgoing:`Səbəb olur`,incoming:`Səbəbi`},clones:{outgoing:`Klonlayır`,incoming:`Tərəfindən klonlanır`},implements:{outgoing:`Həyata keçirir`,incoming:`Tərəfindən həyata keçirilir`},reviews:{outgoing:`Yoxlayır`,incoming:`Tərəfindən yoxlanılır`}},remoteKind:{branch:`Budaq`,pull_request:`Pull request`,issue:`Məsələ`},notificationCategory:{assignment:`Təyinatlar`,status:`Status dəyişiklikləri`,comment:`Şərhlər`,mention:`İstinadlar`,update:`Digər yeniliklər`,due_soon:`Müddəti yaxınlaşır`,overdue:`Gecikmiş`},destinationEvent:{created:`Yeni tiketlər`},deliveryKind:{email:`E-poçt`,webhook:`Webhook`},change:{pull_request:{name:`Pull request`,opened:`Pull request açıldı`,draft:`Qaralama pull request`,merged:`Pull request birləşdirildi`,closed:`Pull request birləşdirilmədən bağlandı`},merge_request:{name:`Merge request`,opened:`Merge request açıldı`,draft:`Qaralama merge request`,merged:`Merge request birləşdirildi`,closed:`Merge request birləşdirilmədən bağlandı`},branchCreated:`Budaq yaradıldı`},issueLinkState:{active:`Sinxronlaşdırılır`,paused:`Dayandırılıb`,review:`Yoxlama tələb olunur`,creating:`Məsələ yaradılır`,unknown:`Yaradılma təsdiqlənməyib`},importStatus:{previewed:`İdxala hazırdır`,running:`İdxal edilir`,completed:`İdxal edildi`,failed:`Uğursuz oldu`,cancelled:`Ləğv edildi`},importProvider:{linear:{project:`Komanda`,setup:`Linear-da Settings › Security & access bölməsində şəxsi API açarı yaradın; oxuma icazəsi kifayətdir.`},jira:{project:`Layihə`,setup:`Jira Cloud hesabınızın e-poçtu və id.atlassian.com-dan alınan API tokeni ilə daxil olur; Data Center və Server şəxsi giriş tokenindən istifadə edir. Layihəyə baxış icazəsi kifayətdir.`},trello:{project:`Lövhə`,setup:`trello.com/power-ups/admin ünvanında idarə etdiyiniz Power-Up-ın API açarından və onun üçün yaradılmış tokendən istifadə edin; oxuma icazəsi kifayətdir.`},plane:{project:`Layihə`,setup:`Plane-də Profile settings › Personal access tokens bölməsində şəxsi giriş tokeni yaradın. Plane Cloud API ünvanı: https://api.plane.so; iş sahəsinin URL açarı onun veb ünvanının ilk hissəsidir.`},planka:{project:`Lövhə`,setup:`Lövhəni aça bilən PLANKA hesabı ilə daxil olun; Kanvil-in açdığı sessiya oxuma bitəndə başa çatır. PLANKA 2 administratorları bunun əvəzinə API açarları verə bilər.`},kaneo:{project:`Layihə`,setup:`Kaneo-da Settings › Account › Developer bölməsində API açarı yaradın.`}},plankaSignIn:{password:`İstifadəçi adı və şifrə`,api_key:`API açarı`},jiraEdition:{cloud:`Cloud`,data_center:`Data Center və ya Server`},event:{time_entry:{created:`Vaxt qeydi yaradıldı`,updated:`Vaxt qeydi yeniləndi`,deleted:`Vaxt qeydi silindi`},label:{created:`Etiket yaradıldı`,updated:`Etiket yeniləndi`,deleted:`Etiket silindi`},workflow:{created:`İş axını vəziyyəti əlavə edildi`,updated:`İş axını vəziyyəti dəyişdirildi`,deleted:`İş axını vəziyyəti silindi`},custom_field:{created:`Sahə yaradıldı`,updated:`Sahə yeniləndi`,deleted:`Sahə silindi`},ticket:{created:`Tiket yaradıldı`,updated:`Tiket yeniləndi`,deleted:`Tiket silindi`,moved:`Başqa layihədən köçürüldü`,moved_away:`Başqa layihəyə köçürüldü`,due_soon:`Müddəti yaxınlaşır`,overdue:`Gecikmiş`,claimed:`İş götürüldü`},claim:{renewed:`Götürmə yeniləndi`,released:`Götürmə buraxıldı`,recovered:`Götürmə bərpa edildi`},comment:{created:`Şərh əlavə edildi`,updated:`Şərh redaktə edildi`,deleted:`Şərh silindi`},relation:{added:`Əlaqə əlavə edildi`,removed:`Əlaqə silindi`},attachment:{created:`Fayl qoşuldu`,deleted:`Fayl silindi`},evidence:{added:`Sübut qoşuldu`},resource:{added:`Keçid əlavə edildi`,updated:`Keçid redaktə edildi`,removed:`Keçid silindi`},calendar_feed:{created:`Təqvim lenti yaradıldı`,revoked:`Təqvim lenti ləğv edildi`},project:{created:`Layihə yaradıldı`,updated:`Layihə yeniləndi`,archived:`Layihə arxivləşdirildi`,restored:`Layihə bərpa edildi`,transferred:`Layihə başqa iş sahəsinə köçürüldü`,deleted:`Layihə silindi`},membership:{updated:`Layihəyə giriş dəyişdirildi`},workspace:{owner_changed:`İş sahəsinin sahibi dəyişdirildi`},role:{created:`Rol yaradıldı`,updated:`Rol dəyişdirildi`,deleted:`Rol silindi`},invitation:{updated:`Dəvət dəyişdirildi`},destination:{created:`Təyinat əlavə edildi`,updated:`Təyinat dəyişdirildi`,deleted:`Təyinat silindi`},repository:{connected:`Repozitori qoşuldu`,updated:`Repozitori bağlantısı dəyişdirildi`,disconnected:`Repozitori ayrıldı`,received:`Repozitori webhooku alındı`},issue_link:{updated:`Məsələ sinxronizasiyası dəyişdirildi`}},error:{network:`Kanvil ilə əlaqə qurulmadı. Bağlantınızı yoxlayıb yenidən cəhd edin.`,fallback:`Sorğu tamamlana bilmədi`,input:`Daxil edilən məlumat`,code:{unauthorized:`Davam etmək üçün daxil olun.`,session_required:`Bu əməliyyat üçün daxil olmuş hesabdan istifadə edin.`,account_disabled:`Bu hesab deaktiv edilib.`,forbidden:`Bunu etməyə icazəniz yoxdur.`,insufficient_scope:`Bu giriş məlumatı bu əməliyyata icazə vermir.`,workspace_scope_required:`Bu əməliyyat üçün iş sahəsinə girişi olan hesabdan istifadə edin.`,invalid_origin:`Bu mənbəyə icazə verilmir.`,not_found:`Bu element tapılmadı və ya ona baxmaq icazəniz yoxdur.`,version_conflict:`Yükləndikdən sonra dəyişdirilib. Yenidən yükləyib təkrar cəhd edin.`,time_entry_conflict:`Bu istifadəçinin tapşırıqda artıq davam edən vaxt qeydi var.`,claim_conflict:`Bu tiket başqa işçidədir.`,idempotency_conflict:`Bu sorğu artıq başqa dəyişiklik üçün istifadə edilib.`,project_archived:`Dəyişməzdən əvvəl arxivləşdirilmiş layihəni bərpa edin.`,rate_limited:`Qısa müddətdə həddən artıq dəyişiklik. Gözləyib yenidən cəhd edin.`,snapshot_required:`Canlı lent geri qaldı. Davam etmək üçün yenidən yükləyin.`,request_failed:`Sorğu tamamlana bilmədi`}},action:{add:`Əlavə et`,apply:`Tətbiq et`,back:`Geri`,cancel:`Ləğv et`,clear:`Təmizlə`,close:`Bağla`,confirm:`Təsdiqlə`,continue:`Davam et`,copy:`Kopyala`,create:`Yarat`,delete:`Sil`,done:`Hazır`,edit:`Redaktə et`,open:`Aç`,remove:`Çıxar`,rename:`Adını dəyiş`,reset:`Sıfırla`,retry:`Yenidən cəhd et`,save:`Yadda saxla`,search:`Axtar`},state:{copied:`Kopyalandı`,loading:`Yüklənir…`,saved:`Yadda saxlanıldı`,saving:`Yadda saxlanılır…`,none:`Yoxdur`,unassigned:`Təyin edilməyib`},evidenceKind:{pull_request:`Pull request`,test:`Test`,review:`Yoxlama`,deployment:`Yerləşdirmə`,device:`Cihaz`,other:`Digər`},evidenceOutcome:{reported:`Bildirilib`,pending:`Gözləyir`,passed:`Uğurlu`,failed:`Uğursuz`},readinessReason:{project_archived:`Layihə arxivləşdirilib`,not_open:`Tiket “Ediləcək” vəziyyətində olmalı və arxivdə olmamalıdır`,not_enabled:`Agent işi aktivləşdirilməyib`,missing_description:`Təsvir əlavə edin`,missing_acceptance_criteria:`Qəbul meyarları əlavə edin`,missing_difficulty:`Çətinlik seçin`,duplicate:`Bu tiket təkrar kimi qeyd edilib`,claim_recovery_required:`Sahibliyi bərpa etməzdən əvvəl əvvəlki işçinin dayandığını təsdiqləyin`,claimed:`{{worker}} tərəfindən götürülüb`,dependency:`{{key}} tamamlanmayıb`},person:{deleted:`Silinmiş hesab`,former:`Keçmiş üzv`},signIn:{codeTitle:`Kodla daxil ol`,resetTitle:`Şifrəni sıfırla`,codeSent:`{{email}} ünvanının hesabı varsa, kod göndərilir. Kodun müddəti 10 dəqiqəyə bitir.`,code:`Kod`,newPassword:`Yeni şifrə`,sendCode:`Kodu e-poçtla göndər`,back:`Daxil olmaya qayıt`,resend:`Kodu yenidən göndər`,resent:`Kod yenidən göndərildi`,emailCode:`Daxil olma kodunu e-poçtla göndər`,forgot:`Şifrəni unutmusunuz?`,resetDone:`Şifrə sıfırlandı. Yeni şifrə ilə daxil olun.`,resetDoneTitle:`Şifrə sıfırlandı`,resetDoneBody:`Yeni şifrə ilə daxil olun.`,invalidCode:`Bu kod yanlışdır, artıq istifadə olunub və ya müddəti bitib. Etibarlı kod almaq üçün yenidən göndərin.`,localPassword:`Yerli şifrə`},language:{label:`Dil`,system:`Sistem standartı`}},shell:{ui:{showValue:`{{label, lowercase}} göstər`,hideValue:`{{label, lowercase}} gizlət`,previousPage:`Əvvəlki {{noun}}`,nextPage:`Növbəti {{noun}}`,pageRange:`{{from}}–{{to}} / {{total}}`,priority:`Prioritet: {{label}}`,working:`İcra olunur…`,searchPlaceholder:`Axtar…`,clearValue:`{{label, lowercase}} təmizlə`,copyValue:`{{label, lowercase}} kopyala`,copied:`{{value}} kopyalandı`},documentTitle:`{{name}} · Kanvil`,openKanvil:`Kanvil-i aç`,page:{home:`Əsas səhifə`,myWork:`Mənim işlərim`,inbox:`Gələnlər`,search:`Axtarış`,settings:`Parametrlər`,planning:`Planlaşdırma`,ticket:`Tiket`,tickets:`Tiketlər`,projects:`Layihələr`},connection:{restored:`Bağlantı bərpa edildi`,lost:`Bağlantı kəsildi`,reconnecting:`Yenidən qoşulur…`,reload:`Yenidən yüklə`,signIn:`Yenidən qoşulmaq üçün daxil olun`},app:{skipToContent:`Məzmuna keç`,assigned:`Sizə təyin edilənlər`,openCount_one:`{{count, number}} açıq`,openCount_other:`{{count, number}} açıq`,noAssigned:`Sizə təyin edilmiş açıq tiket yoxdur`,allInMyWork_one:`“Mənim işlərim”də hamısı ({{count, number}})`,allInMyWork_other:`“Mənim işlərim”də hamısı ({{count, number}})`,openMyWork:`“Mənim işlərim”i aç`,recentActivity:`Son fəaliyyət`,noActivity:`Hələ fəaliyyət yoxdur`,searchProjects:`Layihələrdə axtar`,projectCount_one:`{{count, number}} layihə`,projectCount_other:`{{count, number}} layihə`,noMatchingProjects:`Uyğun layihə yoxdur`,clearSearch:`Axtarışı təmizlə`,noWorkspaces:`Hələ iş sahəsi yoxdur`,noActiveProjects:`Aktiv layihə yoxdur`,noProjects:`Hələ layihə yoxdur`,archivedProjects:`Arxivləşdirilmiş layihələr`,archived:`Arxivləşdirilib`,noOpenTickets:`Açıq tiket yoxdur`,overdueCount_one:`{{count, number}} gecikmiş`,overdueCount_other:`{{count, number}} gecikmiş`,percentDone:`{{percent}} hazırdır`,nextDue:`növbəti son tarix <time/>`,projectSettingsFor:`{{name}} layihəsinin parametrləri`,pageNotFound:`Səhifə tapılmadı`,openHome:`Əsas səhifəni aç`,tryAgain:`Yenidən cəhd edin`,adminRequired:`Nüsxə administratoru girişi tələb olunur`,projectNotFound:`Layihə tapılmadı`,noWorkspaceSelected:`İş sahəsi seçilməyib`},auth:{signIn:`Daxil ol`,createOwner:`Sahib hesabınızı yaradın`,ownerHint:`İlk hesab bu nüsxəni idarə edir.`,invitedHint:`Dəvəti qəbul etmək üçün dəvət edilmiş e-poçt ünvanı ilə daxil olun.`,signInWith:`{{provider}} ilə daxil ol`,useEmail:`E-poçt və şifrədən istifadə et`,or:`və ya`,name:`Ad`,setupToken:`Quraşdırma tokeni`,setupTokenHint:`Nüsxə konfiqurasiyanızdakı KANVIL_SETUP_TOKEN dəyərindən istifadə edin.`,email:`E-poçt`,password:`Şifrə`,signingIn:`Daxil olunur…`,createAccount:`Hesab yarat`,consent:{expired:`Avtorizasiya sorğusunun müddəti bitib. Klientinizi yenidən qoşun.`,title:`{{client}} üçün icazə ver`,titleUnnamed:`Klientə icazə ver`,subtitle:`Bu klient hesabınız adından giriş istəyir. Layihə icazələri yenə də tətbiq olunur.`,deny:`Rədd et`,allow:`Girişə icazə ver`},invitation:{unavailable:`Dəvət əlçatan deyil`,join:`{{workspace}} iş sahəsinə qoşul`,invited:`Dəvət edilən`,from:`Göndərən`,access:`Giriş`,removedProjects:`Silinmiş layihələr`,wholeWorkspace:`Bütün iş sahəsi`,expires:`Bitmə tarixi`,expired:`Bu dəvətin müddəti bitib. İş sahəsi administratorundan yeni dəvət istəyin.`,revoked:`Bu dəvət ləğv edilib. İş sahəsi administratorundan yeni dəvət istəyin.`,accepted:`Bu dəvət artıq qəbul edilib. İş sahəsi administratorundan yeni dəvət istəyin.`,wrongAccount:`<account>{{current}}</account> kimi daxil olmusunuz. Qəbul etmək üçün {{invited}} kimi daxil olun.`,signOut:`Çıxış et`,joined:`{{workspace}} iş sahəsinə qoşuldunuz`,accept:`Dəvəti qəbul et`},callback:{cancelled:`Daxil olma ləğv edildi. Hazır olduqda yenidən cəhd edin.`,linkedElsewhere:`Bu identiklik artıq başqa Kanvil hesabına bağlanıb. Başqa identiklikdən istifadə edin və ya administratorunuzla əlaqə saxlayın.`,notLinked:`Bu identiklik heç bir Kanvil hesabına bağlanmayıb. Yerli hesabınızla daxil olub onu Parametrlərdə bağlayın.`,expired:`Daxil olma sorğusunun müddəti bitib və ya təsdiqlənə bilmədi. Bu ekrandan yenidən cəhd edin.`,emailMismatch:`İdentiklik provayderinin e-poçtu bağlana bilmədi. Administratorunuzla əlaqə saxlayın.`,failed:`Daxil olmaq mümkün olmadı. Yenidən cəhd edin və ya administratorunuzla əlaqə saxlayın.`}},you:`Siz`,inbox:{markAllRead:`Hamısını oxunmuş kimi qeyd et`,clearRead:`Oxunmuşları təmizlə`,cleared_one:`{{count, number}} oxunmuş bildiriş təmizləndi`,cleared_other:`{{count, number}} oxunmuş bildiriş təmizləndi`,unread:`Oxunmamış`,mentions:`İstinadlar`,all:`Bütün bildirişlər`,noUnread:`Oxunmamış bildiriş yoxdur`,noMentions:`İstinad yoxdur`,noNotifications:`Bildiriş yoxdur`,markUnread:`Oxunmamış kimi qeyd et`,markRead:`Oxunmuş kimi qeyd et`,dismiss:`Rədd et`},search:{field:`İş sahəsində axtar`,placeholder:`Tiketlər, layihələr, şərhlər, fəaliyyət üzrə axtar…`,tab:{all:`Hamısı`,project:`Layihələr`,ticket:`Tiketlər`,comment:`Şərhlər`,activity:`Fəaliyyət`},indexing:`Xarici axtarış yenilənir, ona görə tiket nəticələri PostgreSQL-dən gəlir.`,empty:`Tiketlər, layihələr, şərhlər və fəaliyyət üzrə axtarın`,noResults:`“{{query}}” üçün nəticə yoxdur`,results:`Axtarış nəticələri`,pages:`Axtarış səhifələri`,previous:`Əvvəlki`,next:`Növbəti`,type:{project:`Layihə`,ticket:`Tiket`,comment:`Şərh`,activity:`Fəaliyyət`}},column:{key:`Tiket`,labels:`Etiketlər`,scopes:`Əhatələr`,status:`Status`,priority:`Prioritet`,difficulty:`Çətinlik`,kind:`Növ`,assigneeId:`İcraçı`,startAt:`Başlama`,dueAt:`Son tarix`,createdAt:`Yaradılıb`,updatedAt:`Yenilənib`},public:{notShared:`Bu layihə paylaşılmayıb`,notSharedHint:`Keçid söndürülüb və ya heç vaxt mövcud olmayıb. Üzvlər oxumaq üçün daxil ola bilər.`,noTickets:`Tiket yoxdur`,ticketUnavailable:`Tiket əlçatan deyil`,backToTickets:`Tiketlərə qayıt`,description:`Təsvir`,noDescription:`_Təsvir yoxdur_`,acceptanceCriteria:`Qəbul meyarları`,noAcceptanceCriteria:`_Qəbul meyarları yoxdur_`,ticketProperties:`Tiket xüsusiyyətləri`,properties:`Xüsusiyyətlər`,status:`Status`,priority:`Prioritet`,kind:`Növ`,difficulty:`Çətinlik`,startDate:`Başlama tarixi`,dueDate:`Son tarix`,labels:`Etiketlər`,scopes:`Əhatələr`,created:`Yaradılıb`,updated:`Yenilənib`,relationships:`Əlaqələr`,parent:`Valideyn`,child:`Alt tiket`},sidebar:{workspace:`İş sahəsi`,unread_one:`{{count, number}} oxunmamış`,unread_other:`{{count, number}} oxunmamış`,userSettings:`İstifadəçi parametrləri`,administration:`İdarəetmə`,signOut:`Çıxış et`,projectSettings:`Layihə parametrləri`},workspace:{new:`Yeni iş sahəsi`,name:`İş sahəsinin adı`,key:`İş sahəsinin açarı`,create:`İş sahəsi yarat`,workspaces:`İş sahələri`,switch:`İş sahəsini dəyiş`,settings:`İş sahəsi parametrləri`},palette:{open:`Keç…`,title:`Keç`,description:`Layihə, tiket, səhifə və ya əməliyyat tapın`,placeholder:`Tiketlər, layihələr, səhifələr, əməliyyatlar üzrə axtar…`,searching:`Axtarılır…`,noResults:`Nəticə yoxdur`,jumpTo:`Keç`,openKey:`{{project}} layihəsində {{key}} aç`,resultsIn:`{{workspace}} üzrə nəticələr`,results:`Nəticələr`,seeAll_one:`Bütün {{count, number}} nəticəyə bax`,seeAll_other:`Bütün {{count, number}} nəticəyə bax`,actions:`Əməliyyatlar`,pages:`Səhifələr`,newTicketIn:`{{project}} layihəsində yeni tiket`,myTicketsIn:`{{project}} layihəsində mənim tiketlərim`,lightMode:`İşıqlı rejimə keç`,darkMode:`Qaranlıq rejimə keç`,keywords:{jump:`{{query}} keç aç jump open`,allResults:`bütün nəticələr {{query}}`,newTicket:`yeni tiket yarat`,myTickets:`mənim tiketlərim mənə təyin edilən`,lightMode:`işıqlı rejimə keç tema görünüş`,darkMode:`qaranlıq rejimə keç tema görünüş`,help:`klaviatura qısayolları kömək`,home:`əsas səhifə icmal bütün layihələr`,myWork:`mənim işlərim təyin edilmiş tiketlər`,search:`axtarış şərhlər fəaliyyət layihələr tiketlər`,inbox:`gələnlər bildirişlər`,settings:`parametrlər {{scope}} {{label}}`}},actionCategory:{general:`Ümumi`,ticketList:`Tiket siyahısı`,inbox:`Gələnlər`,ticket:`Tiket`,search:`Axtarış`,markdown:`Markdown`},action:{palette:`İstənilən yerə keç`,help:`Klaviatura qısayolları`,sidebar:`Yan paneli aç/bağla`,newTicket:`Yeni tiket`,search:`Tiketlərdə axtar`,highlight:`Növbəti və ya əvvəlki tiketi vurğula`,open:`Vurğulanmış tiketi aç`,preview:`Vurğulanmış tiketə siyahının yanında bax`,select:`Vurğulanmış tiketi seç`,properties:`Vurğulanmış tiketin statusunu, prioritetini və ya icraçısını təyin et`,inboxHighlight:`Növbəti və ya əvvəlki bildirişi vurğula`,inboxOpen:`Vurğulanmış bildirişin tiketini Gələnlərin yanında oxu`,inboxRead:`Vurğulanmış bildirişi oxunmuş və ya oxunmamış kimi qeyd et`,inboxDismiss:`Vurğulanmış bildirişi rədd et`,step:`Növbəti və ya əvvəlki tiketi aç`,edit:`Tiketi redaktə et`,save:`Tiketi və ya şərhi yadda saxla`,results:`Axtarış nəticələri arasında hərəkət et`,insertBlock:`Vizual redaktorda cədvəl, diaqram, fayl və ya başqa blok daxil et`},move:{top:{label:`Vurğulanmış tiketi ən yuxarı köçür`,menu:`Ən yuxarı köçür`},up:{label:`Vurğulanmış tiketi yuxarı köçür`,menu:`Yuxarı köçür`},down:{label:`Vurğulanmış tiketi aşağı köçür`,menu:`Aşağı köçür`},bottom:{label:`Vurğulanmış tiketi ən aşağı köçür`,menu:`Ən aşağı köçür`},previous:{label:`Vurğulanmış tiketi əvvəlki statusa köçür`,menu:`Əvvəlki statusa köçür`},next:{label:`Vurğulanmış tiketi növbəti statusa köçür`,menu:`Növbəti statusa köçür`}},copy:{link:{label:`Keçidi kopyala`,copied:`Tiket keçidi`},keyAndTitle:{label:`Açarı və başlığı kopyala`,copied:`Tiket`},title:{label:`Başlığı kopyala`,copied:`Başlıq`},markdown:{label:`Markdown kimi kopyala`,copied:`Markdown`},branch:{label:`Budaq adını kopyala`,copied:`Budaq adı`}},settingsNav:{account:`Hesab`,settings:`Parametrlər`,workspace:`İş sahəsi`,workspaceSettings:`İş sahəsi parametrləri`,projects:`Layihələr`,projectSettings:`Layihə parametrləri`,administration:`İdarəetmə`,section:{profile:`Profil və təhlükəsizlik`,appearance:`Görünüş`,preferences:`Seçimlər`,credentials:`API açarları`,notifications:`Bildirişlər`,general:`Ümumi`,members:`Üzvlər`,roles:`Rollar`,labels:`Etiketlər`,workflow:`İş axını`,fields:`Sahələr`,integrations:`İnteqrasiyalar`,calendar:`Təqvim lentləri`,storage:`Fayl yaddaşı`,search:`Axtarış`,events:`Hadisə çatdırılması`,users:`İstifadəçilər`,workspaces:`İş sahələri`,authentication:`Autentifikasiya`}},browser:{copyFailed:`Kopyalamaq alınmadı. Mətni seçib əl ilə kopyalayın.`,readFailed:`Fayl oxuna bilmədi`,encodeFailed:`Fayl kodlaşdırıla bilmədi`,imageTooBig:`10 MB-dan kiçik şəkil seçin.`,cannotResize:`Bu brauzer şəklin ölçüsünü dəyişə bilmir.`,prepareFailed:`Şəkil hazırlana bilmədi.`,stillTooLarge:`Ölçü dəyişdikdən sonra da bu şəkil çox böyükdür. Daha sadə şəkil seçin.`},primitive:{close:`Bağla`,more:`Daha çox`,breadcrumb:`naviqasiya zənciri`,sidebar:`Yan panel`,sidebarDescription:`Mobil yan paneli göstərir.`,toggleSidebar:`Yan paneli aç/bağla`,resizeSidebar:`Yan panelin ölçüsünü dəyiş`,expandSidebar:`Yan paneli genişləndir`,commandTitle:`Əmr paneli`,commandDescription:`İcra etmək üçün əmr axtarın…`}},ticket:{list:{selectSection:`{{section}} tiketlərini seç`,expandSection:`{{section}} tiketlərini genişləndir`,collapseSection:`{{section}} tiketlərini yığ`,sectionTickets:`{{section}} tiketləri`,later:`Sonra`,selectAll:`Göstərilən bütün tiketləri seç`,selectTicket:`{{key}} seç`,title:`Başlıq`,all:`Hamısı`,planned:`Planlaşdırılmış`,archived:`Arxivləşdirilmiş`,allTickets:`Bütün tiketlər`,customView:`Fərdi görünüş`,sectionCount:`{{n, number}} {{section}}`,planning:`{{project}} planlaşdırması`,collection:`{{source}} ({{sections}})`,moved:`{{key}} köçürüldü`,movedTo:`{{key}} {{status}} statusuna köçürüldü`,movedAbove:`{{key}} {{neighbor}} üzərinə köçürüldü`,movedBelow:`{{key}} {{neighbor}} altına köçürüldü`,movedToAbove:`{{key}} {{status}} statusuna, {{neighbor}} üzərinə köçürüldü`,movedToBelow:`{{key}} {{status}} statusuna, {{neighbor}} altına köçürüldü`,emptyFiltered:`Uyğun tiket yoxdur`,emptyPlanning:`Planlaşdırılmış və ya arxivləşdirilmiş heç nə yoxdur`,emptyProject:`Hələ tiket yoxdur`,emptyWork:`Sizə təyin edilmiş açıq tiket yoxdur`,createFirst:`İlk tiketi yarat`,activeWork:`Aktiv iş`,views:`Tiket görünüşləri`,saveCurrentView:`Cari görünüşü yadda saxla`,deleteView:`{{name}} görünüşünü sil`,deleteViewConfirm:`{{name}} yadda saxlanılmış görünüşlərdən silinsin? Tiketlər dəyişməz qalır.`,planningLink:`Planlaşdırma`,newTicketShortcut:`Yeni tiket ({{shortcut}})`,saveView:`Görünüşü yadda saxla`,viewSaved:`Görünüş yadda saxlanıldı`,viewName:`Görünüşün adı`,retry:`Tiketləri yenidən yüklə`,board:`Tiket lövhəsi`,tickets:`Tiketlər`,resizeColumn:`{{column}} sütununun ölçüsünü dəyiş`,count_one:`{{count, number}} tiket`,count_other:`{{count, number}} tiket`,scheduledCount_one:`{{count, number}} planlaşdırılmış tiket`,scheduledCount_other:`{{count, number}} planlaşdırılmış tiket`},move:{destination:`Təyinat`,projectPlaceholder:`Layihəyə köçür…`,blockers:`Köçürməzdən əvvəl həll edin`,submit:`Tiketi köçür`,pending:`Köçürülür…`},picker:{placeholder:`Tiket seçin`,loading:`Tiket yüklənir…`,searchLabel:`{{label, lowercase}} axtar`,searchPlaceholder:`Başlıq və ya açar üzrə axtar…`,noParent:`Valideyn yoxdur`,searching:`Axtarılır…`,empty:`Tiket tapılmadı`},property:{status:`Status`,priority:`Prioritet`,kind:`Növ`,difficulty:`Çətinlik`,assignee:`İcraçı`,you:`{{name}} (siz)`,assignTo:`Təyin et…`,set:`{{label, lowercase}} təyin et…`,search:`{{label, lowercase}} axtar`,noMatches:`Uyğunluq yoxdur`},field:{mixed:`Qarışıq`,unchecked:`Xeyr`,noValue:`Dəyər yoxdur`},project:{label:`Layihə`,placeholder:`Layihədə yarat…`,search:`Layihələrdə axtar`},tags:{labels:`Etiketlər`,scopes:`Əhatələr`,summary:`{{label}}: {{tags}}`,edit:`{{label, lowercase}} redaktə et`,editValues:`{{label, lowercase}} redaktə et: {{tags}}`,editMixed:`{{label, lowercase}} redaktə et: qarışıq`,mixed:`Qarışıq {{label, lowercase}}`,none:`{{label}} yoxdur`,mixedNote:`Seçilmiş tiketlərin {{label, lowercase}} fərqlidir.`,clear:`{{label, lowercase}} təmizlə`,replace:`{{label, lowercase}} əvəz et`},tagInput:{remove:`{{label, lowercase}} {{tag}} çıxar`,addAnother:`Daha birini əlavə et…`,add:`{{label, lowercase}} əlavə et…`,choose:`{{label, lowercase}} seç`,chooseLabels:`Etiketləri seç`,searchPlaceholder:`Etiketlərdə axtar…`,searchLabel:`Layihə etiketlərində axtar`,empty:`Uyğun etiket yoxdur`},link:{previewUnavailable:`Önizləmə əlçatan deyil`},userPicker:{label:`Hesab`,placeholder:`Hesab seçin`,searchPlaceholder:`Ad və ya e-poçt üzrə axtar…`,searchLabel:`Hesablarda axtar`,useEmail:`{{email}} istifadə et`,emptyAnyAccount:`Üzv axtarın və ya mövcud hesabın e-poçtunu daxil edin.`,empty:`Uyğun üzv yoxdur`},date:{placeholder:`Tarix seçin`,clear:`{{label, lowercase}} təmizlə`,start:`Başlama tarixi`,startShort:`Başlama`,noStart:`Başlama tarixi yoxdur`,due:`Son tarix`,dueShort:`Son tarix`,noDue:`Son tarix yoxdur`},filter:{me:`Mən`,searchTickets:`Tiketlərdə axtar`,searchTicketsPlaceholder:`Tiketlərdə axtar…`,filters:`Filtrlər`,filtersActive:`Filtrlər ({{active, number}})`,due:`Son tarix`,anyDue:`İstənilən son tarix`,archived:`Arxivləşdirilmiş`,hideArchived:`Arxivləşdirilmişləri gizlət`,includeArchived:`Arxivləşdirilmişləri daxil et`,archivedOnly:`Yalnız arxivləşdirilmiş`,includingArchived:`Arxivləşdirilmişlər daxil`,reset:`Filtrləri sıfırla`,clear:`Filtrləri təmizlə`,active:`Aktiv tiket filtrləri`,searchChip:`Axtarış: {{query}}`,chipAny:`{{field}}:`,chipNone:`{{field}} deyil:`,notValues:`{{values}} deyil`,match:`{{label}} uyğunluğu`,is:`Bərabərdir`,isNot:`Bərabər deyil`,searchPlaceholder:`{{search}}…`,anyValue:{status:`Bütün statuslar`,priority:`Bütün prioritetlər`,kind:`Bütün növlər`,difficulty:`Bütün çətinliklər`,label:`Bütün etiketlər`,assigneeId:`İstənilən şəxs`,customField:`İstənilən sahə dəyəri`},searchValues:{status:`Statuslarda axtar`,priority:`Prioritetlərdə axtar`,kind:`Növlərdə axtar`,difficulty:`Çətinliklərdə axtar`,label:`Etiketlərdə axtar`,assigneeId:`Şəxslərdə axtar`,customField:`Sahələrdə axtar`}},display:{title:`Görünüş`,orderBy:`Sıralama`,direction:`İstiqamət`,ascending:`Artan`,descending:`Azalan`,reset:`Görünüşü sıfırla`,groupBy:`Qruplaşdırma`,dueDate:`Son tarix`,layout:`Tərtibat`,list:`Siyahı`,board:`Lövhə`,backgrounds:`Layihə fonları`,show:`Göstər`,hide:`Gizlət`,rowSpacing:`Sətir aralığı`,compact:`Sıx`,comfortable:`Rahat`,labelDisplay:`Etiket görünüşü`,compactPreview:`Yığcam önizləmə`,allLabels:`Bütün etiketlər`,properties:`Xüsusiyyətlər`},selection:{region:`Seçilmiş tiketlər`,clear:`Seçimi təmizlə`,selected_one:`{{count, number}} seçilib`,selected_other:`{{count, number}} seçilib`,selectedOf_one:`{{count, number}} / {{total}}<wide> seçilib</wide>`,selectedOf_other:`{{count, number}} / {{total}}<wide> seçilib</wide>`,applied_one:`{{label}} {{count, number}} tiketə tətbiq edildi`,applied_other:`{{label}} {{count, number}} tiketə tətbiq edildi`,partlyApplied_one:`{{count, number}} tiketdən {{done, number}} tiketə tətbiq edildi`,partlyApplied_other:`{{count, number}} tiketdən {{done, number}} tiketə tətbiq edildi`,mixedDue:`Qarışıq son tarixlər`,fields:`Sahələr`,more:`Digər əməliyyatlar`,moveTo:`{{status}} statusuna köçür`,restoreChange:`Bərpa`,restore:`Seçilənləri bərpa et`,archive:`Seçilənləri arxivləşdir`,delete:`Seçilənləri sil`,deleteLimit:`Bir dəfəyə ən çox {{limit, number}} sil`,archiveConfirm_one:`{{collection}} mənbəyindən {{count, number}} tiket arxivləşdirilsin?`,archiveConfirm_other:`{{collection}} mənbəyindən {{count, number}} tiket arxivləşdirilsin?`,deleteConfirm_one:`{{collection}} mənbəyindən {{count, number}} tiket şərhləri, əlaqələri və qoşmaları ilə birlikdə həmişəlik silinsin? Seçimdən kənar alt tiketlər valideynini itirəcək.`,deleteConfirm_other:`{{collection}} mənbəyindən {{count, number}} tiket şərhləri, əlaqələri və qoşmaları ilə birlikdə həmişəlik silinsin? Seçimdən kənar alt tiketlər valideynini itirəcək.`,deleted_one:`{{count, number}} tiket silindi`,deleted_other:`{{count, number}} tiket silindi`},menu:{copied:`{{label}} kopyalandı`,open:`Tiketi aç`,preview:`Önizləmə`,newTab:`Yeni tabda aç`,edit:`Tiketi redaktə et`,editTitle:`{{key}} redaktə et`,duplicate:`Tiketin surətini çıxar`,created:`{{key}} yaradıldı`,move:`Köçür`,moveToProject:`Layihəyə köçür…`,moveTitle:`{{key}} köçür`,moved:`{{key}} olaraq köçürüldü`,follow:`Tiketi izlə`,unfollow:`Tiketi izləməyi dayandır`,archive:`Tiketi arxivləşdir`,unarchive:`Tiketi arxivdən çıxar`,delete:`Tiketi sil`,deleteConfirm:`{{key}}, onun şərhləri, əlaqələri və qoşmaları həmişəlik silinsin?`,openProject:`Layihəni aç`,projectLink:`Layihə keçidi`,copyLink:`Keçidi kopyala`,newTicket:`Yeni tiket`,projectSettings:`Layihə parametrləri`,manageAccount:`Hesabı idarə et`,email:`E-poçt`,copyEmail:`E-poçtu kopyala`},schedule:{range:`{{from}} – {{to}}`,calendar:`Təqvim`,timeline:`Zaman xətti`,today:`Bu gün`,previousMonth:`Əvvəlki ay`,nextMonth:`Növbəti ay`,previousWeeks:`Əvvəlki iki həftə`,nextWeeks:`Növbəti iki həftə`,emptyWeeks:`Bu həftələrdə planlaşdırılmış heç nə yoxdur`,monthTickets:`{{title}} tiketləri`,showDay_one:`{{day}} tarixindəki bütün {{count, number}} tiketi göstər`,showDay_other:`{{day}} tarixindəki bütün {{count, number}} tiketi göstər`,more_one:`daha {{count, number}}`,more_other:`daha {{count, number}}`,ticketColumn:`Tiket`,scheduledTickets:`Planlaşdırılmış tiketlər`},copy:{description:`Təsvir`,acceptanceCriteria:`Qəbul meyarları`}},detail:{field:{title:`Başlıq`,description:`Təsvir`,acceptanceCriteria:`Qəbul meyarları`,parent:`Valideyn`,parentTicket:`Valideyn tiket`,labels:`Etiketlər`,scopes:`Əhatələr`,holdReason:`Gözləmə səbəbi`,url:`URL`},page:{preview:`Tiket önizləməsi`,unavailable:`Tiket əlçatan deyil`,noProject:`Keçiddə tiketin layihəsi göstərilməyib.`,gone:`Bu tiket silinib və ya artıq ona girişiniz yoxdur.`,closePreview:`Önizləməni bağla`,openFullPage:`Tam səhifədə aç`,backToTickets:`Tiketlərə qayıt`,tickets:`Tiketlər`,previousTicket:`Əvvəlki tiket`,previousTicketShortcut:`Əvvəlki tiket (K)`,nextTicket:`Növbəti tiket`,nextTicketShortcut:`Növbəti tiket (J)`,position:`{{position, number}} / {{total, number}}`,subscribe:`Abunə ol`,unsubscribe:`Abunəlikdən çıx`,subscribed:`Yeniliklərə abunə oldunuz`,unsubscribed:`Abunəlik ləğv edildi`,archive:`Tiketi arxivləşdir`,unarchive:`Tiketi arxivdən çıxar`,archivedToast:`Tiket arxivləşdirildi`,unarchivedToast:`Tiket arxivdən çıxarıldı`,archived:`Arxivləşdirilib`,edit:`Tiketi redaktə et`,editShortcut:`Tiketi redaktə et (E)`,editTitle:`{{key}} redaktə et`,actions:`Tiket əməliyyatları`,copyKey:`Tiket açarını kopyala`,keyCopied:`{{key}} kopyalandı`,noDescription:`_Təsvir yoxdur_`,noAcceptanceCriteria:`_Qəbul meyarları yoxdur_`,coordination:`Tiket koordinasiyası`,properties:`Xüsusiyyətlər`,created:`Yaradılıb`,updated:`Yenilənib`},section:{discussion:`Müzakirə`,evidence:`Sübutlar`,activity:`Fəaliyyət`},readiness:{heading:`Agent hazırlığı`,ready:`Götürməyə hazırdır`,notReady:`Götürməyə hazır deyil`,hold:`Gözləmə: {{reason}}`,claimedUntil:`{{date}} tarixinədək <strong>{{worker}}</strong> tərəfindən götürülüb.`},claim:{release:`Götürməmi burax`,releaseDescription:`İşçiniz yazmağı dayandırdıqdan sonra sahiblik buraxılsın?`,recover:`Müddəti bitmiş götürməni bərpa et`,recoverHelp:`Əvvəlcə işçinin dayandığını təsdiqləyin.`,reason:`Bərpa səbəbi`,confirmed:`İşçinin dayandığını təsdiqləmişəm`,submit:`Sahibliyi bərpa et`},editor:{storageUnavailable:`Bu brauzer qaralamanızı yadda saxlaya bilmir. Yadda saxlayana qədər redaktoru açıq saxlayın.`,saved:`{{key}} yadda saxlanıldı`,created:`{{key}} yaradıldı`,properties:`Tiket xüsusiyyətləri`,readyForAgents:`Agentlər üçün hazırdır`,readyForAgentsHelp:`Tələblər, bloklayıcılar və sahiblik imkan verdikdə agentlər bu tiketi götürə bilər`,agents:`Agentlər`,notReady:`Hazır deyil`,moreProperties:`Daha çox xüsusiyyət`,holdPlaceholder:`İş niyə gözləməlidir?`,claimsNote:`Götürmələr yenə də tələblərdən, bloklayıcılardan və cari sahiblikdən asılıdır.`,restoreSize:`Redaktorun ölçüsünü bərpa et`,expand:`Redaktoru genişləndir`,staleTitle:`Bu tiket dəyişib.`,staleDescription:`Qaralamanız hələ {{version}} versiyasından istifadə edir; yadda saxlamaq üçün cari versiya tələb olunacaq.`,discardAndReload:`Qaralamanı at və yenidən yüklə`,titlePlaceholder:`Tiket başlığı`,descriptionPlaceholder:`Kontekst, tələblər və ya təkrarlama addımları əlavə edin…`,criteriaPlaceholder:`- [ ] Bunun tamamlandığını nə göstərəcək?`,removeCriteria:`Qəbul meyarlarını sil`,addCriteria:`Qəbul meyarları əlavə et`,savedLocally:`Qaralama bu cihazda yadda saxlanıldı`,notSavedLocally:`Qaralama yadda saxlanıla bilmədi`,discardDraft:`Qaralamanı at`,createMore:`Daha çox yarat`,save:`Tiketi yadda saxla`,create:`Tiket yarat`},resource:{removeConfirm:`{{name}} silinsin?`,uploading:`Yüklənir…`},attachment:{heading:`Qoşmalar`,tooLarge:`Hər biri 10 MB-dan böyük olmayan fayllar seçin.`,attached_one:`Fayl qoşuldu`,attached_other:`{{count, number}} fayl qoşuldu`,attach:`Fayl qoş`,emptyWritable:`Qoşma yoxdur. Faylları bura atın.`,empty:`Qoşma yoxdur`,remove:`Qoşmanı sil`,fileInput:`Fayl (10 MB-a qədər)`},evidence:{description:`Bu tiket üzərində işləyən insanların və inteqrasiyaların bildirdiyi keçidlər və nəticələr.`,add:`Sübut əlavə et`,empty:`Qoşulmuş sübut yoxdur`,added:`Sübut əlavə edildi`,kindLabel:`Növ`,outcomeLabel:`Bildirilən nəticə`,commit:`Commit (tam hash)`,note:`Qeyd`},link:{heading:`Keçidlər`,add:`Keçid əlavə et`,edit:`Keçidi redaktə et`,save:`Keçidi yadda saxla`,remove:`Keçidi sil`,empty:`Keçid yoxdur`,stateUnknown:`Vəziyyət naməlumdur`,remote:`{{kind}} · {{state}}`,remoteState:{open:`Açıq`,closed:`Bağlı`,merged:`Birləşdirilib`,draft:`Qaralama`,active:`Aktiv`,deleted:`Silinib`}},activity:{previous:`Əvvəlki fəaliyyət`,next:`Növbəti fəaliyyət`},relations:{heading:`Əlaqələr`,add:`Əlaqə əlavə et`,linkExisting:`Mövcud tiketi bağla`,newChild:`Yeni alt tiket`,newChildTitle:`{{key}} üçün yeni alt tiket`,children:`Alt tiketlər <count>{{done, number}}/{{total, number}}</count>`,previousChildren:`Əvvəlki alt tiketlər`,nextChildren:`Növbəti alt tiketlər`,empty:`Əlaqə yoxdur`,type:`Əlaqə`,ticket:`Tiket`,statusOf:`{{key}} statusu`,assigneeOf:`{{key}} icraçısı`,remove:{blocks:`“Bloklayır” əlaqəsini sil`,related:`“Əlaqəlidir” əlaqəsini sil`,duplicate:`“Təkrarlayır” əlaqəsini sil`,causes:`“Səbəb olur” əlaqəsini sil`,clones:`“Klonlayır” əlaqəsini sil`,implements:`“Həyata keçirir” əlaqəsini sil`,reviews:`“Yoxlayır” əlaqəsini sil`}},discussion:{empty:`Hələ şərh yoxdur`,previous:`Əvvəlki şərhlər`,next:`Növbəti şərhlər`,comment:`Şərh yaz`,placeholder:`Şərh yazın…`,posting:`Göndərilir…`,edited:`Redaktə edilib`,edit:`Şərhi redaktə et`,delete:`Şərhi sil`,deleteConfirm:`Bu şərh həmişəlik silinsin?`},markdown:{format:{heading:`Başlıq`,bold:`Qalın`,italic:`Kursiv`,link:`Keçid`,linkHelp:`Seçilmiş mətnə keçid əlavə et`,bullets:`Markerli siyahı`,numbers:`Nömrəli siyahı`,tasks:`Yoxlama siyahısı`,code:`Kod bloku`},formatting:`{{label}} formatlaşdırması`,editor:`{{label}} redaktoru`,insertFile:`Fayl daxil et`,insertFileInto:`{{label}} sahəsinə fayl daxil et`,write:`Yaz`,visual:`Vizual`,preview:`Önizləmə`,unsupported:`Bu mətndə vizual redaktorun saxlaya bilmədiyi Markdown var, məsələn, HTML və ya haşiyələr.`,nothingToPreview:`Önizləmək üçün heç nə yoxdur`,mention:`Üzvü qeyd et`,insertBlock:`Blok daxil et`,block:{quote:`Sitat`,table:`Cədvəl`,diagram:`Diaqram`,divider:`Ayırıcı`,file:`Fayl`},table:{addRow:`Sətir əlavə et`,addColumn:`Sütun əlavə et`,deleteRow:`Sətri sil`,deleteColumn:`Sütunu sil`,deleteTable:`Cədvəli sil`},linkAddress:`Keçid ünvanı`,diagram:`Diaqram`,diagramFailed:`Bu diaqram çəkilə bilmədi: {{error}}`,diagramSource:`Diaqram mənbəyi`}},settings:{account:{title:`Parametrlər`,security:`Daxil olma və təhlükəsizlik`,localPassword:`Yerli şifrə`,localPasswordDescription:`Şifrəni dəyişmək digər seanslarınızdan çıxış edir.`,changePassword:`Şifrəni dəyiş`,oidcLinked:`Eyni hesaba daxil olmaq üçün bu identiklik provayderindən istifadə edin.`,oidcUnlinked:`Bu provayder vasitəsilə daxil olmazdan əvvəl hesabınızı bağlayın.`,linked:`Bağlanıb`,link:`{{name}} bağla`,currentPassword:`Cari şifrə`,newPassword:`Yeni şifrə`,confirmPassword:`Yeni şifrəni təsdiqləyin`,passwordMismatch:`Yeni şifrələr uyğun gəlmir.`,passwordUpdated:`Şifrə yeniləndi. Digər seanslardan çıxış edildi.`},preferences:{behavior:`Davranış`,languageDescription:`Bu brauzerdə, o cümlədən daxil olma səhifəsində tətbiq olunur.`,weekStart:`Həftənin başlanğıcı`,weekStartDescription:`Bütün tarix seçicilərinə tətbiq olunur.`,weekStartLanguage:`Dil standartı`,sidebar:`Açılışda yan panel`,sidebarDescription:`Kanvil növbəti dəfə açılanda qüvvəyə minir.`,sidebarLast:`Son vəziyyətdə`,sidebarExpanded:`Genişləndirilmiş`,sidebarCollapsed:`Yığılmış`,ticketLists:`Tiket siyahıları`,reset:`Seçimləri sıfırla`,resetDescription:`Bu brauzerdə bu seçimləri standart dəyərlərə qaytarır.`,resetDone:`Seçimlər sıfırlandı`},notifications:{title:`Bildirişlər`,inbox:`Gələnlər`,inboxDescription:`İzlədiyiniz tiketlərin yenilikləri Kanvil-də görünür.`,inboxSwitch:`Abunə olunmuş tiket yenilikləri Gələnlərdə`,push:`Push bildirişləri`,pushDescription:`Qeydiyyatdan keçirdiyiniz cihazlara yenilikləri göndərin.`,pushUnavailable:`Bu nüsxədə push çatdırılması aktiv deyil.`,pushSwitch:`Qeydiyyatlı cihazlarda push bildirişləri`,categories:`Kateqoriyalar`,categorySwitch:`{{label}} bildirişləri`,reminder:`Müddət yaxınlaşma xatırlatması`,reminderDescription:`İzlənilən tiketin son tarixindən nə qədər əvvəl.`,reminderUnit:`Xatırlatma vahidi`,reminderAmount:`Xatırlatma miqdarı`,unit:{minutes:`Dəqiqə`,hours:`Saat`,days:`Gün`,weeks:`Həftə`}},layout:{nav:`Parametrlər`,section:`Parametrlər bölməsi`,nested:`{{parent}} · {{child}}`,name:`Ad`,description:`Təsvir`,descriptionPlaceholder:`Təsvir əlavə edin`,noDescription:`Təsvir yoxdur`,stale:`Bu başqa yerdə dəyişdirilib. Ləğv edib yenidən başlayın.`},profile:{title:`Profil`,displayName:`Görünən ad`,edit:`Profili redaktə et`,changePhoto:`Şəkli dəyiş`,removePhoto:`Şəkli sil`,photoFile:`Profil şəkli faylı`,photoUpdated:`Şəkil yeniləndi`,photoRemoved:`Şəkil silindi`,saved:`Profil yadda saxlanıldı`},credentials:{global:`Qlobal`,globalDescription:`Hesabınızın indi və gələcəkdə giriş əldə etdiyi bütün layihələrə və iş məkanlarına giriş.`,title:`API açarları`,description:`Skriptləri, inteqrasiyaları və agentləri ləğv edə biləcəyiniz girişlə qoşun.`,create:`API açarı yarat`,creating:`Yaradılır…`,created:`API açarı yaradıldı`,name:`Ad`,access:`Giriş`,projects:`Layihələr`,expires:`Bitmə tarixi`,actions:`Əməliyyatlar`,unavailableProject:`Əlçatan olmayan layihə`,untitled:`Adsız açar`,expired:`Müddəti bitib`,disabled:`Deaktiv`,custom:`Fərdi`,noProjects:`Layihəyə giriş yoxdur`,never:`Heç vaxt`,revoke:`API açarını ləğv et`,revokeNamed:`{{name}} ləğv edilsin? Ondan istifadə edən bağlantılar dərhal girişini itirəcək.`,revokeUnnamed:`Bu API açarı ləğv edilsin? Ondan istifadə edən bağlantılar dərhal girişini itirəcək.`,empty:`API açarı yoxdur`,emptyDescription:`Skript, inteqrasiya və ya agentin layihələrinizə girişi lazım olduqda açar yaradın.`,copyNow:`Bu açarı indi kopyalayın. Onu bir daha görə bilməyəcəksiniz.`,newKey:`Yeni API açarı`,copy:`API açarını kopyala`,noProjectTitle:`Əvvəlcə layihə seçin`,noProjectDescription:`API açarlarının ən azı bir layihəyə girişi olmalıdır.`,goToProjects:`Layihələrə keç`,keyName:`Açarın adı`,keyNamePlaceholder:`məs. Buraxılış avtomatlaşdırması`,projectAccess:`Layihəyə giriş`,findProject:`Layihə tap`,projectDescription:`{{key}} · {{workspace}}`,workspace:`İş sahəsi`,noMatches:`Uyğun layihə yoxdur`,roleLimit:`Mövcud layihə rolunuz bu açarın imkanlarını yenə də məhdudlaşdırır.`,accessLevel:`Giriş səviyyəsi`,preset:{read:`Yalnız oxuma`,write:`Oxuma və yazma`,automation:`Oxuma, yazma və iş götürmə`,custom:`Fərdi icazələr`},permission:{read:{name:`Tiketləri oxumaq`,description:`Layihələrə, tiketlərə, şərhlərə və qoşmalara baxmaq.`},write:{name:`Tiketləri redaktə etmək`,description:`Tiketləri, şərhləri və qoşmaları yaratmaq və yeniləmək.`},claim:{name:`İş götürmək`,description:`Tiket sahibliyini götürmək, yeniləmək və buraxmaq.`},admin:{name:`Layihələri və iş məkanlarını idarə et`,description:`Rolunuzun icazə verdiyi layihə və iş məkanı parametrlərini və üzvlükləri dəyişin.`}},expiry:{7:`7 gün`,30:`30 gün`,90:`90 gün`,365:`1 il`,custom:`Fərdi tarix`,never:`Müddətsiz`},expiresOn:`Bitmə tarixi`,neverDescription:`Açar siz onu silənə qədər işləyir.`},calendarFeeds:{title:`Təqvim lentləri`,new:`Yeni təqvim lenti`,created:`Təqvim lenti yaradıldı`,create:`Lent yarat`,adminOnly:`Təqvim lentlərini layihə administratorları paylaşır və ləğv edir.`,labels:`Etiketlər`,everyLabel:`Bütün etiketlər`,stopped:`· Dayandırılıb: yaradanın artıq girişi yoxdur`,revoke:`{{name}} ləğv et`,revokeDescription:`{{name}} lentinə abunə olan təqvimlər dərhal yenilənməyi dayandırır. Tiketlər dəyişməz qalır.`,revoked:`Təqvim lenti ləğv edildi`,empty:`Təqvim lenti yoxdur`,copyNow:`Bu keçidi indi təqvim tətbiqinə kopyalayın. O, yalnız bir dəfə göstərilir və ona sahib olan hər kəs paylaşılan tiketləri oxuya bilər.`,link:`Lent keçidi`,name:`Lentin adı`,onlyLabels:`Yalnız bu etiketləri olan tiketlər`,explanation:`Lent başlama və ya son tarixi olan açıq, arxivləşdirilməmiş tiketləri {{zone}} saat qurşağında bütün günlük hadisələr kimi göstərir. Keçidə sahib olan hər kəs onların açarlarını, başlıqlarını, statuslarını, prioritetlərini və tarixlərini görür.`},project:{title:`Layihə parametrləri`,edit:`Layihəni redaktə et`,copyPrefix:`Tiket prefiksini kopyala`,saved:`Layihə yadda saxlanıldı`,noOpenTickets:`Açıq tiket yoxdur`,openTickets_one:`{{count, number}} açıq tiket`,openTickets_other:`{{count, number}} açıq tiket`,overdueTickets_one:`· {{count, number}} gecikmiş`,overdueTickets_other:`· {{count, number}} gecikmiş`,created:`· Yaradılıb: <time/>`,access:`Layihəyə giriş`,accessWorkspace:`İş sahəsi üzvləri girişi iş sahəsindəki rollarından alır.`,accessPrivate:`Bu layihəni yalnız əlavə etdiyiniz üzvlər və iş sahəsi administratorları görə bilər.`,accessUpdated:`Layihəyə giriş yeniləndi`,visibility:{workspace:`İş sahəsindəki hər kəs`,private:`Yalnız layihə üzvləri`},workspace:`İş sahəsi`,workspaceSettings:`İş sahəsi parametrləri`,prefix:`Tiket prefiksi`,prefixDescription:`Dəyişiklikdən sonra əvvəlki prefikslər və tiket açarları işləməyə davam edir.`,changePrefix:`Prefiksi dəyiş`,changePrefixTitle:`Tiket prefiksini dəyiş`,changePrefixDescription:`{{name}} layihəsindəki hər tiket yeni prefiksi alır və nömrəsini saxlayır.`,changing:`Dəyişdirilir…`,prefixChanged:`Tiketlər indi {{key}} istifadə edir`,publicLink:`İctimai keçid`,publicDisclosure:`Keçidə sahib olan hər kəs bu layihədəki hər arxivləşdirilməmiş tiketin başlığını, təsvirini, qəbul meyarlarını, statusunu, prioritetini, növünü, çətinliyini, etiketlərini, əhatələrini, tarixlərini və digər tiketlərə keçidlərini oxuya bilər. İnsanlar, şərhlər, fəaliyyət, qoşmalar, sübutlar və götürmələr gizli qalır.`,publicOn:`İctimai keçid aktivdir`,publicOff:`İctimai keçid deaktivdir`,off:`Deaktiv`,copyPublicLink:`İctimai keçidi kopyala`,openPublic:`İctimai görünüşü aç`,stopSharing:`Paylaşımı dayandır`,share:`İctimai paylaş`,background:`Fon`,backgroundDescription:`Bu layihənin tiket siyahısı və lövhəsinin arxasında göstərilir.`,backgroundUpdated:`Fon yeniləndi`,backgroundRemoved:`Fon silindi`,currentBackground:`Cari fon`,uploading:`Yüklənir…`,replace:`Əvəz et`,upload:`Şəkil yüklə`,backgroundFile:`Layihə fonu faylı`},tickets_one:`{{count, number}} tiket`,tickets_other:`{{count, number}} tiket`,moveUp:`{{name}} yuxarı köçür`,moveDown:`{{name}} aşağı köçür`,workflow:{title:`İş axını`,new:`Yeni vəziyyət`,edit:`Vəziyyəti redaktə et`,editNamed:`{{name}} vəziyyətini redaktə et`,deleteNamed:`{{name}} vəziyyətini sil`,onlyState:`{{name}} yeganə “{{category}}” vəziyyətidir`,saved:`Vəziyyət yadda saxlanıldı`,name:`Vəziyyətin adı`,category:`Kateqoriya`,color:`Vəziyyətin rəngi`,categoryChange:`Onun tiketlərinin statusu {{status}} olaraq dəyişir.`,save:`Vəziyyəti yadda saxla`,delete:`Vəziyyəti sil`,deleteTitle:`{{name}} sil`,deleted:`Vəziyyət silindi`,reassign_one:`{{count, number}} tiketi buraya köçür:`,reassign_other:`{{count, number}} tiketi buraya köçür:`},labels:{title:`Etiketlər`,search:`Etiketlərdə axtar`,new:`Yeni etiket`,edit:`Etiketi redaktə et`,workspace:`İş sahəsi`,editNamed:`{{name}} etiketini redaktə et`,deleteNamed:`{{name}} etiketini sil`,deleteUsed_one:`{{name}} arxivləşdirilmişlər daxil {{count, number}} tiketdən çıxarılsın? Bu etiketdən istifadə edən yadda saxlanılmış görünüşlərin etiket filtri silinəcək.`,deleteUsed_other:`{{name}} arxivləşdirilmişlər daxil {{count, number}} tiketdən çıxarılsın? Bu etiketdən istifadə edən yadda saxlanılmış görünüşlərin etiket filtri silinəcək.`,deleteUsedShared_one:`{{name}} bütün layihələrdə arxivləşdirilmişlər daxil {{count, number}} tiketdən çıxarılsın? Bu etiketdən istifadə edən yadda saxlanılmış görünüşlərin etiket filtri silinəcək.`,deleteUsedShared_other:`{{name}} bütün layihələrdə arxivləşdirilmişlər daxil {{count, number}} tiketdən çıxarılsın? Bu etiketdən istifadə edən yadda saxlanılmış görünüşlərin etiket filtri silinəcək.`,deleteUnused:`İstifadə olunmayan {{name}} etiketi silinsin? Ondan istifadə edən yadda saxlanılmış görünüşlərin etiket filtri silinəcək.`,deleted:`Etiket silindi`,noMatches:`Uyğun etiket yoxdur`,empty:`Hələ etiket yoxdur`,saved:`Etiket yadda saxlanıldı`,name:`Etiketin adı`,color:`Etiketin rəngi`,rename:`Adı dəyişdirmək bu etiketi tiketlərdə və yadda saxlanılmış görünüşlərdə yeniləyir.`,renameShared:`Adı dəyişdirmək bu etiketi bütün layihələrdə tiketlərdə və yadda saxlanılmış görünüşlərdə yeniləyir.`,adopt:`Layihə etiketlərini mənimsə`,adoptDescription:`{{projects}} layihələrində müəyyən edilib. Onların tiketləri etiketi saxlayır və bu rəngi alır.`,save:`Etiketi yadda saxla`},fields:{title:`Sahələr`,new:`Yeni sahə`,edit:`Sahəni redaktə et`,editNamed:`{{name}} sahəsini redaktə et`,deleteNamed:`{{name}} sahəsini sil`,deleteUsed_one:`{{name}} silinsin və onun dəyərləri arxivləşdirilmişlər daxil {{count, number}} tiketdən çıxarılsın? Yadda saxlanılmış görünüşlər bu sahə üzrə filtrlərini itirəcək.`,deleteUsed_other:`{{name}} silinsin və onun dəyərləri arxivləşdirilmişlər daxil {{count, number}} tiketdən çıxarılsın? Yadda saxlanılmış görünüşlər bu sahə üzrə filtrlərini itirəcək.`,deleteUnused:`İstifadə olunmayan {{name}} sahəsi silinsin? Yadda saxlanılmış görünüşlər bu sahə üzrə filtrlərini itirəcək.`,deleted:`Sahə silindi`,empty:`Hələ sahə yoxdur`,saved:`Sahə yadda saxlanıldı`,required:`Məcburi`,requiredDescription:`Yeni tiketlər üçün dəyər tələb olunur.`,defaultValue:`Standart: {{value}}`,default:`Standart`,name:`Sahənin adı`,type:`Tip`,options:`Seçimlər`,option:`Seçim {{number}}`,optionInline:`seçim {{number}}`,optionColor:`{{name}} rəngi`,removeOption:`{{name}} sil`,addOption:`Seçim əlavə et`,reassign:`{{name}} olan tiketlər`,clearValue:`Dəyəri təmizlə`,moveTo:`{{name}} seçiminə köçür`,save:`Sahəni yadda saxla`},lifecycle:{archivedToast:`Layihə arxivləşdirildi`,restoredToast:`Layihə bərpa edildi`,archivedNotice:`Arxivləşdirilib: <time/> · yalnız oxuma`,restore:`Bərpa et`,archive:`Arxivləşdir`,archivedDescription:`Arxivləşdirilib: <time/>. Onun tiketləri yalnız oxunur və siyahılarda, axtarışda, “Mənim işlərim”də, təqvim lentlərində və agent növbələrində görünmür.`,archiveDescription:`Layihəni siyahılardan, axtarışdan, “Mənim işlərim”dən, təqvim lentlərindən və agent növbələrindən gizlədir və tiketlərini yalnız oxunan edir. Tarixçə saxlanılır.`,archiveProject:`Layihəni arxivləşdir`,archiveConfirm:`{{name}} bərpa edilənə qədər layihə siyahılarından və agent növbələrindən çıxarılır. Götürülmüş tiketlər əvvəlcə buraxılmalıdır.`,blocker:`{{message}}: {{tickets}}`,row:{tickets:`Tiketlər`,losesAccess:`Girişini itirir`,gainsAccess:`Giriş əldə edir`,labelsCopied:`Kopyalanan etiketlər`,labelsMerged:`Birləşdirilən etiketlər`,followsRemoved:`Silinən izləmələr`,invitations:`Dəvətlər`,comments:`Şərhlər`,files:`Fayllar`,kept:`Saxlanılır`,projects:`Layihələr`,members:`Üzvlər`,sharedLabels:`Ümumi etiketlər`,signIn:`Daxil olma`,sessions:`Seanslar`,credentials:`Giriş məlumatları`,devices:`Cihazlar`,access:`Giriş`,claims:`Götürmələr`,imports:`İdxallar`,profile:`Profil`},transferTickets_one:`{{count, number}}, açarları və keçidləri saxlanılmaqla`,transferTickets_other:`{{count, number}}, açarları və keçidləri saxlanılmaqla`,noOne:`Heç kim`,transferInvitations_one:`{{count, number}} gözləyən dəvətdən layihə çıxarılır`,transferInvitations_other:`{{count, number}} gözləyən dəvətdən layihə çıxarılır`,files_one:`{{count, number}} · {{size}}`,files_other:`{{count, number}} · {{size}}`,keptProject_one:`{{count, number}} fəaliyyət qeydi və {{key}} prefiksi`,keptProject_other:`{{count, number}} fəaliyyət qeydi və {{key}} prefiksi`,keptWorkspace_one:`{{count, number}} fəaliyyət qeydi və {{slug}} URL açarı`,keptWorkspace_other:`{{count, number}} fəaliyyət qeydi və {{slug}} URL açarı`,archivedProject:`arxivləşdirilib`,membersLoseAccess_one:`{{count, number}} nəfər girişini itirir; hesabı qalır`,membersLoseAccess_other:`{{count, number}} nəfər girişini itirir; hesabları qalır`,pendingInvitations_one:`{{count, number}} gözləyir`,pendingInvitations_other:`{{count, number}} gözləyir`,move:`Köçür`,moveTitle:`{{name}} köçür`,moved:`{{name}} iş sahəsinə köçürüldü`,movedUnnamed:`İş sahəsinə köçürüldü`,destination:`Təyinat iş sahəsi`,moveProject:`Layihəni köçür`,moving:`Köçürülür…`,export:`İxrac`,exportDescription:`Tiketlər iş axını, sahələr, etiketlər, əlaqələr, şərhlər, sübutlar və keçidlərlə birlikdə versiyalı JSON kimi. Qoşmalar və fəaliyyət tarixçəsi burada qalır.`,exporting:`İxrac edilir…`,exportJson:`JSON ixrac et`,delete:`Sil`,deleteTitle:`{{name}} sil`,deleted:`{{name}} silindi`,typeToConfirm:`Təsdiqləmək üçün {{value}} yazın`,deleting:`Silinir…`,deleteProject:`Layihəni sil`,deleteProjectDescription:`Tiketləri, şərhləri və faylları həmişəlik silir. Fəaliyyət tarixçəsi qalır və {{key}} bir daha istifadə edilmir.`,signInAgain:`Yenidən daxil ol`,deleteWorkspace:`İş sahəsini sil`,deleteWorkspaceDescription:`İş sahəsindəki bütün layihələri tiketləri, şərhləri və faylları ilə birlikdə həmişəlik silir və üzvlərini çıxarır. Hesablar və fəaliyyət tarixçəsi qalır.`,noSignInMethods:`Bağlanmış daxil olma üsulu yoxdur`,sessions_one:`{{count, number}} seansdan çıxış edildi`,sessions_other:`{{count, number}} seansdan çıxış edildi`,credentials_one:`{{count, number}} API açarı və ya tətbiq bağlantısı ləğv edildi`,credentials_other:`{{count, number}} API açarı və ya tətbiq bağlantısı ləğv edildi`,devices_one:`{{count, number}} cihaz və ya bildiriş kanalı silindi`,devices_other:`{{count, number}} cihaz və ya bildiriş kanalı silindi`,workspaceCount_one:`{{count, number}} iş sahəsi`,workspaceCount_other:`{{count, number}} iş sahəsi`,directProjectCount_one:`{{count, number}} birbaşa layihə`,directProjectCount_other:`{{count, number}} birbaşa layihə`,accessWorkspaces:`{{workspaces}} silindi`,accessBoth:`{{workspaces}} və {{projects}} silindi`,claims_one:`{{count, number}} götürmə buraxıldı`,claims_other:`{{count, number}} götürmə buraxıldı`,imports_one:`{{count, number}} idxal ləğv edildi`,imports_other:`{{count, number}} idxal ləğv edildi`,profileErased:`Ad, e-poçt və şəkil silindi`,commentCount_one:`{{count, number}} şərh`,commentCount_other:`{{count, number}} şərh`,activityCount_one:`{{count, number}} fəaliyyət qeydi`,activityCount_other:`{{count, number}} fəaliyyət qeydi`,keptAccount:`{{tickets}}, {{comments}} və {{events}} “{{name}}” kimi göstərilir`,deleteAccount:`Hesabı sil`,deleteOwnAccount:`Daxil olma məlumatlarınızı, seanslarınızı, açarlarınızı, cihazlarınızı, üzvlüklərinizi və şəxsi məlumatlarınızı həmişəlik silir. Tiketlər, şərhlər və fəaliyyət qalır və “{{name}}” kimi göstərilir.`,deleteOtherAccount:`Bu hesabın daxil olma məlumatlarını, seanslarını, açarlarını, cihazlarını, üzvlüklərini və şəxsi məlumatlarını həmişəlik silir. Tiketlər, şərhlər və fəaliyyət qalır və “{{name}}” kimi göstərilir.`},icon:{change:`İkonu dəyiş`,title:`Layihə ikonu`,library:`İkon kitabxanası`,upload:`Şəkil yüklə`,search:`İkonlarda axtar`,searchPlaceholder:`İkonlarda axtar…`,color:`İkonun rəngi`,choose:`{{name}} seç`,noMatches:`Uyğun ikon yoxdur`,showMore:`Daha çox ikon göstər`,uploading:`Yüklənir…`,chooseImage:`Şəkil seç`,formats:`PNG, JPEG və ya WebP · 10 MB-a qədər`,file:`Layihə ikonu faylı`,reset:`İkonu sıfırla`},createProject:{title:`Yeni layihə`,inWorkspace:`{{name}} iş sahəsində`,everyoneLabel:`Bu iş sahəsindəki hər kəs`,name:`Layihənin adı`,namePlaceholder:`Mobil tətbiq`,create:`Layihə yarat`},workspace:{title:`İş sahəsi parametrləri`,owner:`Sahib`,noOwner:`Sahib yoxdur. Nüsxə administratoru təyin edə bilər.`,transfer:`Sahibliyi ötür`,transferring:`Ötürülür…`,transferred:`{{workspace}} artıq {{name}} istifadəçisinə məxsusdur`,newOwner:`Yeni sahib`,noEligible:`Sahiblik aktiv iş sahəsi administratoruna keçir. Əvvəlcə başqa bir üzvü administrator edin.`,chosenCan:`Bundan sonra {{name}} iş sahəsini ötürə və ya silə bilər.`,newOwnerCan:`Yeni sahib iş sahəsini ötürə və ya silə bilər.`,ownerStays:`{{name}} administrator olaraq qalır.`,unavailable:`İş sahəsi əlçatan deyil`,backToProjects:`Layihələrə qayıt`,name:`İş sahəsinin adı`,edit:`İş sahəsini redaktə et`,copyKey:`İş sahəsinin açarını kopyala`,saved:`İş sahəsi yadda saxlanıldı`,projects:`Layihələr`,projectCount_one:`Bu iş sahəsində girişiniz olan {{count, number}} layihə.`,projectCount_other:`Bu iş sahəsində girişiniz olan {{count, number}} layihə.`,viewProjects:`Layihələrə bax`,members:`Üzvlər`,membersDescription:`İş sahəsi rolları iş sahəsi girişindən istifadə edən layihələrə tətbiq olunur.`,manageMembers:`Üzvləri idarə et`},members:{title:`Üzvlər`,customRole:`Fərdi rol`,search:`Üzvlərdə axtar`,searchPlaceholder:`Ad və ya e-poçt…`,add:`Üzv əlavə et`,added:`Üzv əlavə edildi`,workspaceAccess:`İş sahəsi girişi`,owner:`Sahib`,roleFor:`{{name}} üçün rol`,useWorkspaceAccess:`İş sahəsi girişindən istifadə et`,remove:`Üzvü çıxar`,removeFromWorkspace:`{{name}} bu iş sahəsindən və onun bütün layihələrindən çıxarılsın?`,restoreAccess:`{{name}} üçün {{role}} iş sahəsi girişi bərpa edilsin?`,removeFromProject:`{{name}} üçün layihə girişi silinsin?`,noMatches:`Uyğun üzv yoxdur`,empty:`Üzv yoxdur`,projectRole:`Layihə rolu`,workspaceRole:`İş sahəsi rolu`},invitations:{title:`Dəvətlər`,invite:`E-poçtla dəvət et`,removedProjects:`Silinmiş layihələr`,wholeWorkspace:`Bütün iş sahəsi`,selectedProjects:`Seçilmiş layihələr`,expired:`Müddəti bitib`,expires:`Bitir: <time/>`,resend:`Dəvəti {{email}} ünvanına yenidən göndər`,revoke:`Dəvəti ləğv et`,revokeDescription:`{{email}} ünvanına göndərilən keçid işləməyi dayandıracaq.`,revoked:`Dəvət ləğv edildi`,empty:`Gözləyən dəvət yoxdur`,email:`E-poçt`,role:`Rol`,access:`Giriş`,projects:`Layihələr`,noProjects:`Bu iş sahəsində layihə yoxdur.`,linkExpires:`Keçidin bitmə müddəti`,inDays_one:`{{count, number}} gün sonra`,inDays_other:`{{count, number}} gün sonra`,submit:`Dəvət et`,link:`Dəvət keçidi`,copyLink:`Dəvət keçidini kopyala`,emailed:`{{email}} ünvanına göndərildi. Keçid həmin ünvan üçün bir dəfə işləyir.`,notEmailed:`Bu nüsxədə e-poçt konfiqurasiya edilməyib. Bu keçidi {{email}} ünvanına göndərin; o, həmin ünvan üçün bir dəfə işləyir.`,notEmailedUnknown:`Bu nüsxədə e-poçt konfiqurasiya edilməyib. Bu keçidi alıcıya göndərin; o, həmin ünvan üçün bir dəfə işləyir.`},workspaces:{title:`İş sahələri`,search:`İş sahələrində axtar`,searchPlaceholder:`Ad və ya URL açarı…`,noMatches:`Uyğun iş sahəsi yoxdur`,empty:`İş sahəsi yoxdur`,workspace:`İş sahəsi`,administrators:`Administratorlar`,members:`Üzvlər`,projects:`Layihələr`,created:`Yaradılıb`,actions:`Əməliyyatlar`,noAdministrator:`Administrator yoxdur`,manage:`{{name}} idarə et`,pagerNoun:`iş sahələri`,private:`Gizli`,noProjects:`Layihə yoxdur`},roles:{title:`Rollar`,new:`Yeni rol`,edit:`Rolu redaktə et`,builtIn:`Daxili`,readOnly:`Oxumadan başqa əməliyyat yoxdur`,memberships_one:`{{count, number}} üzvlük`,memberships_other:`{{count, number}} üzvlük`,editNamed:`{{name}} rolunu redaktə et`,deleteNamed:`{{name}} rolunu sil`,deleteUnused:`İstifadə olunmayan {{name}} rolu silinsin?`,deleted:`Rol silindi`,empty:`Rol yoxdur`,saved:`Rol yadda saxlanıldı`,name:`Rolun adı`,actions:`Əməliyyatlar`,everyoneReads:`Hər üzv çatdığı layihələri oxuya bilər.`,changesApply_one:`Dəyişikliklər bu rola malik {{count, number}} üzvlüyə dərhal tətbiq olunur.`,changesApply_other:`Dəyişikliklər bu rola malik {{count, number}} üzvlüyə dərhal tətbiq olunur.`,save:`Rolu yadda saxla`,delete:`Rolu sil`,reassignDescription_one:`{{count, number}} üzvlük {{name}} roluna malikdir. Keçəcəkləri rolu seçin.`,reassignDescription_other:`{{count, number}} üzvlük {{name}} roluna malikdir. Keçəcəkləri rolu seçin.`,moveTo:`Üzvləri bu rola köçür`},theme:{mode:{light:`İşıqlı`,dark:`Qaranlıq`,system:`Sistem`},aligned:`Uyğunlaşdırılıb`,hexColor:`Altırəqəmli hex rəngindən istifadə edin`,contrast:{light:`İşıqlı temanın mətn kontrastını ən azı 4.5:1-ə qədər artırın.`,dark:`Qaranlıq temanın mətn kontrastını ən azı 4.5:1-ə qədər artırın.`},fileTooLarge:`Tema faylları 100 KB-dan kiçik olmalıdır.`,tooMany:`Ən çox 50 fərdi tema saxlayın.`},appearance:{colorScheme:`Rəng sxemi`,theme:`Tema`,themes:`Temalar`,colorTheme:`Rəng teması`,search:`Temalarda axtar`,searchPlaceholder:`Temalarda axtar…`,create:`Tema yarat`,add:`Tema əlavə et`,importFile:`Tema faylını idxal et`,useMode:{light:`{{name}} işıqlı rejimindən istifadə et`,dark:`{{name}} qaranlıq rejimindən istifadə et`},modeOnly:{light:`Yalnız işıqlı rejim üçün istifadə et`,dark:`Yalnız qaranlıq rejim üçün istifadə et`},copyName:`{{name}} surəti`,duplicate:`{{name}} surətini çıxar`,edit:`{{name}} redaktə et`,export:`{{name}} ixrac et`,delete:`{{name}} sil`,deleteDescription:`{{name}} bu brauzerdən silinsin? Ondan istifadə edən işıqlı və ya qaranlıq görünüşlər əsas temanıza qayıdacaq.`,noMatches:`Uyğun tema yoxdur`,customTheme:`Fərdi tema`,name:`Temanın adı`,paletteColor:`{{mode}}: {{color, lowercase}}`,color:{background:`Fon`,foreground:`Mətn`,sidebar:`Yan panel`,primary:`Vurğu`,primaryForeground:`Vurğu mətni`},save:`Temanı yadda saxla`},color:{swatch:`{{color}} {{label, lowercase}}`,removeCustom:`{{hex}} fərdi rənglərdən sil`,custom:`Fərdi rəng`,default:`standart`,red:`qırmızı`,orange:`narıncı`,amber:`kəhrəba`,yellow:`sarı`,lime:`laym`,green:`yaşıl`,emerald:`zümrüd`,teal:`firuzəyi-yaşıl`,cyan:`mavi-yaşıl`,sky:`səma mavisi`,blue:`göy`,indigo:`indiqo`,violet:`bənövşəyi`,purple:`tünd bənövşəyi`,fuchsia:`fuksiya`,pink:`çəhrayı`,rose:`qızılgül`,gray:`boz`}},admin:{instance:{title:`İdarəetmə`,heading:`Nüsxə`,edit:`Nüsxəni redaktə et`,name:`Ad`,description:`Təsvir`,noDescription:`Təsvir yoxdur`,version:`Tətbiq versiyası`,publicUrl:`İctimai URL`,provider:{local:`Yerli PostgreSQL yaddaşı`,s3:`S3-uyğun obyekt yaddaşı`,postgres:`Daxili PostgreSQL`,elasticsearch:`Elasticsearch-uyğun axtarış`},section:{storage:`Fayl yaddaşı`,search:`Tiket axtarışı`,events:`Hadisə çatdırılması`},viewConfiguration:`Konfiqurasiyaya bax`,configure:{storage:`Yaddaşı konfiqurasiya et`,search:`Axtarışı konfiqurasiya et`,events:`Hadisə çatdırılmasını konfiqurasiya et`},providerTitle:`Provayder`,deploymentManaged:`Yerləşdirmə tərəfindən idarə olunur`,instanceManaged:`Nüsxə tərəfindən idarə olunur`,transfers:`Ötürmələr`,indexStatus:`İndeks statusu`,connection:`Bağlantı`,transfersS3:`Fayllar yerli olaraq yadda saxlanıldıqdan sonra obyekt yaddaşına köçürülür.`,transfersLocal:`Yeni fayllar PostgreSQL-də saxlanılır.`,searchBuiltIn:`Axtarış birbaşa PostgreSQL-də icra olunur.`,searchIndexed:`Tiket dəyişiklikləri arxa planda indeksləşdirilir.`,eventsConnection:`Bildirişlər istehlakçıları oyadır; PostgreSQL təkrar oxutma tarixçəsini saxlayır.`,builtIn:`Daxili`,connected:`Qoşulub`,upToDate:`Aktualdır`,status:{ready:`hazırdır`,transferring:`ötürülür`,indexing:`indeksləşdirilir`,connecting:`qoşulur`,unavailable:`əlçatan deyil`},pendingTransfers:`Gözləyən ötürmələr`,pendingRemain:`Ötürmə tamamlanana qədər bu fayllar PostgreSQL-dən əlçatan qalır.`,noPending:`Ötürülməyi gözləyən fayl yoxdur.`,bucket:`Bucket`,storageEndpoint:`Yaddaş ünvanı`,backup:`Ehtiyat nüsxə`,backupBuckets:`PostgreSQL-in və istinad edilən hər obyekt yaddaşı bucket-inin ehtiyat nüsxəsini çıxarın.`,backupDatabase:`Fayl baytları PostgreSQL ehtiyat nüsxənizə daxildir.`,fallback:`Ehtiyat variant`,fallbackDescription:`Xarici indeks geri qaldıqda və ya oflayn olduqda PostgreSQL axtarışı əlçatandır.`,searchEndpoint:`Axtarış ünvanı`,index:`İndeks`,delivery:`Çatdırılma`,deliveryDescription:`Davamlı hadisələr və təkrar oxutma PostgreSQL-də qalır. İstəyə bağlı broker oyanma siqnallarını paylayır.`,brokerEndpoint:`Broker ünvanı`,exchange:`Exchange`},configure:{storageTitle:`Fayl yaddaşını konfiqurasiya et`,searchTitle:`Tiket axtarışını konfiqurasiya et`,eventsTitle:`Hadisə çatdırılmasını konfiqurasiya et`,saved:`Nüsxə parametrləri yadda saxlanıldı`,instanceName:`Nüsxənin adı`,instanceDescription:`Nüsxənin təsviri`,publicUrlSource:`İctimai URL yerləşdirmənizdəki KANVIL_URL ilə təyin edilir.`,storageIntro:`Qoşmalar və profil şəkilləri bu provayderdən istifadə edir. Mövcud fayllar ilkin saxlanma yerində qalır.`,storageProvider:`Yaddaş provayderi`,localDescription:`Fayl baytları yerli verilənlər bazasında saxlanılır və PostgreSQL ehtiyat nüsxələrinə daxil edilir.`,s3Endpoint:`S3 ünvanı`,s3EndpointHint:`Amazon S3 üçün ünvanı boş saxlayın.`,region:`Region`,prefix:`Obyekt açarı prefiksi`,accessKeyId:`Giriş açarı ID-si`,secretAccessKey:`Gizli giriş açarı`,savedSecret:`Yadda saxlanılıb. Saxlamaq üçün boş buraxın.`,deploymentCredentials:`Göstərilmədikdə yerləşdirmənin giriş məlumatları`,pathStyle:`Yol üslublu bucket URL-lərindən istifadə et`,storageVerified:`Yaddaş bağlantısı təsdiqləndi`,testStorage:`Yaddaş bağlantısını yoxla`,filesWaiting_one:`{{count, number}} fayl ötürülməyi gözləyir. Gözləyən fayllar PostgreSQL-dən oxuna bilir.`,filesWaiting_other:`{{count, number}} fayl ötürülməyi gözləyir. Gözləyən fayllar PostgreSQL-dən oxuna bilir.`,transfersComplete:`Bütün fayl ötürmələri tamamlandı.`,storageDeployment:`Yaddaş KANVIL_STORAGE_PROVIDER və S3_* yerləşdirmə dəyişənləri ilə konfiqurasiya edilir.`,locations:`Mövcud saxlanma yerləri`,locationFiles_one:`{{count, number}} fayl`,locationFiles_other:`{{count, number}} fayl`,cleanUp:`İstifadə olunmayan faylları təmizlə`,cleanUpConfirm:`İstinad edilməyən fayllar və bir gündən köhnə uğursuz yükləmələr silinsin? Tiketlərə və ya profillərə qoşulmuş fayllar saxlanılır.`,cleanUpNote:`Təmizləmə həm də avtomatik işləyir. S3 obyektləri üçün verilənlər bazasının ehtiyat nüsxəsi ilə yanaşı bucket-in ehtiyat nüsxəsi lazımdır.`,searchProvider:`Axtarış provayderi`,searchBuiltIn:`Daxili PostgreSQL axtarışı`,indexName:`Kanvil indeksinin adı`,searchAuthentication:`Axtarış autentifikasiyası`,noAuthentication:`Autentifikasiya yoxdur`,apiKey:`API açarı`,basic:`İstifadəçi adı və şifrə`,searchApiKey:`Axtarış API açarı`,optional:`İstəyə bağlı`,searchUsername:`Axtarış istifadəçi adı`,searchPassword:`Axtarış şifrəsi`,searchVerified:`Axtarış bağlantısı təsdiqləndi`,testSearch:`Axtarış bağlantısını yoxla`,rebuildQueued:`Axtarışın yenidən qurulması növbəyə qoyuldu`,rebuild:`İndeksi yenidən qur`,searchNote:`İcazələr və filtrlər üçün PostgreSQL əsas mənbə olaraq qalır. Xarici indeks yenilənərkən və ya əlçatan olmadıqda axtarış PostgreSQL-ə keçir.`,brokerProvider:`Hadisə çatdırılması provayderi`,brokerBuiltIn:`Daxili PostgreSQL bildirişləri`,rabbitUrl:`RabbitMQ bağlantı URL-i`,savedBrokerEndpoint:`Yadda saxlanılmış broker ünvanı`,rabbitExchange:`RabbitMQ exchange`,brokerVerified:`RabbitMQ çatdırılması təsdiqləndi`,testBroker:`Broker bağlantısını yoxla`,brokerNote:`RabbitMQ təsdiqlənmiş hadisə oyanma siqnallarını Kanvil nüsxələrinə paylayır və arxa plan işçilərini oyadır. PostgreSQL davamlı hadisələri və gözləyən işləri saxlayır, broker əlçatan olmadıqda oyanma siqnallarını özü çatdırır.`,brokerDeployment:`Hadisə çatdırılması KANVIL_BROKER_PROVIDER və RABBITMQ_* yerləşdirmə dəyişənləri ilə konfiqurasiya edilir.`,save:`Nüsxə parametrlərini yadda saxla`},authentication:{title:`Autentifikasiya`,saved:`Autentifikasiya yadda saxlanıldı`,emailPassword:`E-poçt və şifrə ilə daxil olma`,emailPasswordDescription:`E-poçtla göndərilən daxil olma kodlarını və şifrə sıfırlamanı əhatə edir.`,ssoProvision:`SSO-dan istifadəçiləri avtomatik yarat`,ssoProvisionDescription:`Yeni SSO istifadəçilərinin dəvət edilənə qədər iş sahəsinə və ya layihəyə girişi olmur.`,registration:`İctimai e-poçt qeydiyyatı`,registrationClosed:`Bağlı`,identityProvider:`İdentiklik provayderi`,notConfigured:`Konfiqurasiya edilməyib`,linkedProviders:`Bağlı hesab provayderləri`,linkedProvidersDescription:`Yalnız onları bağlamış hesablarla daxil olmağa imkan verir; heç vaxt hesab yaratmır.`,save:`Autentifikasiyanı yadda saxla`,discoveryUrl:`OIDC kəşf URL-i`,clientId:`OIDC klient ID-si`,oidcSetup:`Yerləşdirmənizdə OIDC_DISCOVERY_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET və OIDC_NAME dəyərlərini və ya GITHUB_, GOOGLE_ ya da DISCORD_ üçün CLIENT_ID və CLIENT_SECRET cütünü konfiqurasiya edin, sonra Kanvil-i yenidən başladın. Provayder sirləri heç vaxt brauzerə qaytarılmır.`,consumers:`İstehlakçı interfeysləri`,mcpEndpoint:`MCP ünvanı`,eventFeed:`Real vaxt hadisə lenti`,openApi:`Open API spesifikasiyası`},users:{title:`İstifadəçilər`,search:`İstifadəçilərdə axtar`,searchPlaceholder:`Ad və ya e-poçt…`,new:`Yeni istifadəçi`,roleFilter:`Nüsxə rolu filtri`,allRoles:`Bütün rollar`,administrators:`Administratorlar`,users:`İstifadəçilər`,statusFilter:`Hesab statusu filtri`,allAccounts:`Bütün hesablar`,active:`Aktiv`,disabled:`Deaktiv`,noMatches:`Uyğun istifadəçi yoxdur`,empty:`İstifadəçi yoxdur`,user:`İstifadəçi`,role:`Rol`,status:`Status`,joined:`Qoşulub`,actions:`Əməliyyatlar`,administrator:`Administrator`,manage:`{{name}} idarə et`,pagerNoun:`istifadəçilər`,create:`Hesab yarat`,creating:`Yaradılır…`,created:`{{email}} yaradıldı`,name:`Ad`,email:`E-poçt`,initialPassword:`İlkin şifrə`,instanceRole:`Nüsxə rolu`},account:{details:`Hesab məlumatları`,joined:`Qoşulub: {{date}}`,edit:`Hesabı redaktə et`,instanceRole:`Nüsxə rolu`,adminDescription:`Bütün iş sahələrini, hesabları və yerləşdirmə parametrlərini idarə edə bilər.`,userDescription:`Girişi iş sahəsi və layihə rolları müəyyən edir.`,changeRole:`Rolu dəyiş`,changeRoleTitle:`Nüsxə rolunu dəyiş`,signInMethods:`Daxil olma üsulları`,setPassword:`Yerli şifrə təyin et`,activeSessions:`Aktiv seanslar`,sessions_one:`{{count, number}} seans`,sessions_other:`{{count, number}} seans`,revokeSessions:`Seansları ləğv et`,revokeConfirm:`{{name}} bütün cihazlardan çıxarılsın? API açarları aktiv qalır.`,sessionsRevoked:`Seanslar ləğv edildi`,access:`Hesaba giriş`,accessDisabled:`Daxil olma və API girişi deaktiv edilib.`,accessPreserved:`Hesabın deaktiv edilməsi tiketləri və fəaliyyət tarixçəsini saxlayır.`,enable:`Hesabı aktivləşdir`,disable:`Hesabı deaktiv et`,enableConfirm:`{{name}} üçün daxil olma bərpa edilsin?`,disableConfirm:`{{name}} üçün daxil olma və API girişi deaktiv edilsin və seansları ləğv edilsin?`,enabled:`Hesab aktivləşdirildi`,disabledToast:`Hesab deaktiv edildi`,updateFailed:`Hesab yenilənə bilmədi`,workspaces:`İş sahələri`,addToWorkspace:`İş sahəsinə əlavə et`,noWorkspaces:`İş sahəsi üzvlüyü yoxdur`,projectAccess:`Birbaşa layihə girişi`,updated:`Hesab yeniləndi`,roleUpdated:`Nüsxə rolu yeniləndi`,workspaceAdded:`İş sahəsi girişi əlavə edildi`,passwordMismatch:`Yeni şifrələr uyğun gəlmir.`,passwordSet:`Yerli şifrə təyin edildi və seanslar ləğv edildi`,name:`Ad`,email:`E-poçt`,user:`İstifadəçi`,administrator:`Administrator`,adminsNote:`Administratorlar bütün iş sahələrini, istifadəçiləri və nüsxə parametrlərini idarə edə bilər.`,workspace:`İş sahəsi`,workspaceRole:`İş sahəsi rolu`,newPassword:`Yeni şifrə`,confirmPassword:`Yeni şifrəni təsdiqləyin`,passwordNote:`Bu, yerli şifrə təyin edir və istifadəçini bütün cihazlardan çıxarır.`,save:`Dəyişiklikləri yadda saxla`},delivery:{field:{address:`E-poçt ünvanı`,server:`Server`,topic:`Mövzu`,accessToken:`Giriş tokeni`,applicationToken:`Tətbiq tokeni`,endpointUrl:`Ünvan URL-i`,signingSecret:`İmzalama sirri`,incomingWebhookUrl:`Daxil olan webhook URL-i`,webhookUrl:`Webhook URL-i`,channel:`Kanal`,botToken:`Bot tokeni`,chatId:`Çat ID-si`,chatIdPlaceholder:`-1001234567890 və ya @channel`,topicId:`Mövzu ID-si`,optional:`{{label}} (istəyə bağlı)`,removeSaved:`Yadda saxlanılmış {{label, lowercase}} sil`,savedSecret:`Yadda saxlanılıb · saxlamaq üçün boş buraxın`},failure:{blockedAddress:`Bu ünvana icazə verilmir`,redirectBlocked:`Ünvan yönləndirmə etdi`,dnsError:`Host tapılmadı`,networkError:`Qoşulmaq mümkün olmadı`,timeout:`Vaxt bitdi`,invalidEndpoint:`Yanlış ünvan`,emailUnavailable:`Bu serverdə e-poçt konfiqurasiya edilməyib`,smtpError:`Poçt serveri əlçatan deyil`,accessRevoked:`Onu qoşan administrator girişini itirib; yenidən qoşun`,repositoryMismatch:`Çatdırılma başqa repozitoridən gəldi`,http:`HTTP {{status}} ilə rədd edildi`,smtp:`Poçt serveri {{code}} cavabını verdi`},health:{delivery:{done:`Çatdırıldı: <time/>`,pending:`Göndərilir…`,none:`Hələ heç nə göndərilməyib`,failed:`{{reason}}`},access:{done:`Giriş yoxlanıldı: <time/>`,pending:`Giriş yoxlanılır…`,none:`Giriş yoxlanılmayıb`,failed:`Giriş yoxlaması: {{reason}}`},retrying:`yenidən cəhd edilir`},paused:`Dayandırılıb`,failing:`Uğursuz olur`,sendTest:`{{name}} üçün test göndər`,testQueued:`Test növbəyə qoyuldu`,resume:`{{name}} davam etdir`,pause:`{{name}} dayandır`,edit:`{{name}} redaktə et`,disconnect:`{{name}} ayır`,disconnected:`{{name}} ayrıldı`,name:`Ad`,type:`Tip`,channels:`Kanallar`,addChannel:`Kanal əlavə et`,noChannels:`Kanal yoxdur`,editChannel:`Kanalı redaktə et`,channelSaved:`Kanal yadda saxlanıldı`,channelRemoval:`{{name}} ayrılsın? Onun parametrləri və növbədəki bildirişləri silinəcək.`,sendFrom:`Bildirişləri buradan göndər`,routeOff:`Heç nə`,routeAll:`Bütün layihələr`,routeSelected:`Seçilmiş layihələr`,saveChannel:`Kanalı yadda saxla`,adminsOnly:`İnteqrasiyaları layihə administratorları idarə edir`,destinations:`Təyinatlar`,addDestination:`Təyinat əlavə et`,noDestinations:`Təyinat yoxdur`,editDestination:`Təyinatı redaktə et`,destinationSaved:`Təyinat yadda saxlanıldı`,destinationRemoval:`{{name}} ayrılsın? Onun parametrləri və növbədəki mesajları silinəcək.`,send:`Göndər`,saveDestination:`Təyinatı yadda saxla`},repository:{setup:{github:`Tokenin repozitoriyə oxuma girişi, şərh yazmaq və ya məsələləri sinxronlaşdırmaq üçün isə issue və pull request yazma girişi olmalıdır. Bu repozitorinin sətrindən kopyalanan URL, application/json məzmun tipi, bu sirr və Branch or tag creation, Branch or tag deletion, Issues, Issue comments və Pull requests hadisələri ilə GitHub webhooku əlavə edin.`,gitea:`Tokenə read:repository əhatəsi, şərh yazmaq və ya məsələləri sinxronlaşdırmaq üçün isə write:issue lazımdır. Bu repozitorinin sətrindən kopyalanan URL, application/json məzmun tipi, bu sirr və Create, Delete, Issues, Issue Label, Issue Comment və Pull Request hadisələri ilə Gitea webhooku əlavə edin.`,gitlab:`Tokenə read_api əhatəsi, şərh yazmaq və ya məsələləri sinxronlaşdırmaq üçün isə api lazımdır. Bu repozitorinin sətrindən kopyalanan URL, bu gizli token və Push, Issues, Comments və Merge request hadisələri ilə GitLab webhooku əlavə edin.`},noWebhook:`Webhook alınmayıb`,webhookReceived:`Webhook alındı: <time/>`,saved:`Repozitori yadda saxlanıldı`,title:`Repozitorilər`,connect:`Repozitori qoş`,empty:`Repozitori yoxdur`,checkAccess:`{{repository}} girişini yoxla`,accessQueued:`Giriş yoxlaması növbəyə qoyuldu`,copyWebhook:`{{repository}} üçün webhook URL-ini kopyala`,issueSync:`{{repository}} üçün məsələ sinxronizasiyası`,removal:`{{repository}} ayrılsın? Onun webhooku dərhal işləməyi dayandırır. Onun əlavə etdiyi keçidlər tiketlərdə qalır.`,edit:`Repozitorini redaktə et`,host:`Host`,serverUrl:`Server URL-i`,serverUrlOptional:`Server URL-i (istəyə bağlı)`,repository:`{{host}} repozitorisi`,nestedPlaceholder:`group/project`,placeholder:`owner/repository`,accessToken:`Giriş tokeni`,webhookSecret:`Webhook sirri`,savedSecret:`Yadda saxlanılıb · saxlamaq üçün boş buraxın`,commentOnLink:{pull_request:`Yeni bağlanmış məsələlərə və pull request-lərə şərh yaz`,merge_request:`Yeni bağlanmış məsələlərə və merge request-lərə şərh yaz`},moveTickets:`Bağlı tiketləri köçür`,noMove:`Köçürmə yoxdur`,save:`Repozitorini yadda saxla`},issueSync:{reason:{labels:`Onun sinxronizasiya etiketləri dəyişdi`,conflict:`Hər iki tərəfdə dəyişdirilib`,changedWhilePaused:`Repozitori dayandırılmış ikən məsələ dəyişdi`,unverified:`Hostun cavabı dəyişikliyi təsdiqləmədi`,claimConflict:`Tiket götürülüb`,invalidContent:`Məsələ tiketin icazə verdiyindən uzundur`},issuesSyncing_one:`{{count, number}} məsələ sinxronlaşdırılır`,issuesSyncing_other:`{{count, number}} məsələ sinxronlaşdırılır`,linksStopped_one:`{{count, number}} bağlantı dayandı`,linksStopped_other:`{{count, number}} bağlantı dayandı`,match:`Uyğunluq`,matchAny:`Bu etiketlərdən hər hansı biri`,matchAll:`Bu etiketlərin hamısı`,preview:{matching:`Sinxronizasiyanı davam etdir`,export:`Yaradılacaq yeni məsələlər`,pause:`Dayandırılacaq bağlantılar`,review:`Yoxlanılacaq bağlantılar`},applied:`Məsələ sinxronizasiyası tətbiq edildi`,title:`Məsələ sinxronizasiyası · {{repository}}`,outbound:`Kanvil → {{host}}`,exportTickets:`Tiketləri məsələ kimi ixrac et`,exportDescription:`Açıq tiketlər üçün yeni məsələ yaradılır; başlıq, təsvir, vəziyyət və yeni şərhlər uyğun saxlanılır.`,ticketLabels:`Yalnız bu etiketləri olan tiketlər`,everyTicket:`Bütün açıq tiketlər`,inbound:`{{host}} → Kanvil`,importIssues:`Məsələləri tiket kimi idxal et`,importDescription:`Açıq məsələlər {{host}} onları növbəti dəfə bildirəndə idxal edilir.`,issueLabels:`Yalnız bu etiketləri olan məsələlər`,everyIssue:`Bütün açıq məsələlər`,previewLabel:`Önizləmə`,previewAction:`Önizlə`,stopped:`Dayanmış bağlantılar · {{repository}}`,review:`Yoxla`,linkRemoved:`Bağlantı silindi`,resumed:`Sinxronizasiya bərpa edildi`,open:`Açıq`,closed:`Bağlı`,reviewTitle:`{{key}} yoxla`,mayExist:`{{host}}-da bu məsələ artıq ola bilər; yenidən yaratmazdan əvvəl yoxlayın.`,comparison:`Müqayisə`,field:{title:`başlıq`,description:`təsvir`,state:`vəziyyət`},notReported:`Bildirilməyib`,unlink:`Bağlantını kəs`,keepHost:`{{host}} versiyasını saxla`,keepKanvil:`Kanvil versiyasını saxla`,createAgain:`Məsələni yenidən yarat`,unlinkTicket:`{{key}} bağlantısını kəs`,unlinkConfirm:`{{key}} sinxronizasiyası dayandırılsın? Tiket də, məsələ də qalır.`},import:{count:{tickets:`Tiketlər`,workflow:`İş axını vəziyyətləri`,fields:`Fərdi sahələr`,labels:`Etiketlər`,relations:`Əlaqələr`,comments:`Şərhlər`,evidence:`Sübutlar`,links:`Keçidlər`},notExport:`Bu fayl Kanvil tiket ixracı deyil.`,progress_one:`{{status}} · {{count, number}} qeyddən {{done, number}}`,progress_other:`{{status}} · {{count, number}} qeyddən {{done, number}}`,progressFailed_one:`{{status}} · {{count, number}} qeyddən {{done, number}} · {{failed, number}} uğursuz`,progressFailed_other:`{{status}} · {{count, number}} qeyddən {{done, number}} · {{failed, number}} uğursuz`,title:`İdxal`,description:`Kanvil tiket ixracından və ya başqa izləmə sistemindən layihə yaradır: Linear, Jira, Trello, Plane, PLANKA və ya Kaneo.`,action:`Layihəni idxal et`,detailsOf:`{{name}} məlumatları`,details:`Məlumatlar`,exportFile:`Kanvil ixrac faylı`,source:`Mənbə`,file:`İxrac faylı`,jiraEdition:`Jira versiyası`,siteUrl:`Sayt URL-i`,accountEmail:`Hesab e-poçtu`,apiKey:`API açarı`,apiToken:`API tokeni`,personalToken:`Şəxsi giriş tokeni`,token:`Token`,apiUrl:`API URL-i`,workspaceSlug:`İş sahəsinin URL açarı`,signInWith:`Giriş üsulu`,username:`İstifadəçi adı və ya e-poçt`,password:`Şifrə`,checkConnection:`Bağlantı məlumatlarını yoxlayın`,noProjects:`Bu giriş məlumatları heç bir {{label, lowercase}} oxuya bilmir.`,includeComments:`Şərhləri daxil et`,projectName:`Layihənin adı`,start:`İdxal et`,connect:`Qoş`,preview:`Önizlə`,starting:`Başladılır…`,connecting:`Qoşulur…`,checking:`Yoxlanılır…`,matched:`Uyğunlaşdırılmış şəxslər`,unmatched:`Uyğunlaşdırılmamış şəxslər`,previewLabel:`İdxal önizləməsi`,errorPrefix:`Xəta: `,notePrefix:`Qeyd: `,progressLabel:`İdxal gedişatı`,notImported:`İdxal edilməyən qeydlər`,stop:`İdxalı dayandır`,openProject:`Layihəni aç`}},native:{labelValue:`{{label}}: {{value}}`,ui:{dismiss:`Bağla`,loading:`Yüklənir`},server:{url:`Server URL-i`,connect:`Qoş`,change:`Serveri dəyiş`,invalidOrigin:`Kanvil serverinizin yol və ya giriş məlumatları olmadan HTTP və ya HTTPS mənbəyindən istifadə edin.`,httpsRequired:`Kanvil serverinizə qoşulmaq üçün HTTPS-dən istifadə edin.`},login:{setupTitle:`Sahib hesabınızı yaradın`,signIn:`Daxil ol`,signingIn:`Daxil olunur…`,createAccount:`Hesab yarat`,signInWith:`{{provider}} ilə daxil ol`,name:`Ad`,setupToken:`Quraşdırma tokeni`,email:`E-poçt`,password:`Şifrə`},connection:{connecting:`qoşulur`,connected:`● Canlı`,offline:`Oflayn · keşlənmiş məlumat`,closed:`bağlıdır`,signedOut:`Yenidən qoşulmaq üçün daxil olun`},screen:{projects:`Layihələr`,inbox:`Gələnlər`,settings:`Parametrlər`,tickets:`Tiketlər`,ticket:`Tiket`,newTicket:`Yeni tiket`,editTicket:`Tiketi redaktə et`},projects:{empty:`Hələ layihə yoxdur`,new:`Yeni layihə`,name:`Layihənin adı`,prefix:`Tiket prefiksi`,description:`Təsvir`,create:`Layihə yarat`},tickets:{search:`Tiketlərdə axtar`,searchPlaceholder:`Başlıq, təsvir və ya açar`,allStatuses:`Bütün statuslar`,empty:`Uyğun tiket yoxdur`,count:`{{shown}} / {{total}}`},ticket:{priority:`Prioritet: {{priority}}`,noDifficulty:`Çətinlik göstərilməyib`,version:`v{{version}}`,starts:`Başlayır: {{date}}`,due:`Son tarix: {{date}}`},editor:{stale:`Bu tiket dəyişib. Yadda saxlanılmış qaralamanız hələ {{version}} versiyasından istifadə edir.`,discardDraft:`Qaralamanı at və yenidən yüklə`,title:`Başlıq`,noDifficulty:`Göstərilməyib`,labels:`Etiketlər (vergüllə ayrılmış)`,scopes:`Əhatələr (vergüllə ayrılmış)`,editMarkdown:`Markdown-u redaktə et`,previewMarkdown:`Markdown önizləməsi`,description:`Təsvir (Markdown)`,acceptanceCriteria:`Qəbul meyarları (Markdown)`,parent:`Valideyn tiketin ID-si`,automationReady:`Tələblər və asılılıqlar imkan verdikdə agentlər üçün hazırdır`,holdReason:`Gözləmə səbəbi`,draftSaved:`Qaralama bu cihazda yadda saxlanıldı`,save:`Tiketi yadda saxla`},detail:{archived:`Arxivləşdirilib`,subscribe:`Abunə ol`,unsubscribe:`Abunəlikdən çıx`,share:`Paylaş`,description:`Təsvir`,noDescription:`_Təsvir yoxdur_`,acceptanceCriteria:`Qəbul meyarları`,noAcceptanceCriteria:`_Qəbul meyarları yoxdur_`,fields:`Sahələr`,archive:{action:`Tiketi arxivləşdir`,title:`Tiket arxivləşdirilsin?`,message:`Bu tiket aktiv siyahılardan və agentlər üçün uyğunluqdan çıxarılsın?`},unarchive:{action:`Tiketi arxivdən çıxar`,title:`Tiket arxivdən çıxarılsın?`,message:`Bu tiket aktiv siyahılara qaytarılsın?`}},readiness:{title:`Agent hazırlığı`,ready:`Götürməyə hazırdır`,notReady:`Götürməyə hazır deyil`,hold:`Gözləmə: {{reason}}`,claimed:`{{time}} vaxtınadək {{worker}} tərəfindən götürülüb.`,release:{action:`Götürməmi burax`,title:`Götürmə buraxılsın?`,message:`Sahibliyi buraxmazdan əvvəl işçinizi dayandırın.`,confirm:`Burax`},recover:{action:`Müddəti bitmiş götürməni bərpa et`,explanation:`Əvvəlcə işçinin dayandığını təsdiqləyin.`,reason:`Bərpa səbəbi`,confirm:`İşçi dayandı · bərpa et`}},relations:{title:`Əlaqələr`,parent:`Valideyn · {{key}} · {{title}}`,openParent:`Valideyn tiketi aç`,children:`Alt tiketlər`,previousChildren:`Əvvəlki alt tiketlər`,nextChildren:`Növbəti alt tiketlər`,remove:`Əlaqəni sil`,removeTitle:`Əlaqə silinsin?`,add:`Əlaqə əlavə et`,find:`Tiket tap`,type:`Əlaqə`,ticket:`Tiket`,choose:`Tiket seçin`},discussion:{title:`Müzakirə`,previous:`Əvvəlki şərhlər`,next:`Növbəti şərhlər`,field:`Şərh (Markdown)`,submit:`Şərh yaz`,you:`Siz`,edit:`Şərhi redaktə et`,editField:`Şərh`,delete:`Şərhi sil`,deleteTitle:`Şərh silinsin?`,deleteMessage:`Bu şərh həmişəlik silinsin?`,save:`Şərhi yadda saxla`},attachments:{title:`Qoşmalar`,item:`{{name}} · {{size}} kB`,remove:`{{name}} sil`,removeTitle:`Qoşma silinsin?`,working:`İcra olunur…`,add:`Qoşma əlavə et`,tooLarge:`10 MB-dan böyük olmayan fayl seçin.`,sharingUnavailable:`Bu cihazda fayl paylaşımı əlçatan deyil.`},evidence:{title:`Sübutlar`,explanation:`Nəticələri onları təqdim edən hesablar bildirir.`,add:`Sübut əlavə et`,name:`Başlıq`,url:`URL`,outcome:`Bildirilən nəticə`,commit:`Commit (tam hash)`,note:`Qeyd`,save:`Sübutu yadda saxla`},activity:{title:`Fəaliyyət`,previous:`Əvvəlki fəaliyyət`,next:`Növbəti fəaliyyət`},inbox:{markAllRead:`Hamısını oxunmuş kimi qeyd et`,empty:`Bildiriş yoxdur`},notifications:{title:`Bildirişlər`,inApp:`Abunə olunmuş tiket yenilikləri Gələnlərdə`,push:`Push bildirişləri`,enableDevice:`Bu cihazda push-u aktivləşdir`,disableDevice:`Bu cihazda push-u deaktiv et`,unconfigured:`Bu quraşdırma push üçün Expo layihə ID-si və imzalama konfiqurasiyası tələb edir.`,noProjectId:`Bu quraşdırma push bildirişləri üçün Expo layihə ID-si tələb edir.`,permissionDenied:`Push-u aktivləşdirmək üçün cihaz parametrlərində bildirişlərə icazə verin.`,channel:`Tiket yenilikləri`},settings:{password:{title:`Yerli şifrə`,current:`Cari şifrə`,new:`Yeni şifrə`,change:`Şifrəni dəyiş`,updated:`Şifrə yeniləndi; digər seanslar ləğv edildi`},oidc:{linked:`Hesab bağlandı`,explanation:`Bu provayderlə daxil olmazdan əvvəl bu hesabı bağlayın.`,link:`{{provider}} bağla`},signOut:{action:`Çıxış et`,title:`Çıxış edilsin?`,message:`Bu hesabın keşlənmiş tiketləri və qaralamaları bu cihazdan silinəcək.`},changeServer:{title:`Server dəyişdirilsin?`,message:`Çıxış edilsin və bu hesabın yerli keşi və qaralamaları təmizlənsin?`}}}};export{e as default};
|