@treeseed/sdk 0.13.0-rc.34 → 0.13.0-rc.36
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/content/validation/agent-operational-content-schemas.d.ts +4 -4
- package/dist/content/validation/auxiliary-content-schemas.d.ts +34 -34
- package/dist/content/validation/content-model-schemas.d.ts +70 -70
- package/dist/content/validation/portable-content-data.d.ts +12 -12
- package/dist/deployment/schemas.d.ts +19 -1
- package/dist/deployment/schemas.js +8 -1
- package/dist/operator-contracts/canonical-command-tree.js +65 -3
- package/dist/operator-contracts/control-plane-operations.js +10 -10
- package/dist/operator-contracts/seed/seed-bundle.d.ts +22 -7
- package/dist/operator-contracts/seed/seed-bundle.js +17 -3
- package/package.json +1 -1
|
@@ -425,12 +425,26 @@ export declare const hostConfigurationSchema: z.ZodObject<{
|
|
|
425
425
|
role: string;
|
|
426
426
|
architecture: "amd64";
|
|
427
427
|
}>;
|
|
428
|
-
runtime: z.ZodObject<{
|
|
428
|
+
runtime: z.ZodEffects<z.ZodObject<{
|
|
429
429
|
management: z.ZodEnum<["managed", "external"]>;
|
|
430
|
+
environment: z.ZodDefault<z.ZodEnum<["production", "development"]>>;
|
|
431
|
+
dataRoot: z.ZodOptional<z.ZodString>;
|
|
430
432
|
}, "strict", z.ZodTypeAny, {
|
|
431
433
|
management: "managed" | "external";
|
|
434
|
+
environment: "development" | "production";
|
|
435
|
+
dataRoot?: string | undefined;
|
|
436
|
+
}, {
|
|
437
|
+
management: "managed" | "external";
|
|
438
|
+
environment?: "development" | "production" | undefined;
|
|
439
|
+
dataRoot?: string | undefined;
|
|
440
|
+
}>, {
|
|
441
|
+
management: "managed" | "external";
|
|
442
|
+
environment: "development" | "production";
|
|
443
|
+
dataRoot?: string | undefined;
|
|
432
444
|
}, {
|
|
433
445
|
management: "managed" | "external";
|
|
446
|
+
environment?: "development" | "production" | undefined;
|
|
447
|
+
dataRoot?: string | undefined;
|
|
434
448
|
}>;
|
|
435
449
|
updates: z.ZodObject<{
|
|
436
450
|
defaultTrack: z.ZodEnum<["stable", "development"]>;
|
|
@@ -1145,6 +1159,8 @@ export declare const hostConfigurationSchema: z.ZodObject<{
|
|
|
1145
1159
|
};
|
|
1146
1160
|
runtime: {
|
|
1147
1161
|
management: "managed" | "external";
|
|
1162
|
+
environment: "development" | "production";
|
|
1163
|
+
dataRoot?: string | undefined;
|
|
1148
1164
|
};
|
|
1149
1165
|
updates: {
|
|
1150
1166
|
stable: {
|
|
@@ -1253,6 +1269,8 @@ export declare const hostConfigurationSchema: z.ZodObject<{
|
|
|
1253
1269
|
};
|
|
1254
1270
|
runtime: {
|
|
1255
1271
|
management: "managed" | "external";
|
|
1272
|
+
environment?: "development" | "production" | undefined;
|
|
1273
|
+
dataRoot?: string | undefined;
|
|
1256
1274
|
};
|
|
1257
1275
|
updates: {
|
|
1258
1276
|
stable: {
|
|
@@ -69,7 +69,14 @@ const hostConfigurationSchema = z.object({
|
|
|
69
69
|
role: hostRoleSchema,
|
|
70
70
|
architecture: z.literal("amd64")
|
|
71
71
|
}).strict(),
|
|
72
|
-
runtime: z.object({
|
|
72
|
+
runtime: z.object({
|
|
73
|
+
management: z.enum(["managed", "external"]),
|
|
74
|
+
environment: z.enum(["production", "development"]).default("production"),
|
|
75
|
+
dataRoot: z.string().startsWith("/").regex(/\/\.treeseed\/data$/u, "Development dataRoot must end with /.treeseed/data.").optional()
|
|
76
|
+
}).strict().superRefine((runtime, context) => {
|
|
77
|
+
if (runtime.environment === "development" && !runtime.dataRoot) context.addIssue({ code: z.ZodIssueCode.custom, path: ["dataRoot"], message: "Development hosts require an explicit workspace-visible dataRoot." });
|
|
78
|
+
if (runtime.environment === "production" && runtime.dataRoot) context.addIssue({ code: z.ZodIssueCode.custom, path: ["dataRoot"], message: "Production hosts use manager-owned /var/lib state and cannot override dataRoot." });
|
|
79
|
+
}),
|
|
73
80
|
updates: z.object({
|
|
74
81
|
defaultTrack: deploymentTrackSchema,
|
|
75
82
|
stable: z.object({
|
|
@@ -98,6 +98,23 @@ const operationBindings = {
|
|
|
98
98
|
"projects treedx workspaces list": operation("treedx.workspaces.list", [field("path", "projectId", "argument", "project", true), ...page()]),
|
|
99
99
|
"projects treedx workspaces show": operation("treedx.workspaces.show", [field("path", "projectId", "context", "project", true), field("path", "workspaceId", "argument", "workspace", true)]),
|
|
100
100
|
"projects treedx workspaces abandon": operation("treedx.workspaces.abandon", [field("path", "projectId", "context", "project", true), field("path", "workspaceId", "argument", "workspace", true)]),
|
|
101
|
+
"library show": local("local.library.show"),
|
|
102
|
+
"library status": local("local.library.status"),
|
|
103
|
+
"library paths": local("local.library.paths"),
|
|
104
|
+
"library read": local("local.library.read"),
|
|
105
|
+
"library search": local("local.library.search"),
|
|
106
|
+
"library query": local("local.library.query"),
|
|
107
|
+
"library context": local("local.library.context"),
|
|
108
|
+
"library workspace create": operation("knowledge.workspaces.create", [field("path", "projectId", "argument", "project", true), field("body", "requestId", "option", "request")]),
|
|
109
|
+
"library workspace show": operation("knowledge.workspaces.show", [field("path", "workspaceId", "argument", "workspace", true)]),
|
|
110
|
+
"library workspace read": operation("knowledge.workspaces.content.show", [field("path", "workspaceId", "argument", "workspace", true), field("query", "path", "argument", "path", true)]),
|
|
111
|
+
"library workspace diff": operation("knowledge.workspaces.diff", [field("path", "workspaceId", "argument", "workspace", true)]),
|
|
112
|
+
"library workspace write": operation("knowledge.workspaces.content.update", [field("path", "workspaceId", "argument", "workspace", true), field("body", "file", "option", "input", true)]),
|
|
113
|
+
"library workspace submit": operation("knowledge.workspaces.submit", [field("path", "workspaceId", "argument", "workspace", true), field("body", "version", "option", "version", true, "integer"), field("body", "message", "option"), field("body", "notes", "option"), field("body", "contextDigest", "option", "contextDigest")]),
|
|
114
|
+
"library workspace abandon": operation("knowledge.workspaces.abandon", [field("path", "workspaceId", "argument", "workspace", true), field("body", "version", "option", "version", true, "integer")]),
|
|
115
|
+
"library reviews list": operation("knowledge.reviews.list", [field("path", "teamId", "context", "team", true), ...page()]),
|
|
116
|
+
"library reviews decide": operation("knowledge.reviews.decide", [field("path", "reviewId", "argument", "review", true), field("body", "file", "option", "input", true)]),
|
|
117
|
+
"library reviews publish": operation("knowledge.reviews.publish", [field("path", "reviewId", "argument", "review", true), field("body", "file", "option", "input")]),
|
|
101
118
|
"status": operation("status.show"),
|
|
102
119
|
"save": unavailable("Unified GitHub-backed save is intentionally fail-closed during this cutover."),
|
|
103
120
|
"stage": unavailable("Unified GitHub-backed stage is intentionally fail-closed during this cutover."),
|
|
@@ -134,23 +151,49 @@ function hostReset() {
|
|
|
134
151
|
return value;
|
|
135
152
|
}
|
|
136
153
|
function userCreate() {
|
|
137
|
-
const value = leaf("create", "mutation"
|
|
154
|
+
const value = leaf("create", "mutation");
|
|
138
155
|
if (value.nodeType !== "leaf") throw new Error("User creation must be a leaf command.");
|
|
139
156
|
value.description = "Create a local TreeSeed user with a securely prompted password.";
|
|
140
157
|
value.options = [
|
|
141
158
|
...value.options ?? [],
|
|
142
159
|
{ name: "--email", description: "Email address for the new user.", type: "string" },
|
|
143
160
|
{ name: "--username", description: "Unique username for the new user.", type: "string" },
|
|
144
|
-
{ name: "--display-name", description: "Human-readable display name.", type: "string" }
|
|
161
|
+
{ name: "--display-name", description: "Human-readable display name.", type: "string" },
|
|
162
|
+
{ name: "--timeout", description: "Maximum seconds to wait for registration.", type: "number" }
|
|
145
163
|
];
|
|
146
164
|
return value;
|
|
147
165
|
}
|
|
166
|
+
function authLogin() {
|
|
167
|
+
const value = leaf("login", "mutation");
|
|
168
|
+
if (value.nodeType !== "leaf") throw new Error("Authentication login must be a leaf command.");
|
|
169
|
+
value.options = [
|
|
170
|
+
...value.options ?? [],
|
|
171
|
+
{ name: "--timeout", description: "Maximum seconds to wait for device authorization.", type: "number" }
|
|
172
|
+
];
|
|
173
|
+
return value;
|
|
174
|
+
}
|
|
175
|
+
function libraryRead(segment, extraArguments = [], extraOptions = []) {
|
|
176
|
+
return {
|
|
177
|
+
nodeType: "leaf",
|
|
178
|
+
segment,
|
|
179
|
+
description: `${segment[0].toUpperCase()}${segment.slice(1)} project library knowledge.`,
|
|
180
|
+
kind: "read",
|
|
181
|
+
arguments: ["project", ...extraArguments].map((name) => ({ name, description: `${name} value.`, required: true })),
|
|
182
|
+
options: [{ name: "--ref", description: "Exact commit or protected library ref.", type: "string" }, ...extraOptions],
|
|
183
|
+
resultSchemaId: `treeseed.command.library.${segment}/v1`,
|
|
184
|
+
execution: local(`local.library.${segment}`)
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function addOptions(node, options) {
|
|
188
|
+
if (node.nodeType === "leaf") node.options = [...node.options ?? [], ...options];
|
|
189
|
+
return node;
|
|
190
|
+
}
|
|
148
191
|
const commandTree = {
|
|
149
192
|
schemaVersion: "treeseed.command-tree/v1",
|
|
150
193
|
executable: "trsd",
|
|
151
194
|
commands: [
|
|
152
195
|
{ nodeType: "leaf", segment: "send", description: "Send a message to project agents through a team discussion channel.", kind: "mutation", arguments: [{ name: "channel", description: "Team discussion channel.", required: true }, { name: "message", description: "Markdown message to send.", required: true }], options: [planOption, { name: "--team", description: "Team identity or slug.", type: "string" }, { name: "--project", description: "Default project for unqualified agent names.", type: "string" }, { name: "--to", description: "Agent target; use project/agent when names may be ambiguous.", type: "string[]" }, { name: "--topic", description: "Optional discussion topic.", type: "string" }, { name: "--wait", description: "Seconds to wait for durable responses.", type: "number", defaultValue: 0 }], authorization: { capability: "agents.execute", confirmation: "never" }, resultSchemaId: "treeseed.communication-send-receipt/v1", execution: unavailable() },
|
|
153
|
-
branch("auth", [
|
|
196
|
+
branch("auth", [authLogin(), leaf("logout", "mutation"), leaf("status")]),
|
|
154
197
|
branch("users", [userCreate()]),
|
|
155
198
|
branch("secrets", [leaf("list"), leaf("status"), leaf("unlock", "mutation", void 0, "credential"), leaf("lock", "mutation"), leaf("rotate", "mutation", void 0, "credential")]),
|
|
156
199
|
branch("host", [
|
|
@@ -217,6 +260,25 @@ const commandTree = {
|
|
|
217
260
|
leaf("capabilities", "read", "project"),
|
|
218
261
|
branch("workspaces", [leaf("list", "read", "project"), leaf("show", "read", "workspace"), leaf("abandon", "mutation", "workspace", "destructive")])
|
|
219
262
|
])]),
|
|
263
|
+
branch("library", [
|
|
264
|
+
libraryRead("show"),
|
|
265
|
+
libraryRead("status"),
|
|
266
|
+
libraryRead("paths", [], [{ name: "--prefix", description: "Repository-relative path prefix.", type: "string" }, { name: "--limit", description: "Page size.", type: "number" }, { name: "--cursor", description: "Opaque page cursor.", type: "string" }]),
|
|
267
|
+
libraryRead("read", ["path"]),
|
|
268
|
+
libraryRead("search", ["query"], [{ name: "--path", description: "Restrict search to a repository-relative path.", type: "string" }, { name: "--limit", description: "Page size.", type: "number" }, { name: "--cursor", description: "Opaque page cursor.", type: "string" }]),
|
|
269
|
+
libraryRead("query", ["query"], [{ name: "--model", description: "TreeDX content model.", type: "string" }, { name: "--input", description: "YAML or JSON query body.", type: "string" }]),
|
|
270
|
+
libraryRead("context", ["query"], [{ name: "--max-items", description: "Maximum context items.", type: "number" }, { name: "--max-tokens", description: "Maximum context tokens.", type: "number" }]),
|
|
271
|
+
branch("workspace", [
|
|
272
|
+
addOptions(leaf("create", "mutation", "project"), [{ name: "--request", description: "Replay-safe UUID request identity.", type: "string" }]),
|
|
273
|
+
leaf("show", "read", "workspace"),
|
|
274
|
+
{ nodeType: "leaf", segment: "read", description: "Read a file from a governed library workspace.", kind: "read", arguments: [{ name: "workspace", description: "Workspace identity.", required: true }, { name: "path", description: "Repository-relative path.", required: true }], resultSchemaId: "treeseed.command.library.workspace.read/v1", execution: unavailable() },
|
|
275
|
+
leaf("diff", "read", "workspace"),
|
|
276
|
+
addOptions(leaf("write", "mutation", "workspace"), [{ name: "--input", description: "YAML or JSON draft body.", type: "string", required: true }]),
|
|
277
|
+
addOptions(leaf("submit", "mutation", "workspace"), [{ name: "--version", description: "Expected workspace version.", type: "number", required: true }, { name: "--message", description: "Commit message.", type: "string" }, { name: "--notes", description: "Review notes.", type: "string" }, { name: "--context-digest", description: "Verified editorial context digest.", type: "string" }]),
|
|
278
|
+
addOptions(leaf("abandon", "mutation", "workspace", "destructive"), [{ name: "--version", description: "Expected workspace version.", type: "number", required: true }])
|
|
279
|
+
]),
|
|
280
|
+
branch("reviews", [leaf("list"), addOptions(leaf("decide", "mutation", "review"), [{ name: "--input", description: "YAML or JSON review decision.", type: "string", required: true }]), addOptions(leaf("publish", "mutation", "review"), [{ name: "--input", description: "Optional YAML or JSON publication body.", type: "string" }])])
|
|
281
|
+
]),
|
|
220
282
|
leaf("save", "mutation"),
|
|
221
283
|
leaf("stage", "mutation", void 0, "authority"),
|
|
222
284
|
leaf("release", "mutation", void 0, "production"),
|
|
@@ -184,17 +184,17 @@ const CONTROL_PLANE_OPERATIONS = {
|
|
|
184
184
|
search: resource("knowledge.search", "GET", "/v1/knowledge/search", {}, { capability: "knowledge.read", surfaces: ["rest", "mcp_tool"], pagination: "cursor" }),
|
|
185
185
|
teamCatalog: resource("knowledge.catalog.team", "GET", "/v1/teams/{teamId}/knowledge/catalog", { teamId: z.string().min(1) }, { capability: "knowledge.read", pagination: "cursor" }),
|
|
186
186
|
projectCatalog: resource("knowledge.catalog.project", "GET", "/v1/projects/{projectId}/knowledge/catalog", { projectId: z.string().min(1) }, { capability: "knowledge.read", pagination: "cursor" }),
|
|
187
|
-
createWorkspace: resource("knowledge.workspaces.create", "POST", "/v1/projects/{projectId}/knowledge/workspaces", { projectId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"] }),
|
|
188
|
-
workspace: resource("knowledge.workspaces.show", "GET", "/v1/knowledge/workspaces/{workspaceId}", { workspaceId: z.string().min(1) }, { capability: "knowledge.read" }),
|
|
189
|
-
workspaceContent: resource("knowledge.workspaces.content.show", "GET", "/v1/knowledge/workspaces/{workspaceId}/content", { workspaceId: z.string().min(1) }, { capability: "knowledge.read" }),
|
|
190
|
-
updateWorkspaceContent: resource("knowledge.workspaces.content.update", "PUT", "/v1/knowledge/workspaces/{workspaceId}/content", { workspaceId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"], concurrency: true }),
|
|
191
|
-
workspaceDiff: resource("knowledge.workspaces.diff", "GET", "/v1/knowledge/workspaces/{workspaceId}/diff", { workspaceId: z.string().min(1) }, { capability: "knowledge.read" }),
|
|
192
|
-
abandonWorkspace: resource("knowledge.workspaces.abandon", "POST", "/v1/knowledge/workspaces/{workspaceId}/abandon", { workspaceId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"], risk: "destructive", concurrency: true }),
|
|
193
|
-
submitWorkspace: resource("knowledge.workspaces.submit", "POST", "/v1/knowledge/workspaces/{workspaceId}/submit", { workspaceId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"], concurrency: true }),
|
|
194
|
-
reviews: resource("knowledge.reviews.list", "GET", "/v1/teams/{teamId}/knowledge/reviews", { teamId: z.string().min(1) }, { capability: "knowledge.review", pagination: "cursor" }),
|
|
187
|
+
createWorkspace: resource("knowledge.workspaces.create", "POST", "/v1/projects/{projectId}/knowledge/workspaces", { projectId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"], surfaces: ["rest", "cli"] }),
|
|
188
|
+
workspace: resource("knowledge.workspaces.show", "GET", "/v1/knowledge/workspaces/{workspaceId}", { workspaceId: z.string().min(1) }, { capability: "knowledge.read", surfaces: ["rest", "cli"] }),
|
|
189
|
+
workspaceContent: resource("knowledge.workspaces.content.show", "GET", "/v1/knowledge/workspaces/{workspaceId}/content", { workspaceId: z.string().min(1) }, { capability: "knowledge.read", surfaces: ["rest", "cli"] }),
|
|
190
|
+
updateWorkspaceContent: resource("knowledge.workspaces.content.update", "PUT", "/v1/knowledge/workspaces/{workspaceId}/content", { workspaceId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"], surfaces: ["rest", "cli"], concurrency: true }),
|
|
191
|
+
workspaceDiff: resource("knowledge.workspaces.diff", "GET", "/v1/knowledge/workspaces/{workspaceId}/diff", { workspaceId: z.string().min(1) }, { capability: "knowledge.read", surfaces: ["rest", "cli"] }),
|
|
192
|
+
abandonWorkspace: resource("knowledge.workspaces.abandon", "POST", "/v1/knowledge/workspaces/{workspaceId}/abandon", { workspaceId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"], surfaces: ["rest", "cli"], risk: "destructive", concurrency: true }),
|
|
193
|
+
submitWorkspace: resource("knowledge.workspaces.submit", "POST", "/v1/knowledge/workspaces/{workspaceId}/submit", { workspaceId: z.string().min(1) }, { capability: "knowledge.write", scopes: ["treeseed:knowledge:write"], surfaces: ["rest", "cli"], concurrency: true }),
|
|
194
|
+
reviews: resource("knowledge.reviews.list", "GET", "/v1/teams/{teamId}/knowledge/reviews", { teamId: z.string().min(1) }, { capability: "knowledge.review", surfaces: ["rest", "cli"], pagination: "cursor" }),
|
|
195
195
|
commentReview: resource("knowledge.reviews.comments.create", "POST", "/v1/knowledge/reviews/{reviewId}/comments", { reviewId: z.string().min(1) }, { capability: "knowledge.review", scopes: ["treeseed:knowledge:write"] }),
|
|
196
|
-
decideReview: resource("knowledge.reviews.decide", "POST", "/v1/knowledge/reviews/{reviewId}/decision", { reviewId: z.string().min(1) }, { capability: "knowledge.review", scopes: ["treeseed:knowledge:write"], concurrency: true }),
|
|
197
|
-
publishReview: resource("knowledge.reviews.publish", "POST", "/v1/knowledge/reviews/{reviewId}/publish", { reviewId: z.string().min(1) }, { capability: "knowledge.publish", scopes: ["treeseed:knowledge:write"], concurrency: true })
|
|
196
|
+
decideReview: resource("knowledge.reviews.decide", "POST", "/v1/knowledge/reviews/{reviewId}/decision", { reviewId: z.string().min(1) }, { capability: "knowledge.review", scopes: ["treeseed:knowledge:write"], surfaces: ["rest", "cli"], concurrency: true }),
|
|
197
|
+
publishReview: resource("knowledge.reviews.publish", "POST", "/v1/knowledge/reviews/{reviewId}/publish", { reviewId: z.string().min(1) }, { capability: "knowledge.publish", scopes: ["treeseed:knowledge:write"], surfaces: ["rest", "cli"], concurrency: true })
|
|
198
198
|
},
|
|
199
199
|
governance: {
|
|
200
200
|
proposals: resource("governance.proposals.list", "GET", "/v1/projects/{projectId}/proposals", { projectId: z.string().min(1) }, { capability: "governance.read", surfaces: ["rest", "mcp_tool", "mcp_resource"], pagination: "cursor" }),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ProviderLanePurpose } from '../../agent-capacity/contracts/capacity/communication/communication-records.js';
|
|
2
|
-
export declare const SEED_BUNDLE_SCHEMA_VERSION: "treeseed.seed-bundle/
|
|
2
|
+
export declare const SEED_BUNDLE_SCHEMA_VERSION: "treeseed.seed-bundle/v3";
|
|
3
|
+
export declare const HISTORICAL_SEED_BUNDLE_SCHEMA_VERSION: "treeseed.seed-bundle/v2";
|
|
3
4
|
export type SeedEnvironment = 'local' | 'staging' | 'prod' | string;
|
|
4
5
|
export interface SeedTeamResource {
|
|
5
6
|
key: string;
|
|
@@ -43,7 +44,7 @@ export interface SeedRepositoryPolicy {
|
|
|
43
44
|
export interface SeedRepositoryResource {
|
|
44
45
|
key: string;
|
|
45
46
|
project: string;
|
|
46
|
-
role: 'primary' | 'fixture' | 'support' | 'template';
|
|
47
|
+
role: 'primary' | 'library' | 'fixture' | 'support' | 'template';
|
|
47
48
|
provider: 'github';
|
|
48
49
|
owner: string;
|
|
49
50
|
name: string;
|
|
@@ -62,6 +63,7 @@ export interface SeedProjectResource {
|
|
|
62
63
|
description: string;
|
|
63
64
|
kind: string;
|
|
64
65
|
primaryRepository?: string;
|
|
66
|
+
libraryRepository: string;
|
|
65
67
|
metadata?: Record<string, unknown>;
|
|
66
68
|
}
|
|
67
69
|
export interface SeedCapacityProviderPrerequisite {
|
|
@@ -75,8 +77,7 @@ export interface SeedCapacityProviderPrerequisite {
|
|
|
75
77
|
allowedModes: Array<'planning' | 'acting'>;
|
|
76
78
|
requiredLanePurposes: ProviderLanePurpose[];
|
|
77
79
|
}
|
|
78
|
-
|
|
79
|
-
schemaVersion: typeof SEED_BUNDLE_SCHEMA_VERSION;
|
|
80
|
+
interface SeedBundleBody {
|
|
80
81
|
name: string;
|
|
81
82
|
version: number;
|
|
82
83
|
description: string;
|
|
@@ -92,6 +93,19 @@ export interface SeedBundleV2 {
|
|
|
92
93
|
capacityProviders: SeedCapacityProviderPrerequisite[];
|
|
93
94
|
};
|
|
94
95
|
}
|
|
96
|
+
export interface SeedBundleV3 extends SeedBundleBody {
|
|
97
|
+
schemaVersion: typeof SEED_BUNDLE_SCHEMA_VERSION;
|
|
98
|
+
}
|
|
99
|
+
/** Historical receipt shape. It is decodable, but cannot be planned or applied. */
|
|
100
|
+
export interface SeedBundleV2 extends Omit<SeedBundleBody, 'resources'> {
|
|
101
|
+
schemaVersion: typeof HISTORICAL_SEED_BUNDLE_SCHEMA_VERSION;
|
|
102
|
+
resources: Omit<SeedBundleBody['resources'], 'projects'> & {
|
|
103
|
+
projects: Array<Omit<SeedProjectResource, 'libraryRepository'> & {
|
|
104
|
+
libraryRepository?: string;
|
|
105
|
+
}>;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export type SeedBundle = SeedBundleV3 | SeedBundleV2;
|
|
95
109
|
export interface SeedResourceReceipt {
|
|
96
110
|
resourceKey: string;
|
|
97
111
|
action: 'create' | 'adopt' | 'update' | 'noop' | 'blocked';
|
|
@@ -118,6 +132,7 @@ export interface SeedBundleDiagnostic {
|
|
|
118
132
|
path: string;
|
|
119
133
|
message: string;
|
|
120
134
|
}
|
|
121
|
-
export declare function canonicalSeedBundlePayload(bundle: Omit<
|
|
122
|
-
export declare function digestSeedBundle(bundle: Omit<
|
|
123
|
-
export declare function validateSeedBundle(bundle:
|
|
135
|
+
export declare function canonicalSeedBundlePayload(bundle: Omit<SeedBundle, 'digest'> | SeedBundle): string;
|
|
136
|
+
export declare function digestSeedBundle(bundle: Omit<SeedBundle, 'digest'> | SeedBundle): Promise<`sha256:${string}`>;
|
|
137
|
+
export declare function validateSeedBundle(bundle: SeedBundle): SeedBundleDiagnostic[];
|
|
138
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
const SEED_BUNDLE_SCHEMA_VERSION = "treeseed.seed-bundle/
|
|
1
|
+
const SEED_BUNDLE_SCHEMA_VERSION = "treeseed.seed-bundle/v3";
|
|
2
|
+
const HISTORICAL_SEED_BUNDLE_SCHEMA_VERSION = "treeseed.seed-bundle/v2";
|
|
2
3
|
function text(value) {
|
|
3
4
|
return typeof value === "string" && value.trim().length > 0;
|
|
4
5
|
}
|
|
@@ -19,7 +20,11 @@ async function digestSeedBundle(bundle) {
|
|
|
19
20
|
function validateSeedBundle(bundle) {
|
|
20
21
|
const diagnostics = [];
|
|
21
22
|
const add = (code, path, message) => diagnostics.push({ code, path, message });
|
|
22
|
-
if (bundle?.schemaVersion
|
|
23
|
+
if (bundle?.schemaVersion === HISTORICAL_SEED_BUNDLE_SCHEMA_VERSION) {
|
|
24
|
+
add("seed_bundle_v2_migration_required", "schemaVersion", `Historical v2 bundles remain readable but must be migrated to ${SEED_BUNDLE_SCHEMA_VERSION} before planning or apply.`);
|
|
25
|
+
} else if (bundle?.schemaVersion !== SEED_BUNDLE_SCHEMA_VERSION) {
|
|
26
|
+
add("seed_bundle_schema_invalid", "schemaVersion", `schemaVersion must be ${SEED_BUNDLE_SCHEMA_VERSION}.`);
|
|
27
|
+
}
|
|
23
28
|
if (!text(bundle?.name) || !Number.isInteger(bundle?.version) || bundle.version < 1) add("seed_bundle_identity_invalid", "name", "Seed name and positive version are required.");
|
|
24
29
|
if (!/^sha256:[a-f0-9]{64}$/u.test(bundle?.digest ?? "")) add("seed_bundle_digest_invalid", "digest", "Seed digest must be an exact SHA-256 digest.");
|
|
25
30
|
const teams = new Set((bundle?.resources?.teams ?? []).map((entry) => entry.key));
|
|
@@ -35,9 +40,17 @@ function validateSeedBundle(bundle) {
|
|
|
35
40
|
if (!teams.has(project.team)) add("seed_bundle_project_team_unknown", `resources.projects[${index}].team`, "Project references an unknown team.");
|
|
36
41
|
if (!project.primaryRepository && project.kind !== "content") add("seed_bundle_primary_repository_required", `resources.projects[${index}].primaryRepository`, "Non-content projects require a primary source repository.");
|
|
37
42
|
if (project.primaryRepository && !repositories.has(project.primaryRepository)) add("seed_bundle_primary_repository_unknown", `resources.projects[${index}].primaryRepository`, "Project primary repository is missing.");
|
|
43
|
+
if (!project.libraryRepository) add("seed_bundle_library_repository_required", `resources.projects[${index}].libraryRepository`, "Every project requires one library repository.");
|
|
44
|
+
if (project.libraryRepository && !repositories.has(project.libraryRepository)) add("seed_bundle_library_repository_unknown", `resources.projects[${index}].libraryRepository`, "Project library repository is missing.");
|
|
38
45
|
}
|
|
39
46
|
for (const [index, repository] of (bundle?.resources?.repositories ?? []).entries()) {
|
|
40
|
-
if (repository.role === "content") add("
|
|
47
|
+
if (repository.role === "content") add("seed_bundle_content_repository_renamed", `resources.repositories[${index}].role`, "Repository-backed knowledge uses role library.");
|
|
48
|
+
}
|
|
49
|
+
for (const [index, project] of (bundle?.resources?.projects ?? []).entries()) {
|
|
50
|
+
const library = (bundle?.resources?.repositories ?? []).find((entry) => entry.key === project.libraryRepository);
|
|
51
|
+
if (library && (library.role !== "library" || library.project !== project.key)) add("seed_bundle_library_repository_mismatch", `resources.projects[${index}].libraryRepository`, "libraryRepository must reference a library-role repository owned by this project.");
|
|
52
|
+
const projectLibraries = (bundle?.resources?.repositories ?? []).filter((entry) => entry.project === project.key && entry.role === "library");
|
|
53
|
+
if (projectLibraries.length !== 1) add("seed_bundle_library_repository_count_invalid", `resources.projects[${index}].libraryRepository`, "Every project must declare exactly one library-role repository.");
|
|
41
54
|
}
|
|
42
55
|
for (const [index, provider] of (bundle?.runtime?.capacityProviders ?? []).entries()) {
|
|
43
56
|
if (!teams.has(provider.team)) add("seed_bundle_provider_team_unknown", `runtime.capacityProviders[${index}].team`, "Provider references an unknown team.");
|
|
@@ -47,6 +60,7 @@ function validateSeedBundle(bundle) {
|
|
|
47
60
|
return diagnostics;
|
|
48
61
|
}
|
|
49
62
|
export {
|
|
63
|
+
HISTORICAL_SEED_BUNDLE_SCHEMA_VERSION,
|
|
50
64
|
SEED_BUNDLE_SCHEMA_VERSION,
|
|
51
65
|
canonicalSeedBundlePayload,
|
|
52
66
|
digestSeedBundle,
|