@cat-factory/agents 0.95.1 → 0.96.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/kinds/traits.d.ts +22 -0
- package/dist/agents/kinds/traits.d.ts.map +1 -1
- package/dist/agents/kinds/traits.js +42 -3
- package/dist/agents/kinds/traits.js.map +1 -1
- package/dist/foundationalServices/FoundationalServiceCatalogService.d.ts +65 -0
- package/dist/foundationalServices/FoundationalServiceCatalogService.d.ts.map +1 -0
- package/dist/foundationalServices/FoundationalServiceCatalogService.js +284 -0
- package/dist/foundationalServices/FoundationalServiceCatalogService.js.map +1 -0
- package/dist/foundationalServices/FoundationalServiceRunResolver.d.ts +37 -0
- package/dist/foundationalServices/FoundationalServiceRunResolver.d.ts.map +1 -0
- package/dist/foundationalServices/FoundationalServiceRunResolver.js +92 -0
- package/dist/foundationalServices/FoundationalServiceRunResolver.js.map +1 -0
- package/dist/foundationalServices/FoundationalServiceSourceService.d.ts +86 -0
- package/dist/foundationalServices/FoundationalServiceSourceService.d.ts.map +1 -0
- package/dist/foundationalServices/FoundationalServiceSourceService.js +346 -0
- package/dist/foundationalServices/FoundationalServiceSourceService.js.map +1 -0
- package/dist/foundationalServices/contract-validation.d.ts +8 -0
- package/dist/foundationalServices/contract-validation.d.ts.map +1 -0
- package/dist/foundationalServices/contract-validation.js +43 -0
- package/dist/foundationalServices/contract-validation.js.map +1 -0
- package/dist/foundationalServices/foundational-catalog.d.ts +22 -0
- package/dist/foundationalServices/foundational-catalog.d.ts.map +1 -0
- package/dist/foundationalServices/foundational-catalog.js +58 -0
- package/dist/foundationalServices/foundational-catalog.js.map +1 -0
- package/dist/foundationalServices/foundational-source.logic.d.ts +51 -0
- package/dist/foundationalServices/foundational-source.logic.d.ts.map +1 -0
- package/dist/foundationalServices/foundational-source.logic.js +109 -0
- package/dist/foundationalServices/foundational-source.logic.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FoundationalServiceRunResolver.js","sourceRoot":"","sources":["../../src/foundationalServices/FoundationalServiceRunResolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { FoundationalServiceOwnerKind, FoundationalServiceSource, FoundationalServiceSourceStatus, FoundationalServiceSyncResult, LinkFoundationalServiceSourceInput } from '@cat-factory/contracts';
|
|
2
|
+
import type { ApiContractRepository, Clock, FoundationalServiceRepository, FoundationalServiceSourceRepository, GitHubClient, IdGenerator, Logger } from '@cat-factory/kernel';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the GitHub App installation id that can read a tier's repos — a workspace source
|
|
5
|
+
* reads through the workspace's installation, an account source through the account's.
|
|
6
|
+
* Returns null when none is available, so a sync fails with a clear error rather than a
|
|
7
|
+
* silent empty pull.
|
|
8
|
+
*/
|
|
9
|
+
export type ResolveFoundationalInstallationId = (ownerKind: FoundationalServiceOwnerKind, ownerId: string) => Promise<number | null>;
|
|
10
|
+
export interface FoundationalServiceSourceServiceDependencies {
|
|
11
|
+
foundationalServiceSourceRepository: FoundationalServiceSourceRepository;
|
|
12
|
+
foundationalServiceRepository: FoundationalServiceRepository;
|
|
13
|
+
apiContractRepository: ApiContractRepository;
|
|
14
|
+
githubClient: GitHubClient;
|
|
15
|
+
resolveInstallationId: ResolveFoundationalInstallationId;
|
|
16
|
+
idGenerator: IdGenerator;
|
|
17
|
+
clock: Clock;
|
|
18
|
+
logger?: Logger;
|
|
19
|
+
/** Drops the cached merged catalog for a tier after a sync/unlink changed a row. */
|
|
20
|
+
invalidateCatalog?: (ownerKind: FoundationalServiceOwnerKind, ownerId: string) => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Repo-sourced foundational services: link a repo FOLDER of service definitions (or an
|
|
24
|
+
* explicit FILE list for one service), resync it, and answer the cheap "check for changes"
|
|
25
|
+
* without writing. Reads go through the tier's existing GitHub installation — no new
|
|
26
|
+
* credential store.
|
|
27
|
+
*
|
|
28
|
+
* The shared repo-source engine (`repoSourceSync`) owns the mechanics that every repo-sourced
|
|
29
|
+
* library gets identically — pin the head commit BEFORE reading, run the reconcile, sweep
|
|
30
|
+
* tombstones by produced id, stamp the sync state, invalidate only on a real change. The
|
|
31
|
+
* differentiator supplied here is what a UNIT is, and there are two of them:
|
|
32
|
+
*
|
|
33
|
+
* - **`directory`**: one service per immediate subdirectory, identified by its `service.md`.
|
|
34
|
+
* - **`files`**: one service, named on the LINK, whose contracts are the linked file paths.
|
|
35
|
+
* There is no directory convention to read identity from, which is exactly why the link
|
|
36
|
+
* carries it (refused at the write boundary otherwise).
|
|
37
|
+
*
|
|
38
|
+
* Both modes anchor their staleness probe on ONE directory (`dirPath`), so a `files` source
|
|
39
|
+
* with a dozen linked files still costs a single commit read per freshness check.
|
|
40
|
+
*/
|
|
41
|
+
export declare class FoundationalServiceSourceService {
|
|
42
|
+
private readonly deps;
|
|
43
|
+
private readonly log;
|
|
44
|
+
constructor(deps: FoundationalServiceSourceServiceDependencies);
|
|
45
|
+
/** Linked sources for a tier + their last-synced state. */
|
|
46
|
+
list(ownerKind: FoundationalServiceOwnerKind, ownerId: string): Promise<FoundationalServiceSource[]>;
|
|
47
|
+
/** Link a repo folder (or file list) as a source. Does not sync (call {@link sync}). */
|
|
48
|
+
link(ownerKind: FoundationalServiceOwnerKind, ownerId: string, input: LinkFoundationalServiceSourceInput): Promise<FoundationalServiceSource>;
|
|
49
|
+
/** Unlink a source and tombstone every service it produced. */
|
|
50
|
+
unlink(ownerKind: FoundationalServiceOwnerKind, ownerId: string, sourceId: string): Promise<void>;
|
|
51
|
+
/** The cheap "check for changes": one head-commit read compared to the pinned commit. */
|
|
52
|
+
status(ownerKind: FoundationalServiceOwnerKind, ownerId: string, sourceId: string): Promise<FoundationalServiceSourceStatus>;
|
|
53
|
+
/** Resync a source by id, resolving its owner from the stored row (the sweeper's entry point). */
|
|
54
|
+
syncById(sourceId: string): Promise<FoundationalServiceSyncResult>;
|
|
55
|
+
/** Resync a source: read the tree, upsert changed services, tombstone removed ones. */
|
|
56
|
+
sync(ownerKind: FoundationalServiceOwnerKind, ownerId: string, sourceId: string): Promise<FoundationalServiceSyncResult>;
|
|
57
|
+
/**
|
|
58
|
+
* AUTOREFRESH: resync every source whose last sync is older than `staleAfterMs`, bounded to
|
|
59
|
+
* `limit` per pass. Driven by the periodic sweep on both facades (a Cloudflare cron tick, a
|
|
60
|
+
* Node interval), so a linked repo stays fresh whether or not anyone opens the management
|
|
61
|
+
* surface or starts a run.
|
|
62
|
+
*
|
|
63
|
+
* Best-effort PER SOURCE: one repo whose installation was revoked must not stop the pass from
|
|
64
|
+
* refreshing the rest, so each failure is logged with its cause and the sweep continues. The
|
|
65
|
+
* count returned is sources SYNCED, not sources attempted.
|
|
66
|
+
*/
|
|
67
|
+
refreshStale(staleAfterMs: number, limit: number): Promise<number>;
|
|
68
|
+
private runSync;
|
|
69
|
+
/** `directory` mode: one service per immediate subdirectory carrying a `service.md`. */
|
|
70
|
+
private reconcileDirectories;
|
|
71
|
+
/** `files` mode: ONE service, named on the link, whose contracts are the linked paths. */
|
|
72
|
+
private reconcileFiles;
|
|
73
|
+
/**
|
|
74
|
+
* Read the given repo paths and keep the ones that ARE contract documents. A file whose
|
|
75
|
+
* format is unrecognised is skipped rather than stored as an opaque blob: the format is what
|
|
76
|
+
* tells a downstream agent how to read it, and a "contract" nobody can interpret is worse in
|
|
77
|
+
* an agent's context than an absent one.
|
|
78
|
+
*/
|
|
79
|
+
private readContracts;
|
|
80
|
+
private writeService;
|
|
81
|
+
private listDir;
|
|
82
|
+
private readFile;
|
|
83
|
+
private require;
|
|
84
|
+
private requireInstallation;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=FoundationalServiceSourceService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FoundationalServiceSourceService.d.ts","sourceRoot":"","sources":["../../src/foundationalServices/FoundationalServiceSourceService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,4BAA4B,EAC5B,yBAAyB,EACzB,+BAA+B,EAC/B,6BAA6B,EAC7B,kCAAkC,EACnC,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAEV,qBAAqB,EACrB,KAAK,EAEL,6BAA6B,EAE7B,mCAAmC,EACnC,YAAY,EACZ,WAAW,EACX,MAAM,EAEP,MAAM,qBAAqB,CAAA;AAyB5B;;;;;GAKG;AACH,MAAM,MAAM,iCAAiC,GAAG,CAC9C,SAAS,EAAE,4BAA4B,EACvC,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;AAE3B,MAAM,WAAW,4CAA4C;IAC3D,mCAAmC,EAAE,mCAAmC,CAAA;IACxE,6BAA6B,EAAE,6BAA6B,CAAA;IAC5D,qBAAqB,EAAE,qBAAqB,CAAA;IAC5C,YAAY,EAAE,YAAY,CAAA;IAC1B,qBAAqB,EAAE,iCAAiC,CAAA;IACxD,WAAW,EAAE,WAAW,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,4BAA4B,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAChG;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,gCAAgC;IAG/B,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAE5B,YAA6B,IAAI,EAAE,4CAA4C,EAE9E;IAED,2DAA2D;IACrD,IAAI,CACR,SAAS,EAAE,4BAA4B,EACvC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAGtC;IAED,wFAAwF;IAClF,IAAI,CACR,SAAS,EAAE,4BAA4B,EACvC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,kCAAkC,GACxC,OAAO,CAAC,yBAAyB,CAAC,CA4BpC;IAED,+DAA+D;IACzD,MAAM,CACV,SAAS,EAAE,4BAA4B,EACvC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAgBf;IAED,yFAAyF;IACnF,MAAM,CACV,SAAS,EAAE,4BAA4B,EACvC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,+BAA+B,CAAC,CAI1C;IAED,kGAAkG;IAC5F,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,6BAA6B,CAAC,CAQvE;IAED,uFAAuF;IACjF,IAAI,CACR,SAAS,EAAE,4BAA4B,EACvC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,6BAA6B,CAAC,CAExC;IAED;;;;;;;;;OASG;IACG,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBvE;YAIa,OAAO;IA+CrB,wFAAwF;YAC1E,oBAAoB;IA+ElC,0FAA0F;YAC5E,cAAc;IAmD5B;;;;;OAKG;YACW,aAAa;YA4Cb,YAAY;IAsC1B,OAAO,CAAC,OAAO;IAcf,OAAO,CAAC,QAAQ;YAcF,OAAO;YAkBP,mBAAmB;CAUlC"}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { NotFoundError, ValidationError, assertFound, detectContractFormat, noopLogger, runBestEffort, summarizeContract, } from '@cat-factory/kernel';
|
|
2
|
+
import { normalizeDirPath, probeRepoSourceStatus, syncRepoSource, } from '../repoSourceSync/repo-source-sync.js';
|
|
3
|
+
import { SERVICE_MANIFEST_FILE, commonDirectory, contractIdFromPath, contractTitleFromPath, normalizeFilePath, parseServiceManifest, slugFromDirName, } from './foundational-source.logic.js';
|
|
4
|
+
/**
|
|
5
|
+
* Repo-sourced foundational services: link a repo FOLDER of service definitions (or an
|
|
6
|
+
* explicit FILE list for one service), resync it, and answer the cheap "check for changes"
|
|
7
|
+
* without writing. Reads go through the tier's existing GitHub installation — no new
|
|
8
|
+
* credential store.
|
|
9
|
+
*
|
|
10
|
+
* The shared repo-source engine (`repoSourceSync`) owns the mechanics that every repo-sourced
|
|
11
|
+
* library gets identically — pin the head commit BEFORE reading, run the reconcile, sweep
|
|
12
|
+
* tombstones by produced id, stamp the sync state, invalidate only on a real change. The
|
|
13
|
+
* differentiator supplied here is what a UNIT is, and there are two of them:
|
|
14
|
+
*
|
|
15
|
+
* - **`directory`**: one service per immediate subdirectory, identified by its `service.md`.
|
|
16
|
+
* - **`files`**: one service, named on the LINK, whose contracts are the linked file paths.
|
|
17
|
+
* There is no directory convention to read identity from, which is exactly why the link
|
|
18
|
+
* carries it (refused at the write boundary otherwise).
|
|
19
|
+
*
|
|
20
|
+
* Both modes anchor their staleness probe on ONE directory (`dirPath`), so a `files` source
|
|
21
|
+
* with a dozen linked files still costs a single commit read per freshness check.
|
|
22
|
+
*/
|
|
23
|
+
export class FoundationalServiceSourceService {
|
|
24
|
+
deps;
|
|
25
|
+
log;
|
|
26
|
+
constructor(deps) {
|
|
27
|
+
this.deps = deps;
|
|
28
|
+
this.log = deps.logger ?? noopLogger;
|
|
29
|
+
}
|
|
30
|
+
/** Linked sources for a tier + their last-synced state. */
|
|
31
|
+
async list(ownerKind, ownerId) {
|
|
32
|
+
const rows = await this.deps.foundationalServiceSourceRepository.listByOwner(ownerKind, ownerId);
|
|
33
|
+
return rows.map(toWire);
|
|
34
|
+
}
|
|
35
|
+
/** Link a repo folder (or file list) as a source. Does not sync (call {@link sync}). */
|
|
36
|
+
async link(ownerKind, ownerId, input) {
|
|
37
|
+
const now = this.deps.clock.now();
|
|
38
|
+
const filePaths = (input.filePaths ?? []).map(normalizeFilePath).filter(Boolean);
|
|
39
|
+
// A `files` source anchors its probe on the linked files' deepest common directory, so
|
|
40
|
+
// one commit read covers the whole set (see the class note). A `directory` source anchors
|
|
41
|
+
// on the scanned subtree itself.
|
|
42
|
+
const dirPath = input.mode === 'files' ? commonDirectory(filePaths) : normalizeDirPath(input.dirPath);
|
|
43
|
+
const record = {
|
|
44
|
+
id: this.deps.idGenerator.next('fndsrc'),
|
|
45
|
+
ownerKind,
|
|
46
|
+
ownerId,
|
|
47
|
+
repoOwner: input.repoOwner.trim(),
|
|
48
|
+
repoName: input.repoName.trim(),
|
|
49
|
+
gitRef: input.gitRef?.trim() || 'HEAD',
|
|
50
|
+
mode: input.mode,
|
|
51
|
+
dirPath,
|
|
52
|
+
filePaths,
|
|
53
|
+
serviceId: input.mode === 'files' ? (input.serviceId ?? null) : null,
|
|
54
|
+
serviceName: input.mode === 'files' ? (input.serviceName ?? null) : null,
|
|
55
|
+
serviceSummary: input.mode === 'files' ? (input.serviceSummary ?? null) : null,
|
|
56
|
+
lastSyncedCommit: null,
|
|
57
|
+
lastSyncedAt: null,
|
|
58
|
+
createdAt: now,
|
|
59
|
+
deletedAt: null,
|
|
60
|
+
};
|
|
61
|
+
await this.deps.foundationalServiceSourceRepository.upsert(record);
|
|
62
|
+
return toWire(record);
|
|
63
|
+
}
|
|
64
|
+
/** Unlink a source and tombstone every service it produced. */
|
|
65
|
+
async unlink(ownerKind, ownerId, sourceId) {
|
|
66
|
+
const source = await this.require(ownerKind, ownerId, sourceId);
|
|
67
|
+
const now = this.deps.clock.now();
|
|
68
|
+
const services = await this.deps.foundationalServiceRepository.listBySource(sourceId);
|
|
69
|
+
if (services.length > 0) {
|
|
70
|
+
await this.deps.foundationalServiceRepository.softDeleteBySource(sourceId, now);
|
|
71
|
+
for (const service of services) {
|
|
72
|
+
await this.deps.apiContractRepository.deleteForService(service.ownerKind, service.ownerId, service.serviceId);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
await this.deps.foundationalServiceSourceRepository.softDelete(source.id, now);
|
|
76
|
+
if (services.length > 0)
|
|
77
|
+
await this.deps.invalidateCatalog?.(ownerKind, ownerId);
|
|
78
|
+
}
|
|
79
|
+
/** The cheap "check for changes": one head-commit read compared to the pinned commit. */
|
|
80
|
+
async status(ownerKind, ownerId, sourceId) {
|
|
81
|
+
const source = await this.require(ownerKind, ownerId, sourceId);
|
|
82
|
+
const installationId = await this.requireInstallation(source);
|
|
83
|
+
return probeRepoSourceStatus({ source, installationId, githubClient: this.deps.githubClient });
|
|
84
|
+
}
|
|
85
|
+
/** Resync a source by id, resolving its owner from the stored row (the sweeper's entry point). */
|
|
86
|
+
async syncById(sourceId) {
|
|
87
|
+
const source = assertFound(await this.deps.foundationalServiceSourceRepository.get(sourceId), 'FoundationalServiceSource', sourceId);
|
|
88
|
+
if (source.deletedAt !== null)
|
|
89
|
+
throw new NotFoundError('FoundationalServiceSource', sourceId);
|
|
90
|
+
return this.runSync(source);
|
|
91
|
+
}
|
|
92
|
+
/** Resync a source: read the tree, upsert changed services, tombstone removed ones. */
|
|
93
|
+
async sync(ownerKind, ownerId, sourceId) {
|
|
94
|
+
return this.runSync(await this.require(ownerKind, ownerId, sourceId));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* AUTOREFRESH: resync every source whose last sync is older than `staleAfterMs`, bounded to
|
|
98
|
+
* `limit` per pass. Driven by the periodic sweep on both facades (a Cloudflare cron tick, a
|
|
99
|
+
* Node interval), so a linked repo stays fresh whether or not anyone opens the management
|
|
100
|
+
* surface or starts a run.
|
|
101
|
+
*
|
|
102
|
+
* Best-effort PER SOURCE: one repo whose installation was revoked must not stop the pass from
|
|
103
|
+
* refreshing the rest, so each failure is logged with its cause and the sweep continues. The
|
|
104
|
+
* count returned is sources SYNCED, not sources attempted.
|
|
105
|
+
*/
|
|
106
|
+
async refreshStale(staleAfterMs, limit) {
|
|
107
|
+
const now = this.deps.clock.now();
|
|
108
|
+
const stale = await this.deps.foundationalServiceSourceRepository.listStale(now - staleAfterMs, limit);
|
|
109
|
+
let synced = 0;
|
|
110
|
+
for (const source of stale) {
|
|
111
|
+
const result = await runBestEffort(this.log, 'foundationalServices.refreshSource', () => this.runSync(source), { sourceId: source.id, ownerKind: source.ownerKind, ownerId: source.ownerId });
|
|
112
|
+
if (result)
|
|
113
|
+
synced++;
|
|
114
|
+
}
|
|
115
|
+
return synced;
|
|
116
|
+
}
|
|
117
|
+
// --- internals ----------------------------------------------------------
|
|
118
|
+
async runSync(source) {
|
|
119
|
+
// Invariant across the whole sync — resolved ONCE, never per file.
|
|
120
|
+
const installationId = await this.requireInstallation(source);
|
|
121
|
+
return syncRepoSource({
|
|
122
|
+
source,
|
|
123
|
+
installationId,
|
|
124
|
+
githubClient: this.deps.githubClient,
|
|
125
|
+
now: this.deps.clock.now(),
|
|
126
|
+
listExisting: () => this.deps.foundationalServiceRepository.listBySource(source.id),
|
|
127
|
+
existingId: (s) => s.serviceId,
|
|
128
|
+
reconcile: async ({ readRef, commitMoved, now }, existing) => {
|
|
129
|
+
// The source dir's head commit is the exact staleness signal: unchanged ⇒ nothing under
|
|
130
|
+
// it moved (manifest OR contract document), so every service is unchanged and not a
|
|
131
|
+
// single per-directory read is paid for.
|
|
132
|
+
if (!commitMoved) {
|
|
133
|
+
return {
|
|
134
|
+
liveIds: new Set(existing.map((s) => s.serviceId)),
|
|
135
|
+
upserted: 0,
|
|
136
|
+
unchanged: existing.length,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return source.mode === 'files'
|
|
140
|
+
? this.reconcileFiles(source, installationId, readRef, now, existing)
|
|
141
|
+
: this.reconcileDirectories(source, installationId, readRef, now, existing);
|
|
142
|
+
},
|
|
143
|
+
tombstone: async (service, now) => {
|
|
144
|
+
await this.deps.foundationalServiceRepository.softDelete(service.ownerKind, service.ownerId, service.serviceId, now);
|
|
145
|
+
await this.deps.apiContractRepository.deleteForService(service.ownerKind, service.ownerId, service.serviceId);
|
|
146
|
+
},
|
|
147
|
+
updateSyncState: (commit, now) => this.deps.foundationalServiceSourceRepository.updateSyncState(source.id, commit, now),
|
|
148
|
+
invalidate: () => this.deps.invalidateCatalog?.(source.ownerKind, source.ownerId) ?? Promise.resolve(),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/** `directory` mode: one service per immediate subdirectory carrying a `service.md`. */
|
|
152
|
+
async reconcileDirectories(source, installationId, readRef, now, existing) {
|
|
153
|
+
const existingById = new Map(existing.map((s) => [s.serviceId, s]));
|
|
154
|
+
// Sorted by name (code-unit order, locale-independent) so slug-collision resolution is
|
|
155
|
+
// deterministic — first wins — regardless of ICU collation.
|
|
156
|
+
const dirs = (await this.listDir(source, installationId, source.dirPath, readRef))
|
|
157
|
+
.filter((e) => e.type === 'dir')
|
|
158
|
+
.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
159
|
+
const liveIds = new Set();
|
|
160
|
+
const seenSlugs = new Set();
|
|
161
|
+
let upserted = 0;
|
|
162
|
+
let incomplete = false;
|
|
163
|
+
for (const dir of dirs) {
|
|
164
|
+
const serviceId = slugFromDirName(dir.name);
|
|
165
|
+
// Two sibling dirs can slug to the same id (`file_storage` + `file-storage`). Keep the
|
|
166
|
+
// first deterministically rather than silently overwriting one service's row with
|
|
167
|
+
// another's.
|
|
168
|
+
if (seenSlugs.has(serviceId))
|
|
169
|
+
continue;
|
|
170
|
+
seenSlugs.add(serviceId);
|
|
171
|
+
const entries = await this.listDir(source, installationId, dir.path, readRef);
|
|
172
|
+
const manifestEntry = entries.find((e) => e.type === 'file' && e.name.toLowerCase() === SERVICE_MANIFEST_FILE);
|
|
173
|
+
// A directory with no manifest is not a service — skip it entirely rather than
|
|
174
|
+
// registering an unnamed one. It never enters `liveIds`, so a directory that LOSES its
|
|
175
|
+
// manifest retires the service it used to describe.
|
|
176
|
+
if (!manifestEntry)
|
|
177
|
+
continue;
|
|
178
|
+
const manifestFile = await this.readFile(source, installationId, manifestEntry.path, readRef);
|
|
179
|
+
const parsed = manifestFile ? parseServiceManifest(manifestFile.content) : null;
|
|
180
|
+
if (!parsed) {
|
|
181
|
+
// Unreadable/unparseable THIS round: keep any prior row alive rather than retiring a
|
|
182
|
+
// service over a transient read or an in-progress edit, and leave the pinned commit
|
|
183
|
+
// behind so the next sync re-reads it.
|
|
184
|
+
const prior = existingById.get(serviceId);
|
|
185
|
+
if (prior) {
|
|
186
|
+
liveIds.add(serviceId);
|
|
187
|
+
incomplete = true;
|
|
188
|
+
}
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const contracts = await this.readContracts(source, installationId, readRef, serviceId, entries
|
|
192
|
+
.filter((e) => e.type === 'file' && e.path !== manifestEntry.path)
|
|
193
|
+
.map((e) => e.path), now);
|
|
194
|
+
await this.writeService(source, {
|
|
195
|
+
serviceId,
|
|
196
|
+
name: parsed.name,
|
|
197
|
+
summary: parsed.summary,
|
|
198
|
+
description: parsed.description,
|
|
199
|
+
capabilities: parsed.capabilities,
|
|
200
|
+
sourcePath: dir.path,
|
|
201
|
+
}, contracts, existingById.get(serviceId), readRef, now);
|
|
202
|
+
liveIds.add(serviceId);
|
|
203
|
+
upserted++;
|
|
204
|
+
}
|
|
205
|
+
// Every live service is rewritten on a moved commit, so nothing is reported unchanged —
|
|
206
|
+
// the cheap path is the `!commitMoved` short-circuit above, which is where an untouched
|
|
207
|
+
// source actually saves its reads.
|
|
208
|
+
return { liveIds, upserted, unchanged: 0, ...(incomplete ? { incomplete: true } : {}) };
|
|
209
|
+
}
|
|
210
|
+
/** `files` mode: ONE service, named on the link, whose contracts are the linked paths. */
|
|
211
|
+
async reconcileFiles(source, installationId, readRef, now, existing) {
|
|
212
|
+
const serviceId = source.serviceId;
|
|
213
|
+
// Guaranteed by the link-time `v.check`, but a stored row outlives the schema that wrote
|
|
214
|
+
// it — so this is a real guard, not a type narrowing convenience.
|
|
215
|
+
if (!serviceId || !source.serviceName) {
|
|
216
|
+
throw new ValidationError('This source does not name the service its files describe', {
|
|
217
|
+
reason: 'foundational_source_unnamed_service',
|
|
218
|
+
sourceId: source.id,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
const prior = existing.find((s) => s.serviceId === serviceId);
|
|
222
|
+
const contracts = await this.readContracts(source, installationId, readRef, serviceId, source.filePaths, now);
|
|
223
|
+
if (contracts.length === 0) {
|
|
224
|
+
// Every linked file read back unusable. Keep a prior row alive and leave the pin behind
|
|
225
|
+
// so the next pass re-reads — the alternative is retiring a service that a momentary
|
|
226
|
+
// read failure made look deleted.
|
|
227
|
+
if (prior)
|
|
228
|
+
return { liveIds: new Set([serviceId]), upserted: 0, unchanged: 1, incomplete: true };
|
|
229
|
+
return { liveIds: new Set(), upserted: 0, unchanged: 0, incomplete: true };
|
|
230
|
+
}
|
|
231
|
+
await this.writeService(source, {
|
|
232
|
+
serviceId,
|
|
233
|
+
name: source.serviceName,
|
|
234
|
+
summary: source.serviceSummary ?? source.serviceName,
|
|
235
|
+
description: '',
|
|
236
|
+
capabilities: [],
|
|
237
|
+
sourcePath: source.dirPath,
|
|
238
|
+
}, contracts, prior, readRef, now);
|
|
239
|
+
return { liveIds: new Set([serviceId]), upserted: 1, unchanged: 0 };
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Read the given repo paths and keep the ones that ARE contract documents. A file whose
|
|
243
|
+
* format is unrecognised is skipped rather than stored as an opaque blob: the format is what
|
|
244
|
+
* tells a downstream agent how to read it, and a "contract" nobody can interpret is worse in
|
|
245
|
+
* an agent's context than an absent one.
|
|
246
|
+
*/
|
|
247
|
+
async readContracts(source, installationId, readRef, serviceId, paths, now) {
|
|
248
|
+
const out = [];
|
|
249
|
+
const seen = new Set();
|
|
250
|
+
for (const path of paths) {
|
|
251
|
+
const file = await this.readFile(source, installationId, path, readRef);
|
|
252
|
+
if (!file)
|
|
253
|
+
continue;
|
|
254
|
+
const format = detectContractFormat(path, file.content);
|
|
255
|
+
if (!format)
|
|
256
|
+
continue;
|
|
257
|
+
const contractId = contractIdFromPath(path);
|
|
258
|
+
if (seen.has(contractId))
|
|
259
|
+
continue;
|
|
260
|
+
seen.add(contractId);
|
|
261
|
+
const summary = summarizeContract({
|
|
262
|
+
contractId,
|
|
263
|
+
format,
|
|
264
|
+
title: contractTitleFromPath(path),
|
|
265
|
+
path,
|
|
266
|
+
body: file.content,
|
|
267
|
+
});
|
|
268
|
+
out.push({
|
|
269
|
+
ownerKind: source.ownerKind,
|
|
270
|
+
ownerId: source.ownerId,
|
|
271
|
+
serviceId,
|
|
272
|
+
contractId,
|
|
273
|
+
format,
|
|
274
|
+
title: summary.title,
|
|
275
|
+
body: file.content,
|
|
276
|
+
operations: summary.operations,
|
|
277
|
+
omittedOperations: summary.omittedOperations,
|
|
278
|
+
sourcePath: path,
|
|
279
|
+
sourceSha: file.sha,
|
|
280
|
+
createdAt: now,
|
|
281
|
+
updatedAt: now,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
return out;
|
|
285
|
+
}
|
|
286
|
+
async writeService(source, identity, contracts, prior, readRef, now) {
|
|
287
|
+
await this.deps.foundationalServiceRepository.upsert({
|
|
288
|
+
serviceId: identity.serviceId,
|
|
289
|
+
ownerKind: source.ownerKind,
|
|
290
|
+
ownerId: source.ownerId,
|
|
291
|
+
name: identity.name,
|
|
292
|
+
summary: identity.summary,
|
|
293
|
+
description: identity.description,
|
|
294
|
+
capabilities: identity.capabilities,
|
|
295
|
+
sourceId: source.id,
|
|
296
|
+
sourcePath: identity.sourcePath,
|
|
297
|
+
pinnedCommit: readRef,
|
|
298
|
+
createdAt: prior?.createdAt ?? now,
|
|
299
|
+
updatedAt: now,
|
|
300
|
+
deletedAt: null,
|
|
301
|
+
});
|
|
302
|
+
await this.deps.apiContractRepository.replaceForService(source.ownerKind, source.ownerId, identity.serviceId, contracts);
|
|
303
|
+
}
|
|
304
|
+
listDir(source, installationId, path, readRef) {
|
|
305
|
+
return this.deps.githubClient.listDirectory(installationId, { owner: source.repoOwner, repo: source.repoName }, path, readRef);
|
|
306
|
+
}
|
|
307
|
+
readFile(source, installationId, path, readRef) {
|
|
308
|
+
return this.deps.githubClient.getFileContent(installationId, { owner: source.repoOwner, repo: source.repoName }, path, readRef);
|
|
309
|
+
}
|
|
310
|
+
async require(ownerKind, ownerId, sourceId) {
|
|
311
|
+
const source = assertFound(await this.deps.foundationalServiceSourceRepository.get(sourceId), 'FoundationalServiceSource', sourceId);
|
|
312
|
+
// The route gates only authorize the addressed owner prefix, so the record must belong to
|
|
313
|
+
// that owner; a 404 hides other tenants' sources entirely.
|
|
314
|
+
if (source.ownerKind !== ownerKind || source.ownerId !== ownerId || source.deletedAt !== null) {
|
|
315
|
+
throw new NotFoundError('FoundationalServiceSource', sourceId);
|
|
316
|
+
}
|
|
317
|
+
return source;
|
|
318
|
+
}
|
|
319
|
+
async requireInstallation(source) {
|
|
320
|
+
const installationId = await this.deps.resolveInstallationId(source.ownerKind, source.ownerId);
|
|
321
|
+
if (installationId === null) {
|
|
322
|
+
throw new ValidationError('No GitHub installation is available for this scope; connect GitHub before syncing a source', { reason: 'foundational_source_no_installation', sourceId: source.id });
|
|
323
|
+
}
|
|
324
|
+
return installationId;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function toWire(record) {
|
|
328
|
+
return {
|
|
329
|
+
id: record.id,
|
|
330
|
+
ownerKind: record.ownerKind,
|
|
331
|
+
ownerId: record.ownerId,
|
|
332
|
+
repoOwner: record.repoOwner,
|
|
333
|
+
repoName: record.repoName,
|
|
334
|
+
gitRef: record.gitRef,
|
|
335
|
+
mode: record.mode,
|
|
336
|
+
dirPath: record.dirPath,
|
|
337
|
+
filePaths: record.filePaths,
|
|
338
|
+
serviceId: record.serviceId,
|
|
339
|
+
serviceName: record.serviceName,
|
|
340
|
+
serviceSummary: record.serviceSummary,
|
|
341
|
+
lastSyncedCommit: record.lastSyncedCommit,
|
|
342
|
+
lastSyncedAt: record.lastSyncedAt,
|
|
343
|
+
createdAt: record.createdAt,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
//# sourceMappingURL=FoundationalServiceSourceService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FoundationalServiceSourceService.js","sourceRoot":"","sources":["../../src/foundationalServices/FoundationalServiceSourceService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { UploadApiContract } from '@cat-factory/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* Refuse an uploaded contract whose body does not match its declared format. Throws a
|
|
4
|
+
* `ValidationError` carrying a machine-readable `reason` the SPA maps to translated copy;
|
|
5
|
+
* returns silently when the document is acceptable.
|
|
6
|
+
*/
|
|
7
|
+
export declare function validateUploadedContract(upload: UploadApiContract): void;
|
|
8
|
+
//# sourceMappingURL=contract-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-validation.d.ts","sourceRoot":"","sources":["../../src/foundationalServices/contract-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAyBlF;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAqBxE"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ValidationError, isOpenApiDocument } from '@cat-factory/kernel';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// Write-boundary validation for an UPLOADED contract document.
|
|
4
|
+
//
|
|
5
|
+
// The point is to refuse a document that will read as garbage to a downstream agent at the
|
|
6
|
+
// moment someone uploads it, rather than at the moment a coder is handed it. The check is
|
|
7
|
+
// per-format and deliberately shallow:
|
|
8
|
+
//
|
|
9
|
+
// - `openapi` is PARSED — a document claiming to be OpenAPI but which is not is the failure
|
|
10
|
+
// mode that produces a service whose catalog entry lists zero operations while looking
|
|
11
|
+
// perfectly registered.
|
|
12
|
+
// - the two TypeScript formats are checked only for the LIBRARY IMPORT they claim. They
|
|
13
|
+
// cannot be evaluated (no code generation in a Worker isolate, and executing an uploaded
|
|
14
|
+
// module would be a remote-code-execution hole), so the honest check is that the document
|
|
15
|
+
// is what its declared format says it is; the agent reads the source itself.
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
/** The package import each TypeScript contract format is recognised by. */
|
|
18
|
+
const REQUIRED_IMPORT = {
|
|
19
|
+
'toad-contract': { package: '@toad-contracts/', label: '@toad-contracts/core' },
|
|
20
|
+
'lokalise-api-contract': { package: '@lokalise/api-contract', label: '@lokalise/api-contract' },
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Refuse an uploaded contract whose body does not match its declared format. Throws a
|
|
24
|
+
* `ValidationError` carrying a machine-readable `reason` the SPA maps to translated copy;
|
|
25
|
+
* returns silently when the document is acceptable.
|
|
26
|
+
*/
|
|
27
|
+
export function validateUploadedContract(upload) {
|
|
28
|
+
if (upload.format === 'openapi') {
|
|
29
|
+
if (!isOpenApiDocument(upload.body)) {
|
|
30
|
+
throw new ValidationError(`Contract '${upload.contractId}' is not a valid OpenAPI 3.x document (JSON or YAML)`, { reason: 'invalid_openapi_document', contractId: upload.contractId });
|
|
31
|
+
}
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const required = REQUIRED_IMPORT[upload.format];
|
|
35
|
+
if (required && !upload.body.includes(required.package)) {
|
|
36
|
+
throw new ValidationError(`Contract '${upload.contractId}' does not reference ${required.label}`, {
|
|
37
|
+
reason: 'contract_library_not_referenced',
|
|
38
|
+
contractId: upload.contractId,
|
|
39
|
+
expected: required.label,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=contract-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-validation.js","sourceRoot":"","sources":["../../src/foundationalServices/contract-validation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAExE,8EAA8E;AAC9E,+DAA+D;AAC/D,EAAE;AACF,2FAA2F;AAC3F,0FAA0F;AAC1F,uCAAuC;AACvC,EAAE;AACF,8FAA8F;AAC9F,2FAA2F;AAC3F,4BAA4B;AAC5B,0FAA0F;AAC1F,6FAA6F;AAC7F,8FAA8F;AAC9F,iFAAiF;AACjF,8EAA8E;AAE9E,2EAA2E;AAC3E,MAAM,eAAe,GAA2E;IAC9F,eAAe,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,sBAAsB,EAAE;IAC/E,uBAAuB,EAAE,EAAE,OAAO,EAAE,wBAAwB,EAAE,KAAK,EAAE,wBAAwB,EAAE;CAChG,CAAA;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAyB;IAChE,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAChC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,eAAe,CACvB,aAAa,MAAM,CAAC,UAAU,sDAAsD,EACpF,EAAE,MAAM,EAAE,0BAA0B,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CACtE,CAAA;QACH,CAAC;QACD,OAAM;IACR,CAAC;IACD,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC/C,IAAI,QAAQ,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,eAAe,CACvB,aAAa,MAAM,CAAC,UAAU,wBAAwB,QAAQ,CAAC,KAAK,EAAE,EACtE;YACE,MAAM,EAAE,iCAAiC;YACzC,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,QAAQ,EAAE,QAAQ,CAAC,KAAK;SACzB,CACF,CAAA;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { FoundationalService, ResolvedFoundationalService } from '@cat-factory/contracts';
|
|
2
|
+
import type { ApiContractManifestEntry, FoundationalServiceRecord } from '@cat-factory/kernel';
|
|
3
|
+
/** A stored record + its contract manifest → the wire shape. */
|
|
4
|
+
export declare function toWire(record: FoundationalServiceRecord, manifest: ApiContractManifestEntry[]): FoundationalService;
|
|
5
|
+
export interface MergeFoundationalTiersInput {
|
|
6
|
+
accountRows: FoundationalServiceRecord[];
|
|
7
|
+
/** Read `includeDeleted` — a workspace tombstone is what suppresses an inherited service. */
|
|
8
|
+
workspaceRows: FoundationalServiceRecord[];
|
|
9
|
+
accountManifest: Map<string, ApiContractManifestEntry[]>;
|
|
10
|
+
workspaceManifest: Map<string, ApiContractManifestEntry[]>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Merge the account and workspace tiers into the catalog an agent sees. The workspace tier
|
|
14
|
+
* wins by id, and a workspace TOMBSTONE removes the id entirely — including an account service
|
|
15
|
+
* the workspace never authored, which is the whole point of reading the workspace tier with
|
|
16
|
+
* tombstones included.
|
|
17
|
+
*
|
|
18
|
+
* Sorted by id so a catalog rendered into a prompt is byte-stable across resolves; an unstable
|
|
19
|
+
* order would break prompt caching for every design dispatch in the workspace.
|
|
20
|
+
*/
|
|
21
|
+
export declare function mergeFoundationalTiers(input: MergeFoundationalTiersInput): ResolvedFoundationalService[];
|
|
22
|
+
//# sourceMappingURL=foundational-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foundational-catalog.d.ts","sourceRoot":"","sources":["../../src/foundationalServices/foundational-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EAEnB,2BAA2B,EAC5B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAA;AAM9F,gEAAgE;AAChE,wBAAgB,MAAM,CACpB,MAAM,EAAE,yBAAyB,EACjC,QAAQ,EAAE,wBAAwB,EAAE,GACnC,mBAAmB,CAyBrB;AAED,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,yBAAyB,EAAE,CAAA;IACxC,6FAA6F;IAC7F,aAAa,EAAE,yBAAyB,EAAE,CAAA;IAC1C,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,wBAAwB,EAAE,CAAC,CAAA;IACxD,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,wBAAwB,EAAE,CAAC,CAAA;CAC3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,2BAA2B,GACjC,2BAA2B,EAAE,CAW/B"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Pure tier-merge for the foundational-services catalog. No I/O — the service hands in the
|
|
2
|
+
// two tiers' rows plus their contract manifests, so every precedence rule here is
|
|
3
|
+
// unit-testable and identical on both runtimes.
|
|
4
|
+
/** A stored record + its contract manifest → the wire shape. */
|
|
5
|
+
export function toWire(record, manifest) {
|
|
6
|
+
return {
|
|
7
|
+
id: record.serviceId,
|
|
8
|
+
ownerKind: record.ownerKind,
|
|
9
|
+
name: record.name,
|
|
10
|
+
summary: record.summary,
|
|
11
|
+
description: record.description,
|
|
12
|
+
capabilities: record.capabilities,
|
|
13
|
+
contracts: manifest.map((entry) => ({
|
|
14
|
+
contractId: entry.contractId,
|
|
15
|
+
format: entry.format,
|
|
16
|
+
title: entry.title,
|
|
17
|
+
size: entry.size,
|
|
18
|
+
path: entry.sourcePath,
|
|
19
|
+
// Indexed once at WRITE time and stored on the row, so this projection shows an agent
|
|
20
|
+
// what the interface offers without loading a single document body.
|
|
21
|
+
operations: entry.operations,
|
|
22
|
+
omittedOperations: entry.omittedOperations,
|
|
23
|
+
})),
|
|
24
|
+
sourceId: record.sourceId,
|
|
25
|
+
sourcePath: record.sourcePath,
|
|
26
|
+
pinnedCommit: record.pinnedCommit,
|
|
27
|
+
createdAt: record.createdAt,
|
|
28
|
+
updatedAt: record.updatedAt,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Merge the account and workspace tiers into the catalog an agent sees. The workspace tier
|
|
33
|
+
* wins by id, and a workspace TOMBSTONE removes the id entirely — including an account service
|
|
34
|
+
* the workspace never authored, which is the whole point of reading the workspace tier with
|
|
35
|
+
* tombstones included.
|
|
36
|
+
*
|
|
37
|
+
* Sorted by id so a catalog rendered into a prompt is byte-stable across resolves; an unstable
|
|
38
|
+
* order would break prompt caching for every design dispatch in the workspace.
|
|
39
|
+
*/
|
|
40
|
+
export function mergeFoundationalTiers(input) {
|
|
41
|
+
const merged = new Map();
|
|
42
|
+
for (const row of input.accountRows) {
|
|
43
|
+
if (row.deletedAt)
|
|
44
|
+
continue;
|
|
45
|
+
merged.set(row.serviceId, entry(row, input.accountManifest, 'account'));
|
|
46
|
+
}
|
|
47
|
+
for (const row of input.workspaceRows) {
|
|
48
|
+
if (row.deletedAt)
|
|
49
|
+
merged.delete(row.serviceId);
|
|
50
|
+
else
|
|
51
|
+
merged.set(row.serviceId, entry(row, input.workspaceManifest, 'workspace'));
|
|
52
|
+
}
|
|
53
|
+
return [...merged.values()].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
54
|
+
}
|
|
55
|
+
function entry(record, manifest, tier) {
|
|
56
|
+
return { ...toWire(record, manifest.get(record.serviceId) ?? []), tier };
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=foundational-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foundational-catalog.js","sourceRoot":"","sources":["../../src/foundationalServices/foundational-catalog.ts"],"names":[],"mappings":"AAOA,2FAA2F;AAC3F,kFAAkF;AAClF,gDAAgD;AAEhD,gEAAgE;AAChE,MAAM,UAAU,MAAM,CACpB,MAAiC,EACjC,QAAoC;IAEpC,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,SAAS;QACpB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,SAAS,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAClC,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,UAAU;YACtB,sFAAsF;YACtF,oEAAoE;YACpE,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;SAC3C,CAAC,CAAC;QACH,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAA;AACH,CAAC;AAUD;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAAkC;IAElC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuC,CAAA;IAC7D,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;QACpC,IAAI,GAAG,CAAC,SAAS;YAAE,SAAQ;QAC3B,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC,CAAA;IACzE,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;QACtC,IAAI,GAAG,CAAC,SAAS;YAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;;YAC1C,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC,CAAA;IAClF,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACtF,CAAC;AAED,SAAS,KAAK,CACZ,MAAiC,EACjC,QAAiD,EACjD,IAA6B;IAE7B,OAAO,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAA;AAC1E,CAAC"}
|