@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
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { reenqueueStaleBootstrap } from './bootstrapRunner.js';
|
|
2
|
+
import { driveExecution } from './drive.js';
|
|
3
|
+
// Durable execution on pg-boss: the analogue of the Worker's Cloudflare Workflows
|
|
4
|
+
// driver. `startRun` enqueues an advance job (deduped per run via singletonKey); a
|
|
5
|
+
// registered worker drives the run to a standstill via `driveExecution`. A resolved
|
|
6
|
+
// decision re-enqueues an advance to resume a parked run. State lives in Postgres,
|
|
7
|
+
// so a crash mid-run is recovered two ways: pg-boss retries an expired/failed advance
|
|
8
|
+
// job, and the stale-run sweeper re-enqueues runs still `running` in storage.
|
|
9
|
+
const QUEUE = 'execution.advance';
|
|
10
|
+
// A separate, delayed queue that fails a run still parked on a decision after the
|
|
11
|
+
// `decisionTimeout` window — the Node analogue of the Cloudflare driver's
|
|
12
|
+
// `waitForEvent(..., { timeout })`. Kept off the advance queue so the delay never holds
|
|
13
|
+
// up real drives, and `exclusive` so at most one pending timeout exists per (run, decision).
|
|
14
|
+
const DECISION_TIMEOUT_QUEUE = 'execution.decision-timeout';
|
|
15
|
+
// The queue MUST be created with the `exclusive` policy for the dedup below to hold.
|
|
16
|
+
// Under pg-boss's default `standard` policy, `singletonKey` alone enforces NO uniqueness
|
|
17
|
+
// (the singleton unique indexes are policy-gated, and the policy-independent one requires
|
|
18
|
+
// `singletonSeconds`, which we don't set). `exclusive` makes (name, singletonKey) unique
|
|
19
|
+
// across the `created`/`retry`/`active` states, so at most one advance job per run is
|
|
20
|
+
// alive at a time and a duplicate `send` is an `ON CONFLICT DO NOTHING` no-op.
|
|
21
|
+
const QUEUE_POLICY = 'exclusive';
|
|
22
|
+
function sendOptions(executionId, opts) {
|
|
23
|
+
return {
|
|
24
|
+
singletonKey: executionId,
|
|
25
|
+
expireInSeconds: opts.expireInSeconds,
|
|
26
|
+
heartbeatSeconds: opts.heartbeatSeconds,
|
|
27
|
+
retryLimit: opts.retryLimit,
|
|
28
|
+
retryDelay: opts.retryDelaySeconds,
|
|
29
|
+
retryBackoff: true,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export class PgBossWorkRunner {
|
|
33
|
+
boss;
|
|
34
|
+
queueOptions;
|
|
35
|
+
constructor(boss, queueOptions) {
|
|
36
|
+
this.boss = boss;
|
|
37
|
+
this.queueOptions = queueOptions;
|
|
38
|
+
}
|
|
39
|
+
async startRun(workspaceId, executionId) {
|
|
40
|
+
await this.boss.send(QUEUE, { workspaceId, executionId }, sendOptions(executionId, this.queueOptions));
|
|
41
|
+
}
|
|
42
|
+
async signalDecision(workspaceId, executionId, _decisionId, _choice) {
|
|
43
|
+
// The decision is already persisted by resolveDecision; re-enqueue an advance so
|
|
44
|
+
// the parked run resumes. The DB write is the source of truth either way.
|
|
45
|
+
await this.boss.send(QUEUE, { workspaceId, executionId }, sendOptions(executionId, this.queueOptions));
|
|
46
|
+
}
|
|
47
|
+
async cancelRun(_workspaceId, _executionId) {
|
|
48
|
+
// Best-effort: the run is finalized via ExecutionService.stopRun; any in-flight
|
|
49
|
+
// advance job is a no-op once the run is terminal (advanceInstance returns noop).
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Create the execution queue and start the worker that drives runs.
|
|
54
|
+
*
|
|
55
|
+
* `concurrency` (pg-boss `localConcurrency`) spawns that many INDEPENDENT workers for
|
|
56
|
+
* the queue on this node: each polls, fetches one job (`batchSize` stays 1) and acks /
|
|
57
|
+
* retries it on its own, so up to `concurrency` runs drive in parallel. This is the key
|
|
58
|
+
* to throughput — a single drive parks for the whole of a step's poll budget (sleeping
|
|
59
|
+
* between polls), so without parallel workers one slow run would block every other run
|
|
60
|
+
* behind it. We deliberately keep `batchSize: 1` rather than raising it: a batch handler
|
|
61
|
+
* completes/fails all its jobs together, which would couple unrelated runs' retries;
|
|
62
|
+
* independent workers keep per-run retry semantics intact. The `exclusive` queue policy
|
|
63
|
+
* still prevents the SAME run being driven by two workers at once (one live advance job per
|
|
64
|
+
* run id; duplicate sends no-op). Scale `concurrency` with the DB pool
|
|
65
|
+
* (each active drive borrows a connection only for its brief reads/writes between sleeps).
|
|
66
|
+
*/
|
|
67
|
+
export async function startExecutionWorker(boss, container, cfg, log, options = {}) {
|
|
68
|
+
const concurrency = options.concurrency ?? 10;
|
|
69
|
+
const decisionTimeoutSeconds = options.decisionTimeoutSeconds ?? 0;
|
|
70
|
+
await boss.createQueue(QUEUE, { policy: QUEUE_POLICY });
|
|
71
|
+
await boss.work(QUEUE, { localConcurrency: Math.max(1, concurrency) }, async (jobs) => {
|
|
72
|
+
for (const job of jobs) {
|
|
73
|
+
const { workspaceId, executionId } = job.data;
|
|
74
|
+
try {
|
|
75
|
+
const outcome = await driveExecution(container.executionService, workspaceId, executionId, cfg, { log });
|
|
76
|
+
// Arm a decision timeout when the run parked awaiting a human. There is no
|
|
77
|
+
// event to cancel it on resolution (unlike Cloudflare's waitForEvent), so the
|
|
78
|
+
// timeout job re-checks state and `expireDecision` no-ops if it was resolved.
|
|
79
|
+
if (outcome.parkedDecisionId && decisionTimeoutSeconds > 0) {
|
|
80
|
+
await boss.send(DECISION_TIMEOUT_QUEUE, { workspaceId, executionId, decisionId: outcome.parkedDecisionId }, {
|
|
81
|
+
startAfter: decisionTimeoutSeconds,
|
|
82
|
+
singletonKey: `${executionId}:${outcome.parkedDecisionId}`,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
log.error({
|
|
88
|
+
workspaceId,
|
|
89
|
+
executionId,
|
|
90
|
+
err: error instanceof Error ? error.message : String(error),
|
|
91
|
+
}, 'execution driver failed');
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Start the worker that expires overdue decisions. A delayed job (armed by
|
|
99
|
+
* {@link startExecutionWorker} when a run parks on a decision) fires after the
|
|
100
|
+
* `decisionTimeout`; `expireDecision` fails the run as `decision_timeout` ONLY if it is
|
|
101
|
+
* still parked on that exact decision, so a decision resolved meanwhile is a safe no-op
|
|
102
|
+
* (no driving — that stays on the advance queue). This is the Node analogue of the
|
|
103
|
+
* Cloudflare driver's `waitForEvent` timeout. Create the queue before the advance worker
|
|
104
|
+
* so the advance worker's `boss.send` to it always has a target.
|
|
105
|
+
*/
|
|
106
|
+
export async function startDecisionTimeoutWorker(boss, container, log) {
|
|
107
|
+
await boss.createQueue(DECISION_TIMEOUT_QUEUE, { policy: QUEUE_POLICY });
|
|
108
|
+
await boss.work(DECISION_TIMEOUT_QUEUE, async (jobs) => {
|
|
109
|
+
for (const job of jobs) {
|
|
110
|
+
const { workspaceId, executionId, decisionId } = job.data;
|
|
111
|
+
try {
|
|
112
|
+
await container.executionService.expireDecision(workspaceId, executionId, decisionId);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
log.error({
|
|
116
|
+
workspaceId,
|
|
117
|
+
executionId,
|
|
118
|
+
decisionId,
|
|
119
|
+
err: error instanceof Error ? error.message : String(error),
|
|
120
|
+
}, 'decision-timeout check failed');
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Backstop for runs that are still `running` in storage but whose advance job is gone
|
|
128
|
+
* (the worker crashed/was evicted before the job retried). Mirrors the Worker's cron
|
|
129
|
+
* `sweepStuckRuns`: on each tick it re-enqueues every stale `running` execution run.
|
|
130
|
+
* The re-enqueue carries the run's `singletonKey` and the queue is `exclusive`, so a run
|
|
131
|
+
* that IS still being driven (its advance job active/retrying) is a silent no-op — only
|
|
132
|
+
* genuinely orphaned runs re-drive.
|
|
133
|
+
* Decision-parked (`blocked`) and spend-paused (`paused`) runs aren't `running`, so the
|
|
134
|
+
* sweeper leaves them alone. Returns a stop function (clears the interval).
|
|
135
|
+
*/
|
|
136
|
+
export function startStaleRunSweeper(boss, container, cfg, queueOptions, log) {
|
|
137
|
+
const tick = async () => {
|
|
138
|
+
try {
|
|
139
|
+
const stale = await container.agentRunRepository.listStale(Date.now() - cfg.leaseMs);
|
|
140
|
+
for (const ref of stale) {
|
|
141
|
+
// Both durable kinds are re-driven: an orphaned execution back onto the advance
|
|
142
|
+
// queue, an orphaned bootstrap onto the bootstrap drive queue (parity with the
|
|
143
|
+
// Worker's sweepStuckRuns, which covers execution + bootstrap).
|
|
144
|
+
if (ref.kind === 'bootstrap') {
|
|
145
|
+
log.warn({ workspaceId: ref.workspaceId, jobId: ref.id }, 're-driving stale bootstrap');
|
|
146
|
+
await reenqueueStaleBootstrap(boss, ref.workspaceId, ref.id, queueOptions);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (ref.kind !== 'execution')
|
|
150
|
+
continue;
|
|
151
|
+
log.warn({ workspaceId: ref.workspaceId, executionId: ref.id }, 're-driving stale run');
|
|
152
|
+
await boss.send(QUEUE, { workspaceId: ref.workspaceId, executionId: ref.id }, sendOptions(ref.id, queueOptions));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
log.error({ err: error instanceof Error ? error.message : String(error) }, 'stale-run sweep failed');
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const timer = setInterval(() => void tick(), cfg.intervalMs);
|
|
160
|
+
timer.unref?.(); // never keep the process alive on the sweep timer alone
|
|
161
|
+
return () => clearInterval(timer);
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=pgBossRunner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pgBossRunner.js","sourceRoot":"","sources":["../../src/execution/pgBossRunner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateways.d.ts","sourceRoot":"","sources":["../src/gateways.ts"],"names":[],"mappings":"AAOA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,qBAAqB,CAAA;AAmF5B,wDAAwD;AACxD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,eAAe,CAU1E"}
|
package/dist/gateways.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { DEEPSEEK_BASE_URL, MOONSHOT_BASE_URL, OPENAI_BASE_URL, OPENROUTER_BASE_URL, QWEN_BASE_URL, } from '@cat-factory/agents';
|
|
2
|
+
import { createWebSearchUpstreamFromEnv, } from '@cat-factory/server';
|
|
3
|
+
// Node implementations of the runtime gateway seams. Async GitHub ingest still falls
|
|
4
|
+
// back to the "inline / not enabled" paths the shared controllers handle, and the LLM
|
|
5
|
+
// proxy forwards to OpenAI-compatible providers over HTTP (no in-process binding).
|
|
6
|
+
//
|
|
7
|
+
// Real-time delivery, by contrast, IS implemented — but NOT through this gateway seam.
|
|
8
|
+
// The seam returns a Hono `Response` (the Cloudflare model: a 101 from the per-workspace
|
|
9
|
+
// Durable Object). `@hono/node-server` can't complete a WebSocket upgrade from a
|
|
10
|
+
// `Response`, so the Node facade intercepts the `/workspaces/:ws/events` upgrade on the
|
|
11
|
+
// HTTP server directly (see `attachRealtime` in `realtime.ts`) before it reaches this
|
|
12
|
+
// controller. This gateway therefore stays a no-op: it is never invoked for an actual
|
|
13
|
+
// upgrade on Node, and the shared events route only ever falls through to its 426/501.
|
|
14
|
+
//
|
|
15
|
+
// Production swap-in for a multi-replica deployment (follow-up): front the in-process
|
|
16
|
+
// `NodeRealtimeHub` with a shared bus (Postgres LISTEN/NOTIFY); single-process Node and
|
|
17
|
+
// local mode need nothing more. Async GitHub ingest: pg-boss `githubBackfill` / `githubWebhook`.
|
|
18
|
+
/**
|
|
19
|
+
* No-op: Node handles the WebSocket upgrade at the HTTP-server level (`attachRealtime`),
|
|
20
|
+
* not through this Response-returning seam — see the file header. Returning null keeps
|
|
21
|
+
* the shared controller's contract intact for the (unreachable on Node) delegation path.
|
|
22
|
+
*/
|
|
23
|
+
class NodeRealtimeGateway {
|
|
24
|
+
upgrade() {
|
|
25
|
+
return Promise.resolve(null);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** No async backfill scheduler yet: report "not scheduled" so the caller runs it inline. */
|
|
29
|
+
class InlineGitHubBackfillScheduler {
|
|
30
|
+
scheduleBackfill() {
|
|
31
|
+
return Promise.resolve(false);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** No async queue yet: report "not queued" so the caller handles webhooks/resyncs inline. */
|
|
35
|
+
class InlineGitHubWebhookIngest {
|
|
36
|
+
enqueueWebhook() {
|
|
37
|
+
return Promise.resolve(false);
|
|
38
|
+
}
|
|
39
|
+
queueRepoResync() {
|
|
40
|
+
return Promise.resolve(false);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// `baseUrl` is the built-in default; LiteLLM has none (operator-hosted), so it relies
|
|
44
|
+
// purely on its env override and resolves to null until LITELLM_BASE_URL is set.
|
|
45
|
+
const OPENAI_COMPATIBLE = {
|
|
46
|
+
qwen: { baseUrl: QWEN_BASE_URL, baseUrlEnv: 'QWEN_BASE_URL' },
|
|
47
|
+
deepseek: { baseUrl: DEEPSEEK_BASE_URL, baseUrlEnv: 'DEEPSEEK_BASE_URL' },
|
|
48
|
+
moonshot: { baseUrl: MOONSHOT_BASE_URL, baseUrlEnv: 'MOONSHOT_BASE_URL' },
|
|
49
|
+
openai: { baseUrl: OPENAI_BASE_URL, baseUrlEnv: 'OPENAI_BASE_URL' },
|
|
50
|
+
openrouter: { baseUrl: OPENROUTER_BASE_URL, baseUrlEnv: 'OPENROUTER_BASE_URL' },
|
|
51
|
+
litellm: { baseUrlEnv: 'LITELLM_BASE_URL' },
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Forwards the container LLM proxy to OpenAI-compatible providers over HTTP. Only the
|
|
55
|
+
* base URL is resolved here (overridable per provider via env); the API key is leased
|
|
56
|
+
* per call from the DB-backed pool by the proxy. There is no in-process path on Node,
|
|
57
|
+
* so `runInProcess` returns null (a `workers-ai`-pinned model is unavailable here; use
|
|
58
|
+
* a direct provider, or enable the Cloudflare REST flavour).
|
|
59
|
+
*/
|
|
60
|
+
class HttpLlmUpstream {
|
|
61
|
+
env;
|
|
62
|
+
constructor(env) {
|
|
63
|
+
this.env = env;
|
|
64
|
+
}
|
|
65
|
+
resolveOpenAiCompatible(provider) {
|
|
66
|
+
const entry = OPENAI_COMPATIBLE[provider];
|
|
67
|
+
if (!entry)
|
|
68
|
+
return null;
|
|
69
|
+
// `||` not `??`: a set-but-blank base-URL env must fall back to the default, not
|
|
70
|
+
// collapse to an empty URL the SDK then chokes on. For a provider with no default
|
|
71
|
+
// (LiteLLM), an unset env yields null so the proxy reports "not available" cleanly.
|
|
72
|
+
const baseURL = this.env[entry.baseUrlEnv] || entry.baseUrl;
|
|
73
|
+
return baseURL ? { baseURL } : null;
|
|
74
|
+
}
|
|
75
|
+
runInProcess() {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** Build the Node runtime gateways from process env. */
|
|
80
|
+
export function createNodeGateways(env) {
|
|
81
|
+
return {
|
|
82
|
+
realtime: new NodeRealtimeGateway(),
|
|
83
|
+
githubBackfill: new InlineGitHubBackfillScheduler(),
|
|
84
|
+
githubWebhook: new InlineGitHubWebhookIngest(),
|
|
85
|
+
llmUpstream: new HttpLlmUpstream(env),
|
|
86
|
+
// Container web-search proxy upstream (Brave / self-hosted SearXNG from env);
|
|
87
|
+
// absent ⇒ the `/v1/web-search` route 503s and container web search stays off.
|
|
88
|
+
webSearch: createWebSearchUpstreamFromEnv(env),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=gateways.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateways.js","sourceRoot":"","sources":["../src/gateways.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,aAAa,GACd,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAOL,8BAA8B,GAC/B,MAAM,qBAAqB,CAAA;AAE5B,qFAAqF;AACrF,sFAAsF;AACtF,mFAAmF;AACnF,EAAE;AACF,uFAAuF;AACvF,yFAAyF;AACzF,iFAAiF;AACjF,wFAAwF;AACxF,sFAAsF;AACtF,sFAAsF;AACtF,uFAAuF;AACvF,EAAE;AACF,sFAAsF;AACtF,wFAAwF;AACxF,iGAAiG;AAEjG;;;;GAIG;AACH,MAAM,mBAAmB;IACvB,OAAO;QACL,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9B,CAAC;CACF;AAED,4FAA4F;AAC5F,MAAM,6BAA6B;IACjC,gBAAgB;QACd,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAC/B,CAAC;CACF;AAED,6FAA6F;AAC7F,MAAM,yBAAyB;IAC7B,cAAc;QACZ,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAC/B,CAAC;IAED,eAAe;QACb,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAC/B,CAAC;CACF;AAED,sFAAsF;AACtF,iFAAiF;AACjF,MAAM,iBAAiB,GAA6D;IAClF,IAAI,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE;IAC7D,QAAQ,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,mBAAmB,EAAE;IACzE,QAAQ,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,mBAAmB,EAAE;IACzE,MAAM,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,iBAAiB,EAAE;IACnE,UAAU,EAAE,EAAE,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,qBAAqB,EAAE;IAC/E,OAAO,EAAE,EAAE,UAAU,EAAE,kBAAkB,EAAE;CAC5C,CAAA;AAED;;;;;;GAMG;AACH,MAAM,eAAe;IACU,GAAG;IAAhC,YAA6B,GAAsB;mBAAtB,GAAG;IAAsB,CAAC;IAEvD,uBAAuB,CAAC,QAAgB;QACtC,MAAM,KAAK,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAA;QACzC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QACvB,iFAAiF;QACjF,kFAAkF;QAClF,oFAAoF;QACpF,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,OAAO,CAAA;QAC3D,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IACrC,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAA;IACb,CAAC;CACF;AAED,wDAAwD;AACxD,MAAM,UAAU,kBAAkB,CAAC,GAAsB;IACvD,OAAO;QACL,QAAQ,EAAE,IAAI,mBAAmB,EAAE;QACnC,cAAc,EAAE,IAAI,6BAA6B,EAAE;QACnD,aAAa,EAAE,IAAI,yBAAyB,EAAE;QAC9C,WAAW,EAAE,IAAI,eAAe,CAAC,GAAG,CAAC;QACrC,8EAA8E;QAC9E,+EAA+E;QAC/E,SAAS,EAAE,8BAA8B,CAAC,GAAG,CAAC;KAC/C,CAAA;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { createApp, createServer, start, type CreateServerOptions } from './server.js';
|
|
2
|
+
export { buildNodeContainer, type NodeContainerOptions } from './container.js';
|
|
3
|
+
export { loadNodeConfig } from './config.js';
|
|
4
|
+
export { createNodeGateways } from './gateways.js';
|
|
5
|
+
export { createNodeModelProviderResolver } from './modelProvider.js';
|
|
6
|
+
export { registerAgentKind, registerAgentKinds, clearRegisteredAgentKinds, type AgentKindDefinition, } from '@cat-factory/agents';
|
|
7
|
+
export { registerPipeline, registerPipelines, clearRegisteredPipelines } from '@cat-factory/kernel';
|
|
8
|
+
export { createDbClient, type DbClient, type DrizzleDb } from './db/client.js';
|
|
9
|
+
export { migrate } from './db/migrate.js';
|
|
10
|
+
export { createDrizzleRepositories, type CoreRepositories } from './repositories/drizzle.js';
|
|
11
|
+
export { DrizzleGitHubInstallationRepository } from './repositories/containerExecution.js';
|
|
12
|
+
export * as schema from './db/schema.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACtF,OAAO,EAAE,kBAAkB,EAAE,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,+BAA+B,EAAE,MAAM,oBAAoB,CAAA;AAMpE,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,EACzB,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAA;AACnG,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACzC,OAAO,EAAE,yBAAyB,EAAE,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAC5F,OAAO,EAAE,mCAAmC,EAAE,MAAM,sCAAsC,CAAA;AAC1F,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// @cat-factory/node-server — the Node.js runtime facade. Serves the shared
|
|
2
|
+
// @cat-factory/server Hono app via @hono/node-server, wiring Node implementations of
|
|
3
|
+
// the runtime ports over a Drizzle/Postgres persistence layer (the single store used
|
|
4
|
+
// in dev, test and prod). `start()` boots an HTTP server; `createServer()` returns the
|
|
5
|
+
// app (for embedding/tests); `buildNodeContainer()` is the composition root.
|
|
6
|
+
export { createApp, createServer, start } from './server.js';
|
|
7
|
+
export { buildNodeContainer } from './container.js';
|
|
8
|
+
export { loadNodeConfig } from './config.js';
|
|
9
|
+
export { createNodeGateways } from './gateways.js';
|
|
10
|
+
export { createNodeModelProviderResolver } from './modelProvider.js';
|
|
11
|
+
// Installation-level extension points (mirroring the Worker facade): a deployment —
|
|
12
|
+
// typically a proprietary org package — registers custom agent kinds and predefined
|
|
13
|
+
// pipelines at startup (before `start()`), and the shared prompt catalog / workspace
|
|
14
|
+
// seeding pick them up. Bedrock-style model providers mix in via createNodeModelProvider.
|
|
15
|
+
export { registerAgentKind, registerAgentKinds, clearRegisteredAgentKinds, } from '@cat-factory/agents';
|
|
16
|
+
export { registerPipeline, registerPipelines, clearRegisteredPipelines } from '@cat-factory/kernel';
|
|
17
|
+
export { createDbClient } from './db/client.js';
|
|
18
|
+
export { migrate } from './db/migrate.js';
|
|
19
|
+
export { createDrizzleRepositories } from './repositories/drizzle.js';
|
|
20
|
+
export { DrizzleGitHubInstallationRepository } from './repositories/containerExecution.js';
|
|
21
|
+
export * as schema from './db/schema.js';
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,qFAAqF;AACrF,qFAAqF;AACrF,uFAAuF;AACvF,6EAA6E;AAC7E,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAA4B,MAAM,aAAa,CAAA;AACtF,OAAO,EAAE,kBAAkB,EAA6B,MAAM,gBAAgB,CAAA;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,+BAA+B,EAAE,MAAM,oBAAoB,CAAA;AAEpE,oFAAoF;AACpF,oFAAoF;AACpF,qFAAqF;AACrF,0FAA0F;AAC1F,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,GAE1B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAA;AACnG,OAAO,EAAE,cAAc,EAAiC,MAAM,gBAAgB,CAAA;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACzC,OAAO,EAAE,yBAAyB,EAAyB,MAAM,2BAA2B,CAAA;AAC5F,OAAO,EAAE,mCAAmC,EAAE,MAAM,sCAAsC,CAAA;AAC1F,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAA"}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { logger } from '@cat-factory/server';
|
|
2
|
+
import { start } from './server.js';
|
|
3
|
+
// Default entrypoint: `pnpm build` then `node dist/main.js`. Requires DATABASE_URL;
|
|
4
|
+
// set PORT to override the listen port.
|
|
5
|
+
start().catch((err) => {
|
|
6
|
+
logger.error({ err: err instanceof Error ? err.message : String(err) }, 'failed to start');
|
|
7
|
+
process.exit(1);
|
|
8
|
+
});
|
|
9
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,oFAAoF;AACpF,wCAAwC;AACxC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC7B,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,iBAAiB,CAAC,CAAA;IAC1F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ApiKeyService, LocalModelEndpointService } from '@cat-factory/integrations';
|
|
2
|
+
import type { ModelProviderResolver } from '@cat-factory/kernel';
|
|
3
|
+
/** The base URL for a direct provider: env override (e.g. QWEN_BASE_URL), else default. */
|
|
4
|
+
export declare function baseUrlForNode(provider: string, env: NodeJS.ProcessEnv): string | undefined;
|
|
5
|
+
export declare function createNodeModelProviderResolver(env: NodeJS.ProcessEnv, apiKeys: ApiKeyService | undefined, localModelEndpoints?: LocalModelEndpointService): ModelProviderResolver;
|
|
6
|
+
//# sourceMappingURL=modelProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelProvider.d.ts","sourceRoot":"","sources":["../src/modelProvider.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAA;AACzF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAuBhE,2FAA2F;AAC3F,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,GAAG,SAAS,CAG3F;AAED,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,OAAO,EAAE,aAAa,GAAG,SAAS,EAClC,mBAAmB,CAAC,EAAE,yBAAyB,GAC9C,qBAAqB,CAqDvB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {} from '@cat-factory/agents';
|
|
2
|
+
import { DEEPSEEK_BASE_URL, MOONSHOT_BASE_URL, OPENAI_BASE_URL, OPENROUTER_BASE_URL, QWEN_BASE_URL, } from '@cat-factory/agents';
|
|
3
|
+
import { bedrockRegistry } from '@cat-factory/provider-bedrock';
|
|
4
|
+
import { cloudflareRestRegistry } from '@cat-factory/provider-cloudflare';
|
|
5
|
+
import { createLangfuseSink } from '@cat-factory/observability-langfuse';
|
|
6
|
+
import { createScopedModelProviderResolver } from '@cat-factory/server';
|
|
7
|
+
// The Node deployment's ModelProvider RESOLVER: builds a per-scope provider from the
|
|
8
|
+
// DB-backed API-key pool (account/workspace/user), plus opt-in registries that need no
|
|
9
|
+
// per-scope key — AWS Bedrock (when AWS creds/region are set) and Cloudflare Workers AI
|
|
10
|
+
// over REST (when CLOUDFLARE_ACCOUNT_ID + CLOUDFLARE_API_TOKEN are set). There is no
|
|
11
|
+
// Workers AI binding on Node, so `workers-ai` is served via the Cloudflare REST flavour.
|
|
12
|
+
// Inline calls are wrapped for Langfuse exactly like the proxied path when configured.
|
|
13
|
+
const NODE_BASE_URLS = {
|
|
14
|
+
openai: OPENAI_BASE_URL,
|
|
15
|
+
qwen: QWEN_BASE_URL,
|
|
16
|
+
deepseek: DEEPSEEK_BASE_URL,
|
|
17
|
+
moonshot: MOONSHOT_BASE_URL,
|
|
18
|
+
openrouter: OPENROUTER_BASE_URL,
|
|
19
|
+
// `litellm` has no default: its base URL is the operator's own gateway, supplied via
|
|
20
|
+
// LITELLM_BASE_URL (honored automatically by baseUrlForNode's `${PROVIDER}_BASE_URL`).
|
|
21
|
+
};
|
|
22
|
+
/** The base URL for a direct provider: env override (e.g. QWEN_BASE_URL), else default. */
|
|
23
|
+
export function baseUrlForNode(provider, env) {
|
|
24
|
+
// `||` not `??`: a set-but-blank override must fall back to the default.
|
|
25
|
+
return env[`${provider.toUpperCase()}_BASE_URL`] || NODE_BASE_URLS[provider];
|
|
26
|
+
}
|
|
27
|
+
export function createNodeModelProviderResolver(env, apiKeys, localModelEndpoints) {
|
|
28
|
+
const extraRegistries = [];
|
|
29
|
+
// Opt-in Cloudflare Workers AI over REST.
|
|
30
|
+
if (env.CLOUDFLARE_ACCOUNT_ID && env.CLOUDFLARE_API_TOKEN) {
|
|
31
|
+
extraRegistries.push(cloudflareRestRegistry({
|
|
32
|
+
accountId: env.CLOUDFLARE_ACCOUNT_ID,
|
|
33
|
+
apiToken: env.CLOUDFLARE_API_TOKEN,
|
|
34
|
+
gateway: env.CLOUDFLARE_AI_GATEWAY,
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
// Opt-in Bedrock: registered only when a region is configured.
|
|
38
|
+
if (env.BEDROCK_REGION) {
|
|
39
|
+
const supportedModels = env.BEDROCK_MODELS?.split(',')
|
|
40
|
+
.map((m) => m.trim())
|
|
41
|
+
.filter(Boolean);
|
|
42
|
+
extraRegistries.push(bedrockRegistry({
|
|
43
|
+
region: env.BEDROCK_REGION,
|
|
44
|
+
accessKeyId: env.AWS_ACCESS_KEY_ID,
|
|
45
|
+
secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
|
|
46
|
+
sessionToken: env.AWS_SESSION_TOKEN,
|
|
47
|
+
supportedModels: supportedModels?.length ? supportedModels : undefined,
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
const instrument = env.LANGFUSE_ENABLED?.trim() === 'true' &&
|
|
51
|
+
env.LANGFUSE_PUBLIC_KEY?.trim() &&
|
|
52
|
+
env.LANGFUSE_SECRET_KEY?.trim()
|
|
53
|
+
? {
|
|
54
|
+
traceSink: createLangfuseSink({
|
|
55
|
+
publicKey: env.LANGFUSE_PUBLIC_KEY.trim(),
|
|
56
|
+
secretKey: env.LANGFUSE_SECRET_KEY.trim(),
|
|
57
|
+
baseUrl: env.LANGFUSE_BASE_URL?.trim() || undefined,
|
|
58
|
+
}),
|
|
59
|
+
recordPrompts: env.LLM_RECORD_PROMPTS?.trim() !== 'false',
|
|
60
|
+
}
|
|
61
|
+
: undefined;
|
|
62
|
+
return createScopedModelProviderResolver({
|
|
63
|
+
apiKeys,
|
|
64
|
+
baseUrlFor: (provider) => baseUrlForNode(provider, env),
|
|
65
|
+
extraRegistries,
|
|
66
|
+
localEndpointsFor: localModelEndpoints
|
|
67
|
+
? (userId) => localModelEndpoints.listResolved(userId)
|
|
68
|
+
: undefined,
|
|
69
|
+
instrument,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=modelProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelProvider.js","sourceRoot":"","sources":["../src/modelProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'node:http';
|
|
2
|
+
import type { Duplex } from 'node:stream';
|
|
3
|
+
import type { Block, BootstrapJob, ConsensusSession, ClarityReview, ExecutionInstance, LlmCallActivity, Notification, RequirementReview } from '@cat-factory/contracts';
|
|
4
|
+
import type { ExecutionEventPublisher } from '@cat-factory/kernel';
|
|
5
|
+
import { type AuthConfig } from '@cat-factory/server';
|
|
6
|
+
import { WebSocket } from 'ws';
|
|
7
|
+
/** The minimal logger shape this module needs (a pino logger satisfies it). */
|
|
8
|
+
interface RealtimeLogger {
|
|
9
|
+
info(obj: object, msg?: string): void;
|
|
10
|
+
warn(obj: object, msg?: string): void;
|
|
11
|
+
}
|
|
12
|
+
/** The subset of a Node HTTP/HTTP2 server we attach the upgrade listener to. */
|
|
13
|
+
interface UpgradableServer {
|
|
14
|
+
on(event: 'upgrade', listener: (request: IncomingMessage, socket: Duplex, head: Buffer) => void): void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Per-workspace subscriber registry. Every browser subscribed to a workspace's stream
|
|
18
|
+
* converges here, so a published event fans out to all of them. In-memory and
|
|
19
|
+
* single-process: the Node service runs as one process (unlike the Worker's globally
|
|
20
|
+
* addressed Durable Object), which is the right model for the self-hosted / local
|
|
21
|
+
* deployments this facade targets. A multi-replica deployment would need a shared bus
|
|
22
|
+
* (Postgres LISTEN/NOTIFY) in front of this — a follow-up, not needed for local mode.
|
|
23
|
+
*/
|
|
24
|
+
export declare class NodeRealtimeHub {
|
|
25
|
+
private readonly rooms;
|
|
26
|
+
/** Add a socket to a workspace's room; it is reaped on close/error. */
|
|
27
|
+
subscribe(workspaceId: string, socket: WebSocket): void;
|
|
28
|
+
private unsubscribe;
|
|
29
|
+
/** Fan a pre-serialised JSON event out to every socket on a workspace's stream. */
|
|
30
|
+
broadcast(workspaceId: string, payload: string): void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Pushes execution/board events to the {@link NodeRealtimeHub}, which fans them out to
|
|
34
|
+
* subscribed browsers. The event shapes are IDENTICAL to the Worker's
|
|
35
|
+
* `DurableObjectEventPublisher`, so the SPA's stream handling is runtime-agnostic.
|
|
36
|
+
* Best-effort: a publish failure must never break a state transition (the persisted
|
|
37
|
+
* row is the source of truth and clients reconcile on reconnect), so each publish
|
|
38
|
+
* swallows its own errors.
|
|
39
|
+
*/
|
|
40
|
+
export declare class NodeEventPublisher implements ExecutionEventPublisher {
|
|
41
|
+
private readonly hub;
|
|
42
|
+
constructor(hub: NodeRealtimeHub);
|
|
43
|
+
executionChanged(workspaceId: string, instance: ExecutionInstance, block?: Block | null): Promise<void>;
|
|
44
|
+
boardChanged(workspaceId: string, reason: string, _blockId?: string | null): Promise<void>;
|
|
45
|
+
bootstrapChanged(workspaceId: string, job: BootstrapJob, block?: Block | null): Promise<void>;
|
|
46
|
+
notificationChanged(workspaceId: string, notification: Notification): Promise<void>;
|
|
47
|
+
llmCallObserved(workspaceId: string, activity: LlmCallActivity): Promise<void>;
|
|
48
|
+
requirementReviewChanged(workspaceId: string, review: RequirementReview): Promise<void>;
|
|
49
|
+
consensusSessionChanged(workspaceId: string, session: ConsensusSession): Promise<void>;
|
|
50
|
+
clarityReviewChanged(workspaceId: string, review: ClarityReview): Promise<void>;
|
|
51
|
+
private publish;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Attach the real-time WebSocket transport to a running Node HTTP server: accept
|
|
55
|
+
* `GET /workspaces/:ws/events` upgrades (authorising the `?ticket=` exactly like the
|
|
56
|
+
* shared EventsController), register each socket into the {@link NodeRealtimeHub}, and
|
|
57
|
+
* run a heartbeat that terminates dead connections. Returns a stop function that clears
|
|
58
|
+
* the heartbeat and closes the WS server (call it on graceful shutdown).
|
|
59
|
+
*/
|
|
60
|
+
export declare function attachRealtime(server: UpgradableServer, hub: NodeRealtimeHub, auth: AuthConfig, log: RealtimeLogger): () => void;
|
|
61
|
+
export {};
|
|
62
|
+
//# sourceMappingURL=realtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../src/realtime.ts"],"names":[],"mappings":"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"}
|