@cat-factory/integrations 0.31.0 → 0.33.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/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -6
- package/dist/index.js.map +1 -1
- package/dist/modules/documents/ConfluenceProvider.d.ts +1 -2
- package/dist/modules/documents/ConfluenceProvider.d.ts.map +1 -1
- package/dist/modules/documents/DocumentConnectionService.d.ts +11 -28
- package/dist/modules/documents/DocumentConnectionService.d.ts.map +1 -1
- package/dist/modules/documents/DocumentConnectionService.js +12 -71
- package/dist/modules/documents/DocumentConnectionService.js.map +1 -1
- package/dist/modules/documents/DocumentImportService.d.ts +4 -7
- package/dist/modules/documents/DocumentImportService.d.ts.map +1 -1
- package/dist/modules/documents/DocumentImportService.js +6 -9
- package/dist/modules/documents/DocumentImportService.js.map +1 -1
- package/dist/modules/documents/FigmaProvider.d.ts +4 -5
- package/dist/modules/documents/FigmaProvider.d.ts.map +1 -1
- package/dist/modules/documents/FigmaProvider.js +22 -21
- package/dist/modules/documents/FigmaProvider.js.map +1 -1
- package/dist/modules/documents/GitHubDocsProvider.d.ts +1 -2
- package/dist/modules/documents/GitHubDocsProvider.d.ts.map +1 -1
- package/dist/modules/documents/LinearDocumentProvider.d.ts +1 -2
- package/dist/modules/documents/LinearDocumentProvider.d.ts.map +1 -1
- package/dist/modules/documents/NotionProvider.d.ts +1 -2
- package/dist/modules/documents/NotionProvider.d.ts.map +1 -1
- package/dist/modules/documents/{ClaudeDesignProvider.d.ts → ZeplinProvider.d.ts} +12 -11
- package/dist/modules/documents/ZeplinProvider.d.ts.map +1 -0
- package/dist/modules/documents/ZeplinProvider.js +126 -0
- package/dist/modules/documents/ZeplinProvider.js.map +1 -0
- package/dist/modules/documents/design.logic.d.ts +60 -0
- package/dist/modules/documents/design.logic.d.ts.map +1 -0
- package/dist/modules/documents/design.logic.js +0 -0
- package/dist/modules/documents/design.logic.js.map +1 -0
- package/dist/modules/documents/figma.logic.d.ts +30 -9
- package/dist/modules/documents/figma.logic.d.ts.map +1 -1
- package/dist/modules/documents/figma.logic.js +51 -36
- package/dist/modules/documents/figma.logic.js.map +1 -1
- package/dist/modules/documents/zeplin.logic.d.ts +102 -0
- package/dist/modules/documents/zeplin.logic.d.ts.map +1 -0
- package/dist/modules/documents/zeplin.logic.js +193 -0
- package/dist/modules/documents/zeplin.logic.js.map +1 -0
- package/dist/modules/environments/environments.logic.d.ts.map +1 -1
- package/dist/modules/environments/environments.logic.js +2 -99
- package/dist/modules/environments/environments.logic.js.map +1 -1
- package/dist/modules/kubernetes/KubernetesRunnerTransport.d.ts +29 -0
- package/dist/modules/kubernetes/KubernetesRunnerTransport.d.ts.map +1 -0
- package/dist/modules/kubernetes/KubernetesRunnerTransport.js +202 -0
- package/dist/modules/kubernetes/KubernetesRunnerTransport.js.map +1 -0
- package/dist/modules/kubernetes/kubernetes.logic.d.ts +54 -0
- package/dist/modules/kubernetes/kubernetes.logic.d.ts.map +1 -0
- package/dist/modules/kubernetes/kubernetes.logic.js +169 -0
- package/dist/modules/kubernetes/kubernetes.logic.js.map +1 -0
- package/dist/modules/runners/RunnerPoolConnectionService.d.ts +35 -23
- package/dist/modules/runners/RunnerPoolConnectionService.d.ts.map +1 -1
- package/dist/modules/runners/RunnerPoolConnectionService.js +94 -50
- package/dist/modules/runners/RunnerPoolConnectionService.js.map +1 -1
- package/dist/modules/runners/runner-backends.d.ts +51 -0
- package/dist/modules/runners/runner-backends.d.ts.map +1 -0
- package/dist/modules/runners/runner-backends.js +101 -0
- package/dist/modules/runners/runner-backends.js.map +1 -0
- package/package.json +3 -3
- package/dist/modules/documents/ClaudeDesignProvider.d.ts.map +0 -1
- package/dist/modules/documents/ClaudeDesignProvider.js +0 -163
- package/dist/modules/documents/ClaudeDesignProvider.js.map +0 -1
- package/dist/modules/documents/claudeDesign.logic.d.ts +0 -88
- package/dist/modules/documents/claudeDesign.logic.d.ts.map +0 -1
- package/dist/modules/documents/claudeDesign.logic.js +0 -317
- package/dist/modules/documents/claudeDesign.logic.js.map +0 -1
|
@@ -1,29 +1,55 @@
|
|
|
1
|
-
import { ConflictError,
|
|
1
|
+
import { ConflictError, ValidationError } from '@cat-factory/kernel';
|
|
2
2
|
import { requireWorkspace } from '@cat-factory/kernel';
|
|
3
3
|
import { missingRequiredConfigKeys } from '../environments/environments.logic.js';
|
|
4
|
-
import {
|
|
4
|
+
import { runnerBackend } from './runner-backends.js';
|
|
5
5
|
export class RunnerPoolConnectionService {
|
|
6
6
|
deps;
|
|
7
7
|
constructor(deps) {
|
|
8
8
|
this.deps = deps;
|
|
9
9
|
}
|
|
10
|
-
/**
|
|
10
|
+
/** The per-call context a backend provider needs to build/test a transport. */
|
|
11
|
+
context(resolveSecret) {
|
|
12
|
+
return {
|
|
13
|
+
resolveSecret,
|
|
14
|
+
...(this.deps.urlPolicy ? { urlPolicy: this.deps.urlPolicy } : {}),
|
|
15
|
+
...(this.deps.runnerPoolProvider ? { runnerPoolProvider: this.deps.runnerPoolProvider } : {}),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
provider(kind) {
|
|
19
|
+
const provider = runnerBackend(kind);
|
|
20
|
+
if (!provider)
|
|
21
|
+
throw new ValidationError(`Unknown runner backend kind: '${kind}'`);
|
|
22
|
+
return provider;
|
|
23
|
+
}
|
|
24
|
+
/** The write-boundary safety options (URL policy + this runtime's TLS capability). */
|
|
25
|
+
safetyOptions() {
|
|
26
|
+
return {
|
|
27
|
+
...(this.deps.urlPolicy ? { urlPolicy: this.deps.urlPolicy } : {}),
|
|
28
|
+
...(this.deps.customTlsSupported !== undefined
|
|
29
|
+
? { customTlsSupported: this.deps.customTlsSupported }
|
|
30
|
+
: {}),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** Register (or replace) a workspace's runner backend. */
|
|
11
34
|
async register(workspaceId, input) {
|
|
12
35
|
await requireWorkspace(this.deps.workspaceRepository, workspaceId);
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
36
|
+
const config = input.config;
|
|
37
|
+
const provider = this.provider(config.kind);
|
|
38
|
+
provider.assertConfigSafe(config, this.safetyOptions());
|
|
39
|
+
const missing = provider.referencedSecretKeys(config).filter((key) => !(key in input.secrets));
|
|
16
40
|
if (missing.length) {
|
|
17
41
|
throw new ValidationError(`Missing secret values for: ${missing.join(', ')}`);
|
|
18
42
|
}
|
|
19
43
|
const existing = await this.deps.runnerPoolConnectionRepository.getByWorkspace(workspaceId);
|
|
44
|
+
const meta = provider.connectionMeta(config);
|
|
20
45
|
const secretsCipher = await this.deps.secretCipher.encrypt(JSON.stringify(input.secrets));
|
|
21
46
|
const record = {
|
|
22
47
|
workspaceId,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
48
|
+
kind: config.kind,
|
|
49
|
+
providerId: meta.providerId,
|
|
50
|
+
label: meta.label,
|
|
51
|
+
baseUrl: meta.baseUrl,
|
|
52
|
+
configJson: JSON.stringify(config),
|
|
27
53
|
secretsCipher,
|
|
28
54
|
createdAt: existing?.createdAt ?? this.deps.clock.now(),
|
|
29
55
|
deletedAt: null,
|
|
@@ -31,10 +57,11 @@ export class RunnerPoolConnectionService {
|
|
|
31
57
|
await this.deps.runnerPoolConnectionRepository.upsert(record);
|
|
32
58
|
return this.toConnection(record, Object.keys(input.secrets));
|
|
33
59
|
}
|
|
34
|
-
/** Rotate/replace the secret bundle without re-sending the
|
|
60
|
+
/** Rotate/replace the secret bundle without re-sending the config. */
|
|
35
61
|
async updateSecrets(workspaceId, secrets) {
|
|
36
|
-
const { record,
|
|
37
|
-
const
|
|
62
|
+
const { record, config } = await this.requireConnection(workspaceId);
|
|
63
|
+
const provider = this.provider(record.kind);
|
|
64
|
+
const missing = provider.referencedSecretKeys(config).filter((key) => !(key in secrets));
|
|
38
65
|
if (missing.length) {
|
|
39
66
|
throw new ValidationError(`Missing secret values for: ${missing.join(', ')}`);
|
|
40
67
|
}
|
|
@@ -43,50 +70,47 @@ export class RunnerPoolConnectionService {
|
|
|
43
70
|
await this.deps.runnerPoolConnectionRepository.upsert(updated);
|
|
44
71
|
return this.toConnection(updated, Object.keys(secrets));
|
|
45
72
|
}
|
|
46
|
-
/** Describe the
|
|
73
|
+
/** Describe the backend's config fields + test availability for the UI. */
|
|
47
74
|
async describeProvider(workspaceId) {
|
|
48
75
|
const record = await this.deps.runnerPoolConnectionRepository.getByWorkspace(workspaceId);
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
76
|
+
const config = record ? JSON.parse(record.configJson) : undefined;
|
|
77
|
+
// The manifest backend renders a flat field form (its secret-ref keys + baseUrl);
|
|
78
|
+
// the native (kubernetes) backend uses an explicit UI form, so it has no flat fields.
|
|
79
|
+
const manifest = config?.kind === 'manifest' ? config.manifest : undefined;
|
|
80
|
+
const configFields = manifest
|
|
81
|
+
? (this.deps.runnerPoolProvider?.describeConfig?.(manifest) ?? [])
|
|
82
|
+
: [];
|
|
55
83
|
const storedKeys = record ? Object.keys(await this.decryptSecrets(record)) : [];
|
|
56
84
|
if (manifest?.baseUrl)
|
|
57
85
|
storedKeys.push('baseUrl');
|
|
58
86
|
const provider = this.deps.runnerPoolProvider;
|
|
59
87
|
return {
|
|
60
|
-
providerId:
|
|
61
|
-
label:
|
|
62
|
-
kind
|
|
88
|
+
providerId: record?.providerId ?? 'http',
|
|
89
|
+
label: record?.label ?? 'Agent runner backend',
|
|
90
|
+
// `kind` here is the UI FORM-STYLE discriminator (manifest editor vs native flat
|
|
91
|
+
// form), not the runner-backend kind: only the manifest backend uses this
|
|
92
|
+
// descriptor-driven form, so it stays 'manifest'. The actual backend kind is
|
|
93
|
+
// surfaced on the connection (`connection.kind` + the non-secret `config`), which
|
|
94
|
+
// is what the tab's backend selector + the Kubernetes form read.
|
|
95
|
+
kind: 'manifest',
|
|
63
96
|
configFields,
|
|
64
|
-
supportsTest:
|
|
97
|
+
supportsTest: true,
|
|
65
98
|
missingRequired: missingRequiredConfigKeys(configFields, storedKeys),
|
|
66
|
-
// The current saved manifest (non-secret), so the native connect form overlays edits
|
|
67
|
-
// onto the real stored manifest instead of the bare scaffold (mirrors the env service).
|
|
68
99
|
...(manifest ? { savedManifest: manifest } : {}),
|
|
69
|
-
...(provider?.describeManifestTemplate
|
|
100
|
+
...(manifest && provider?.describeManifestTemplate
|
|
70
101
|
? { manifestTemplate: provider.describeManifestTemplate() }
|
|
71
102
|
: {}),
|
|
72
103
|
};
|
|
73
104
|
}
|
|
74
|
-
/** Probe a candidate
|
|
105
|
+
/** Probe a candidate backend connection before saving (nothing is persisted). */
|
|
75
106
|
async testConnection(workspaceId, input) {
|
|
76
107
|
await requireWorkspace(this.deps.workspaceRepository, workspaceId);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (input.manifest) {
|
|
82
|
-
assertManifestUrlsSafe(input.manifest, this.deps.urlPolicy ?? STRICT_URL_SAFETY_POLICY);
|
|
83
|
-
}
|
|
108
|
+
if (!input.config)
|
|
109
|
+
return { ok: true, message: 'Nothing to test.' };
|
|
110
|
+
const provider = this.provider(input.config.kind);
|
|
111
|
+
provider.assertConfigSafe(input.config, this.safetyOptions());
|
|
84
112
|
const secrets = input.secrets ?? {};
|
|
85
|
-
return provider.testConnection(
|
|
86
|
-
manifest: input.manifest,
|
|
87
|
-
config: input.config ?? {},
|
|
88
|
-
resolveSecret: (key) => secrets[key],
|
|
89
|
-
});
|
|
113
|
+
return provider.testConnection(input.config, this.context((key) => secrets[key]));
|
|
90
114
|
}
|
|
91
115
|
/** The workspace's current connection (safe metadata), or null. */
|
|
92
116
|
async getConnection(workspaceId) {
|
|
@@ -96,29 +120,34 @@ export class RunnerPoolConnectionService {
|
|
|
96
120
|
const keys = Object.keys(await this.decryptSecrets(record));
|
|
97
121
|
return this.toConnection(record, keys);
|
|
98
122
|
}
|
|
99
|
-
/** Resolve the live connection + parsed
|
|
123
|
+
/** Resolve the live connection + parsed config, or throw if not registered. */
|
|
100
124
|
async requireConnection(workspaceId) {
|
|
101
125
|
const record = await this.deps.runnerPoolConnectionRepository.getByWorkspace(workspaceId);
|
|
102
126
|
if (!record) {
|
|
103
|
-
throw new ConflictError(`Workspace '${workspaceId}' has no runner
|
|
127
|
+
throw new ConflictError(`Workspace '${workspaceId}' has no runner backend registered`);
|
|
104
128
|
}
|
|
105
|
-
const
|
|
106
|
-
return { record,
|
|
129
|
+
const config = JSON.parse(record.configJson);
|
|
130
|
+
return { record, config };
|
|
107
131
|
}
|
|
108
132
|
/**
|
|
109
|
-
* Resolve the workspace's
|
|
110
|
-
*
|
|
111
|
-
*
|
|
133
|
+
* Resolve the workspace's runner backend into a live {@link RunnerTransport} (the
|
|
134
|
+
* provider builds it from the stored config + a secret resolver over its decrypted
|
|
135
|
+
* bundle), or null when it has no live backend registered / its kind is no longer
|
|
136
|
+
* registered. Used by the wiring to pick the dispatch backend per job.
|
|
112
137
|
*/
|
|
113
138
|
async resolve(workspaceId) {
|
|
114
139
|
const record = await this.deps.runnerPoolConnectionRepository.getByWorkspace(workspaceId);
|
|
115
140
|
if (!record)
|
|
116
141
|
return null;
|
|
117
|
-
const
|
|
142
|
+
const provider = runnerBackend(record.kind);
|
|
143
|
+
if (!provider)
|
|
144
|
+
return null;
|
|
145
|
+
const config = JSON.parse(record.configJson);
|
|
118
146
|
const bundle = await this.decryptSecrets(record);
|
|
119
|
-
|
|
147
|
+
const transport = provider.buildTransport(config, this.context((key) => bundle[key]));
|
|
148
|
+
return { transport, kind: record.kind, providerId: record.providerId };
|
|
120
149
|
}
|
|
121
|
-
/** Unregister the
|
|
150
|
+
/** Unregister the backend (tombstones the binding). */
|
|
122
151
|
async unregister(workspaceId) {
|
|
123
152
|
const record = await this.deps.runnerPoolConnectionRepository.getByWorkspace(workspaceId);
|
|
124
153
|
if (!record)
|
|
@@ -132,13 +161,28 @@ export class RunnerPoolConnectionService {
|
|
|
132
161
|
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
133
162
|
}
|
|
134
163
|
toConnection(record, secretKeys) {
|
|
164
|
+
// The stored config holds NO secrets (those live in the separate encrypted bundle),
|
|
165
|
+
// so it is safe to expose so the connect form can prefill the non-secret fields on
|
|
166
|
+
// edit (namespace/image/… for kubernetes) instead of forcing a full re-entry.
|
|
167
|
+
const config = this.parseConfig(record);
|
|
135
168
|
return {
|
|
169
|
+
kind: record.kind,
|
|
136
170
|
providerId: record.providerId,
|
|
137
171
|
label: record.label,
|
|
138
172
|
baseUrl: record.baseUrl,
|
|
139
173
|
connectedAt: record.createdAt,
|
|
140
174
|
secretKeys,
|
|
175
|
+
...(config ? { config } : {}),
|
|
141
176
|
};
|
|
142
177
|
}
|
|
178
|
+
/** Parse the stored discriminated config, tolerating a malformed/legacy blob. */
|
|
179
|
+
parseConfig(record) {
|
|
180
|
+
try {
|
|
181
|
+
return JSON.parse(record.configJson);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
143
187
|
}
|
|
144
188
|
//# sourceMappingURL=RunnerPoolConnectionService.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RunnerPoolConnectionService.js","sourceRoot":"","sources":["../../../src/modules/runners/RunnerPoolConnectionService.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RunnerPoolConnectionService.js","sourceRoot":"","sources":["../../../src/modules/runners/RunnerPoolConnectionService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ConnectionTestResult, RunnerBackendConfig, RunnerPoolProvider, RunnerTransport, SecretResolver, UrlSafetyPolicy } from '@cat-factory/kernel';
|
|
2
|
+
/** Per-call dependencies a provider may need to build/test its transport. */
|
|
3
|
+
export interface RunnerBackendContext {
|
|
4
|
+
resolveSecret: SecretResolver;
|
|
5
|
+
/** Manifest SSRF policy (the Kubernetes backend does its own apiserver-URL check). */
|
|
6
|
+
urlPolicy?: UrlSafetyPolicy;
|
|
7
|
+
/**
|
|
8
|
+
* A shared manifest HTTP provider (its OAuth cache reused), injectable for tests
|
|
9
|
+
* and for native pool adapters. Used by the `manifest` backend only.
|
|
10
|
+
*/
|
|
11
|
+
runnerPoolProvider?: RunnerPoolProvider;
|
|
12
|
+
}
|
|
13
|
+
/** Capabilities/policies a backend validates its config against at the write boundary. */
|
|
14
|
+
export interface RunnerBackendSafetyOptions {
|
|
15
|
+
/** Manifest SSRF policy. Absent ⇒ strict. */
|
|
16
|
+
urlPolicy?: UrlSafetyPolicy;
|
|
17
|
+
/**
|
|
18
|
+
* Whether THIS deployment runtime can honor a backend's custom TLS trust material
|
|
19
|
+
* (a private CA / insecure-skip). The Cloudflare Worker cannot (no undici / no
|
|
20
|
+
* custom-CA fetch), so it sets this `false` and the kubernetes backend rejects such
|
|
21
|
+
* a config up front instead of letting it save and then die at first dispatch.
|
|
22
|
+
* Absent/`true` ⇒ supported (Node/local).
|
|
23
|
+
*/
|
|
24
|
+
customTlsSupported?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface RunnerBackendProvider {
|
|
27
|
+
readonly kind: RunnerBackendConfig['kind'];
|
|
28
|
+
/** Every secret-bundle key the config references (validated present at registration). */
|
|
29
|
+
referencedSecretKeys(config: RunnerBackendConfig): string[];
|
|
30
|
+
/** Non-secret metadata persisted on the connection row + shown in the UI. */
|
|
31
|
+
connectionMeta(config: RunnerBackendConfig): {
|
|
32
|
+
providerId: string;
|
|
33
|
+
label: string;
|
|
34
|
+
baseUrl: string;
|
|
35
|
+
};
|
|
36
|
+
/** Validate the config at the write boundary (SSRF / URL + runtime safety). Throws if unsafe. */
|
|
37
|
+
assertConfigSafe(config: RunnerBackendConfig, opts?: RunnerBackendSafetyOptions): void;
|
|
38
|
+
/** Build the live transport the execution engine dispatches/polls/releases through. */
|
|
39
|
+
buildTransport(config: RunnerBackendConfig, ctx: RunnerBackendContext): RunnerTransport;
|
|
40
|
+
/** Probe the backend without persisting anything. */
|
|
41
|
+
testConnection(config: RunnerBackendConfig, ctx: RunnerBackendContext): Promise<ConnectionTestResult>;
|
|
42
|
+
}
|
|
43
|
+
/** Register a runner-backend provider (built-ins on import; third-party for side effect). */
|
|
44
|
+
export declare function registerRunnerBackend(provider: RunnerBackendProvider): void;
|
|
45
|
+
/** The provider for a backend kind, or undefined when unregistered. */
|
|
46
|
+
export declare function runnerBackend(kind: string): RunnerBackendProvider | undefined;
|
|
47
|
+
/** All registered backend kinds (for diagnostics / a UI capabilities list). */
|
|
48
|
+
export declare function registeredRunnerBackendKinds(): string[];
|
|
49
|
+
export declare const manifestRunnerBackend: RunnerBackendProvider;
|
|
50
|
+
export declare const kubernetesRunnerBackend: RunnerBackendProvider;
|
|
51
|
+
//# sourceMappingURL=runner-backends.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner-backends.d.ts","sourceRoot":"","sources":["../../../src/modules/runners/runner-backends.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,eAAe,EAChB,MAAM,qBAAqB,CAAA;AAqB5B,6EAA6E;AAC7E,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,cAAc,CAAA;IAC7B,sFAAsF;IACtF,SAAS,CAAC,EAAE,eAAe,CAAA;IAC3B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;CACxC;AAED,0FAA0F;AAC1F,MAAM,WAAW,0BAA0B;IACzC,6CAA6C;IAC7C,SAAS,CAAC,EAAE,eAAe,CAAA;IAC3B;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAA;IAC1C,yFAAyF;IACzF,oBAAoB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,EAAE,CAAA;IAC3D,6EAA6E;IAC7E,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG;QAC3C,UAAU,EAAE,MAAM,CAAA;QAClB,KAAK,EAAE,MAAM,CAAA;QACb,OAAO,EAAE,MAAM,CAAA;KAChB,CAAA;IACD,iGAAiG;IACjG,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,EAAE,IAAI,CAAC,EAAE,0BAA0B,GAAG,IAAI,CAAA;IACtF,uFAAuF;IACvF,cAAc,CAAC,MAAM,EAAE,mBAAmB,EAAE,GAAG,EAAE,oBAAoB,GAAG,eAAe,CAAA;IACvF,qDAAqD;IACrD,cAAc,CACZ,MAAM,EAAE,mBAAmB,EAC3B,GAAG,EAAE,oBAAoB,GACxB,OAAO,CAAC,oBAAoB,CAAC,CAAA;CACjC;AAID,6FAA6F;AAC7F,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAE3E;AAED,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAE7E;AAED,+EAA+E;AAC/E,wBAAgB,4BAA4B,IAAI,MAAM,EAAE,CAEvD;AASD,eAAO,MAAM,qBAAqB,EAAE,qBAmCnC,CAAA;AAID,eAAO,MAAM,uBAAuB,EAAE,qBAqCrC,CAAA"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { STRICT_URL_SAFETY_POLICY } from '@cat-factory/kernel';
|
|
2
|
+
import { assertApiServerUrlSafe, KUBERNETES_TOKEN_KEY } from '../kubernetes/kubernetes.logic.js';
|
|
3
|
+
import { KubernetesRunnerTransport } from '../kubernetes/KubernetesRunnerTransport.js';
|
|
4
|
+
import { HttpRunnerPoolProvider } from './HttpRunnerPoolProvider.js';
|
|
5
|
+
import { RunnerPoolTransport } from './RunnerPoolTransport.js';
|
|
6
|
+
import { assertManifestUrlsSafe, referencedSecretKeys as manifestSecretKeys, } from './runners.logic.js';
|
|
7
|
+
const REGISTRY = new Map();
|
|
8
|
+
/** Register a runner-backend provider (built-ins on import; third-party for side effect). */
|
|
9
|
+
export function registerRunnerBackend(provider) {
|
|
10
|
+
REGISTRY.set(provider.kind, provider);
|
|
11
|
+
}
|
|
12
|
+
/** The provider for a backend kind, or undefined when unregistered. */
|
|
13
|
+
export function runnerBackend(kind) {
|
|
14
|
+
return REGISTRY.get(kind);
|
|
15
|
+
}
|
|
16
|
+
/** All registered backend kinds (for diagnostics / a UI capabilities list). */
|
|
17
|
+
export function registeredRunnerBackendKinds() {
|
|
18
|
+
return [...REGISTRY.keys()];
|
|
19
|
+
}
|
|
20
|
+
// --- Built-in: manifest (the original BYO HTTP scheduler pool) ----------------
|
|
21
|
+
let sharedHttpProvider;
|
|
22
|
+
function defaultHttpProvider(urlPolicy) {
|
|
23
|
+
return (sharedHttpProvider ??= new HttpRunnerPoolProvider(urlPolicy ? { urlPolicy } : {}));
|
|
24
|
+
}
|
|
25
|
+
export const manifestRunnerBackend = {
|
|
26
|
+
kind: 'manifest',
|
|
27
|
+
referencedSecretKeys: (config) => config.kind === 'manifest' ? manifestSecretKeys(config.manifest) : [],
|
|
28
|
+
connectionMeta: (config) => {
|
|
29
|
+
if (config.kind !== 'manifest')
|
|
30
|
+
throw new Error('Expected a manifest runner config');
|
|
31
|
+
return {
|
|
32
|
+
providerId: config.manifest.providerId,
|
|
33
|
+
label: config.manifest.label,
|
|
34
|
+
baseUrl: config.manifest.baseUrl,
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
assertConfigSafe: (config, opts) => {
|
|
38
|
+
if (config.kind === 'manifest') {
|
|
39
|
+
assertManifestUrlsSafe(config.manifest, opts?.urlPolicy ?? STRICT_URL_SAFETY_POLICY);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
buildTransport: (config, ctx) => {
|
|
43
|
+
if (config.kind !== 'manifest')
|
|
44
|
+
throw new Error('Expected a manifest runner config');
|
|
45
|
+
const provider = ctx.runnerPoolProvider ?? defaultHttpProvider(ctx.urlPolicy);
|
|
46
|
+
return new RunnerPoolTransport(provider, config.manifest, ctx.resolveSecret);
|
|
47
|
+
},
|
|
48
|
+
testConnection: (config, ctx) => {
|
|
49
|
+
if (config.kind !== 'manifest') {
|
|
50
|
+
return Promise.resolve({ ok: false, message: 'Expected a manifest runner config' });
|
|
51
|
+
}
|
|
52
|
+
const provider = ctx.runnerPoolProvider ?? defaultHttpProvider(ctx.urlPolicy);
|
|
53
|
+
return (provider.testConnection?.({
|
|
54
|
+
manifest: config.manifest,
|
|
55
|
+
config: {},
|
|
56
|
+
resolveSecret: ctx.resolveSecret,
|
|
57
|
+
}) ?? Promise.resolve({ ok: true, message: 'This pool provider has no connection test.' }));
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
// --- Built-in: kubernetes (native per-run pods over the apiserver pod-proxy) ---
|
|
61
|
+
export const kubernetesRunnerBackend = {
|
|
62
|
+
kind: 'kubernetes',
|
|
63
|
+
referencedSecretKeys: (config) => (config.kind === 'kubernetes' ? [KUBERNETES_TOKEN_KEY] : []),
|
|
64
|
+
connectionMeta: (config) => {
|
|
65
|
+
if (config.kind !== 'kubernetes')
|
|
66
|
+
throw new Error('Expected a kubernetes runner config');
|
|
67
|
+
return {
|
|
68
|
+
providerId: 'kubernetes',
|
|
69
|
+
label: config.kubernetes.label,
|
|
70
|
+
baseUrl: config.kubernetes.apiServerUrl,
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
assertConfigSafe: (config, opts) => {
|
|
74
|
+
if (config.kind !== 'kubernetes')
|
|
75
|
+
return;
|
|
76
|
+
assertApiServerUrlSafe(config.kubernetes.apiServerUrl);
|
|
77
|
+
// Custom TLS trust material is honored only on a runtime with undici (Node/local).
|
|
78
|
+
// Reject it up front on a runtime that can't (the Cloudflare Worker) so the
|
|
79
|
+
// connection can't save and then fail at every dispatch.
|
|
80
|
+
const needsCustomTls = !!config.kubernetes.caCertPem || !!config.kubernetes.insecureSkipTlsVerify;
|
|
81
|
+
if (needsCustomTls && opts?.customTlsSupported === false) {
|
|
82
|
+
throw new Error('This runtime cannot verify a custom CA / skip TLS for the Kubernetes apiserver ' +
|
|
83
|
+
'(it requires the Node runtime). Use a publicly-trusted apiserver certificate, or ' +
|
|
84
|
+
'run this workspace on the Node/local deployment.');
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
buildTransport: (config, ctx) => {
|
|
88
|
+
if (config.kind !== 'kubernetes')
|
|
89
|
+
throw new Error('Expected a kubernetes runner config');
|
|
90
|
+
return new KubernetesRunnerTransport(config.kubernetes, ctx.resolveSecret);
|
|
91
|
+
},
|
|
92
|
+
testConnection: (config, ctx) => {
|
|
93
|
+
if (config.kind !== 'kubernetes') {
|
|
94
|
+
return Promise.resolve({ ok: false, message: 'Expected a kubernetes runner config' });
|
|
95
|
+
}
|
|
96
|
+
return new KubernetesRunnerTransport(config.kubernetes, ctx.resolveSecret).testConnection();
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
registerRunnerBackend(manifestRunnerBackend);
|
|
100
|
+
registerRunnerBackend(kubernetesRunnerBackend);
|
|
101
|
+
//# sourceMappingURL=runner-backends.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner-backends.js","sourceRoot":"","sources":["../../../src/modules/runners/runner-backends.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/integrations",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"description": "External-system integration domain logic for the Agent Architecture Board (GitHub, documents, tasks, environments, runners).",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"ai": "^6.0.209",
|
|
28
|
-
"@cat-factory/contracts": "0.
|
|
29
|
-
"@cat-factory/kernel": "0.
|
|
28
|
+
"@cat-factory/contracts": "0.49.0",
|
|
29
|
+
"@cat-factory/kernel": "0.50.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"typescript": "7.0.1-rc",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ClaudeDesignProvider.d.ts","sourceRoot":"","sources":["../../../src/modules/documents/ClaudeDesignProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAA;AA8C5B,qBAAa,oBAAqB,YAAW,sBAAsB;IACjE,QAAQ,CAAC,IAAI,EAAG,eAAe,CAAS;IACxC,QAAQ,CAAC,UAAU;;;;;;;;;;;;;;;MAA2B;IAE9C,mBAAmB,CAAC,KAAK,EAAE,mBAAmB,GAAG,oBAAoB,CAMpE;IAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAErC;IAEK,aAAa,CACjB,WAAW,EAAE,mBAAmB,EAChC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,eAAe,CAAC,CAmB1B;IAED,0FAA0F;YAC5E,iBAAiB;IAoC/B,2FAA2F;YAC7E,SAAS;IA+BvB,yFAAyF;IACzF,OAAO,CAAC,UAAU;IAkBlB,OAAO,CAAC,OAAO;YAQD,GAAG;CAiBlB"}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { ValidationError, } from '@cat-factory/kernel';
|
|
2
|
-
import { CLAUDE_DESIGN_API_HOST, CLAUDE_DESIGN_DESCRIPTOR, DS_MANIFEST_PATH, claudeDesignUrlFor, parseClaudeDesignRef, renderClaudeDesignProject, splitClaudeDesignExternalId, } from './claudeDesign.logic.js';
|
|
3
|
-
import { DocumentHttpError, createHostPinnedFetch, readCappedText } from './http.js';
|
|
4
|
-
// ClaudeDesignProvider: the document-source provider for Anthropic's Claude Design. It
|
|
5
|
-
// authenticates with a **per-user** personal access token (the credential is stored
|
|
6
|
-
// keyed by user id, not shared across the workspace — see the descriptor's
|
|
7
|
-
// `credentialScope: 'user'`), reads a design-system project's files over the REST API,
|
|
8
|
-
// and renders the component inventory + design tokens to the Markdown the planner +
|
|
9
|
-
// `.cat-context/` materialisation consume. All Claude-Design-specific *pure* logic (ref
|
|
10
|
-
// parsing, the host-pin guard, the HTML/manifest → Markdown normalizer) lives in
|
|
11
|
-
// `claudeDesign.logic.ts`; this class is the thin `fetch` shell.
|
|
12
|
-
//
|
|
13
|
-
// PROVISIONAL API SHAPE: the endpoint paths/headers below target the per-user-PAT read
|
|
14
|
-
// the product is moving toward (today the design-system read is claude.ai-login-bound).
|
|
15
|
-
// The host is pinned; re-verify the exact endpoints against the current API when the
|
|
16
|
-
// credentialed read ships — they are the intended shape, not a frozen contract. The
|
|
17
|
-
// normalizer they feed is solid and fully unit-tested regardless.
|
|
18
|
-
const API_BASE = `https://${CLAUDE_DESIGN_API_HOST}/v1`;
|
|
19
|
-
const USER_AGENT = 'cat-factory';
|
|
20
|
-
/** Hard cap on the bytes read off any response body, to protect the isolate. */
|
|
21
|
-
const MAX_RESPONSE_BYTES = 5_000_000;
|
|
22
|
-
/** Bound how many project files we pull for a whole-project import. */
|
|
23
|
-
const MAX_PROJECT_FILES = 12;
|
|
24
|
-
/**
|
|
25
|
-
* `fetch` pinned to the Claude Design host, following redirects by hand so the SSRF host
|
|
26
|
-
* guard runs against every hop (a 302 can't chase the PAT off-host). Shared transport.
|
|
27
|
-
*/
|
|
28
|
-
const safeFetch = createHostPinnedFetch({ host: CLAUDE_DESIGN_API_HOST, label: 'Claude Design' });
|
|
29
|
-
export class ClaudeDesignProvider {
|
|
30
|
-
kind = 'claude-design';
|
|
31
|
-
descriptor = CLAUDE_DESIGN_DESCRIPTOR;
|
|
32
|
-
normalizeConnection(input) {
|
|
33
|
-
const apiToken = input.apiToken?.trim();
|
|
34
|
-
if (!apiToken) {
|
|
35
|
-
throw new ValidationError('Claude Design requires a personal access token');
|
|
36
|
-
}
|
|
37
|
-
return { credentials: { apiToken }, label: 'Claude Design' };
|
|
38
|
-
}
|
|
39
|
-
parseRef(input) {
|
|
40
|
-
return parseClaudeDesignRef(input);
|
|
41
|
-
}
|
|
42
|
-
async fetchDocument(credentials, externalId) {
|
|
43
|
-
const { projectId, filePath } = splitClaudeDesignExternalId(externalId);
|
|
44
|
-
if (!projectId) {
|
|
45
|
-
throw new DocumentHttpError(400, `Claude Design ref is missing a project id: ${externalId}`);
|
|
46
|
-
}
|
|
47
|
-
const files = filePath
|
|
48
|
-
? [{ path: filePath, content: await this.fetchFile(credentials, projectId, filePath) }]
|
|
49
|
-
: await this.fetchProjectFiles(credentials, projectId);
|
|
50
|
-
const projectName = this.deriveName(projectId, filePath, files);
|
|
51
|
-
const body = renderClaudeDesignProject(projectName, files);
|
|
52
|
-
return {
|
|
53
|
-
externalId,
|
|
54
|
-
title: projectName,
|
|
55
|
-
url: claudeDesignUrlFor(externalId),
|
|
56
|
-
body,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
/** List a project's files, then fetch the manifest + a bounded slice of HTML previews. */
|
|
60
|
-
async fetchProjectFiles(credentials, projectId) {
|
|
61
|
-
const listed = await this.get(credentials, `/design/projects/${encodeURIComponent(projectId)}/files`);
|
|
62
|
-
const paths = normalizePaths(listed);
|
|
63
|
-
if (paths.length === 0) {
|
|
64
|
-
throw new DocumentHttpError(404, `Claude Design project ${projectId} has no readable files`);
|
|
65
|
-
}
|
|
66
|
-
// The manifest is the authoritative index; then prefer component-preview HTML and any
|
|
67
|
-
// stylesheet (for tokens), bounded so a huge project can't stall the import.
|
|
68
|
-
const manifest = paths.filter((p) => p.endsWith(DS_MANIFEST_PATH));
|
|
69
|
-
const styles = paths.filter((p) => /\.css$/i.test(p));
|
|
70
|
-
const html = paths.filter((p) => /\.html?$/i.test(p));
|
|
71
|
-
const chosen = [...manifest, ...styles, ...html].slice(0, MAX_PROJECT_FILES);
|
|
72
|
-
const out = [];
|
|
73
|
-
for (const path of chosen) {
|
|
74
|
-
try {
|
|
75
|
-
out.push({ path, content: await this.fetchFile(credentials, projectId, path) });
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
// A single unreadable file shouldn't fail the whole import — skip it.
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (out.length === 0) {
|
|
82
|
-
throw new DocumentHttpError(502, `Claude Design project ${projectId} returned no file content`);
|
|
83
|
-
}
|
|
84
|
-
return out;
|
|
85
|
-
}
|
|
86
|
-
/** Fetch one file's textual content (raw, or unwrapped from a `{ content }` JSON body). */
|
|
87
|
-
async fetchFile(credentials, projectId, path) {
|
|
88
|
-
const url = `${API_BASE}/design/projects/${encodeURIComponent(projectId)}/files/${path
|
|
89
|
-
.split('/')
|
|
90
|
-
.map(encodeURIComponent)
|
|
91
|
-
.join('/')}`;
|
|
92
|
-
const res = await safeFetch(url, { method: 'GET', headers: this.headers(credentials) });
|
|
93
|
-
if (!res.ok) {
|
|
94
|
-
const text = await readCappedText(res, MAX_RESPONSE_BYTES, 'Claude Design').catch(() => '');
|
|
95
|
-
throw new DocumentHttpError(res.status, `Claude Design GET ${url} → ${res.status}: ${text.slice(0, 300)}`);
|
|
96
|
-
}
|
|
97
|
-
const text = await readCappedText(res, MAX_RESPONSE_BYTES, 'Claude Design');
|
|
98
|
-
// The files endpoint may return raw file bytes or a `{ content }` JSON envelope.
|
|
99
|
-
const contentType = res.headers.get('content-type') ?? '';
|
|
100
|
-
if (contentType.includes('application/json')) {
|
|
101
|
-
try {
|
|
102
|
-
const json = JSON.parse(text);
|
|
103
|
-
if (typeof json.content === 'string')
|
|
104
|
-
return json.content;
|
|
105
|
-
}
|
|
106
|
-
catch {
|
|
107
|
-
// Not a JSON envelope after all — fall through and use the raw text.
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return text;
|
|
111
|
-
}
|
|
112
|
-
/** Name the document: the manifest's `name`, else the file path, else the project id. */
|
|
113
|
-
deriveName(projectId, filePath, files) {
|
|
114
|
-
const manifest = files.find((f) => f.path.endsWith(DS_MANIFEST_PATH));
|
|
115
|
-
if (manifest) {
|
|
116
|
-
try {
|
|
117
|
-
const json = JSON.parse(manifest.content);
|
|
118
|
-
if (typeof json.name === 'string' && json.name.trim())
|
|
119
|
-
return json.name.trim();
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
// ignore — fall through
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (filePath)
|
|
126
|
-
return `${projectId} — ${filePath}`;
|
|
127
|
-
return projectId;
|
|
128
|
-
}
|
|
129
|
-
headers(credentials) {
|
|
130
|
-
return {
|
|
131
|
-
authorization: `Bearer ${credentials.apiToken ?? ''}`,
|
|
132
|
-
accept: 'application/json',
|
|
133
|
-
'user-agent': USER_AGENT,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
async get(credentials, path) {
|
|
137
|
-
const url = `${API_BASE}${path}`;
|
|
138
|
-
const res = await safeFetch(url, { method: 'GET', headers: this.headers(credentials) });
|
|
139
|
-
if (!res.ok) {
|
|
140
|
-
const text = await readCappedText(res, MAX_RESPONSE_BYTES, 'Claude Design').catch(() => '');
|
|
141
|
-
throw new DocumentHttpError(res.status, `Claude Design GET ${url} → ${res.status}: ${text.slice(0, 300)}`);
|
|
142
|
-
}
|
|
143
|
-
const text = await readCappedText(res, MAX_RESPONSE_BYTES, 'Claude Design');
|
|
144
|
-
try {
|
|
145
|
-
return JSON.parse(text);
|
|
146
|
-
}
|
|
147
|
-
catch {
|
|
148
|
-
throw new DocumentHttpError(502, `Claude Design returned an unparseable body for ${path}`);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
/** Flatten the file-list response into a clean string[] of paths. */
|
|
153
|
-
function normalizePaths(listed) {
|
|
154
|
-
const raw = listed.files ?? listed.paths ?? [];
|
|
155
|
-
const out = [];
|
|
156
|
-
for (const entry of raw) {
|
|
157
|
-
const path = typeof entry === 'string' ? entry : entry?.path;
|
|
158
|
-
if (typeof path === 'string' && path.trim())
|
|
159
|
-
out.push(path.trim());
|
|
160
|
-
}
|
|
161
|
-
return out;
|
|
162
|
-
}
|
|
163
|
-
//# sourceMappingURL=ClaudeDesignProvider.js.map
|