@danypops/tickets 0.8.1 → 0.8.3
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/README.md +1 -1
- package/package.json +3 -3
- package/src/{vehicle → agent-tools}/tickets-vehicle.ts +5 -5
- package/src/auth/device-flow.ts +1 -1
- package/src/auth/github-oauth.ts +1 -1
- package/src/auth/gitlab-oauth.ts +1 -1
- package/src/auth/jira-oauth.ts +1 -1
- package/src/cli/index.ts +4 -4
- package/src/cli/systemd-service.ts +2 -2
- package/src/{client → cli}/tickets-client.ts +6 -6
- package/src/config/config.ts +5 -5
- package/src/{adapters → github}/github.ts +2 -2
- package/src/{adapters → gitlab}/gitlab.ts +2 -2
- package/src/{adapters/http.ts → http-client.ts} +2 -2
- package/src/index.ts +10 -10
- package/src/{ports → issue}/repository.ts +2 -2
- package/src/{application → issue}/service.ts +5 -5
- package/src/{adapters → jira}/jira.ts +8 -8
- package/src/{daemon → process}/bootstrap.ts +8 -8
- package/src/{daemon → process}/poller.ts +2 -2
- package/src/{daemon → rpc}/ops.ts +4 -4
- package/src/{daemon → rpc}/server.ts +11 -11
- package/src/{daemon → sqlite}/ledger.ts +1 -1
- package/src/{daemon → sqlite}/saved-queries.ts +1 -1
- /package/src/{util → cli}/package-root.ts +0 -0
- /package/src/{adapters → issue}/errors.ts +0 -0
- /package/src/{domain → issue}/issue.ts +0 -0
- /package/src/{domain → issue}/template.ts +0 -0
- /package/src/{manifest → jira}/manifest.ts +0 -0
- /package/src/{daemon → process}/main.ts +0 -0
- /package/src/{daemon → sqlite}/focus.ts +0 -0
package/README.md
CHANGED
|
@@ -277,7 +277,7 @@ bun test # both packages
|
|
|
277
277
|
```
|
|
278
278
|
|
|
279
279
|
Tests never hit real GitHub/GitLab/Jira/Atlassian: adapters take an
|
|
280
|
-
injectable `fetchImpl`, and the daemon tests (`test/
|
|
280
|
+
injectable `fetchImpl`, and the daemon tests (`test/rpc/`, `test/sqlite/`, `test/process/`) run the real
|
|
281
281
|
`@danypops/daemon-kit` `startDaemon()`/SQLite/HTTP stack against a scratch
|
|
282
282
|
XDG root with a fake `IssueRepository`.
|
|
283
283
|
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/tickets",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "Unified CLI, daemon, and TypeScript library for issue tracking across GitHub, GitLab, and Jira.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
8
8
|
"tickets": "./src/cli/index.ts",
|
|
9
|
-
"tickets-daemon": "./src/
|
|
9
|
+
"tickets-daemon": "./src/process/main.ts"
|
|
10
10
|
},
|
|
11
11
|
"main": "./src/index.ts",
|
|
12
12
|
"types": "./src/index.ts",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"RESEARCH.md"
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
|
-
"daemon": "bun run src/
|
|
22
|
+
"daemon": "bun run src/process/main.ts",
|
|
23
23
|
"test": "bun test",
|
|
24
24
|
"typecheck": "tsc --noEmit"
|
|
25
25
|
},
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Every real ticket operation projected as its own VehicleRegistry entry,
|
|
3
3
|
* one per TicketOperation, instead of pi-tickets' hand-rolled
|
|
4
4
|
* `tickets(action=X)` mega-tool. Operation names are already dotted
|
|
5
|
-
* (issue.list, focus.set, discover.fields, ...) in
|
|
5
|
+
* (issue.list, focus.set, discover.fields, ...) in rpc/ops.ts -- Vehicle's
|
|
6
6
|
* tool-name projection turns each into its own Pi tool (issue_list,
|
|
7
7
|
* focus_set, discover_fields, ...) with zero renaming needed.
|
|
8
8
|
*
|
|
9
|
-
* Delegates every operation to
|
|
9
|
+
* Delegates every operation to rpc/server.ts's TICKET_OP_HANDLERS, the
|
|
10
10
|
* exact same implementation the existing /api/v1/ops dispatch calls -- this
|
|
11
11
|
* is a projection/contract layer on top of the existing application logic,
|
|
12
12
|
* not a second copy of it.
|
|
@@ -23,9 +23,9 @@ import {
|
|
|
23
23
|
type VehicleEffect,
|
|
24
24
|
} from "@danypops/vehicle-core";
|
|
25
25
|
import { VehicleRegistry } from "@danypops/vehicle-server";
|
|
26
|
-
import type { BackendCapabilities, TicketService } from "../
|
|
27
|
-
import type { TicketOperation } from "../
|
|
28
|
-
import { TICKET_OP_HANDLERS, type TicketsAppDeps } from "../
|
|
26
|
+
import type { BackendCapabilities, TicketService } from "../issue/service.js";
|
|
27
|
+
import type { TicketOperation } from "../rpc/ops.js";
|
|
28
|
+
import { TICKET_OP_HANDLERS, type TicketsAppDeps } from "../rpc/server.js";
|
|
29
29
|
|
|
30
30
|
const OWNER = "tickets";
|
|
31
31
|
|
package/src/auth/device-flow.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* URI/local callback server needed, ideal for a headless daemon. Jira/Atlassian
|
|
12
12
|
* has no equivalent (see jira-oauth.ts, which uses Authorization Code instead).
|
|
13
13
|
*/
|
|
14
|
-
import type { FetchLike } from "../
|
|
14
|
+
import type { FetchLike } from "../http-client.js";
|
|
15
15
|
|
|
16
16
|
export const DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
17
17
|
|
package/src/auth/github-oauth.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Requires a GitHub OAuth App (or GitHub App) with the device flow enabled in
|
|
5
5
|
* its settings; the client ID is public (no secret needed for this flow).
|
|
6
6
|
*/
|
|
7
|
-
import type { FetchLike } from "../
|
|
7
|
+
import type { FetchLike } from "../http-client.js";
|
|
8
8
|
import { type DeviceFlowConfig, type DeviceFlowToken, type PollOptions, pollForToken, requestDeviceAuthorization } from "./device-flow.js";
|
|
9
9
|
|
|
10
10
|
export const GITHUB_DEVICE_AUTHORIZATION_URL = "https://github.com/login/device/code";
|
package/src/auth/gitlab-oauth.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Requires a "non-confidential" GitLab OAuth application (no client secret
|
|
6
6
|
* for this flow, same shape as GitHub's device flow / RFC 8628).
|
|
7
7
|
*/
|
|
8
|
-
import type { FetchLike } from "../
|
|
8
|
+
import type { FetchLike } from "../http-client.js";
|
|
9
9
|
import { type DeviceFlowConfig, type DeviceFlowToken, type PollOptions, pollForToken, requestDeviceAuthorization } from "./device-flow.js";
|
|
10
10
|
import type { DevicePrompt } from "./github-oauth.js";
|
|
11
11
|
|
package/src/auth/jira-oauth.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { randomBytes } from "node:crypto";
|
|
18
18
|
import { createServer, type Server } from "node:http";
|
|
19
19
|
import type { AddressInfo } from "node:net";
|
|
20
|
-
import type { FetchLike } from "../
|
|
20
|
+
import type { FetchLike } from "../http-client.js";
|
|
21
21
|
|
|
22
22
|
export const ATLASSIAN_AUTHORIZE_URL = "https://auth.atlassian.com/authorize";
|
|
23
23
|
export const ATLASSIAN_TOKEN_URL = "https://auth.atlassian.com/oauth/token";
|
package/src/cli/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
3
|
* The tickets CLI is a thin client of the tickets daemon: every command maps
|
|
4
|
-
* 1:1 to a daemon RPC op (see
|
|
4
|
+
* 1:1 to a daemon RPC op (see rpc/ops.ts and rpc/server.ts), the same
|
|
5
5
|
* ops the pi-tickets extension calls. Nothing here opens the daemon's SQLite
|
|
6
6
|
* ledger or a backend adapter directly.
|
|
7
7
|
*/
|
|
@@ -13,10 +13,10 @@ import { loginWithGitLabDeviceFlow } from "../auth/gitlab-oauth.js";
|
|
|
13
13
|
import { loginWithJiraAuthorizationCode } from "../auth/jira-oauth.js";
|
|
14
14
|
import { promptMaskedSecret } from "../auth/masked-prompt.js";
|
|
15
15
|
import { deleteToken, isTokenFresh, listStoredBackends, loadToken, saveToken } from "../auth/token-store.js";
|
|
16
|
-
import {
|
|
17
|
-
import
|
|
18
|
-
import { parseStatus } from "../domain/issue.js";
|
|
16
|
+
import type { CreateInput, ListFilter, Priority, Status, UpdateInput } from "../issue/issue.js";
|
|
17
|
+
import { parseStatus } from "../issue/issue.js";
|
|
19
18
|
import { installTicketsService, systemctlTickets, systemdUnitPath } from "./systemd-service.js";
|
|
19
|
+
import { createTicketsClient, type TicketsRpcClient } from "./tickets-client.js";
|
|
20
20
|
|
|
21
21
|
function printJson(value: unknown): void {
|
|
22
22
|
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
import { execFileSync } from "node:child_process";
|
|
13
13
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
14
14
|
import { dirname, join } from "node:path";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import { TICKETS_DAEMON_NAMES } from "../rpc/ops.js";
|
|
16
|
+
import { resolveDaemonEntryPath } from "./tickets-client.js";
|
|
17
17
|
|
|
18
18
|
export interface SystemdUnitOptions {
|
|
19
19
|
bunBin: string;
|
|
@@ -10,8 +10,8 @@ import { fileURLToPath } from "node:url";
|
|
|
10
10
|
import { AuthenticatedRpcClient } from "@danypops/vehicle-client/rpc-client";
|
|
11
11
|
import type { DaemonHandle } from "@danypops/vehicle-server/paths";
|
|
12
12
|
import { ensureAuthToken, readDaemonHandle, resolveDaemonPaths } from "@danypops/vehicle-server/paths";
|
|
13
|
-
import { TICKETS_DAEMON_NAMES, type TicketOperation, type TicketOpInputs, type TicketOpOutputs } from "../
|
|
14
|
-
import { packageRoot } from "
|
|
13
|
+
import { TICKETS_DAEMON_NAMES, type TicketOperation, type TicketOpInputs, type TicketOpOutputs } from "../rpc/ops.js";
|
|
14
|
+
import { packageRoot } from "./package-root.js";
|
|
15
15
|
|
|
16
16
|
export function ticketsPaths(env?: Record<string, string | undefined>) {
|
|
17
17
|
return resolveDaemonPaths(TICKETS_DAEMON_NAMES, env ? { env } : {});
|
|
@@ -32,7 +32,7 @@ async function isAlive(handle: DaemonHandle, token: string): Promise<boolean> {
|
|
|
32
32
|
/** Absolute path to the daemon's real entry point, resolved from this package's own root. Used both to spawn it on demand and to point a systemd unit's ExecStart at it (see cli/systemd-service.ts). */
|
|
33
33
|
export function resolveDaemonEntryPath(): string {
|
|
34
34
|
const root = packageRoot(dirname(fileURLToPath(import.meta.url)));
|
|
35
|
-
return join(root, "src", "
|
|
35
|
+
return join(root, "src", "process", "main.ts");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function spawnDaemon(): void {
|
|
@@ -68,7 +68,7 @@ export async function ensureDaemonRunning(opts: EnsureDaemonOptions = {}): Promi
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
if (opts.autoStart === false) {
|
|
71
|
-
throw new Error("tickets daemon is not running. Start it with `npm run daemon` (or `bun run src/
|
|
71
|
+
throw new Error("tickets daemon is not running. Start it with `npm run daemon` (or `bun run src/process/main.ts`).");
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
spawnDaemon();
|
|
@@ -80,7 +80,7 @@ export async function ensureDaemonRunning(opts: EnsureDaemonOptions = {}): Promi
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
export interface VehicleClientTarget {
|
|
83
|
-
/** Base URL for tickets' VehicleRegistry (see ../
|
|
83
|
+
/** Base URL for tickets' VehicleRegistry (see ../agent-tools/tickets-vehicle.ts) -- @danypops/vehicle-client's RemoteVehicleClient mounts its own /vehicle/manifest, /vehicle/invoke, /vehicle/cancel routes under this. */
|
|
84
84
|
baseUrl: string;
|
|
85
85
|
token: string;
|
|
86
86
|
}
|
|
@@ -88,7 +88,7 @@ export interface VehicleClientTarget {
|
|
|
88
88
|
/**
|
|
89
89
|
* Narrow, side-effect-free surface for a Vehicle-projected domain consumer --
|
|
90
90
|
* same daemon, same handle file, same Bearer token every other tickets RPC
|
|
91
|
-
* call already uses (see
|
|
91
|
+
* call already uses (see rpc/server.ts's buildApp, which mounts the
|
|
92
92
|
* Vehicle HTTP app at /vehicle/* on this same port). Deliberately does NOT
|
|
93
93
|
* call ensureDaemonRunning: that spawns the daemon and mints a fresh auth
|
|
94
94
|
* token file as a side effect, which is wrong to do just from a Pi
|
package/src/config/config.ts
CHANGED
|
@@ -10,12 +10,12 @@ import { type TryEnigmaCredential, tryEnigmaCredential } from "@danypops/enigma-
|
|
|
10
10
|
import type { MaintenanceTask } from "@danypops/vehicle-server/daemon";
|
|
11
11
|
import type { Logger } from "@danypops/vehicle-server/logging";
|
|
12
12
|
import { parse as parseYaml } from "yaml";
|
|
13
|
-
import { GitHubRepository } from "../adapters/github.js";
|
|
14
|
-
import { GitLabRepository } from "../adapters/gitlab.js";
|
|
15
|
-
import { JiraRepository } from "../adapters/jira.js";
|
|
16
|
-
import type { TicketService } from "../application/service.js";
|
|
17
13
|
import { isTokenFresh, loadToken } from "../auth/token-store.js";
|
|
18
|
-
import
|
|
14
|
+
import { GitHubRepository } from "../github/github.js";
|
|
15
|
+
import { GitLabRepository } from "../gitlab/gitlab.js";
|
|
16
|
+
import type { IssueRepository } from "../issue/repository.js";
|
|
17
|
+
import type { TicketService } from "../issue/service.js";
|
|
18
|
+
import { JiraRepository } from "../jira/jira.js";
|
|
19
19
|
|
|
20
20
|
export interface BackendConfig {
|
|
21
21
|
/** Adapter type: "github" | "gitlab" | "jira". Falls back to the config key when omitted. */
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
|
|
22
22
|
import { RequestError } from "@octokit/request-error";
|
|
23
23
|
import { Octokit } from "octokit";
|
|
24
|
-
import
|
|
25
|
-
import {
|
|
24
|
+
import { ApiError, AuthRequiredError, IssueNotFoundError } from "../issue/errors.js";
|
|
25
|
+
import type { Comment, CreateInput, Issue, ListFilter, parsePriority, Status, UpdateInput } from "../issue/issue.js";
|
|
26
26
|
|
|
27
27
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
28
28
|
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
import { isIP } from "node:net";
|
|
15
15
|
import { GitbeakerRequestError, type RequesterType, type ResourceOptions } from "@gitbeaker/requester-utils";
|
|
16
16
|
import { Gitlab } from "@gitbeaker/rest";
|
|
17
|
-
import
|
|
18
|
-
import {
|
|
17
|
+
import { ApiError, AuthRequiredError, InvalidUrlError, IssueNotFoundError } from "../issue/errors.js";
|
|
18
|
+
import type { Comment, CreateInput, Issue, ListFilter, parsePriority, Status, UpdateInput } from "../issue/issue.js";
|
|
19
19
|
|
|
20
20
|
export interface GitLabOptions {
|
|
21
21
|
projectId: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApiError, IssueNotFoundError } from "./errors.js";
|
|
1
|
+
import { ApiError, IssueNotFoundError } from "./issue/errors.js";
|
|
2
2
|
|
|
3
3
|
export type FetchLike = typeof fetch;
|
|
4
4
|
|
|
@@ -14,7 +14,7 @@ export interface HttpClientOptions {
|
|
|
14
14
|
* Thin authenticated JSON HTTP client shared by all adapters. Each adapter owns its
|
|
15
15
|
* own auth header construction (Bearer, Basic, PRIVATE-TOKEN, ...) and passes it in
|
|
16
16
|
* via `headers`. Accepts an injectable `fetchImpl` so adapters are testable without
|
|
17
|
-
* a network — see test/
|
|
17
|
+
* a network — see test/github/*.test.ts, test/gitlab/*.test.ts, test/jira/*.test.ts.
|
|
18
18
|
*/
|
|
19
19
|
export class HttpClient {
|
|
20
20
|
private readonly baseUrl: string;
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
export * from "./adapters/errors.js";
|
|
2
|
-
export { type GitHubOptions, GitHubRepository } from "./adapters/github.js";
|
|
3
|
-
export { type GitLabOptions, GitLabRepository } from "./adapters/gitlab.js";
|
|
4
|
-
export { type JiraOptions, JiraRepository } from "./adapters/jira.js";
|
|
5
|
-
export { NotSupportedError, TicketService, UnknownBackendError } from "./application/service.js";
|
|
6
1
|
export { openUrl } from "./auth/browser.js";
|
|
7
2
|
// Delegated OAuth (device flow for GitHub/GitLab, authorization code for Jira)
|
|
8
3
|
// — see RESEARCH.md for why each backend gets a different flow.
|
|
@@ -19,7 +14,7 @@ export {
|
|
|
19
14
|
type TicketsRpcClient,
|
|
20
15
|
ticketsPaths,
|
|
21
16
|
type VehicleClientTarget,
|
|
22
|
-
} from "./
|
|
17
|
+
} from "./cli/tickets-client.js";
|
|
23
18
|
export {
|
|
24
19
|
type BackendConfig,
|
|
25
20
|
buildRepositories,
|
|
@@ -28,7 +23,12 @@ export {
|
|
|
28
23
|
defaultConfigPath,
|
|
29
24
|
loadConfig,
|
|
30
25
|
} from "./config/config.js";
|
|
31
|
-
export type
|
|
32
|
-
export
|
|
33
|
-
export * from "./
|
|
34
|
-
export * from "./
|
|
26
|
+
export { type GitHubOptions, GitHubRepository } from "./github/github.js";
|
|
27
|
+
export { type GitLabOptions, GitLabRepository } from "./gitlab/gitlab.js";
|
|
28
|
+
export * from "./issue/errors.js";
|
|
29
|
+
export * from "./issue/issue.js";
|
|
30
|
+
export * from "./issue/repository.js";
|
|
31
|
+
export { NotSupportedError, TicketService, UnknownBackendError } from "./issue/service.js";
|
|
32
|
+
export { type JiraOptions, JiraRepository } from "./jira/jira.js";
|
|
33
|
+
export type { TicketOperation, TicketOpInputs, TicketOpOutputs } from "./rpc/ops.js";
|
|
34
|
+
export type { FocusStatus, TicketFocusState } from "./sqlite/focus.js";
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Outbound ports — contracts driven adapters (GitHub, GitLab, Jira, ...) implement.
|
|
3
3
|
* The application layer depends only on these interfaces, never on a concrete adapter.
|
|
4
4
|
*/
|
|
5
|
-
import type { Comment, CreateInput, Issue, ListFilter, UpdateInput } from "
|
|
6
|
-
import type { Template } from "
|
|
5
|
+
import type { Comment, CreateInput, Issue, ListFilter, UpdateInput } from "./issue.js";
|
|
6
|
+
import type { Template } from "./template.js";
|
|
7
7
|
|
|
8
8
|
export interface IssueRepository {
|
|
9
9
|
/** Backend identifier used in refs, e.g. "github", "gitlab", "jira". */
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
* routes every call to the named backend's repository. Depends only on ports,
|
|
5
5
|
* never on a concrete adapter.
|
|
6
6
|
*/
|
|
7
|
-
import type { Comment, CreateInput, Issue, ListFilter, UpdateInput } from "
|
|
8
|
-
import { parseRef } from "
|
|
9
|
-
import type { Template } from "../domain/template.js";
|
|
7
|
+
import type { Comment, CreateInput, Issue, ListFilter, UpdateInput } from "./issue.js";
|
|
8
|
+
import { parseRef } from "./issue.js";
|
|
10
9
|
import {
|
|
11
10
|
hasBoardFilterDiscovery,
|
|
12
11
|
hasBoardQuickFilterDiscovery,
|
|
@@ -17,7 +16,8 @@ import {
|
|
|
17
16
|
hasSyncScopeExpansion,
|
|
18
17
|
hasTemplateDiscovery,
|
|
19
18
|
type IssueRepository,
|
|
20
|
-
} from "
|
|
19
|
+
} from "./repository.js";
|
|
20
|
+
import type { Template } from "./template.js";
|
|
21
21
|
|
|
22
22
|
export interface BackendCapabilities {
|
|
23
23
|
readonly name: string;
|
|
@@ -92,7 +92,7 @@ export class TicketService {
|
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
94
|
* Fetches issues for the poller's own background sync pass (see
|
|
95
|
-
*
|
|
95
|
+
* process/poller.ts). Prefers a backend's own expanded sync scope
|
|
96
96
|
* (SyncScopeExpandable -- Jira: multiple configured projects plus
|
|
97
97
|
* everything assigned to the authenticated user, unioned into one query)
|
|
98
98
|
* over its plain default-project list() when one is configured; falls
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
import type { AxiosAdapter } from "axios";
|
|
17
17
|
import type { HttpException, Config as JiraClientConfig } from "jira.js";
|
|
18
18
|
import { AgileClient, Version2Client } from "jira.js";
|
|
19
|
-
import
|
|
20
|
-
import type {
|
|
21
|
-
import {
|
|
22
|
-
import
|
|
23
|
-
import
|
|
19
|
+
import { ApiError, IssueNotFoundError } from "../issue/errors.js";
|
|
20
|
+
import type { Comment, CreateInput, Issue, IssueLink, ListFilter, parsePriority, Status, UpdateInput } from "../issue/issue.js";
|
|
21
|
+
import type { Template } from "../issue/template.js";
|
|
22
|
+
import { buildTemplateBody, extractTemplateSections } from "../issue/template.js";
|
|
23
|
+
import * as manifest from "./manifest.js";
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Basic-auth mode (email + API token) hits the tenant's own *.atlassian.net
|
|
@@ -48,7 +48,7 @@ export interface JiraBasicAuthOptions {
|
|
|
48
48
|
axiosAdapter?: AxiosAdapter;
|
|
49
49
|
/**
|
|
50
50
|
* Directory holding this daemon's persisted field/status discovery manifests
|
|
51
|
-
* (see
|
|
51
|
+
* (see ./manifest.ts), typically configDir() from config.ts. When
|
|
52
52
|
* omitted, discovery still works but nothing is persisted across restarts —
|
|
53
53
|
* every test and any caller that doesn't care about persistence can leave it out.
|
|
54
54
|
*/
|
|
@@ -404,7 +404,7 @@ export class JiraRepository {
|
|
|
404
404
|
* Discovers every custom field's display name -> customfield_XXXXX ID via
|
|
405
405
|
* client.issueFields.getFields() (GET /rest/api/2/field), same live call
|
|
406
406
|
* resolveCustomField already made lazily -- this just also persists the
|
|
407
|
-
* result to a manifest (see
|
|
407
|
+
* result to a manifest (see ./manifest.ts) so a later inbound
|
|
408
408
|
* lookup (fieldDisplayName) doesn't need a network round trip at all, and
|
|
409
409
|
* survives a daemon restart. Ported from emcee's FieldService.DiscoverFields
|
|
410
410
|
* (~/Workspace/emcee), same manifest file shape.
|
|
@@ -444,7 +444,7 @@ export class JiraRepository {
|
|
|
444
444
|
/**
|
|
445
445
|
* Samples the most recently created issues for a project/issue-type pair
|
|
446
446
|
* and extracts the description section headers common to all of them --
|
|
447
|
-
* see ../
|
|
447
|
+
* see ../issue/template.ts. Ported from emcee's TemplateService.DiscoverTemplate.
|
|
448
448
|
*/
|
|
449
449
|
async discoverTemplate(project: string, issueType: string, sampleSize = 5): Promise<Template | undefined> {
|
|
450
450
|
const jql = `project = ${jqlQuote(project)} AND issuetype = ${jqlQuote(issueType)} ORDER BY created DESC`;
|
|
@@ -10,16 +10,16 @@ import type { StartDaemonOptions } from "@danypops/vehicle-server/daemon";
|
|
|
10
10
|
import { createLogger, type Logger } from "@danypops/vehicle-server/logging";
|
|
11
11
|
import { ensureAuthToken, type PathEnvironment, resolveDaemonPaths } from "@danypops/vehicle-server/paths";
|
|
12
12
|
import { checkpoint, openSqliteWithPragmas } from "@danypops/vehicle-server/storage";
|
|
13
|
-
import {
|
|
13
|
+
import { createTicketsVehicleRegistry, syncDiscoverAvailability } from "../agent-tools/tickets-vehicle.js";
|
|
14
14
|
import { type BuildRepositories, buildRepositories, type Config, createBackendRefreshTask, loadConfig } from "../config/config.js";
|
|
15
|
-
import type { IssueRepository } from "../
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
15
|
+
import type { IssueRepository } from "../issue/repository.js";
|
|
16
|
+
import { TicketService } from "../issue/service.js";
|
|
17
|
+
import { TICKETS_DAEMON_NAMES } from "../rpc/ops.js";
|
|
18
|
+
import { buildApp, type TicketsAppDeps } from "../rpc/server.js";
|
|
19
|
+
import { FOCUS_MIGRATIONS, FocusStore } from "../sqlite/focus.js";
|
|
20
|
+
import { LEDGER_MIGRATIONS, Ledger } from "../sqlite/ledger.js";
|
|
21
|
+
import { SAVED_QUERY_MIGRATIONS, SavedQueryStore } from "../sqlite/saved-queries.js";
|
|
20
22
|
import { createSyncTask } from "./poller.js";
|
|
21
|
-
import { SAVED_QUERY_MIGRATIONS, SavedQueryStore } from "./saved-queries.js";
|
|
22
|
-
import { buildApp, type TicketsAppDeps } from "./server.js";
|
|
23
23
|
|
|
24
24
|
export interface BootstrapOptions {
|
|
25
25
|
pathEnv?: PathEnvironment;
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
import type { MaintenanceTask } from "@danypops/vehicle-server/daemon";
|
|
10
10
|
import type { Logger } from "@danypops/vehicle-server/logging";
|
|
11
|
-
import type { TicketService } from "../
|
|
12
|
-
import type { Ledger } from "
|
|
11
|
+
import type { TicketService } from "../issue/service.js";
|
|
12
|
+
import type { Ledger } from "../sqlite/ledger.js";
|
|
13
13
|
|
|
14
14
|
const DEFAULT_SYNC_LIMIT = 50;
|
|
15
15
|
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* whatever consumes this package). Pure types, zero runtime imports, safe to
|
|
5
5
|
* import from either side without pulling in bun:sqlite or Bun.serve.
|
|
6
6
|
*/
|
|
7
|
-
import type { Comment, CreateInput, Issue, ListFilter, UpdateInput } from "../
|
|
8
|
-
import type { Template } from "../
|
|
9
|
-
import type { TicketFocusState } from "
|
|
10
|
-
import type { SavedQuery } from "
|
|
7
|
+
import type { Comment, CreateInput, Issue, ListFilter, UpdateInput } from "../issue/issue.js";
|
|
8
|
+
import type { Template } from "../issue/template.js";
|
|
9
|
+
import type { TicketFocusState } from "../sqlite/focus.js";
|
|
10
|
+
import type { SavedQuery } from "../sqlite/saved-queries.js";
|
|
11
11
|
|
|
12
12
|
export type TicketOperation =
|
|
13
13
|
| "backends.list"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Daemon HTTP surface: Bearer-token auth, /health, /ready, a single
|
|
3
3
|
* dispatch endpoint (/api/v1/ops) per vehicle-server's http.ts convention,
|
|
4
|
-
* and a VehicleRegistry (see ../
|
|
4
|
+
* and a VehicleRegistry (see ../agent-tools/tickets-vehicle.ts) mounted at
|
|
5
5
|
* /vehicle/* -- same daemon, same auth, same port, not a second service to
|
|
6
6
|
* stand up. Every operation here has a CLI command (cli/index.ts) and a
|
|
7
7
|
* pi-tickets tool action — no operation exists only for one caller.
|
|
@@ -11,13 +11,13 @@ import type { VehicleRegistry } from "@danypops/vehicle-server";
|
|
|
11
11
|
import { createVehicleHttpApp } from "@danypops/vehicle-server/http";
|
|
12
12
|
import type { Logger } from "@danypops/vehicle-server/logging";
|
|
13
13
|
import { errorResponse, healthResponse, jsonResponse, readyResponse, requireBearerToken } from "@danypops/vehicle-server/rpc-http";
|
|
14
|
-
import { AuthRequiredError, IssueNotFoundError } from "../
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { FocusError, type FocusStore } from "
|
|
18
|
-
import type { Ledger } from "
|
|
14
|
+
import { AuthRequiredError, IssueNotFoundError } from "../issue/errors.js";
|
|
15
|
+
import { parseRef } from "../issue/issue.js";
|
|
16
|
+
import { NotSupportedError, type TicketService, UnknownBackendError } from "../issue/service.js";
|
|
17
|
+
import { FocusError, type FocusStore } from "../sqlite/focus.js";
|
|
18
|
+
import type { Ledger } from "../sqlite/ledger.js";
|
|
19
|
+
import { SavedQueryNotFoundError, type SavedQueryStore } from "../sqlite/saved-queries.js";
|
|
19
20
|
import { TICKET_OPERATIONS, type TicketOperation, type TicketOpInputs, type TicketOpOutputs } from "./ops.js";
|
|
20
|
-
import { SavedQueryNotFoundError, type SavedQueryStore } from "./saved-queries.js";
|
|
21
21
|
|
|
22
22
|
export interface TicketsAppDeps {
|
|
23
23
|
service: TicketService;
|
|
@@ -36,7 +36,7 @@ export interface TicketsAppDeps {
|
|
|
36
36
|
*/
|
|
37
37
|
onShutdownRequested?: () => void;
|
|
38
38
|
/**
|
|
39
|
-
* Built by ../
|
|
39
|
+
* Built by ../agent-tools/tickets-vehicle.ts's createTicketsVehicleRegistry,
|
|
40
40
|
* from the same base deps this interface describes -- passed in rather
|
|
41
41
|
* than built here to avoid a server.ts <-> tickets-vehicle.ts import cycle
|
|
42
42
|
* (tickets-vehicle.ts already imports TICKET_OP_HANDLERS and this type
|
|
@@ -46,7 +46,7 @@ export interface TicketsAppDeps {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// Narrower than TicketsAppDeps on purpose: no real handler reads
|
|
49
|
-
// deps.vehicleRegistry, and
|
|
49
|
+
// deps.vehicleRegistry, and agent-tools/tickets-vehicle.ts's own registry
|
|
50
50
|
// builder needs to call these before a registry exists to put there.
|
|
51
51
|
export type Handler<Op extends TicketOperation> = (
|
|
52
52
|
deps: Omit<TicketsAppDeps, "vehicleRegistry">,
|
|
@@ -55,7 +55,7 @@ export type Handler<Op extends TicketOperation> = (
|
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* The one real implementation of every ticket operation, shared verbatim by
|
|
58
|
-
* the hand-rolled /api/v1/ops dispatch below and
|
|
58
|
+
* the hand-rolled /api/v1/ops dispatch below and agent-tools/tickets-vehicle.ts's
|
|
59
59
|
* VehicleRegistry projection -- never reimplemented a second time for the
|
|
60
60
|
* newer surface.
|
|
61
61
|
*/
|
|
@@ -125,7 +125,7 @@ function statusFor(error: unknown): number {
|
|
|
125
125
|
|
|
126
126
|
export function buildApp(deps: TicketsAppDeps): { fetch(request: Request): Promise<Response> } {
|
|
127
127
|
// Same Bearer token as the rest of this API -- the Vehicle-projected
|
|
128
|
-
// domain (see ../
|
|
128
|
+
// domain (see ../agent-tools/tickets-vehicle.ts) rides the same daemon, same
|
|
129
129
|
// auth, same port; it is not a second service to stand up or
|
|
130
130
|
// authenticate against separately.
|
|
131
131
|
const vehicleApp = createVehicleHttpApp({ registry: deps.vehicleRegistry, token: deps.token });
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Database } from "bun:sqlite";
|
|
10
10
|
import type { Migration } from "@danypops/vehicle-server/storage";
|
|
11
|
-
import type { Issue } from "../
|
|
11
|
+
import type { Issue } from "../issue/issue.js";
|
|
12
12
|
|
|
13
13
|
export const LEDGER_MIGRATIONS: Migration[] = [
|
|
14
14
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Saved queries — a name plus a raw query string (Jira JQL today; the backend
|
|
3
|
-
* decides what "raw query" means via
|
|
3
|
+
* decides what "raw query" means via issue/repository.ts's RawQueryable) that
|
|
4
4
|
* can be run again by name instead of re-typing the query every time. This is
|
|
5
5
|
* how a board/backlog view (e.g. a Jira board's quickFilter, or its backlog's
|
|
6
6
|
* customFilter) becomes something the CLI/TUI/tool surface can browse
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|