@cat-factory/node-server 0.107.11 → 0.107.13

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.
@@ -0,0 +1,127 @@
1
+ import { AiAgentExecutor, inlineWebSearchOptionsFromEnv, vendorConcurrencyLimiterFromEnv, } from '@cat-factory/agents';
2
+ import { wrapResolverWithLimiter } from '@cat-factory/server';
3
+ import { buildTraceSink } from './container-executor-deps.js';
4
+ import { createNodeModelProviderResolver } from './modelProvider.js';
5
+ import { buildNodeApiKeyService, buildNodeLocalModelEndpointService, buildNodeOpenRouterCatalogService, buildNodePersonalSubscriptionService, buildNodePublicApiKeyService, buildNodeSubscriptionService, buildNodeUserSecretService, } from './wireCredentialServices.js';
6
+ /**
7
+ * The Node model-provider RESOLVER (instrumented when Langfuse is on), shared per
8
+ * `(env, db)`. Builds a per-scope provider from the DB-backed API-key pool plus opt-in
9
+ * Cloudflare-REST / Bedrock registries. Mirrors the Worker's buildModelProviderResolver.
10
+ */
11
+ const modelResolverCache = new WeakMap();
12
+ function buildModelProviderResolver(env, db, apiKeys, localModelEndpoints,
13
+ // The shared inline instrument (one trace sink for the proxied path, the core AND the
14
+ // inline calls) so the OTel SDK exporter isn't rebuilt per wiring site.
15
+ instrument) {
16
+ // The cache keys on the db handle (one resolver per Drizzle client). Mothership mode has no
17
+ // db, so skip the cache entirely (WeakMap keys must be objects) and build a fresh resolver —
18
+ // a mothership node builds one container, so there is nothing to share it with anyway.
19
+ if (!db)
20
+ return createNodeModelProviderResolver(env, apiKeys, localModelEndpoints, instrument);
21
+ const cached = modelResolverCache.get(db);
22
+ if (cached)
23
+ return cached;
24
+ const resolver = createNodeModelProviderResolver(env, apiKeys, localModelEndpoints, instrument);
25
+ modelResolverCache.set(db, resolver);
26
+ return resolver;
27
+ }
28
+ /**
29
+ * The credential/token stores + the model-provisioning stack of the Node composition root,
30
+ * lifted out of `buildNodeContainer` so that root stays within the file-size budget (the same
31
+ * reason `container-executor-deps.ts` exists). Builds the direct-provider API-key pool, the
32
+ * public-API + local-model-endpoint + user-secret + OpenRouter-catalog + subscription +
33
+ * personal-subscription stores, then the trace sink, the (optionally facade-wrapped +
34
+ * vendor-limited) model-provider resolver, and the inline agent executor.
35
+ */
36
+ export function buildNodeModelDeps(input) {
37
+ const { env, config, db, workspaceRepository, idGenerator, clock, agentKindRegistry, userSecretKindRegistry, resolveWorkspaceModelDefault, providerApiKeyRepository, localModelEndpointRepository, providerSubscriptionTokenRepository, personalSubscriptionRepository, subscriptionActivationRepository, wrapModelProviderResolver, cloudflareModelsEnabled: cloudflareModelsEnabledOverride, caches, } = input;
38
+ // The direct-provider API-key pool + the per-scope model-provider resolver, shared by
39
+ // the inline executor, the inline modules (planner/reviewer/fragment selector), the
40
+ // API-key controller, and the LLM proxy key lease.
41
+ const apiKeys = buildNodeApiKeyService(env, db, workspaceRepository, idGenerator, clock, providerApiKeyRepository);
42
+ // The inbound public-API key store — drives the public `/api/v1` surface's authentication.
43
+ const publicApiKeys = buildNodePublicApiKeyService(env, db, idGenerator, clock);
44
+ // The per-user locally-run model endpoints store (Ollama / LM Studio / …), shared by
45
+ // the local-runner controller, the per-user model catalog, the inline model provider,
46
+ // and the LLM proxy.
47
+ const localModelEndpoints = buildNodeLocalModelEndpointService(env, db, clock, localModelEndpointRepository);
48
+ // The per-user generic secret store (a GitHub PAT today), shared by the user-secret
49
+ // controller and the run-initiator PAT resolver below.
50
+ const userSecrets = buildNodeUserSecretService(env, db, clock, userSecretKindRegistry, caches?.viewerRepos);
51
+ // Resolve the run initiator's stored GitHub PAT (when set) — preferred over the
52
+ // App/env token by the container push-token mint + the engine GitHub client.
53
+ const resolveUserGitHubToken = userSecrets
54
+ ? (userId) => userSecrets.resolve(userId, 'github_pat')
55
+ : undefined;
56
+ // The per-workspace OpenRouter dynamic-catalog store — shared by the catalog controller,
57
+ // the per-workspace model catalog's dynamic OpenRouter entries, and the spend overlay.
58
+ const openRouterCatalog = buildNodeOpenRouterCatalogService(env, db, clock, apiKeys, config.spend.currency);
59
+ // The subscription-token pool (Claude Code / Codex credentials), shared by the
60
+ // container executor (lease + usage feedback) and the vendor-credential controller.
61
+ // Built HERE (before the model-provider wrap below) so its lease closures can be handed
62
+ // to `wrapModelProviderResolver` — the local facade's inline-harness wrap serves an
63
+ // inline subscription ref through a warm container on a LEASED credential, so it needs the
64
+ // same lease seams the container executor uses (built once, shared by both).
65
+ const subscriptions = buildNodeSubscriptionService(env, db, workspaceRepository, idGenerator, clock, providerSubscriptionTokenRepository);
66
+ // The per-user individual-usage subscription store (Claude), shared by the
67
+ // container executor's personal lease, the personal-subscription controller, and the
68
+ // inline-harness wrap's per-run personal lease.
69
+ const personalSubscriptions = buildNodePersonalSubscriptionService(env, db, idGenerator, clock, personalSubscriptionRepository, subscriptionActivationRepository);
70
+ // The ONE external trace sink for this container (memoised per config): the core, the
71
+ // container executor AND the inline model-provider instrumentation all share this single
72
+ // instance, so the OTel SDK exporter's batch processors/timers exist exactly once (and its
73
+ // shutdown is wired below). Its `recordPrompts` matches the proxied path's gating.
74
+ const traceSink = buildTraceSink(config);
75
+ const baseModelProviderResolver = buildModelProviderResolver(env, db, apiKeys, localModelEndpoints, traceSink ? { traceSink, recordPrompts: config.observability.recordPrompts } : undefined);
76
+ const wrappedModelProviderResolver = wrapModelProviderResolver
77
+ ? wrapModelProviderResolver(baseModelProviderResolver, {
78
+ ...(personalSubscriptions
79
+ ? {
80
+ leasePersonalSubscriptionToken: (executionId, userId, vendor) => personalSubscriptions.leaseForRun(executionId, userId, vendor),
81
+ }
82
+ : {}),
83
+ ...(subscriptions
84
+ ? {
85
+ leaseSubscriptionToken: (workspaceId, vendor) => subscriptions.leaseToken(workspaceId, vendor),
86
+ }
87
+ : {}),
88
+ })
89
+ : baseModelProviderResolver;
90
+ // Cap concurrent inline calls to a subscription vendor, OUTERMOST so it sits outside the
91
+ // local facade's subscription-inline harness wrap above (and therefore sees the un-degraded
92
+ // subscription ref). One limiter per container = per process for a stock node, per tenant in
93
+ // mothership mode; a pass-through when nothing is capped. Symmetric with the Worker's wrap in
94
+ // `buildModelProviderResolver` (see "Keep the runtimes symmetric").
95
+ const modelProviderResolver = wrapResolverWithLimiter(wrappedModelProviderResolver, vendorConcurrencyLimiterFromEnv((key) => env[key]));
96
+ // Cloudflare Workers AI is opt-in on Node: enabled when the REST creds are present.
97
+ const cloudflareModelsEnabled = cloudflareModelsEnabledOverride ?? !!(env.CLOUDFLARE_ACCOUNT_ID && env.CLOUDFLARE_API_TOKEN);
98
+ const inline = new AiAgentExecutor({
99
+ modelProviderResolver,
100
+ agentRouting: config.agents.routing,
101
+ resolveBlockModel: config.agents.resolveBlockModel,
102
+ resolveWorkspaceModelDefault,
103
+ // In local mode this keeps an ambient-eligible subscription harness ref so the inline
104
+ // design/research kinds run on the developer's Claude Code / Codex CLI; undefined on
105
+ // stock Node (no inline harness), where such a ref degrades to the routing default.
106
+ ...(config.agents.inlineHarnessRef ? { runsInline: config.agents.inlineHarnessRef } : {}),
107
+ // Opt-in provider web search for the inline design/research kinds (no-op unless
108
+ // INLINE_WEB_SEARCH_ENABLED and an Anthropic/OpenAI model).
109
+ webSearch: inlineWebSearchOptionsFromEnv(env),
110
+ agentKindRegistry,
111
+ });
112
+ return {
113
+ apiKeys,
114
+ publicApiKeys,
115
+ localModelEndpoints,
116
+ userSecrets,
117
+ resolveUserGitHubToken,
118
+ openRouterCatalog,
119
+ subscriptions,
120
+ personalSubscriptions,
121
+ traceSink,
122
+ modelProviderResolver,
123
+ cloudflareModelsEnabled,
124
+ inline,
125
+ };
126
+ }
127
+ //# sourceMappingURL=container-model-deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-model-deps.js","sourceRoot":"","sources":["../src/container-model-deps.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { type AgentKindRegistry } from '@cat-factory/agents';
2
+ import { type AgentExecutor, type ModelProviderResolver, type NotificationChannel } from '@cat-factory/kernel';
3
+ import type { CoreDependencies } from '@cat-factory/orchestration';
4
+ import { type AppConfig, type CompositeAgentExecutor, FanOutEventPublisher } from '@cat-factory/server';
5
+ import type { DrizzleDb } from './db/client.js';
6
+ import { type LocalEventSink } from './realtime.js';
7
+ import type { createDrizzleRepositories } from './repositories/drizzle.js';
8
+ type NodeRepositories = ReturnType<typeof createDrizzleRepositories>;
9
+ /** Inputs {@link buildNodeRealtimeDeps} needs from the composition root. */
10
+ export interface NodeRealtimeDepsInput {
11
+ env: NodeJS.ProcessEnv;
12
+ config: AppConfig;
13
+ repos: NodeRepositories;
14
+ sourced: <T>(name: string, build: (d: DrizzleDb) => T) => T;
15
+ realtimeSink?: LocalEventSink;
16
+ standardAgentExecutor: CompositeAgentExecutor;
17
+ modelProviderResolver: ModelProviderResolver;
18
+ resolveWorkspaceModelDefault: (workspaceId: string, agentKind: string, modelPresetId?: string) => Promise<string | undefined>;
19
+ agentKindRegistry: AgentKindRegistry;
20
+ }
21
+ /**
22
+ * The real-time event-publisher + notification-channel + optional consensus wrap of the Node
23
+ * composition root, lifted out of `buildNodeContainer` so that root stays within the file-size
24
+ * budget. Builds the Slack deps, the fan-out event publisher (when a realtime hub is wired),
25
+ * the (optionally consensus-wrapped) agent executor, and the composite notification channel.
26
+ */
27
+ export declare function buildNodeRealtimeDeps(input: NodeRealtimeDepsInput): {
28
+ slackDeps: Partial<CoreDependencies>;
29
+ executionEventPublisher: FanOutEventPublisher | undefined;
30
+ agentExecutor: AgentExecutor;
31
+ notificationChannel: NotificationChannel | undefined;
32
+ };
33
+ export {};
34
+ //# sourceMappingURL=container-realtime-deps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-realtime-deps.d.ts","sourceRoot":"","sources":["../src/container-realtime-deps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAG5D,OAAO,EACL,KAAK,aAAa,EAElB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACzB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAClE,OAAO,EACL,KAAK,SAAS,EACd,KAAK,sBAAsB,EAC3B,oBAAoB,EAIrB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,KAAK,cAAc,EAAsB,MAAM,eAAe,CAAA;AACvE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAA;AAO1E,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAsEpE,4EAA4E;AAC5E,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;IACtB,MAAM,EAAE,SAAS,CAAA;IACjB,KAAK,EAAE,gBAAgB,CAAA;IACvB,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,CAAC,KAAK,CAAC,CAAA;IAC3D,YAAY,CAAC,EAAE,cAAc,CAAA;IAC7B,qBAAqB,EAAE,sBAAsB,CAAA;IAC7C,qBAAqB,EAAE,qBAAqB,CAAA;IAC5C,4BAA4B,EAAE,CAC5B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,aAAa,CAAC,EAAE,MAAM,KACnB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IAChC,iBAAiB,EAAE,iBAAiB,CAAA;CACrC;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,qBAAqB;;;;;EA2DjE"}
@@ -0,0 +1,112 @@
1
+ import {} from '@cat-factory/agents';
2
+ import { ConsensusAgentExecutor, registerConsensusTraits } from '@cat-factory/consensus';
3
+ import { SLACK_CIPHER_INFO, SlackNotificationChannel } from '@cat-factory/integrations';
4
+ import { CompositeNotificationChannel, } from '@cat-factory/kernel';
5
+ import { FanOutEventPublisher, InAppNotificationChannel, WebCryptoSecretCipher, logger, } from '@cat-factory/server';
6
+ import { NodeEventPublisher } from './realtime.js';
7
+ import { DrizzleSlackConnectionRepository, DrizzleSlackMemberMappingRepository, DrizzleSlackSettingsRepository, } from './repositories/slack.js';
8
+ /** Truthy env flag (`true`/`1`/`yes`). */
9
+ function isTruthy(value) {
10
+ return value === 'true' || value === '1' || value === 'yes';
11
+ }
12
+ /**
13
+ * Wire the Slack integration when enabled: the notification *channel* (an extra
14
+ * delivery transport composed onto the notification mechanism — Node has no in-app
15
+ * channel, so this is its only one) plus the management repositories (per-account
16
+ * connect + per-workspace routing + member map) and the bot-token cipher. The
17
+ * per-account bot token is sealed with the shared ENCRYPTION_KEY under a
18
+ * slack-scoped HKDF info, mirroring the Worker. OAuth credentials are optional.
19
+ */
20
+ function selectNodeSlackDeps(config, repos,
21
+ // The remote-source seam (mothership mode): `sourced('name', build)` returns the remote registry
22
+ // entry when there's no `db`, else builds the Drizzle repo. Routing the three Slack repos through
23
+ // it makes the connect / route / member-map management surface functional in mothership mode —
24
+ // AND keeps the `SlackNotificationChannel` (which captures these repos directly) reading the SAME
25
+ // remote-backed repos, so it can't drift to the broken db-less Drizzle instances. The bot token
26
+ // rides a SEALED `tokenCipher` (sealed/decrypted under the LOCAL key), so the sealed blob — never
27
+ // plaintext — crosses the machine API; the settings + member-mapping rows carry no secrets. The
28
+ // RPC allow-list gates each method by its account/workspace scope. (Mothership-SIDE delivery for a
29
+ // hosted teammate's notification — the mothership decrypting a laptop-sealed token — is the later
30
+ // secrets-delegation slice; local delivery, where the run's own node holds the key, works.)
31
+ sourced) {
32
+ if (!config.slack.enabled || !config.slack.encryptionKey)
33
+ return {};
34
+ const secretCipher = new WebCryptoSecretCipher({
35
+ masterKeyBase64: config.slack.encryptionKey,
36
+ info: SLACK_CIPHER_INFO,
37
+ });
38
+ const slackConnectionRepository = sourced('slackConnectionRepository', (d) => new DrizzleSlackConnectionRepository(d));
39
+ const slackSettingsRepository = sourced('slackSettingsRepository', (d) => new DrizzleSlackSettingsRepository(d));
40
+ const slackMemberMappingRepository = sourced('slackMemberMappingRepository', (d) => new DrizzleSlackMemberMappingRepository(d));
41
+ return {
42
+ notificationChannel: new SlackNotificationChannel({
43
+ workspaceRepository: repos.workspaceRepository,
44
+ slackConnectionRepository,
45
+ slackSettingsRepository,
46
+ slackMemberMappingRepository,
47
+ blockRepository: repos.blockRepository,
48
+ secretCipher,
49
+ // Best-effort delivery still surfaces failures (revoked token, missing channel
50
+ // invite) through the structured logger so a broken route is diagnosable.
51
+ onError: (error, ctx) => logger.warn({ err: error instanceof Error ? error.message : String(error), ...ctx }, 'slack notification delivery failed'),
52
+ }),
53
+ slackConnectionRepository,
54
+ slackSettingsRepository,
55
+ slackMemberMappingRepository,
56
+ slackSecretCipher: secretCipher,
57
+ };
58
+ }
59
+ /**
60
+ * The real-time event-publisher + notification-channel + optional consensus wrap of the Node
61
+ * composition root, lifted out of `buildNodeContainer` so that root stays within the file-size
62
+ * budget. Builds the Slack deps, the fan-out event publisher (when a realtime hub is wired),
63
+ * the (optionally consensus-wrapped) agent executor, and the composite notification channel.
64
+ */
65
+ export function buildNodeRealtimeDeps(input) {
66
+ const { env, config, repos, sourced, realtimeSink, standardAgentExecutor, modelProviderResolver, resolveWorkspaceModelDefault, agentKindRegistry, } = input;
67
+ // Real-time push + notification delivery. When a realtime hub is wired (start()), the
68
+ // engine pushes execution/board/notification events to subscribed browsers via the
69
+ // NodeEventPublisher, decorated with FanOutEventPublisher so a shared service's live
70
+ // events reach EVERY board that mounts it (parity with the Worker's selectEventPublisher).
71
+ // The in-app push is also a notification channel, composed alongside Slack (when
72
+ // enabled) so a raised notification both lands in the inbox live AND fans to Slack.
73
+ const slackDeps = selectNodeSlackDeps(config, repos, sourced);
74
+ const executionEventPublisher = realtimeSink
75
+ ? new FanOutEventPublisher(new NodeEventPublisher(realtimeSink), {
76
+ workspaceMountRepository: repos.workspaceMountRepository,
77
+ })
78
+ : undefined;
79
+ // Optionally wrap the executor with the consensus mechanism (CONSENSUS_ENABLED). Off ⇒
80
+ // the standard composite, unchanged. Registers the capability traits + routes
81
+ // consensus-enabled steps through a multi-model process, persisting + pushing the
82
+ // transcript (same hub as run/board events).
83
+ const agentExecutor = isTruthy(env.CONSENSUS_ENABLED)
84
+ ? (registerConsensusTraits(agentKindRegistry),
85
+ new ConsensusAgentExecutor({
86
+ standard: standardAgentExecutor,
87
+ modelProviderResolver,
88
+ agentRouting: config.agents.routing,
89
+ resolveBlockModel: config.agents.resolveBlockModel,
90
+ resolveWorkspaceModelDefault,
91
+ // Consensus runs its participants INLINE, so in local mode keep an ambient-eligible
92
+ // subscription harness ref (served via the CLI) instead of degrading it; undefined on
93
+ // stock Node/Worker, where such a ref degrades to the routing default as before.
94
+ ...(config.agents.inlineHarnessRef ? { runsInline: config.agents.inlineHarnessRef } : {}),
95
+ sessionRepository: repos.consensusSessionRepository,
96
+ ...(executionEventPublisher ? { eventPublisher: executionEventPublisher } : {}),
97
+ agentKindRegistry,
98
+ }))
99
+ : standardAgentExecutor;
100
+ const notificationChannels = [];
101
+ if (executionEventPublisher)
102
+ notificationChannels.push(new InAppNotificationChannel(executionEventPublisher));
103
+ if (slackDeps.notificationChannel)
104
+ notificationChannels.push(slackDeps.notificationChannel);
105
+ const notificationChannel = notificationChannels.length === 0
106
+ ? undefined
107
+ : notificationChannels.length === 1
108
+ ? notificationChannels[0]
109
+ : new CompositeNotificationChannel(notificationChannels);
110
+ return { slackDeps, executionEventPublisher, agentExecutor, notificationChannel };
111
+ }
112
+ //# sourceMappingURL=container-realtime-deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-realtime-deps.js","sourceRoot":"","sources":["../src/container-realtime-deps.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { RegistrySubscriptionQuotaProvider, TestSecretsService } from '@cat-factory/integrations';
2
+ import type { AppCaches, Clock, IdGenerator, WebSearchAvailability } from '@cat-factory/kernel';
3
+ import { AgentContextObservabilityService, SearchQueryObservabilityService } from '@cat-factory/orchestration';
4
+ import { type AppConfig, WebCryptoSecretCipher } from '@cat-factory/server';
5
+ import type { createDrizzleRepositories } from './repositories/drizzle.js';
6
+ type NodeRepositories = ReturnType<typeof createDrizzleRepositories>;
7
+ /** Inputs {@link buildNodeRunServices} needs from the composition root. */
8
+ export interface NodeRunServicesInput {
9
+ env: NodeJS.ProcessEnv;
10
+ config: AppConfig;
11
+ repos: NodeRepositories;
12
+ idGenerator: IdGenerator;
13
+ clock: Clock;
14
+ caches?: AppCaches;
15
+ }
16
+ /**
17
+ * The per-run agent-observability + web-search + sealed-secret services of the Node
18
+ * composition root, lifted out of `buildNodeContainer` so that root stays within the
19
+ * file-size budget. Builds the agent-context / search-query / harness-call telemetry sinks,
20
+ * the deployment-wide web-search upstream + availability resolver, the package-registry +
21
+ * test-secret dispatch resolvers, and the modeled subscription-quota provider.
22
+ */
23
+ export declare function buildNodeRunServices(input: NodeRunServicesInput): {
24
+ agentContextObservability: AgentContextObservabilityService;
25
+ searchQueryObservability: SearchQueryObservabilityService;
26
+ recordHarnessCalls: (input: import("@cat-factory/orchestration").HarnessCallsRecordInput) => Promise<void>;
27
+ defaultWebSearchUpstream: import("@cat-factory/server").WebSearchUpstream | undefined;
28
+ resolveWebSearchAvailability: ((workspaceId: string) => Promise<WebSearchAvailability>) | undefined;
29
+ packageRegistrySecretCipher: WebCryptoSecretCipher | undefined;
30
+ resolvePackageRegistries: ((workspaceId: string) => Promise<import("@cat-factory/orchestration").DispatchPackageRegistry[]>) | undefined;
31
+ testSecretsService: TestSecretsService | undefined;
32
+ resolveTestSecrets: ((workspaceId: string, blockId: string) => Promise<{
33
+ key: string;
34
+ description: string;
35
+ value: string;
36
+ }[]>) | undefined;
37
+ resolveTestSecretRefs: ((workspaceId: string, blockId: string) => Promise<{
38
+ key: string;
39
+ description: string;
40
+ }[]>) | undefined;
41
+ subscriptionQuotaProvider: RegistrySubscriptionQuotaProvider;
42
+ };
43
+ export {};
44
+ //# sourceMappingURL=container-run-services-deps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-run-services-deps.d.ts","sourceRoot":"","sources":["../src/container-run-services-deps.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,iCAAiC,EAEjC,kBAAkB,EAEnB,MAAM,2BAA2B,CAAA;AAClC,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAC/F,OAAO,EACL,gCAAgC,EAGhC,+BAA+B,EAGhC,MAAM,4BAA4B,CAAA;AACnC,OAAO,EACL,KAAK,SAAS,EACd,qBAAqB,EAGtB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAA;AAE1E,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAEpE,2EAA2E;AAC3E,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;IACtB,MAAM,EAAE,SAAS,CAAA;IACjB,KAAK,EAAE,gBAAgB,CAAA;IACvB,WAAW,EAAE,WAAW,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,CAAC,EAAE,SAAS,CAAA;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,oBAAoB;;;;;iDAkEpC,MAAM,KAAG,OAAO,CAAC,qBAAqB,CAAC;;6CA8B/C,MAAM;;uCAuBN,MAAM,WAAW,MAAM;;;;;0CAIvB,MAAM,WAAW,MAAM;;;;;EA0B1C"}
@@ -0,0 +1,148 @@
1
+ import { ACCOUNT_SETTINGS_CIPHER_INFO, AccountSettingsService, RegistrySubscriptionQuotaProvider, TEST_SECRETS_CIPHER_INFO, TestSecretsService, defaultSubscriptionQuotaRegistry, } from '@cat-factory/integrations';
2
+ import { AgentContextObservabilityService, LlmObservabilityService, PACKAGE_REGISTRY_CIPHER_INFO, SearchQueryObservabilityService, makeHarnessCallRecorder, resolvePackageRegistriesForDispatch, } from '@cat-factory/orchestration';
3
+ import { WebCryptoSecretCipher, createDefaultWebSearchUpstream, createWebSearchUpstream, } from '@cat-factory/server';
4
+ /**
5
+ * The per-run agent-observability + web-search + sealed-secret services of the Node
6
+ * composition root, lifted out of `buildNodeContainer` so that root stays within the
7
+ * file-size budget. Builds the agent-context / search-query / harness-call telemetry sinks,
8
+ * the deployment-wide web-search upstream + availability resolver, the package-registry +
9
+ * test-secret dispatch resolvers, and the modeled subscription-quota provider.
10
+ */
11
+ export function buildNodeRunServices(input) {
12
+ const { env, config, repos, idGenerator, clock, caches } = input;
13
+ // Agent-context observability sink: records the complete, redacted context provided
14
+ // to each container agent (composed prompts + folded-in fragments + injected files).
15
+ // Gated by the deployment prompt-recording switch + the workspace storeAgentContext
16
+ // setting. Wired into the executor (write) AND createCore (read). The telemetry rows
17
+ // live in the `telemetry` Postgres schema (see schema.ts).
18
+ const agentContextObservability = new AgentContextObservabilityService({
19
+ agentContextSnapshotRepository: repos.agentContextSnapshotRepository,
20
+ workspaceSettingsRepository: repos.workspaceSettingsRepository,
21
+ idGenerator,
22
+ clock,
23
+ recordPrompts: config.observability.recordPrompts,
24
+ });
25
+ // Agent-search-query observability sink: records each web search a container agent
26
+ // performed through the search proxy. Same double gate + retention window as the
27
+ // agent-context sink. Wired into the search proxy (write, via the container) AND
28
+ // createCore (read). Telemetry rows live in the `telemetry` Postgres schema.
29
+ const searchQueryObservability = new SearchQueryObservabilityService({
30
+ agentSearchQueryRepository: repos.agentSearchQueryRepository,
31
+ workspaceSettingsRepository: repos.workspaceSettingsRepository,
32
+ idGenerator,
33
+ clock,
34
+ recordPrompts: config.observability.recordPrompts,
35
+ });
36
+ // Record a subscription harness's (Claude Code / Codex) per-call telemetry into the
37
+ // SAME `llm_call_metrics` store the LLM proxy writes for Pi — those harnesses bypass
38
+ // the proxy, so the executor lifts the metrics off the CLI stream and feeds them here.
39
+ const recordHarnessCalls = makeHarnessCallRecorder(new LlmObservabilityService({
40
+ llmCallMetricRepository: repos.llmCallMetricRepository,
41
+ idGenerator,
42
+ clock,
43
+ recordPrompts: config.observability.recordPrompts,
44
+ }));
45
+ // A deployment-wide trusted web-search upstream, built from this facade's own `WEB_SEARCH_*`
46
+ // env, used by the search proxy as a fallback when a run's account has no web-search config
47
+ // (local mode defaults `WEB_SEARCH_SEARXNG_URL` to its self-hosted SearXNG). Distinct from the
48
+ // harness's own `SEARXNG_URL`/`BRAVE_SEARCH_API_KEY` runner-pool autodetect — those are for
49
+ // self-hosted pool containers; these keys stay on the backend. Surfaced on the ServerContainer
50
+ // below and read by `WebSearchProxyController`.
51
+ const defaultWebSearchUpstream = createDefaultWebSearchUpstream({
52
+ braveApiKey: env.WEB_SEARCH_BRAVE_API_KEY,
53
+ searxngUrl: env.WEB_SEARCH_SEARXNG_URL,
54
+ searxngApiKey: env.WEB_SEARCH_SEARXNG_API_KEY,
55
+ });
56
+ // Web-search keys live per-account; advertise Pi's `web_search` tool to a run only when a
57
+ // usable upstream exists — either the deployment default above (⇒ always on) or the run's
58
+ // account has its own keys (else the tool would just fail/return nothing). The per-account
59
+ // check runs off a dedicated account-settings instance (short-TTL cache).
60
+ const webSearchAccountKey = env.ENCRYPTION_KEY?.trim();
61
+ const webSearchAccountSettings = webSearchAccountKey
62
+ ? new AccountSettingsService({
63
+ accountSettingsRepository: repos.accountSettingsRepository,
64
+ secretCipher: new WebCryptoSecretCipher({
65
+ masterKeyBase64: webSearchAccountKey,
66
+ info: ACCOUNT_SETTINGS_CIPHER_INFO,
67
+ }),
68
+ clock,
69
+ ...(caches ? { settingsCache: caches.accountSettings } : {}),
70
+ })
71
+ : undefined;
72
+ const resolveWebSearchAvailability = defaultWebSearchUpstream || webSearchAccountSettings
73
+ ? async (workspaceId) => {
74
+ // Mirror the proxy's own resolution (`accountUpstream ?? defaultWebSearchUpstream`):
75
+ // the run's account keys WIN and the deployment default is only the fallback, so the
76
+ // surfaced provider matches the one that will actually serve the run's searches. Build
77
+ // the account upstream the SAME way the proxy does before falling back to the default.
78
+ if (webSearchAccountSettings) {
79
+ const accountId = await repos.workspaceRepository.accountOf(workspaceId);
80
+ if (accountId) {
81
+ const accountUpstream = createWebSearchUpstream((await webSearchAccountSettings.resolve(accountId)).webSearch ?? {});
82
+ if (accountUpstream)
83
+ return { available: true, provider: accountUpstream.provider };
84
+ }
85
+ }
86
+ if (defaultWebSearchUpstream)
87
+ return { available: true, provider: defaultWebSearchUpstream.provider };
88
+ return { available: false, provider: null };
89
+ }
90
+ : undefined;
91
+ // Private package registries (npm private orgs, GitHub Packages): sealed per-workspace
92
+ // entries decrypted only at container dispatch, rendered by the harness into ~/.npmrc.
93
+ // The cipher is shared by the dispatch resolver here and the management service below.
94
+ const packageRegistryEncryptionKey = env.ENCRYPTION_KEY?.trim();
95
+ const packageRegistrySecretCipher = packageRegistryEncryptionKey
96
+ ? new WebCryptoSecretCipher({
97
+ masterKeyBase64: packageRegistryEncryptionKey,
98
+ info: PACKAGE_REGISTRY_CIPHER_INFO,
99
+ })
100
+ : undefined;
101
+ const resolvePackageRegistries = packageRegistrySecretCipher
102
+ ? (workspaceId) => resolvePackageRegistriesForDispatch(repos.packageRegistryConnectionRepository, packageRegistrySecretCipher, workspaceId)
103
+ : undefined;
104
+ // Sensitive per-service test credentials (sealed): the service backs the CRUD controller, the
105
+ // engine's prompt refs (via `resolveTestSecretRefs`) and the executor's out-of-band value
106
+ // injection (via `resolveTestSecrets`). Guarded by ENCRYPTION_KEY like the other sealed stores.
107
+ const testSecretsEncryptionKey = env.ENCRYPTION_KEY?.trim();
108
+ const testSecretsService = testSecretsEncryptionKey
109
+ ? new TestSecretsService({
110
+ testSecretsRepository: repos.testSecretsRepository,
111
+ secretCipher: new WebCryptoSecretCipher({
112
+ masterKeyBase64: testSecretsEncryptionKey,
113
+ info: TEST_SECRETS_CIPHER_INFO,
114
+ }),
115
+ blockRepository: repos.blockRepository,
116
+ clock,
117
+ })
118
+ : undefined;
119
+ const resolveTestSecrets = testSecretsService
120
+ ? (workspaceId, blockId) => testSecretsService.resolveValuesForBlock(workspaceId, blockId)
121
+ : undefined;
122
+ const resolveTestSecretRefs = testSecretsService
123
+ ? (workspaceId, blockId) => testSecretsService.resolveRefsForBlock(workspaceId, blockId)
124
+ : undefined;
125
+ // Modeled subscription quota-cycle provider (usage-and-quota-tracking, Part B): folds a
126
+ // finished subscription run's tokens into rolling windows (real reads land in B2). The
127
+ // registry of REAL vendor adapters is empty today, so every vendor reports modeled.
128
+ const subscriptionQuotaProvider = new RegistrySubscriptionQuotaProvider({
129
+ subscriptionQuotaCycleRepository: repos.subscriptionQuotaCycleRepository,
130
+ idGenerator,
131
+ clock,
132
+ registry: defaultSubscriptionQuotaRegistry,
133
+ });
134
+ return {
135
+ agentContextObservability,
136
+ searchQueryObservability,
137
+ recordHarnessCalls,
138
+ defaultWebSearchUpstream,
139
+ resolveWebSearchAvailability,
140
+ packageRegistrySecretCipher,
141
+ resolvePackageRegistries,
142
+ testSecretsService,
143
+ resolveTestSecrets,
144
+ resolveTestSecretRefs,
145
+ subscriptionQuotaProvider,
146
+ };
147
+ }
148
+ //# sourceMappingURL=container-run-services-deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-run-services-deps.js","sourceRoot":"","sources":["../src/container-run-services-deps.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { type DeployJobClient, type RunnerBackendRegistry } from '@cat-factory/integrations';
2
+ import type { AppCaches, Clock, DeployCloneTarget, GitHubClient, GitHubInstallationRepository, IdGenerator, RepoProjectionRepository, WorkspaceRepository } from '@cat-factory/kernel';
3
+ import { type AppConfig, type GitHubAppRegistry, type JobPackageRegistrySpec, type ResolveRepoOrigin, type ResolveRepoTarget, type ResolveRunnerTransport } from '@cat-factory/server';
4
+ import type { CoreDependencies } from '@cat-factory/orchestration';
5
+ import { buildNodeResolveTransport } from './container-executor-deps.js';
6
+ import type { DrizzleDb } from './db/client.js';
7
+ import { DrizzleBootstrapJobRepository } from './repositories/bootstrap.js';
8
+ import type { DrizzleRunnerPoolConnectionRepository } from './repositories/containerExecution.js';
9
+ import type { createDrizzleRepositories } from './repositories/drizzle.js';
10
+ type NodeRepositories = ReturnType<typeof createDrizzleRepositories>;
11
+ /** Inputs {@link buildNodeTransportDeploy} needs from the composition root. */
12
+ export interface NodeTransportDeployInput {
13
+ config: AppConfig;
14
+ repos: NodeRepositories;
15
+ idGenerator: IdGenerator;
16
+ clock: Clock;
17
+ runnerPoolConnectionRepository: DrizzleRunnerPoolConnectionRepository;
18
+ runnerBackendRegistry: RunnerBackendRegistry;
19
+ appRegistry: GitHubAppRegistry | undefined;
20
+ resolveRepoTarget: ResolveRepoTarget;
21
+ workspaceRepository: WorkspaceRepository;
22
+ /** Facade overrides (`resolveTransport`, the deploy seams) threaded from `NodeContainerOptions`. */
23
+ resolveTransportOverride?: ResolveRunnerTransport | null;
24
+ runnerPoolProvider?: Parameters<typeof buildNodeResolveTransport>[5];
25
+ skipProvisioningLogWrap?: boolean;
26
+ mintInstallationToken?: (installationId: number) => Promise<string>;
27
+ deployJobClientOverride?: DeployJobClient;
28
+ disableDefaultDeployJobClient?: boolean;
29
+ resolveDeployCloneTargetOverride?: (workspaceId: string, blockId: string, ref?: string) => Promise<DeployCloneTarget | null>;
30
+ resolveRepoOrigin?: ResolveRepoOrigin;
31
+ }
32
+ /**
33
+ * The runner-transport resolver + the container-backed deploy lifecycle seams of the Node
34
+ * composition root, lifted out of `buildNodeContainer` so that root stays within the file-size
35
+ * budget. Resolves the workspace's runner transport (a sibling facade's injected one wins, else
36
+ * the self-hosted pool), wraps it with the provisioning-log decorator, and builds the deploy job
37
+ * client + clone-target resolver (default: the pool-backed client + an App-token github.com origin).
38
+ */
39
+ export declare function buildNodeTransportDeploy(input: NodeTransportDeployInput): {
40
+ resolveTransport: ResolveRunnerTransport | null;
41
+ baseDeployMint: ((installationId: number) => Promise<string>) | undefined;
42
+ deployDeps: Partial<CoreDependencies>;
43
+ };
44
+ /** Inputs {@link buildNodeBootstrapper} needs from the composition root. */
45
+ export interface NodeBootstrapperInput {
46
+ env: NodeJS.ProcessEnv;
47
+ config: AppConfig;
48
+ sourced: <T>(name: string, build: (d: DrizzleDb) => T) => T;
49
+ resolveTransport: ResolveRunnerTransport | null;
50
+ githubInstallationRepository: GitHubInstallationRepository;
51
+ repoProjectionRepository: RepoProjectionRepository;
52
+ appRegistry: GitHubAppRegistry | undefined;
53
+ githubClient: GitHubClient | undefined;
54
+ mintInstallationToken?: (installationId: number) => Promise<string>;
55
+ resolvePackageRegistries?: (workspaceId: string) => Promise<JobPackageRegistrySpec[]>;
56
+ caches?: AppCaches;
57
+ }
58
+ /**
59
+ * The repo-bootstrap slice of the Node composition root (the reference-architecture library +
60
+ * the container-dispatching `repoBootstrapper`), lifted out of `buildNodeContainer` so that root
61
+ * stays within the file-size budget. The bootstrap-job repo is wired unconditionally (the module
62
+ * + ref-arch CRUD then work like the Worker); the `repoBootstrapper` wires only when its
63
+ * prerequisites are met (transport + proxy + token + GitHub client) — the same token source the
64
+ * container executor uses.
65
+ */
66
+ export declare function buildNodeBootstrapper(input: NodeBootstrapperInput): {
67
+ bootstrapJobRepository: DrizzleBootstrapJobRepository;
68
+ bootstrapMintInstallationToken: ((installationId: number) => Promise<string>) | undefined;
69
+ repoBootstrapper: import("@cat-factory/server").ContainerRepoBootstrapper | undefined;
70
+ };
71
+ export {};
72
+ //# sourceMappingURL=container-transport-deps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-transport-deps.d.ts","sourceRoot":"","sources":["../src/container-transport-deps.ts"],"names":[],"mappings":"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"}