@cat-factory/node-server 0.6.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/LICENSE +21 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +297 -0
- package/dist/config.js.map +1 -0
- package/dist/container.d.ts +88 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/container.js +937 -0
- package/dist/container.js.map +1 -0
- package/dist/db/client.d.ts +13 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +21 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/migrate.d.ts +12 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +40 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/schema.d.ts +7858 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +928 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/environments.d.ts +11 -0
- package/dist/environments.d.ts.map +1 -0
- package/dist/environments.js +31 -0
- package/dist/environments.js.map +1 -0
- package/dist/execution/bootstrapRunner.d.ts +27 -0
- package/dist/execution/bootstrapRunner.d.ts.map +1 -0
- package/dist/execution/bootstrapRunner.js +79 -0
- package/dist/execution/bootstrapRunner.js.map +1 -0
- package/dist/execution/config.d.ts +37 -0
- package/dist/execution/config.d.ts.map +1 -0
- package/dist/execution/config.js +86 -0
- package/dist/execution/config.js.map +1 -0
- package/dist/execution/drive.d.ts +6 -0
- package/dist/execution/drive.d.ts.map +1 -0
- package/dist/execution/drive.js +13 -0
- package/dist/execution/drive.js.map +1 -0
- package/dist/execution/pgBossRunner.d.ts +82 -0
- package/dist/execution/pgBossRunner.d.ts.map +1 -0
- package/dist/execution/pgBossRunner.js +163 -0
- package/dist/execution/pgBossRunner.js.map +1 -0
- package/dist/gateways.d.ts +4 -0
- package/dist/gateways.d.ts.map +1 -0
- package/dist/gateways.js +91 -0
- package/dist/gateways.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +9 -0
- package/dist/main.js.map +1 -0
- package/dist/modelProvider.d.ts +6 -0
- package/dist/modelProvider.d.ts.map +1 -0
- package/dist/modelProvider.js +72 -0
- package/dist/modelProvider.js.map +1 -0
- package/dist/realtime.d.ts +62 -0
- package/dist/realtime.d.ts.map +1 -0
- package/dist/realtime.js +171 -0
- package/dist/realtime.js.map +1 -0
- package/dist/recurring.d.ts +11 -0
- package/dist/recurring.d.ts.map +1 -0
- package/dist/recurring.js +33 -0
- package/dist/recurring.js.map +1 -0
- package/dist/repositories/bootstrap.d.ts +25 -0
- package/dist/repositories/bootstrap.d.ts.map +1 -0
- package/dist/repositories/bootstrap.js +280 -0
- package/dist/repositories/bootstrap.js.map +1 -0
- package/dist/repositories/containerExecution.d.ts +33 -0
- package/dist/repositories/containerExecution.d.ts.map +1 -0
- package/dist/repositories/containerExecution.js +199 -0
- package/dist/repositories/containerExecution.js.map +1 -0
- package/dist/repositories/documents.d.ts +31 -0
- package/dist/repositories/documents.d.ts.map +1 -0
- package/dist/repositories/documents.js +176 -0
- package/dist/repositories/documents.js.map +1 -0
- package/dist/repositories/drizzle.d.ts +105 -0
- package/dist/repositories/drizzle.d.ts.map +1 -0
- package/dist/repositories/drizzle.js +1872 -0
- package/dist/repositories/drizzle.js.map +1 -0
- package/dist/repositories/environments.d.ts +23 -0
- package/dist/repositories/environments.d.ts.map +1 -0
- package/dist/repositories/environments.js +162 -0
- package/dist/repositories/environments.js.map +1 -0
- package/dist/repositories/fragments.d.ts +23 -0
- package/dist/repositories/fragments.d.ts.map +1 -0
- package/dist/repositories/fragments.js +190 -0
- package/dist/repositories/fragments.js.map +1 -0
- package/dist/repositories/github.d.ts +53 -0
- package/dist/repositories/github.d.ts.map +1 -0
- package/dist/repositories/github.js +441 -0
- package/dist/repositories/github.js.map +1 -0
- package/dist/repositories/localModelEndpoint.d.ts +12 -0
- package/dist/repositories/localModelEndpoint.d.ts.map +1 -0
- package/dist/repositories/localModelEndpoint.js +75 -0
- package/dist/repositories/localModelEndpoint.js.map +1 -0
- package/dist/repositories/notifications.d.ts +11 -0
- package/dist/repositories/notifications.d.ts.map +1 -0
- package/dist/repositories/notifications.js +88 -0
- package/dist/repositories/notifications.js.map +1 -0
- package/dist/repositories/personalSubscription.d.ts +22 -0
- package/dist/repositories/personalSubscription.d.ts.map +1 -0
- package/dist/repositories/personalSubscription.js +159 -0
- package/dist/repositories/personalSubscription.js.map +1 -0
- package/dist/repositories/providerApiKey.d.ts +18 -0
- package/dist/repositories/providerApiKey.d.ts.map +1 -0
- package/dist/repositories/providerApiKey.js +111 -0
- package/dist/repositories/providerApiKey.js.map +1 -0
- package/dist/repositories/providerSubscription.d.ts +16 -0
- package/dist/repositories/providerSubscription.d.ts.map +1 -0
- package/dist/repositories/providerSubscription.js +88 -0
- package/dist/repositories/providerSubscription.js.map +1 -0
- package/dist/repositories/slack.d.ts +23 -0
- package/dist/repositories/slack.d.ts.map +1 -0
- package/dist/repositories/slack.js +150 -0
- package/dist/repositories/slack.js.map +1 -0
- package/dist/repositories/tasks.d.ts +24 -0
- package/dist/repositories/tasks.d.ts.map +1 -0
- package/dist/repositories/tasks.js +194 -0
- package/dist/repositories/tasks.js.map +1 -0
- package/dist/retention.d.ts +38 -0
- package/dist/retention.d.ts.map +1 -0
- package/dist/retention.js +53 -0
- package/dist/retention.js.map +1 -0
- package/dist/runtime.d.ts +10 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +13 -0
- package/dist/runtime.js.map +1 -0
- package/dist/server.d.ts +41 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +138 -0
- package/dist/server.js.map +1 -0
- package/dist/tasks/JiraProvider.d.ts +27 -0
- package/dist/tasks/JiraProvider.d.ts.map +1 -0
- package/dist/tasks/JiraProvider.js +79 -0
- package/dist/tasks/JiraProvider.js.map +1 -0
- package/drizzle/20260622175812_flashy_maginty/migration.sql +689 -0
- package/drizzle/20260622175812_flashy_maginty/snapshot.json +8318 -0
- package/drizzle/20260623172634_loud_wallop/migration.sql +11 -0
- package/drizzle/20260623172634_loud_wallop/snapshot.json +8439 -0
- package/drizzle/20260623174706_acoustic_zemo/migration.sql +16 -0
- package/drizzle/20260623174706_acoustic_zemo/snapshot.json +8506 -0
- package/drizzle/20260623184400_silent_cardiac/migration.sql +24 -0
- package/drizzle/20260623184400_silent_cardiac/snapshot.json +8639 -0
- package/drizzle/20260623205323_quick_arclight/migration.sql +1 -0
- package/drizzle/20260623205323_quick_arclight/snapshot.json +8963 -0
- package/drizzle/20260623221910_black_zombie/migration.sql +22 -0
- package/drizzle/20260623221910_black_zombie/snapshot.json +9189 -0
- package/drizzle/20260624131343_far_lily_hollister/migration.sql +3 -0
- package/drizzle/20260624131343_far_lily_hollister/snapshot.json +9228 -0
- package/drizzle/20260624135452_tiny_norman_osborn/migration.sql +11 -0
- package/drizzle/20260624135452_tiny_norman_osborn/snapshot.json +9126 -0
- package/drizzle/20260624140138_wandering_avengers/migration.sql +1 -0
- package/drizzle/20260624140138_wandering_avengers/snapshot.json +9045 -0
- package/package.json +62 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { serve } from '@hono/node-server';
|
|
2
|
+
import { handleError, logger, mountAuthGate, registerCoreControllers, resolveCorsOrigin, } from '@cat-factory/server';
|
|
3
|
+
import { Hono } from 'hono';
|
|
4
|
+
import { cors } from 'hono/cors';
|
|
5
|
+
import { PgBoss } from 'pg-boss';
|
|
6
|
+
import { buildNodeContainer } from './container.js';
|
|
7
|
+
import { createDbClient } from './db/client.js';
|
|
8
|
+
import { migrate } from './db/migrate.js';
|
|
9
|
+
import { executionRuntime } from './execution/config.js';
|
|
10
|
+
import { startDecisionTimeoutWorker, startExecutionWorker, startStaleRunSweeper, } from './execution/pgBossRunner.js';
|
|
11
|
+
import { startBootstrapWorker } from './execution/bootstrapRunner.js';
|
|
12
|
+
import { startEnvironmentSweeper } from './environments.js';
|
|
13
|
+
import { startScheduleSweeper } from './recurring.js';
|
|
14
|
+
import { NodeRealtimeHub, attachRealtime } from './realtime.js';
|
|
15
|
+
import { createDrizzleRepositories } from './repositories/drizzle.js';
|
|
16
|
+
import { DrizzleSubscriptionActivationRepository } from './repositories/personalSubscription.js';
|
|
17
|
+
import { startRetentionSweeper } from './retention.js';
|
|
18
|
+
import { SystemClock } from './runtime.js';
|
|
19
|
+
/** Build the Hono app around a ready container (shared by `createServer` + `start`). */
|
|
20
|
+
export function createApp(container, env = process.env) {
|
|
21
|
+
const app = new Hono();
|
|
22
|
+
app.use('*', cors({ origin: (origin) => resolveCorsOrigin(origin, env.CORS_ALLOWED_ORIGINS) }));
|
|
23
|
+
app.use('*', async (c, next) => {
|
|
24
|
+
c.set('container', container);
|
|
25
|
+
await next();
|
|
26
|
+
});
|
|
27
|
+
app.get('/health', (c) => c.json({ status: 'ok' }));
|
|
28
|
+
// Default-deny session gate + per-workspace authz, shared verbatim with the Worker
|
|
29
|
+
// (one implementation in @cat-factory/server so the runtimes can't drift).
|
|
30
|
+
mountAuthGate(app);
|
|
31
|
+
registerCoreControllers(app);
|
|
32
|
+
app.onError(handleError);
|
|
33
|
+
return app;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Build the app from container options (convenience, e.g. embedding / tests).
|
|
37
|
+
*
|
|
38
|
+
* WARNING: unless a started `boss` is passed in `options`, the container wires the
|
|
39
|
+
* engine's NoopWorkRunner — a started execution then returns `running` but is never
|
|
40
|
+
* driven to completion. Use {@link start} for a fully-wired service (durable pg-boss
|
|
41
|
+
* worker + stale-run sweeper); pass `boss` here only if you drive runs yourself.
|
|
42
|
+
*/
|
|
43
|
+
export function createServer(options) {
|
|
44
|
+
return createApp(buildNodeContainer(options), options.env);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Boot the Node HTTP server: connect to Postgres (`DATABASE_URL`), ensure the schema,
|
|
48
|
+
* start pg-boss + the durable execution worker + the stale-run sweeper, build the app,
|
|
49
|
+
* and listen. Registers SIGTERM/SIGINT handlers for a clean, ordered shutdown.
|
|
50
|
+
*/
|
|
51
|
+
export async function start(options = {}) {
|
|
52
|
+
const env = options.env ?? process.env;
|
|
53
|
+
const databaseUrl = env.DATABASE_URL;
|
|
54
|
+
if (!databaseUrl) {
|
|
55
|
+
throw new Error('DATABASE_URL is required to start the Node server');
|
|
56
|
+
}
|
|
57
|
+
const { db, pool } = createDbClient(databaseUrl);
|
|
58
|
+
await migrate(db, pool);
|
|
59
|
+
const boss = new PgBoss(databaseUrl);
|
|
60
|
+
await boss.start();
|
|
61
|
+
// Build the repositories once and share them with both the container and the
|
|
62
|
+
// retention sweeper (so the sweeper prunes the very stores the app writes to).
|
|
63
|
+
const clock = new SystemClock();
|
|
64
|
+
const repos = createDrizzleRepositories(db, clock);
|
|
65
|
+
const buildContainer = options.buildContainer ?? buildNodeContainer;
|
|
66
|
+
// The per-workspace real-time subscriber registry. Created here (not in the container
|
|
67
|
+
// builder) because it must be shared between the engine's event publisher — wired
|
|
68
|
+
// inside the container — and the HTTP server's WebSocket upgrade listener attached
|
|
69
|
+
// below. The local facade's builder forwards this option to buildNodeContainer
|
|
70
|
+
// unchanged, so local mode gets live updates too.
|
|
71
|
+
const realtimeHub = new NodeRealtimeHub();
|
|
72
|
+
const container = buildContainer({ db, boss, env, repos, realtimeHub });
|
|
73
|
+
const runtime = executionRuntime(container.config, env);
|
|
74
|
+
// The decision-timeout worker creates its queue first so the advance worker's send to
|
|
75
|
+
// it (when a run parks on a decision) always has a target.
|
|
76
|
+
await startDecisionTimeoutWorker(boss, container, logger);
|
|
77
|
+
await startExecutionWorker(boss, container, runtime.drive, logger, {
|
|
78
|
+
concurrency: runtime.concurrency,
|
|
79
|
+
decisionTimeoutSeconds: runtime.decisionTimeoutSeconds,
|
|
80
|
+
});
|
|
81
|
+
// Durably drive bootstrap runs too (the Worker uses a per-run BootstrapWorkflow);
|
|
82
|
+
// a no-op queue when the bootstrap module isn't wired.
|
|
83
|
+
await startBootstrapWorker(boss, container, runtime.drive, logger, {
|
|
84
|
+
concurrency: runtime.concurrency,
|
|
85
|
+
});
|
|
86
|
+
const stopSweeper = startStaleRunSweeper(boss, container, runtime.sweeper, runtime.queue, logger);
|
|
87
|
+
// Bound the unbounded tables (`token_usage`, the heavy `llm_call_metrics`): the Worker
|
|
88
|
+
// prunes these from cron, Node has none, so a timer mirrors it. Without this the
|
|
89
|
+
// observability sink — full per-call prompt/response — grows forever on Postgres.
|
|
90
|
+
const stopRetention = startRetentionSweeper({
|
|
91
|
+
tokenUsageRepository: repos.tokenUsageRepository,
|
|
92
|
+
llmCallMetricRepository: repos.llmCallMetricRepository,
|
|
93
|
+
pipelineScheduleRepository: repos.pipelineScheduleRepository,
|
|
94
|
+
subscriptionActivationRepository: new DrizzleSubscriptionActivationRepository(db),
|
|
95
|
+
}, container.config.retention, clock, logger);
|
|
96
|
+
// Fire due recurring pipelines on a one-minute timer (the Worker uses cron).
|
|
97
|
+
const stopScheduleSweeper = startScheduleSweeper(container, clock, logger);
|
|
98
|
+
// Tear down expired ephemeral environments (the Worker uses cron); no-op unless the
|
|
99
|
+
// environments integration is wired.
|
|
100
|
+
const stopEnvironmentSweeper = startEnvironmentSweeper(container, clock, logger);
|
|
101
|
+
const app = createApp(container, env);
|
|
102
|
+
const port = Number(env.PORT ?? 8787);
|
|
103
|
+
const host = options.host ?? env.HOST?.trim() ?? undefined;
|
|
104
|
+
const server = serve({ fetch: app.fetch, port, ...(host ? { hostname: host } : {}) });
|
|
105
|
+
// Accept the SPA's WebSocket event-stream upgrades on the same listener and push the
|
|
106
|
+
// engine's events to subscribers (the Worker uses a per-workspace Durable Object;
|
|
107
|
+
// `@hono/node-server` doesn't upgrade on its own, so we attach a `ws` server here).
|
|
108
|
+
const stopRealtime = attachRealtime(server, realtimeHub, container.config.auth, logger);
|
|
109
|
+
logger.info({ port, host: host ?? '0.0.0.0' }, 'cat-factory node server listening');
|
|
110
|
+
// Ordered graceful shutdown: stop accepting connections, halt the sweeper + pg-boss
|
|
111
|
+
// worker, release the pool, then exit. Without closing the HTTP server the process
|
|
112
|
+
// would keep the event loop alive and hang until the orchestrator SIGKILLs it.
|
|
113
|
+
let shuttingDown = false;
|
|
114
|
+
const shutdown = async (signal) => {
|
|
115
|
+
if (shuttingDown)
|
|
116
|
+
return;
|
|
117
|
+
shuttingDown = true;
|
|
118
|
+
logger.info({ signal }, 'shutting down cat-factory node server');
|
|
119
|
+
stopSweeper();
|
|
120
|
+
stopRetention();
|
|
121
|
+
stopScheduleSweeper();
|
|
122
|
+
stopEnvironmentSweeper();
|
|
123
|
+
stopRealtime();
|
|
124
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
125
|
+
try {
|
|
126
|
+
await boss.stop();
|
|
127
|
+
await pool.end();
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
logger.error({ err: err instanceof Error ? err.message : String(err) }, 'shutdown error');
|
|
131
|
+
}
|
|
132
|
+
process.exit(0);
|
|
133
|
+
};
|
|
134
|
+
process.once('SIGTERM', () => void shutdown('SIGTERM'));
|
|
135
|
+
process.once('SIGINT', () => void shutdown('SIGINT'));
|
|
136
|
+
return server;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type TaskContent, type TaskCredentials, type TaskSourceProvider, type NormalizedTaskConnection } from '@cat-factory/kernel';
|
|
2
|
+
export declare class JiraApiError extends Error {
|
|
3
|
+
readonly status: number;
|
|
4
|
+
constructor(status: number, message: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class JiraProvider implements TaskSourceProvider {
|
|
7
|
+
readonly kind: 'jira';
|
|
8
|
+
readonly descriptor: {
|
|
9
|
+
source: "github" | "jira";
|
|
10
|
+
label: string;
|
|
11
|
+
icon: string;
|
|
12
|
+
credentialFields: {
|
|
13
|
+
key: string;
|
|
14
|
+
label: string;
|
|
15
|
+
help?: string | undefined;
|
|
16
|
+
placeholder?: string | undefined;
|
|
17
|
+
secret?: boolean | undefined;
|
|
18
|
+
}[];
|
|
19
|
+
refLabel: string;
|
|
20
|
+
refPlaceholder: string;
|
|
21
|
+
searchable?: boolean | undefined;
|
|
22
|
+
};
|
|
23
|
+
normalizeConnection(input: TaskCredentials): NormalizedTaskConnection;
|
|
24
|
+
parseRef(input: string): string | null;
|
|
25
|
+
fetchTask(credentials: TaskCredentials, externalId: string): Promise<TaskContent>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=JiraProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JiraProvider.d.ts","sourceRoot":"","sources":["../../src/tasks/JiraProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC9B,MAAM,qBAAqB,CAAA;AAY5B,qBAAa,YAAa,SAAQ,KAAK;IAEnC,QAAQ,CAAC,MAAM,EAAE,MAAM;IADzB,YACW,MAAM,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM,EAIhB;CACF;AAsBD,qBAAa,YAAa,YAAW,kBAAkB;IACrD,QAAQ,CAAC,IAAI,EAAG,MAAM,CAAS;IAC/B,QAAQ,CAAC,UAAU;;;;;;;;;;;;;;MAAkB;IAErC,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG,wBAAwB,CAapE;IAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAErC;IAEK,SAAS,CAAC,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA4CtF;CACF"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ValidationError, atlassianLogic, } from '@cat-factory/kernel';
|
|
2
|
+
import { JIRA_DESCRIPTOR, jiraLogic } from '@cat-factory/integrations';
|
|
3
|
+
// JiraProvider for the Node facade — a faithful copy of the Cloudflare facade's
|
|
4
|
+
// provider (`runtimes/cloudflare/src/infrastructure/tasks/JiraProvider.ts`). Both
|
|
5
|
+
// are thin `fetch` shells around the shared Jira *pure* logic in
|
|
6
|
+
// `@cat-factory/integrations` (ref parsing, ADF→Markdown); only the HTTP shell is
|
|
7
|
+
// runtime-bound. Node exposes the same global `fetch` + `btoa`, so the two stay
|
|
8
|
+
// behaviourally identical (see CLAUDE.md "Keep the runtimes symmetric").
|
|
9
|
+
const USER_AGENT = 'cat-factory';
|
|
10
|
+
export class JiraApiError extends Error {
|
|
11
|
+
status;
|
|
12
|
+
constructor(status, message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.status = status;
|
|
15
|
+
this.name = 'JiraApiError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class JiraProvider {
|
|
19
|
+
kind = 'jira';
|
|
20
|
+
descriptor = JIRA_DESCRIPTOR;
|
|
21
|
+
normalizeConnection(input) {
|
|
22
|
+
const baseUrlRaw = input.baseUrl?.trim();
|
|
23
|
+
const accountEmail = input.accountEmail?.trim();
|
|
24
|
+
const apiToken = input.apiToken?.trim();
|
|
25
|
+
if (!baseUrlRaw || !accountEmail || !apiToken) {
|
|
26
|
+
throw new ValidationError('Jira requires a site URL, account email and API token');
|
|
27
|
+
}
|
|
28
|
+
const baseUrl = atlassianLogic.normalizeAtlassianBaseUrl(baseUrlRaw);
|
|
29
|
+
atlassianLogic.assertSafeAtlassianBaseUrl(baseUrl);
|
|
30
|
+
return {
|
|
31
|
+
credentials: { baseUrl, accountEmail, apiToken },
|
|
32
|
+
label: baseUrl,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
parseRef(input) {
|
|
36
|
+
return jiraLogic.parseJiraRef(input);
|
|
37
|
+
}
|
|
38
|
+
async fetchTask(credentials, externalId) {
|
|
39
|
+
const base = credentials.baseUrl.replace(/\/+$/, '');
|
|
40
|
+
const fields = 'summary,description,status,issuetype,assignee,priority,labels,comment';
|
|
41
|
+
const url = `${base}/rest/api/3/issue/${encodeURIComponent(externalId)}?fields=${fields}`;
|
|
42
|
+
const auth = btoa(`${credentials.accountEmail}:${credentials.apiToken}`);
|
|
43
|
+
const res = await fetch(url, {
|
|
44
|
+
method: 'GET',
|
|
45
|
+
headers: {
|
|
46
|
+
authorization: `Basic ${auth}`,
|
|
47
|
+
accept: 'application/json',
|
|
48
|
+
'user-agent': USER_AGENT,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
const text = await res.text().catch(() => '');
|
|
53
|
+
throw new JiraApiError(res.status, `Jira GET ${url} → ${res.status}: ${text.slice(0, 300)}`);
|
|
54
|
+
}
|
|
55
|
+
const json = (await res.json().catch(() => null));
|
|
56
|
+
if (!json || !json.key || !json.fields) {
|
|
57
|
+
throw new JiraApiError(502, `Jira returned an unexpected body for issue ${externalId}`);
|
|
58
|
+
}
|
|
59
|
+
const f = json.fields;
|
|
60
|
+
const comments = (f.comment?.comments ?? []).map((c) => ({
|
|
61
|
+
author: c.author?.displayName ?? '',
|
|
62
|
+
createdAt: c.created ?? '',
|
|
63
|
+
body: jiraLogic.adfToMarkdown(c.body),
|
|
64
|
+
}));
|
|
65
|
+
return {
|
|
66
|
+
externalId: json.key,
|
|
67
|
+
url: `${base}/browse/${json.key}`,
|
|
68
|
+
title: f.summary ?? '(untitled)',
|
|
69
|
+
status: f.status?.name ?? '',
|
|
70
|
+
type: f.issuetype?.name ?? '',
|
|
71
|
+
assignee: f.assignee?.displayName ?? null,
|
|
72
|
+
priority: f.priority?.name ?? null,
|
|
73
|
+
labels: Array.isArray(f.labels) ? f.labels : [],
|
|
74
|
+
description: jiraLogic.adfToMarkdown(f.description),
|
|
75
|
+
comments,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=JiraProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JiraProvider.js","sourceRoot":"","sources":["../../src/tasks/JiraProvider.ts"],"names":[],"mappings":"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"}
|