@cat-factory/server 0.294.0 → 0.295.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/agents/promptOverrides.d.ts +3 -4
- package/dist/agents/promptOverrides.d.ts.map +1 -1
- package/dist/agents/promptOverrides.js +5 -9
- package/dist/agents/promptOverrides.js.map +1 -1
- package/dist/github/FetchGitHubClient.d.ts +2 -0
- package/dist/github/FetchGitHubClient.d.ts.map +1 -1
- package/dist/github/FetchGitHubClient.js +17 -91
- package/dist/github/FetchGitHubClient.js.map +1 -1
- package/dist/github/githubHttpHelpers.d.ts +59 -3
- package/dist/github/githubHttpHelpers.d.ts.map +1 -1
- package/dist/github/githubHttpHelpers.js +59 -4
- package/dist/github/githubHttpHelpers.js.map +1 -1
- package/dist/github/repoContents.d.ts +31 -0
- package/dist/github/repoContents.d.ts.map +1 -0
- package/dist/github/repoContents.js +121 -0
- package/dist/github/repoContents.js.map +1 -0
- package/dist/http/env.d.ts +10 -1
- package/dist/http/env.d.ts.map +1 -1
- package/dist/modules/publicApi/PublicProvisioningController.d.ts +57 -1
- package/dist/modules/publicApi/PublicProvisioningController.d.ts.map +1 -1
- package/dist/modules/publicApi/PublicProvisioningController.js +204 -8
- package/dist/modules/publicApi/PublicProvisioningController.js.map +1 -1
- package/dist/modules/publicApi/boardProjection.d.ts +9 -3
- package/dist/modules/publicApi/boardProjection.d.ts.map +1 -1
- package/dist/modules/publicApi/boardProjection.js +41 -8
- package/dist/modules/publicApi/boardProjection.js.map +1 -1
- package/dist/modules/publicApi/openapiDocument.generated.d.ts +1 -1
- package/dist/modules/publicApi/openapiDocument.generated.d.ts.map +1 -1
- package/dist/modules/publicApi/openapiDocument.generated.js +1 -1
- package/dist/modules/publicApi/openapiDocument.generated.js.map +1 -1
- package/package.json +8 -8
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { GitHubRepoRef, RepoContentEntry, RepoEntry, RepoFileContent } from '@cat-factory/kernel';
|
|
2
|
+
/** The narrow slice of `FetchGitHubClient.request` these reads need. */
|
|
3
|
+
export type ContentsRequest = (path: string, opts: {
|
|
4
|
+
installationId: number;
|
|
5
|
+
}) => Promise<{
|
|
6
|
+
json: unknown;
|
|
7
|
+
}>;
|
|
8
|
+
/** The repo root's entries, or `[]` for a repository with no commits yet. */
|
|
9
|
+
export declare function readRootEntries(request: ContentsRequest, installationId: number, ref: GitHubRepoRef): Promise<RepoEntry[]>;
|
|
10
|
+
/** One directory's entries on `gitRef`, or `[]` for a missing path (mirrors {@link readRootEntries}). */
|
|
11
|
+
export declare function readDirectory(request: ContentsRequest, installationId: number, ref: GitHubRepoRef, path: string, gitRef?: string): Promise<RepoContentEntry[]>;
|
|
12
|
+
/**
|
|
13
|
+
* The WHOLE tree in one recursive git-trees read, so file search never walks the contents API
|
|
14
|
+
* directory by directory. `HEAD` resolves to the repository's default branch.
|
|
15
|
+
*/
|
|
16
|
+
export declare function readTree(request: ContentsRequest, installationId: number, ref: GitHubRepoRef, gitRef?: string): Promise<RepoContentEntry[]>;
|
|
17
|
+
/**
|
|
18
|
+
* One file's content and blob sha on `gitRef`, or null when the path is absent.
|
|
19
|
+
*
|
|
20
|
+
* The two refusals this endpoint states badly are translated HERE, because this is the only layer
|
|
21
|
+
* that knows the contents API's own ceiling:
|
|
22
|
+
*
|
|
23
|
+
* - An over-limit blob arrives EITHER as a `403` whose body says `too_large`, or as a `200` with
|
|
24
|
+
* an empty body and `encoding: 'none'`. Left bare, the first reaches a consumer as the same fact
|
|
25
|
+
* as a revoked token (the public file read turned it into a `503` telling an operator to re-mint
|
|
26
|
+
* a credential that worked), and the second as a file that is genuinely empty.
|
|
27
|
+
* - A file whose bytes are not UTF-8 rides back with `lossy: true` rather than pretending the
|
|
28
|
+
* replacement characters are its content.
|
|
29
|
+
*/
|
|
30
|
+
export declare function readFileContent(request: ContentsRequest, installationId: number, ref: GitHubRepoRef, path: string, gitRef?: string): Promise<RepoFileContent | null>;
|
|
31
|
+
//# sourceMappingURL=repoContents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repoContents.d.ts","sourceRoot":"","sources":["../../src/github/repoContents.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,eAAe,EAChB,MAAM,qBAAqB,CAAA;AAyB5B,wEAAwE;AACxE,MAAM,MAAM,eAAe,GAAG,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,cAAc,EAAE,MAAM,CAAA;CAAE,KAC7B,OAAO,CAAC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAAA;AAE/B,6EAA6E;AAC7E,wBAAsB,eAAe,CACnC,OAAO,EAAE,eAAe,EACxB,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE,aAAa,GACjB,OAAO,CAAC,SAAS,EAAE,CAAC,CActB;AAED,yGAAyG;AACzG,wBAAsB,aAAa,CACjC,OAAO,EAAE,eAAe,EACxB,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAuB7B;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,eAAe,EACxB,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE,aAAa,EAClB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA2B7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,eAAe,EACxB,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAsBjC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { VcsBlobTooLargeError } from '@cat-factory/kernel';
|
|
2
|
+
import { GITHUB_CONTENTS_MAX_BYTES, decodeRepoFileBase64, githubApiStatus, isGitHubBlobTooLarge, } from './githubHttpHelpers.js';
|
|
3
|
+
/** The repo root's entries, or `[]` for a repository with no commits yet. */
|
|
4
|
+
export async function readRootEntries(request, installationId, ref) {
|
|
5
|
+
let json;
|
|
6
|
+
try {
|
|
7
|
+
;
|
|
8
|
+
({ json } = await request(`/repos/${ref.owner}/${ref.repo}/contents/`, { installationId }));
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
// An empty repository has no default branch, so the contents endpoint 404s. That's the signal we
|
|
12
|
+
// want — treat it as "no entries", not an error.
|
|
13
|
+
if (githubApiStatus(err) === 404)
|
|
14
|
+
return [];
|
|
15
|
+
throw err;
|
|
16
|
+
}
|
|
17
|
+
const entries = Array.isArray(json)
|
|
18
|
+
? json
|
|
19
|
+
: [];
|
|
20
|
+
return entries.map((e) => ({ path: e.path ?? e.name ?? '', type: e.type ?? 'file' }));
|
|
21
|
+
}
|
|
22
|
+
/** One directory's entries on `gitRef`, or `[]` for a missing path (mirrors {@link readRootEntries}). */
|
|
23
|
+
export async function readDirectory(request, installationId, ref, path, gitRef) {
|
|
24
|
+
const clean = path.replace(/^\/+|\/+$/g, '');
|
|
25
|
+
const query = gitRef ? `?ref=${encodeURIComponent(gitRef)}` : '';
|
|
26
|
+
let json;
|
|
27
|
+
try {
|
|
28
|
+
;
|
|
29
|
+
({ json } = await request(`/repos/${ref.owner}/${ref.repo}/contents/${clean}${query}`, {
|
|
30
|
+
installationId,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (githubApiStatus(err) === 404)
|
|
35
|
+
return [];
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
// A directory returns an array; a single file returns an object — coerce both.
|
|
39
|
+
const arr = Array.isArray(json) ? json : [json];
|
|
40
|
+
return arr.map((e) => ({
|
|
41
|
+
path: e.path ?? e.name ?? '',
|
|
42
|
+
name: e.name ?? (e.path ?? '').split('/').pop() ?? '',
|
|
43
|
+
type: e.type ?? 'file',
|
|
44
|
+
sha: e.sha ?? '',
|
|
45
|
+
...(typeof e.size === 'number' ? { size: e.size } : {}),
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The WHOLE tree in one recursive git-trees read, so file search never walks the contents API
|
|
50
|
+
* directory by directory. `HEAD` resolves to the repository's default branch.
|
|
51
|
+
*/
|
|
52
|
+
export async function readTree(request, installationId, ref, gitRef) {
|
|
53
|
+
const treeRef = encodeURIComponent(gitRef && gitRef !== 'HEAD' ? gitRef : 'HEAD');
|
|
54
|
+
let json;
|
|
55
|
+
try {
|
|
56
|
+
;
|
|
57
|
+
({ json } = await request(`/repos/${ref.owner}/${ref.repo}/git/trees/${treeRef}?recursive=1`, {
|
|
58
|
+
installationId,
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
// Empty repo / unknown ref → no entries (mirrors readDirectory).
|
|
63
|
+
if (githubApiStatus(err) === 404)
|
|
64
|
+
return [];
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
const body = json;
|
|
68
|
+
const entries = Array.isArray(body.tree) ? body.tree : [];
|
|
69
|
+
// GitHub git-tree `type` is `blob` | `tree` | `commit` (submodule); normalise to the neutral
|
|
70
|
+
// file/dir vocabulary and drop submodules (they have no browsable content here).
|
|
71
|
+
return entries
|
|
72
|
+
.filter((e) => e.type === 'blob' || e.type === 'tree')
|
|
73
|
+
.map((e) => ({
|
|
74
|
+
path: e.path ?? '',
|
|
75
|
+
name: (e.path ?? '').split('/').pop() ?? '',
|
|
76
|
+
type: e.type === 'tree' ? 'dir' : 'file',
|
|
77
|
+
sha: e.sha ?? '',
|
|
78
|
+
...(typeof e.size === 'number' ? { size: e.size } : {}),
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* One file's content and blob sha on `gitRef`, or null when the path is absent.
|
|
83
|
+
*
|
|
84
|
+
* The two refusals this endpoint states badly are translated HERE, because this is the only layer
|
|
85
|
+
* that knows the contents API's own ceiling:
|
|
86
|
+
*
|
|
87
|
+
* - An over-limit blob arrives EITHER as a `403` whose body says `too_large`, or as a `200` with
|
|
88
|
+
* an empty body and `encoding: 'none'`. Left bare, the first reaches a consumer as the same fact
|
|
89
|
+
* as a revoked token (the public file read turned it into a `503` telling an operator to re-mint
|
|
90
|
+
* a credential that worked), and the second as a file that is genuinely empty.
|
|
91
|
+
* - A file whose bytes are not UTF-8 rides back with `lossy: true` rather than pretending the
|
|
92
|
+
* replacement characters are its content.
|
|
93
|
+
*/
|
|
94
|
+
export async function readFileContent(request, installationId, ref, path, gitRef) {
|
|
95
|
+
const clean = path.replace(/^\/+/, '');
|
|
96
|
+
const query = gitRef ? `?ref=${encodeURIComponent(gitRef)}` : '';
|
|
97
|
+
let json;
|
|
98
|
+
try {
|
|
99
|
+
;
|
|
100
|
+
({ json } = await request(`/repos/${ref.owner}/${ref.repo}/contents/${clean}${query}`, {
|
|
101
|
+
installationId,
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
if (githubApiStatus(err) === 404)
|
|
106
|
+
return null;
|
|
107
|
+
if (isGitHubBlobTooLarge(err))
|
|
108
|
+
throw new VcsBlobTooLargeError('github', GITHUB_CONTENTS_MAX_BYTES);
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
const file = json;
|
|
112
|
+
if (file.type !== 'file' || typeof file.content !== 'string')
|
|
113
|
+
return null;
|
|
114
|
+
if (file.encoding === 'none')
|
|
115
|
+
throw new VcsBlobTooLargeError('github', GITHUB_CONTENTS_MAX_BYTES);
|
|
116
|
+
const { content, lossy } = file.encoding === 'base64'
|
|
117
|
+
? decodeRepoFileBase64(file.content)
|
|
118
|
+
: { content: file.content, lossy: false };
|
|
119
|
+
return { content, sha: file.sha ?? '', ...(lossy ? { lossy: true } : {}) };
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=repoContents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repoContents.js","sourceRoot":"","sources":["../../src/github/repoContents.ts"],"names":[],"mappings":"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"}
|
package/dist/http/env.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Context } from 'hono';
|
|
2
|
-
import type { AgentRunRepository, AuthAttemptRepository, ExecutionRepository, MachineNodeRepository, NotificationChannel, PlatformAlertSink, ResolveBinaryArtifactStore, ConsensusSessionRepository, ResolveRunRepoContext, LocalVcsSetup, Logger, SealedSecretInventory, SecretCipher, ToolSecretResolver, UserRepoAccessRepository, VcsIdentityRegistry, VcsProviderRegistry, VcsWebhookSink, WorkspacePermission, WorkspaceRole } from '@cat-factory/kernel';
|
|
2
|
+
import type { AgentRunRepository, AuthAttemptRepository, ExecutionRepository, MachineNodeRepository, NotificationChannel, PlatformAlertSink, ResolveBinaryArtifactStore, ConsensusSessionRepository, ResolveRepoFilesForCoords, ResolveRunRepoContext, LocalVcsSetup, Logger, SealedSecretInventory, SecretCipher, ToolSecretResolver, UserRepoAccessRepository, VcsIdentityRegistry, VcsProviderRegistry, VcsWebhookSink, WorkspacePermission, WorkspaceRole } from '@cat-factory/kernel';
|
|
3
3
|
import type { ApiKeyService, EnvironmentBackendRegistry, LocalModelEndpointService, LocalSettingsService, OpenRouterCatalogService, PersonalSubscriptionService, ProviderSubscriptionService, NotificationWebhookService, McpAuthorizationServer, PublicApiKeyService, RunnerBackendRegistry, TestSecretsService, CapabilityCredentialsService, McpOAuthService, ValidationConfigService, UserSecretService } from '@cat-factory/integrations';
|
|
4
4
|
import type { Core } from '@cat-factory/orchestration';
|
|
5
5
|
import type { AgentKindRegistry } from '@cat-factory/agents';
|
|
@@ -76,6 +76,15 @@ export interface ServerContainer extends Core {
|
|
|
76
76
|
* spec endpoint returns an empty view.
|
|
77
77
|
*/
|
|
78
78
|
resolveRunRepoContext?: ResolveRunRepoContext;
|
|
79
|
+
/**
|
|
80
|
+
* The BLOCK-LESS sibling of {@link resolveRunRepoContext}: the same checkout-free
|
|
81
|
+
* {@link RepoFiles}, from repo coordinates a caller names.
|
|
82
|
+
* `GET /api/v1/repos/:owner/:name/contents?path=` reads through it (the path is a QUERY value, not
|
|
83
|
+
* a path segment: see the contract), so the public file read is scoped to what this workspace has
|
|
84
|
+
* LINKED rather than to whatever the deployment's credential can reach. Present only when a VCS is
|
|
85
|
+
* wired (both facades compose it with `makeResolveRepoFilesForCoords`); absent → the read is a 503.
|
|
86
|
+
*/
|
|
87
|
+
resolveRepoFilesForCoords?: ResolveRepoFilesForCoords;
|
|
79
88
|
/**
|
|
80
89
|
* Resolve the repo (installation + owner/name + default branch) linked to a
|
|
81
90
|
* block's enclosing service frame — the same ancestry walk the container executor
|
package/dist/http/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/http/env.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/http/env.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type BootstrapJob, type GitHubAvailableRepo, type GitHubConnection, type KubernetesManifestSource, type PublicAvailableRepo, type PublicBootstrapJob, type PublicKubernetesManifestSource, type ServiceProvisioning, type UpdatePublicServiceInput } from '@cat-factory/contracts';
|
|
1
|
+
import { type BootstrapJob, type EnvironmentHandlerView, type GitHubAvailableRepo, type GitHubConnection, type KubernetesManifestSource, type PublicAvailableRepo, type PublicBootstrapJob, type PublicEnvironmentHandler, type PublicKubernetesManifestSource, type ServiceProvisioning, type UpdatePublicServiceInput } from '@cat-factory/contracts';
|
|
2
2
|
import type { RepoUseByRepoId } from '@cat-factory/orchestration';
|
|
3
|
+
import { type RepoFileContent, type RunRepoContext } from '@cat-factory/kernel';
|
|
3
4
|
import { Hono } from 'hono';
|
|
4
5
|
import type { AppEnv, ServerContainer } from '../../http/env.js';
|
|
5
6
|
/**
|
|
@@ -64,11 +65,66 @@ export declare function toPublicAvailableRepo(repo: GitHubAvailableRepo, use: Re
|
|
|
64
65
|
* prevent, on the deployment least able to tell that is what happened.
|
|
65
66
|
*/
|
|
66
67
|
export declare function asVcsRefusal(error: unknown): unknown;
|
|
68
|
+
/**
|
|
69
|
+
* Read one file, turning the two answers only this layer can name into the refusals it documents.
|
|
70
|
+
*
|
|
71
|
+
* **A blob past the PROVIDER's own contents ceiling is the same refusal as one past ours.** GitHub
|
|
72
|
+
* reports it as a `403`, which {@link asVcsRefusal} would otherwise read as a rejected credential,
|
|
73
|
+
* so an operator whose lockfile is 1.4 MB would be told to re-mint a token that works. The adapter
|
|
74
|
+
* names the fact (`VcsBlobTooLargeError`) and this maps it onto `file_too_large` with the provider's
|
|
75
|
+
* limit and NO size, because nothing here measured one.
|
|
76
|
+
*
|
|
77
|
+
* **Bytes that are not UTF-8 are refused rather than answered.** The decode is lossy for a PNG, a
|
|
78
|
+
* tarball or a Latin-1 source file, and handing back replacement characters under a field documented
|
|
79
|
+
* as the file's content is the same lie the truncation above refuses to tell: a caller comparing
|
|
80
|
+
* against its own copy sees a mismatch it cannot attribute. The `sha` rides the refusal, so the
|
|
81
|
+
* byte-exact join a grader wanted is still available.
|
|
82
|
+
*
|
|
83
|
+
* Anything else is a fact about the provider and goes through {@link asVcsRefusal} unchanged.
|
|
84
|
+
*/
|
|
85
|
+
export declare function readGradableFile(repo: RunRepoContext['repo'], at: string, path: string, ref: string | undefined): Promise<RepoFileContent | null>;
|
|
67
86
|
/**
|
|
68
87
|
* Lower a public manifest source onto the internal one, for the same reason and with the same
|
|
69
88
|
* exhaustiveness as the URL source above.
|
|
70
89
|
*/
|
|
71
90
|
export declare function toKubernetesManifestSource(source: PublicKubernetesManifestSource): KubernetesManifestSource;
|
|
91
|
+
/**
|
|
92
|
+
* Project a handler for the LIST, whatever engine services it.
|
|
93
|
+
*
|
|
94
|
+
* `engine` and `backendKind` are open strings here rather than the connect view's literal, because
|
|
95
|
+
* this read reports handlers the deployment SEEDED from its composition root, and a deployment may
|
|
96
|
+
* register an environment backend of its own: the whole point of the registry. Reporting one as
|
|
97
|
+
* `kubernetes` would be the coercion the board projection refuses next door, and omitting it would
|
|
98
|
+
* make a seeded handler indistinguishable from an absent one, which is the state this read exists to
|
|
99
|
+
* make checkable.
|
|
100
|
+
*
|
|
101
|
+
* `endpoint` and not `apiServerUrl`: the Kubernetes noun is false of every other engine, and stating
|
|
102
|
+
* it anyway is what sends an operator whose environment is a VM looking for an apiserver.
|
|
103
|
+
*
|
|
104
|
+
* BOTH manifest-id fields are reported, because the engine's own resolution
|
|
105
|
+
* (`resolveInfraHandler` → `matchesCustom`) matches a service's pinned `manifestId` against EITHER,
|
|
106
|
+
* and the two ways of registering a handler each set only one: a deployment SEEDING one keys it with
|
|
107
|
+
* `manifestId`, while a `remote-custom` connection declares `acceptsManifestId`. Publishing only the
|
|
108
|
+
* second made the commonest seed shape read as a handler serving nothing, so a setup script checking
|
|
109
|
+
* that its own seed landed found no entry naming it while a run against it resolved fine.
|
|
110
|
+
*
|
|
111
|
+
* No `config`, though `EnvironmentHandlerView` carries one for the app's connect-form prefill: it is
|
|
112
|
+
* the internal per-engine bag, deliberately open, and this surface may not freeze it (ADR 0034).
|
|
113
|
+
*/
|
|
114
|
+
export declare function toPublicHandler(view: EnvironmentHandlerView): PublicEnvironmentHandler;
|
|
115
|
+
/**
|
|
116
|
+
* Order handlers by what IDENTIFIES one, comparing code units rather than collating.
|
|
117
|
+
*
|
|
118
|
+
* `localeCompare` is ICU- and locale-dependent, so the same set of handlers can serialise in one
|
|
119
|
+
* order from a workerd isolate and in another from a Node build with a different ICU: precisely the
|
|
120
|
+
* spurious diff the ordering was added to prevent. A code-unit comparison answers the same everywhere,
|
|
121
|
+
* which is what a wire ordering needs.
|
|
122
|
+
*
|
|
123
|
+
* Both manifest ids join the key, since two `custom` handlers keyed to different manifests are
|
|
124
|
+
* otherwise equal here. A handler that ties on all four keys keeps its position from the repository
|
|
125
|
+
* read (the sort is stable), which is the honest fallback: nothing published distinguishes them.
|
|
126
|
+
*/
|
|
127
|
+
export declare function byHandlerIdentity(left: PublicEnvironmentHandler, right: PublicEnvironmentHandler): number;
|
|
72
128
|
/**
|
|
73
129
|
* Lower a public service patch onto the board patch.
|
|
74
130
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PublicProvisioningController.d.ts","sourceRoot":"","sources":["../../../src/modules/publicApi/PublicProvisioningController.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"PublicProvisioningController.d.ts","sourceRoot":"","sources":["../../../src/modules/publicApi/PublicProvisioningController.ts"],"names":[],"mappings":"AAAA,OAAO,EAgBL,KAAK,YAAY,EACjB,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EAErB,KAAK,wBAAwB,EAI7B,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAGvB,KAAK,wBAAwB,EAC7B,KAAK,8BAA8B,EAQnC,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC9B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAKV,eAAe,EAEhB,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EASL,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAE3B,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAgEhE;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,eAAe,EAC1B,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAQlC;AA6FD,wBAAgB,4BAA4B,IAAI,IAAI,CAAC,MAAM,CAAC,CAQ3D;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,YAAY,GAAG,kBAAkB,CAgB1E;AAiDD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,mBAAmB,EACzB,GAAG,EAAE,eAAe,GACnB,mBAAmB,CAsBrB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAoBpD;AAoJD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,EAC5B,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GAAG,SAAS,GACtB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAsBjC;AA6DD;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,8BAA8B,GACrC,wBAAwB,CAW1B;AA8BD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,wBAAwB,CAYtF;AA8CD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,wBAAwB,EAC9B,KAAK,EAAE,wBAAwB,GAC9B,MAAM,CAOR;AAiCD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,wBAAwB,EAC/B,MAAM,EAAE,mBAAmB,GAAG,SAAS,GACtC;IACD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,mBAAmB,CAAA;CACnC,CAQA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { connectPublicEnvironmentContract, getPublicRepoBootstrapContract, getPublicVcsConnectionContract, linkPublicRepoContract, listPublicAvailableReposContract, listPublicModelPresetsContract, listPublicRiskPoliciesContract, listPublicWiredModelsContract, startPublicRepoBootstrapContract, testPublicEnvironmentConnectionContract, updatePublicServiceContract, UNATTRIBUTED_BLOCK_EDIT_AUTHORITY, } from '@cat-factory/contracts';
|
|
2
|
-
import { NotFoundError, RateLimitedError, UnavailableError, VcsApiError, individualVendorForModelId, isVcsRateLimited, } from '@cat-factory/kernel';
|
|
1
|
+
import { connectPublicEnvironmentContract, getPublicRepoBootstrapContract, getPublicRepoFileContract, getPublicVcsConnectionContract, linkPublicRepoContract, listPublicAvailableReposContract, listPublicEnvironmentConnectionsContract, listPublicModelPresetsContract, listPublicRiskPoliciesContract, listPublicWiredModelsContract, MAX_REPO_FILE_CHARS, startPublicRepoBootstrapContract, testPublicEnvironmentConnectionContract, updatePublicServiceContract, UNATTRIBUTED_BLOCK_EDIT_AUTHORITY, } from '@cat-factory/contracts';
|
|
2
|
+
import { NotFoundError, RateLimitedError, UnavailableError, ValidationError, VcsApiError, VcsBlobTooLargeError, individualVendorForModelId, isVcsRateLimited, } from '@cat-factory/kernel';
|
|
3
3
|
import { buildHonoRoute } from '@toad-contracts/hono';
|
|
4
4
|
import { Hono } from 'hono';
|
|
5
5
|
import { requireCapability } from '../../http/guards.js';
|
|
@@ -365,6 +365,99 @@ function registerRepoAdoptionRoutes(app) {
|
|
|
365
365
|
}
|
|
366
366
|
return c.json(toPublicRepo(adopted), 200);
|
|
367
367
|
});
|
|
368
|
+
registerRepoFileRoute(app);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* ONE file out of a linked repository, so a caller can grade what a run COMMITTED.
|
|
372
|
+
*
|
|
373
|
+
* It closes the loop on everything else this surface can start. A caller could create a repository,
|
|
374
|
+
* adopt one, file work against it and watch a run merge, and then had nothing to read but the
|
|
375
|
+
* agent's own final reply, which is asserting on model prose: swap the model and it goes red having
|
|
376
|
+
* found nothing wrong. The only real alternative was a SECOND VCS credential in the caller's config,
|
|
377
|
+
* with its own scopes to get right, for data the workspace's own connection already reads.
|
|
378
|
+
*
|
|
379
|
+
* Five outcomes, kept apart because each takes a different action, and because folding any of them
|
|
380
|
+
* onto "no such file" is the same mistake `PublicSpecController` documents at length: a repository
|
|
381
|
+
* that answers nothing and a file that is genuinely absent look identical from a provider. Two of the
|
|
382
|
+
* five are what {@link readGradableFile} exists to name.
|
|
383
|
+
*/
|
|
384
|
+
function registerRepoFileRoute(app) {
|
|
385
|
+
buildHonoRoute(app, getPublicRepoFileContract, async (c) => {
|
|
386
|
+
const auth = await authorizeOrThrow(c, getPublicRepoFileContract.minScope);
|
|
387
|
+
const resolve = requireCapability(c.get('container').resolveRepoFilesForCoords, 'No version-control integration is configured for this deployment', 'vcs_not_configured');
|
|
388
|
+
const { owner, name } = c.req.valid('param');
|
|
389
|
+
const { path, ref } = c.req.valid('query');
|
|
390
|
+
// Resolved against the workspace's PROJECTED repos, so this reads only what this workspace has
|
|
391
|
+
// LINKED. A repository the deployment's credential could reach and nobody adopted is absent
|
|
392
|
+
// here exactly as it is from `GET /api/v1/repos`, which is what stops the endpoint becoming a
|
|
393
|
+
// way to read any repository an installation happens to cover.
|
|
394
|
+
const context = await resolve(auth.workspaceId, { owner, repo: name });
|
|
395
|
+
if (!context) {
|
|
396
|
+
throw new NotFoundError('repository', `${owner}/${name}`, { reason: 'repo_not_linked' });
|
|
397
|
+
}
|
|
398
|
+
// `ref` is passed through EXACTLY as the request gave it, absent included, because the port
|
|
399
|
+
// resolves the repository's own default branch for an omitted one. `context.baseBranch` is the
|
|
400
|
+
// wrong value to substitute: `makeResolveRepoFilesForCoords` INVENTS `main` for a projection row
|
|
401
|
+
// carrying no default branch, so a repository whose default is `master` would be read at a
|
|
402
|
+
// branch it does not have and answer `file_not_found` for a file that is right there, while the
|
|
403
|
+
// response named the invented branch as the thing graded.
|
|
404
|
+
const at = `${owner}/${name}:${path}`;
|
|
405
|
+
const file = await readGradableFile(context.repo, at, path, ref);
|
|
406
|
+
if (!file) {
|
|
407
|
+
throw new NotFoundError('file', `${at}@${ref ?? 'the default branch'}`, {
|
|
408
|
+
reason: 'file_not_found',
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
if (file.content.length > MAX_REPO_FILE_CHARS) {
|
|
412
|
+
// REFUSED, never truncated. A caller reading a file to grade what an agent committed is
|
|
413
|
+
// joining on its exact bytes, and a silently shortened answer is indistinguishable from an
|
|
414
|
+
// agent that wrote a shorter file. The size and the limit ride `details` because those are
|
|
415
|
+
// facts a caller can act on where a truncation is not.
|
|
416
|
+
//
|
|
417
|
+
// A 422 and not a 413: `Content Too Large` is a statement about the REQUEST entity, and this
|
|
418
|
+
// request is perfectly well formed. The status class is "structurally valid, refused by a
|
|
419
|
+
// domain rule", which is the same reading `resolveRepoTarget` takes for an unlinked service,
|
|
420
|
+
// and the machine-readable cause is `details.reason` as it is everywhere else on this surface.
|
|
421
|
+
throw new ValidationError(`${at} is ${file.content.length} characters, past the ${MAX_REPO_FILE_CHARS} this read ` +
|
|
422
|
+
`answers with`, { reason: 'file_too_large', size: file.content.length, limit: MAX_REPO_FILE_CHARS });
|
|
423
|
+
}
|
|
424
|
+
return c.json({ owner, name, path, ref: ref ?? null, sha: file.sha, content: file.content }, 200);
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Read one file, turning the two answers only this layer can name into the refusals it documents.
|
|
429
|
+
*
|
|
430
|
+
* **A blob past the PROVIDER's own contents ceiling is the same refusal as one past ours.** GitHub
|
|
431
|
+
* reports it as a `403`, which {@link asVcsRefusal} would otherwise read as a rejected credential,
|
|
432
|
+
* so an operator whose lockfile is 1.4 MB would be told to re-mint a token that works. The adapter
|
|
433
|
+
* names the fact (`VcsBlobTooLargeError`) and this maps it onto `file_too_large` with the provider's
|
|
434
|
+
* limit and NO size, because nothing here measured one.
|
|
435
|
+
*
|
|
436
|
+
* **Bytes that are not UTF-8 are refused rather than answered.** The decode is lossy for a PNG, a
|
|
437
|
+
* tarball or a Latin-1 source file, and handing back replacement characters under a field documented
|
|
438
|
+
* as the file's content is the same lie the truncation above refuses to tell: a caller comparing
|
|
439
|
+
* against its own copy sees a mismatch it cannot attribute. The `sha` rides the refusal, so the
|
|
440
|
+
* byte-exact join a grader wanted is still available.
|
|
441
|
+
*
|
|
442
|
+
* Anything else is a fact about the provider and goes through {@link asVcsRefusal} unchanged.
|
|
443
|
+
*/
|
|
444
|
+
export async function readGradableFile(repo, at, path, ref) {
|
|
445
|
+
let file;
|
|
446
|
+
try {
|
|
447
|
+
file = await repo.getFile(path, ref);
|
|
448
|
+
}
|
|
449
|
+
catch (error) {
|
|
450
|
+
if (error instanceof VcsBlobTooLargeError) {
|
|
451
|
+
throw new ValidationError(`${at} is past the ${error.limitBytes} bytes ${error.provider} will serve through its ` +
|
|
452
|
+
`contents API, so this read cannot answer with it`, { reason: 'file_too_large', limit: error.limitBytes });
|
|
453
|
+
}
|
|
454
|
+
throw asVcsRefusal(error);
|
|
455
|
+
}
|
|
456
|
+
if (file?.lossy) {
|
|
457
|
+
throw new ValidationError(`${at} is not UTF-8 text, so this read cannot answer with its content; its blob sha is ` +
|
|
458
|
+
`${file.sha}`, { reason: 'file_not_text', sha: file.sha });
|
|
459
|
+
}
|
|
460
|
+
return file;
|
|
368
461
|
}
|
|
369
462
|
// ---- the environment connection (the ENGINE half) ---------------------------
|
|
370
463
|
/**
|
|
@@ -449,6 +542,10 @@ function toInfraHandlerConfig(connection) {
|
|
|
449
542
|
*
|
|
450
543
|
* `baseUrl` is the connection's own endpoint, which for a Kubernetes engine is the apiserver, so it
|
|
451
544
|
* is reported under the name the caller supplied it as rather than the engine's generic one.
|
|
545
|
+
*
|
|
546
|
+
* The CONNECT response only, which is why `engine` can be the literal: this surface registers
|
|
547
|
+
* kubernetes and nothing else, so it is true of every handler this call can produce. The LIST is a
|
|
548
|
+
* different question and takes {@link toPublicHandler}.
|
|
452
549
|
*/
|
|
453
550
|
function toPublicConnectionView(view) {
|
|
454
551
|
return {
|
|
@@ -459,6 +556,42 @@ function toPublicConnectionView(view) {
|
|
|
459
556
|
secretKeys: view.secretKeys,
|
|
460
557
|
};
|
|
461
558
|
}
|
|
559
|
+
/**
|
|
560
|
+
* Project a handler for the LIST, whatever engine services it.
|
|
561
|
+
*
|
|
562
|
+
* `engine` and `backendKind` are open strings here rather than the connect view's literal, because
|
|
563
|
+
* this read reports handlers the deployment SEEDED from its composition root, and a deployment may
|
|
564
|
+
* register an environment backend of its own: the whole point of the registry. Reporting one as
|
|
565
|
+
* `kubernetes` would be the coercion the board projection refuses next door, and omitting it would
|
|
566
|
+
* make a seeded handler indistinguishable from an absent one, which is the state this read exists to
|
|
567
|
+
* make checkable.
|
|
568
|
+
*
|
|
569
|
+
* `endpoint` and not `apiServerUrl`: the Kubernetes noun is false of every other engine, and stating
|
|
570
|
+
* it anyway is what sends an operator whose environment is a VM looking for an apiserver.
|
|
571
|
+
*
|
|
572
|
+
* BOTH manifest-id fields are reported, because the engine's own resolution
|
|
573
|
+
* (`resolveInfraHandler` → `matchesCustom`) matches a service's pinned `manifestId` against EITHER,
|
|
574
|
+
* and the two ways of registering a handler each set only one: a deployment SEEDING one keys it with
|
|
575
|
+
* `manifestId`, while a `remote-custom` connection declares `acceptsManifestId`. Publishing only the
|
|
576
|
+
* second made the commonest seed shape read as a handler serving nothing, so a setup script checking
|
|
577
|
+
* that its own seed landed found no entry naming it while a run against it resolved fine.
|
|
578
|
+
*
|
|
579
|
+
* No `config`, though `EnvironmentHandlerView` carries one for the app's connect-form prefill: it is
|
|
580
|
+
* the internal per-engine bag, deliberately open, and this surface may not freeze it (ADR 0034).
|
|
581
|
+
*/
|
|
582
|
+
export function toPublicHandler(view) {
|
|
583
|
+
return {
|
|
584
|
+
provisionType: view.provisionType,
|
|
585
|
+
manifestId: view.manifestId,
|
|
586
|
+
acceptsManifestId: view.acceptsManifestId,
|
|
587
|
+
engine: view.engine,
|
|
588
|
+
backendKind: view.backendKind,
|
|
589
|
+
label: view.label,
|
|
590
|
+
endpoint: view.baseUrl,
|
|
591
|
+
secretKeys: view.secretKeys,
|
|
592
|
+
connectedAt: view.connectedAt,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
462
595
|
function registerEnvironmentRoutes(app) {
|
|
463
596
|
// Probe without persisting. `ok: false` is an ANSWER (the cluster refused the token), so it is a
|
|
464
597
|
// 200 carrying the verdict rather than an error: a caller distinguishes "the cluster said no"
|
|
@@ -490,6 +623,41 @@ function registerEnvironmentRoutes(app) {
|
|
|
490
623
|
});
|
|
491
624
|
return c.json(toPublicConnectionView(view), 201);
|
|
492
625
|
});
|
|
626
|
+
// The read half. Ordered so a caller diffing two workspaces (or its own setup before and after)
|
|
627
|
+
// compares two stable lists rather than two insertion orders.
|
|
628
|
+
buildHonoRoute(app, listPublicEnvironmentConnectionsContract, async (c) => {
|
|
629
|
+
const auth = await authorizeOrThrow(c, listPublicEnvironmentConnectionsContract.minScope);
|
|
630
|
+
const environments = requireEnvironments(c);
|
|
631
|
+
const handlers = await environments.connectionService.listHandlers(auth.workspaceId);
|
|
632
|
+
const connections = handlers.map(toPublicHandler).sort(byHandlerIdentity);
|
|
633
|
+
return c.json({ connections }, 200);
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Order handlers by what IDENTIFIES one, comparing code units rather than collating.
|
|
638
|
+
*
|
|
639
|
+
* `localeCompare` is ICU- and locale-dependent, so the same set of handlers can serialise in one
|
|
640
|
+
* order from a workerd isolate and in another from a Node build with a different ICU: precisely the
|
|
641
|
+
* spurious diff the ordering was added to prevent. A code-unit comparison answers the same everywhere,
|
|
642
|
+
* which is what a wire ordering needs.
|
|
643
|
+
*
|
|
644
|
+
* Both manifest ids join the key, since two `custom` handlers keyed to different manifests are
|
|
645
|
+
* otherwise equal here. A handler that ties on all four keys keeps its position from the repository
|
|
646
|
+
* read (the sort is stable), which is the honest fallback: nothing published distinguishes them.
|
|
647
|
+
*/
|
|
648
|
+
export function byHandlerIdentity(left, right) {
|
|
649
|
+
return (compareCodeUnits(left.provisionType, right.provisionType) ||
|
|
650
|
+
compareCodeUnits(left.manifestId, right.manifestId) ||
|
|
651
|
+
compareCodeUnits(left.acceptsManifestId, right.acceptsManifestId) ||
|
|
652
|
+
compareCodeUnits(left.label, right.label));
|
|
653
|
+
}
|
|
654
|
+
/** An absent id sorts first, which is the only ordering a null can honestly take. */
|
|
655
|
+
function compareCodeUnits(left, right) {
|
|
656
|
+
const a = left ?? '';
|
|
657
|
+
const b = right ?? '';
|
|
658
|
+
if (a === b)
|
|
659
|
+
return 0;
|
|
660
|
+
return a < b ? -1 : 1;
|
|
493
661
|
}
|
|
494
662
|
// ---- a service's provisioning (the SOURCE half) -----------------------------
|
|
495
663
|
function registerServiceRoutes(app) {
|
|
@@ -529,7 +697,7 @@ export function toBlockPatch(input, stored) {
|
|
|
529
697
|
* Overlay a public provisioning patch onto what the service already declares.
|
|
530
698
|
*
|
|
531
699
|
* `provisioning` is ONE JSON column and `updateBlock` REPLACES it wholesale, where this surface
|
|
532
|
-
* publishes
|
|
700
|
+
* publishes a few of its dozen fields. Writing just those would therefore delete every field the
|
|
533
701
|
* public shape cannot express: a Kubernetes service's `images`, `secretInjections` and
|
|
534
702
|
* `helmReleases`, authored in the app by someone who is not the caller. The next deploy would
|
|
535
703
|
* render manifests with no image overrides and no Secrets, which is the "empty environment that
|
|
@@ -539,14 +707,42 @@ export function toBlockPatch(input, stored) {
|
|
|
539
707
|
* A patch that CHANGES the provision type replaces instead of overlaying: the stored remainder
|
|
540
708
|
* belongs to the type being left behind, so carrying it forward would attach one engine's
|
|
541
709
|
* configuration to another.
|
|
710
|
+
*
|
|
711
|
+
* Exhaustive over the public variant through a `never`, so a member added there cannot reach the
|
|
712
|
+
* stored column as an unlowered shape.
|
|
542
713
|
*/
|
|
543
714
|
function mergeProvisioning(patch, stored) {
|
|
544
715
|
const base = stored?.type === patch.type ? stored : undefined;
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
716
|
+
switch (patch.type) {
|
|
717
|
+
case 'kubernetes':
|
|
718
|
+
return {
|
|
719
|
+
...base,
|
|
720
|
+
type: 'kubernetes',
|
|
721
|
+
manifestSource: toKubernetesManifestSource(patch.manifestSource),
|
|
722
|
+
};
|
|
723
|
+
case 'custom': {
|
|
724
|
+
// `manifestPath` is the one stored field this surface PUBLISHES, so it is also the only one a
|
|
725
|
+
// patch can be said to have left out on purpose: omitted, it is CLEARED, and the manifest
|
|
726
|
+
// type's own default applies again. Carried over from the stored row (which is what a plain
|
|
727
|
+
// `...base` does), a caller correcting a path back to the default would keep deploying the old
|
|
728
|
+
// one and have no way to say so, since the public shape has no other way to express "none".
|
|
729
|
+
// Every field the public shape cannot express still rides `...rest`, for the reason the
|
|
730
|
+
// function's own doc gives.
|
|
731
|
+
const { manifestPath: _cleared, ...rest } = base ?? {};
|
|
732
|
+
return {
|
|
733
|
+
...rest,
|
|
734
|
+
type: 'custom',
|
|
735
|
+
manifestId: patch.manifestId,
|
|
736
|
+
...(patch.manifestPath === undefined ? {} : { manifestPath: patch.manifestPath }),
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
default:
|
|
740
|
+
return unreachableProvisioning(patch);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
/** The compile-time half of the lowering above: a new public member has to be lowered explicitly. */
|
|
744
|
+
function unreachableProvisioning(value) {
|
|
745
|
+
throw new Error(`unmapped public service provisioning: ${JSON.stringify(value)}`);
|
|
550
746
|
}
|
|
551
747
|
// ---- what this deployment has WIRED -----------------------------------------
|
|
552
748
|
/**
|