@treeseed/sdk 0.13.0-rc.60 → 0.13.0-rc.62
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/.treeseed-build-complete.json +1 -1
- package/dist/agent-capacity/validation/agent-definition-schema.d.ts +120 -120
- package/dist/capacity-provider/capability-ontology.d.ts +190 -190
- package/dist/capacity-provider/client.d.ts +3 -3
- package/dist/capacity-provider/core-capability-catalog.d.ts +13 -13
- package/dist/capacity-provider/environment-catalog.d.ts +62 -62
- package/dist/capacity-provider/sandbox-contracts.d.ts +66 -66
- package/dist/content/validation/agent-operational-content-schemas.d.ts +72 -72
- package/dist/content/validation/auxiliary-content-schemas.d.ts +74 -74
- package/dist/content/validation/content-model-schemas.d.ts +435 -435
- package/dist/content/validation/portable-content-data.d.ts +53 -53
- package/dist/deployment/ai-mode.d.ts +46 -46
- package/dist/deployment/schemas.d.ts +648 -648
- package/dist/development/schemas.d.ts +290 -290
- package/dist/operator-contracts/canonical-command-tree.js +10 -0
- package/dist/operator-contracts/catalog/capability-ontology-operations.d.ts +41 -41
- package/dist/operator-contracts/catalog/communication-operations.d.ts +62 -62
- package/dist/operator-contracts/catalog/inbox-operations.d.ts +34 -34
- package/dist/operator-contracts/communication/contracts.d.ts +228 -228
- package/dist/operator-contracts/control-plane-operations.d.ts +145 -145
- package/dist/operator-contracts/inbox/contracts.d.ts +108 -108
- package/dist/platform/contracts.d.ts +383 -0
- package/dist/platform/contracts.js +79 -0
- package/dist/platform/index.d.ts +5 -0
- package/dist/platform/index.js +5 -0
- package/dist/platform/inventory.d.ts +11 -0
- package/dist/platform/inventory.js +53 -0
- package/dist/platform/schemas.d.ts +806 -0
- package/dist/platform/schemas.js +68 -0
- package/dist/platform/verification.d.ts +9 -0
- package/dist/platform/verification.js +104 -0
- package/dist/platform/workset.d.ts +21 -0
- package/dist/platform/workset.js +102 -0
- package/dist/security/encrypted-envelope.d.ts +14 -14
- package/package.json +5 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const repositorySchema = z.object({
|
|
3
|
+
key: z.string().min(1),
|
|
4
|
+
project: z.string().min(1),
|
|
5
|
+
role: z.enum(["primary", "library", "fixture"]),
|
|
6
|
+
gitUrl: z.string().min(1),
|
|
7
|
+
defaultBranch: z.string().min(1).default("main"),
|
|
8
|
+
repositoryPolicy: z.object({ stagingBranch: z.string().min(1).optional() }).passthrough().optional()
|
|
9
|
+
}).passthrough();
|
|
10
|
+
const projectSchema = z.object({
|
|
11
|
+
key: z.string().min(1),
|
|
12
|
+
slug: z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u),
|
|
13
|
+
primaryRepository: z.string().min(1)
|
|
14
|
+
}).passthrough();
|
|
15
|
+
const inventorySchema = z.object({
|
|
16
|
+
schemaVersion: z.string().min(1),
|
|
17
|
+
resources: z.object({
|
|
18
|
+
projects: z.array(projectSchema),
|
|
19
|
+
repositories: z.array(repositorySchema)
|
|
20
|
+
}).passthrough()
|
|
21
|
+
}).passthrough();
|
|
22
|
+
const profileSchema = z.object({
|
|
23
|
+
schemaVersion: z.literal("treeseed.platform-profile/v1"),
|
|
24
|
+
id: z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u),
|
|
25
|
+
extends: z.array(z.string()).default([]),
|
|
26
|
+
sources: z.object({ projects: z.array(z.string()).default([]) }).default({ projects: [] }),
|
|
27
|
+
runtime: z.object({ targets: z.array(z.string()).default([]) }).passthrough().default({ targets: [] })
|
|
28
|
+
}).passthrough();
|
|
29
|
+
const worksetSelectionSchema = z.object({
|
|
30
|
+
profiles: z.array(z.string()).default([]),
|
|
31
|
+
projects: z.array(z.string()).default([]),
|
|
32
|
+
exclude: z.array(z.string()).default([])
|
|
33
|
+
});
|
|
34
|
+
const worksetEntrySchema = z.object({
|
|
35
|
+
project: z.string(),
|
|
36
|
+
repository: z.string(),
|
|
37
|
+
gitUrl: z.string(),
|
|
38
|
+
branch: z.string(),
|
|
39
|
+
commit: z.string().regex(/^[0-9a-f]{40}$/u),
|
|
40
|
+
path: z.string(),
|
|
41
|
+
action: z.enum(["clone", "fast-forward", "noop", "blocked"]),
|
|
42
|
+
blockers: z.array(z.string()).default([])
|
|
43
|
+
});
|
|
44
|
+
const worksetPlanSchema = z.object({
|
|
45
|
+
schemaVersion: z.literal("treeseed.platform-workset-plan/v1"),
|
|
46
|
+
root: z.string(),
|
|
47
|
+
inventoryPath: z.string(),
|
|
48
|
+
inventoryDigest: z.string(),
|
|
49
|
+
selection: worksetSelectionSchema,
|
|
50
|
+
entries: z.array(worksetEntrySchema),
|
|
51
|
+
ok: z.boolean()
|
|
52
|
+
});
|
|
53
|
+
const worksetReceiptSchema = z.object({
|
|
54
|
+
schemaVersion: z.literal("treeseed.platform-workset-receipt/v1"),
|
|
55
|
+
planDigest: z.string(),
|
|
56
|
+
inventoryDigest: z.string(),
|
|
57
|
+
entries: z.array(worksetEntrySchema.omit({ action: true, blockers: true }).extend({ action: z.enum(["clone", "fast-forward", "noop"]) }))
|
|
58
|
+
});
|
|
59
|
+
export {
|
|
60
|
+
inventorySchema,
|
|
61
|
+
profileSchema,
|
|
62
|
+
projectSchema,
|
|
63
|
+
repositorySchema,
|
|
64
|
+
worksetEntrySchema,
|
|
65
|
+
worksetPlanSchema,
|
|
66
|
+
worksetReceiptSchema,
|
|
67
|
+
worksetSelectionSchema
|
|
68
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PlatformDiagnostic } from './schemas.js';
|
|
2
|
+
export interface PlatformVerification {
|
|
3
|
+
schemaVersion: 'treeseed.platform-verification/v1';
|
|
4
|
+
root: string;
|
|
5
|
+
digest: string;
|
|
6
|
+
ok: boolean;
|
|
7
|
+
diagnostics: PlatformDiagnostic[];
|
|
8
|
+
}
|
|
9
|
+
export declare function verifyPlatformRepository(root?: string): PlatformVerification;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, lstatSync, readFileSync } from "node:fs";
|
|
3
|
+
import { extname, relative, resolve } from "node:path";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
import { parse } from "yaml";
|
|
6
|
+
import { hostTemplateSchema, integrationLockSchema } from "./contracts.js";
|
|
7
|
+
import { inventorySchema, profileSchema } from "./schemas.js";
|
|
8
|
+
const allowedRoots = /* @__PURE__ */ new Set([".github", "config", "docs", "profiles", "seeds", "templates"]);
|
|
9
|
+
const allowedFiles = /* @__PURE__ */ new Set([".gitignore", "AGENTS.md", "LICENSE", "LICENSE.md", "README.md", "skills-lock.json", "treeseed.site.yaml"]);
|
|
10
|
+
const allowedExtensions = /* @__PURE__ */ new Set([".md", ".yaml", ".yml", ".json", ".lock", ".toml"]);
|
|
11
|
+
const forbiddenExtensions = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".py", ".sh", ".go", ".rs", ".java"]);
|
|
12
|
+
const personalPath = /(?:^|[\s'"`:=])(?:\/home\/[^/\s]+|\/Users\/[^/\s]+|[A-Za-z]:\\Users\\[^\\\s]+)/u;
|
|
13
|
+
function semanticDiagnostics(root, tracked) {
|
|
14
|
+
const diagnostics = [];
|
|
15
|
+
const document = (path) => parse(readFileSync(resolve(root, path), "utf8"));
|
|
16
|
+
const report = (code, path, message) => diagnostics.push({ code, path, message });
|
|
17
|
+
const safe = (path, load) => {
|
|
18
|
+
try {
|
|
19
|
+
return load();
|
|
20
|
+
} catch (error) {
|
|
21
|
+
report("declaration_invalid", path, error instanceof Error ? error.message : String(error));
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const profiles = /* @__PURE__ */ new Map();
|
|
26
|
+
for (const path of [...tracked].filter((value) => /^profiles\/[^/]+\.ya?ml$/u.test(value))) {
|
|
27
|
+
const profile = safe(path, () => profileSchema.parse(document(path)));
|
|
28
|
+
if (profile) profiles.set(profile.id, profile);
|
|
29
|
+
}
|
|
30
|
+
const sitePath = "treeseed.site.yaml";
|
|
31
|
+
if (!tracked.has(sitePath)) return diagnostics;
|
|
32
|
+
const site = safe(sitePath, () => document(sitePath));
|
|
33
|
+
if (!site) return diagnostics;
|
|
34
|
+
const development = site.development;
|
|
35
|
+
const inventoryPath = development?.local?.inventory?.path;
|
|
36
|
+
let projectSlugs = /* @__PURE__ */ new Set();
|
|
37
|
+
if (development?.local?.inventory?.source !== "seed" || !inventoryPath || !tracked.has(inventoryPath)) {
|
|
38
|
+
report("inventory_reference_invalid", sitePath, "The local inventory must reference a tracked seed declaration.");
|
|
39
|
+
} else {
|
|
40
|
+
const inventory = safe(inventoryPath, () => inventorySchema.parse(document(inventoryPath)));
|
|
41
|
+
if (inventory) projectSlugs = new Set(inventory.resources.projects.map((project) => project.slug));
|
|
42
|
+
}
|
|
43
|
+
for (const [id, profile] of profiles) {
|
|
44
|
+
for (const project of profile.sources.projects) if (!projectSlugs.has(project)) report("profile_project_unknown", `profiles/${id}.yaml`, `Profile project ${project} is absent from the portable inventory.`);
|
|
45
|
+
for (const parent of profile.extends) if (!profiles.has(parent)) report("profile_parent_unknown", `profiles/${id}.yaml`, `Profile parent ${parent} does not exist.`);
|
|
46
|
+
}
|
|
47
|
+
const integrations = site.integrations;
|
|
48
|
+
const integrationDigests = /* @__PURE__ */ new Map();
|
|
49
|
+
for (const path of Object.values(integrations ?? {}).filter((value) => value.includes("/integrations/"))) {
|
|
50
|
+
if (!tracked.has(path)) {
|
|
51
|
+
report("integration_reference_invalid", sitePath, `Integration lock ${path} is not tracked.`);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const lock = safe(path, () => integrationLockSchema.parse(document(path)));
|
|
55
|
+
if (!lock) continue;
|
|
56
|
+
const digest = `sha256:${createHash("sha256").update(JSON.stringify(lock.components)).digest("hex")}`;
|
|
57
|
+
if (digest !== lock.digest) report("integration_digest_mismatch", path, "Integration lock digest does not bind its exact component list.");
|
|
58
|
+
integrationDigests.set(lock.release.replace(/-\d+$/u, ""), lock.digest);
|
|
59
|
+
}
|
|
60
|
+
for (const path of [...tracked].filter((value) => /^config\/hosts\/[^/]+\.ya?ml$/u.test(value))) {
|
|
61
|
+
const host = safe(path, () => hostTemplateSchema.parse(document(path)));
|
|
62
|
+
if (!host) continue;
|
|
63
|
+
for (const profile of host.profiles) if (!profiles.has(profile)) report("host_profile_unknown", path, `Host template profile ${profile} does not exist.`);
|
|
64
|
+
if (![...integrationDigests.values()].includes(host.integration.digest)) report("host_integration_unknown", path, "Host template integration digest is not present in a referenced integration lock.");
|
|
65
|
+
}
|
|
66
|
+
const production = site.production;
|
|
67
|
+
if (production?.mutation !== "blocked") report("production_not_fail_closed", sitePath, "Production mutation must remain blocked until promotion gates pass.");
|
|
68
|
+
if (production?.topology && !tracked.has(production.topology)) report("topology_reference_invalid", sitePath, "Production topology must reference a tracked declaration.");
|
|
69
|
+
return diagnostics;
|
|
70
|
+
}
|
|
71
|
+
function verifyPlatformRepository(root = process.cwd()) {
|
|
72
|
+
const absoluteRoot = resolve(root);
|
|
73
|
+
const listed = execFileSync("git", ["ls-files", "-z"], { cwd: absoluteRoot, encoding: "utf8" }).split("\0").filter(Boolean).sort();
|
|
74
|
+
const diagnostics = [];
|
|
75
|
+
const hash = createHash("sha256");
|
|
76
|
+
for (const path of listed) {
|
|
77
|
+
const fullPath = resolve(absoluteRoot, path);
|
|
78
|
+
if (!fullPath.startsWith(`${absoluteRoot}/`)) {
|
|
79
|
+
diagnostics.push({ code: "path_not_portable", path, message: "Tracked paths must stay beneath the repository and cannot be symbolic links." });
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (!existsSync(fullPath)) {
|
|
83
|
+
diagnostics.push({ code: "tracked_file_missing", path, message: "Tracked Platform files must exist in the working tree." });
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (lstatSync(fullPath).isSymbolicLink()) {
|
|
87
|
+
diagnostics.push({ code: "path_not_portable", path, message: "Tracked paths must stay beneath the repository and cannot be symbolic links." });
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const rootSegment = path.split("/")[0];
|
|
91
|
+
const extension = extname(path);
|
|
92
|
+
if (!allowedFiles.has(path) && !allowedRoots.has(rootSegment)) diagnostics.push({ code: "content_root_forbidden", path, message: "Platform may contain only declarations, documentation, locks, and GitHub configuration." });
|
|
93
|
+
if (forbiddenExtensions.has(extension)) diagnostics.push({ code: "implementation_forbidden", path, message: "Executable implementation belongs in its owning package." });
|
|
94
|
+
if (!allowedFiles.has(path) && !allowedExtensions.has(extension)) diagnostics.push({ code: "file_type_forbidden", path, message: "This tracked file type is outside the declarative Platform boundary." });
|
|
95
|
+
const source = readFileSync(fullPath);
|
|
96
|
+
hash.update(relative(absoluteRoot, fullPath)).update("\0").update(source);
|
|
97
|
+
if (personalPath.test(source.toString("utf8"))) diagnostics.push({ code: "personal_path_forbidden", path, message: "Committed home-directory paths are forbidden." });
|
|
98
|
+
}
|
|
99
|
+
diagnostics.push(...semanticDiagnostics(absoluteRoot, new Set(listed)));
|
|
100
|
+
return { schemaVersion: "treeseed.platform-verification/v1", root: absoluteRoot, digest: `sha256:${hash.digest("hex")}`, ok: diagnostics.length === 0, diagnostics };
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
verifyPlatformRepository
|
|
104
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Inventory, type PlatformProfile, type WorksetPlan, type WorksetReceipt, type WorksetSelection } from './schemas.js';
|
|
2
|
+
export interface GitRunner {
|
|
3
|
+
run(cwd: string, args: string[]): string;
|
|
4
|
+
}
|
|
5
|
+
export interface RemoteObserver {
|
|
6
|
+
observe(url: string, branch: string): string;
|
|
7
|
+
isAncestor(url: string, base: string, target: string): boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface PlanWorksetInput {
|
|
10
|
+
root: string;
|
|
11
|
+
inventoryPath: string;
|
|
12
|
+
inventoryDigest: string;
|
|
13
|
+
inventory: Inventory;
|
|
14
|
+
profiles?: PlatformProfile[];
|
|
15
|
+
selection?: Partial<WorksetSelection>;
|
|
16
|
+
git?: GitRunner;
|
|
17
|
+
remote?: RemoteObserver;
|
|
18
|
+
}
|
|
19
|
+
export declare function planPlatformWorkset(input: PlanWorksetInput): WorksetPlan;
|
|
20
|
+
export declare function applyPlatformWorkset(plan: WorksetPlan, git?: GitRunner, remote?: RemoteObserver): WorksetReceipt;
|
|
21
|
+
export declare function readWorksetReceipt(path: string): WorksetReceipt;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
6
|
+
import { worksetReceiptSchema } from "./schemas.js";
|
|
7
|
+
import { resolveProfileProjects } from "./inventory.js";
|
|
8
|
+
const systemGit = { run: (cwd, args) => execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim() };
|
|
9
|
+
const systemRemote = { observe: (url, branch) => {
|
|
10
|
+
const output = execFileSync("git", ["ls-remote", "--exit-code", url, `refs/heads/${branch}`], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
11
|
+
const commit = output.split(/\s+/u)[0];
|
|
12
|
+
if (!commit) throw new Error(`Remote branch ${branch} was not found at ${url}.`);
|
|
13
|
+
return commit;
|
|
14
|
+
}, isAncestor: (url, base, target) => {
|
|
15
|
+
const temporary = mkdtempSync(resolve(tmpdir(), "platform-workset-"));
|
|
16
|
+
try {
|
|
17
|
+
execFileSync("git", ["init", "--bare", temporary], { stdio: "ignore" });
|
|
18
|
+
execFileSync("git", ["fetch", "--quiet", "--filter=blob:none", "--no-tags", url, target], { cwd: temporary, stdio: "ignore" });
|
|
19
|
+
execFileSync("git", ["merge-base", "--is-ancestor", base, target], { cwd: temporary, stdio: "ignore" });
|
|
20
|
+
return true;
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
} finally {
|
|
24
|
+
rmSync(temporary, { recursive: true, force: true });
|
|
25
|
+
}
|
|
26
|
+
} };
|
|
27
|
+
const digest = (value) => `sha256:${createHash("sha256").update(JSON.stringify(value)).digest("hex")}`;
|
|
28
|
+
const normalizedRemote = (url) => url.replace(/^git@github\.com:/u, "https://github.com/").replace(/\.git$/u, "").toLowerCase();
|
|
29
|
+
function safePath(root, slug) {
|
|
30
|
+
const path = resolve(root, "packages", slug);
|
|
31
|
+
if (!path.startsWith(`${resolve(root)}/packages/`)) throw new Error(`Project ${slug} escapes packages/.`);
|
|
32
|
+
return path;
|
|
33
|
+
}
|
|
34
|
+
function inspectCheckout(entry, git, remote) {
|
|
35
|
+
if (!existsSync(entry.path)) return { ...entry, action: "clone", blockers: [] };
|
|
36
|
+
const blockers = [];
|
|
37
|
+
if (lstatSync(entry.path).isSymbolicLink()) blockers.push("checkout_path_is_symlink");
|
|
38
|
+
try {
|
|
39
|
+
if (realpathSync(entry.path) !== entry.path) blockers.push("checkout_path_not_canonical");
|
|
40
|
+
if (normalizedRemote(git.run(entry.path, ["remote", "get-url", "origin"])) !== normalizedRemote(entry.gitUrl)) blockers.push("origin_mismatch");
|
|
41
|
+
if (git.run(entry.path, ["status", "--porcelain"])) blockers.push("checkout_dirty");
|
|
42
|
+
if (git.run(entry.path, ["branch", "--show-current"]) !== entry.branch) blockers.push("branch_mismatch");
|
|
43
|
+
const current = git.run(entry.path, ["rev-parse", "HEAD"]);
|
|
44
|
+
if (current === entry.commit) return { ...entry, action: blockers.length ? "blocked" : "noop", blockers };
|
|
45
|
+
if (!remote.isAncestor(entry.gitUrl, current, entry.commit)) blockers.push("remote_history_diverged");
|
|
46
|
+
return { ...entry, action: blockers.length ? "blocked" : "fast-forward", blockers };
|
|
47
|
+
} catch {
|
|
48
|
+
blockers.push("checkout_not_safe_git_repository");
|
|
49
|
+
return { ...entry, action: "blocked", blockers: [...new Set(blockers)] };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function planPlatformWorkset(input) {
|
|
53
|
+
const selection = { profiles: input.selection?.profiles ?? [], projects: input.selection?.projects ?? [], exclude: input.selection?.exclude ?? [] };
|
|
54
|
+
const projectCatalog = new Map(input.inventory.resources.projects.map((project) => [project.slug, project]));
|
|
55
|
+
const profileProjects = resolveProfileProjects(input.profiles ?? [], selection.profiles);
|
|
56
|
+
const requested = new Set(selection.projects.length || profileProjects.length ? [...profileProjects, ...selection.projects] : [...projectCatalog.keys()]);
|
|
57
|
+
selection.exclude.forEach((slug) => requested.delete(slug));
|
|
58
|
+
requested.delete("platform");
|
|
59
|
+
const repositories = new Map(input.inventory.resources.repositories.map((repository) => [repository.key, repository]));
|
|
60
|
+
const entries = [...requested].sort().map((slug) => {
|
|
61
|
+
const project = projectCatalog.get(slug);
|
|
62
|
+
if (!project) throw new Error(`Unknown project ${slug}.`);
|
|
63
|
+
const repository = repositories.get(project.primaryRepository);
|
|
64
|
+
if (!repository || repository.role !== "primary") throw new Error(`Project ${slug} has no primary source repository.`);
|
|
65
|
+
const branch = repository.repositoryPolicy?.stagingBranch ?? repository.defaultBranch;
|
|
66
|
+
const remote = input.remote ?? systemRemote;
|
|
67
|
+
const commit = remote.observe(repository.gitUrl, branch);
|
|
68
|
+
return inspectCheckout({ project: slug, repository: repository.key, gitUrl: repository.gitUrl, branch, commit, path: safePath(input.root, slug) }, input.git ?? systemGit, remote);
|
|
69
|
+
});
|
|
70
|
+
return { schemaVersion: "treeseed.platform-workset-plan/v1", root: resolve(input.root), inventoryPath: input.inventoryPath, inventoryDigest: input.inventoryDigest, selection, entries, ok: entries.every((entry) => entry.action !== "blocked") };
|
|
71
|
+
}
|
|
72
|
+
function applyPlatformWorkset(plan, git = systemGit, remote = systemRemote) {
|
|
73
|
+
if (!plan.ok || plan.entries.some((entry) => entry.action === "blocked")) throw new Error("Blocked workset plans cannot be applied.");
|
|
74
|
+
for (const entry of plan.entries) {
|
|
75
|
+
if (remote.observe(entry.gitUrl, entry.branch) !== entry.commit) throw new Error(`Remote branch moved after planning for ${entry.project}; create a new plan.`);
|
|
76
|
+
}
|
|
77
|
+
for (const entry of plan.entries) {
|
|
78
|
+
if (entry.action === "clone") {
|
|
79
|
+
mkdirSync(dirname(entry.path), { recursive: true });
|
|
80
|
+
git.run(plan.root, ["clone", "--branch", entry.branch, "--single-branch", entry.gitUrl, entry.path]);
|
|
81
|
+
} else if (entry.action === "fast-forward") {
|
|
82
|
+
git.run(entry.path, ["fetch", "origin", entry.branch]);
|
|
83
|
+
git.run(entry.path, ["merge", "--ff-only", entry.commit]);
|
|
84
|
+
}
|
|
85
|
+
const actual = git.run(entry.path, ["rev-parse", "HEAD"]);
|
|
86
|
+
if (actual !== entry.commit) throw new Error(`Checkout ${entry.project} did not converge to ${entry.commit}.`);
|
|
87
|
+
}
|
|
88
|
+
const receipt = worksetReceiptSchema.parse({ schemaVersion: "treeseed.platform-workset-receipt/v1", planDigest: digest(plan), inventoryDigest: plan.inventoryDigest, entries: plan.entries.map(({ blockers: _blockers, ...entry }) => ({ ...entry, action: entry.action === "blocked" ? "noop" : entry.action })) });
|
|
89
|
+
const receiptPath = resolve(plan.root, ".treeseed", "worksets", `${receipt.planDigest.slice("sha256:".length)}.json`);
|
|
90
|
+
mkdirSync(dirname(receiptPath), { recursive: true });
|
|
91
|
+
writeFileSync(receiptPath, `${JSON.stringify(receipt, null, 2)}
|
|
92
|
+
`, { mode: 384 });
|
|
93
|
+
return receipt;
|
|
94
|
+
}
|
|
95
|
+
function readWorksetReceipt(path) {
|
|
96
|
+
return worksetReceiptSchema.parse(JSON.parse(readFileSync(path, "utf8")));
|
|
97
|
+
}
|
|
98
|
+
export {
|
|
99
|
+
applyPlatformWorkset,
|
|
100
|
+
planPlatformWorkset,
|
|
101
|
+
readWorksetReceipt
|
|
102
|
+
};
|
|
@@ -12,27 +12,27 @@ export declare const encryptedEnvelopeAadSchema: z.ZodObject<{
|
|
|
12
12
|
eventType: z.ZodOptional<z.ZodString>;
|
|
13
13
|
schemaVersion: z.ZodString;
|
|
14
14
|
}, "strict", z.ZodTypeAny, {
|
|
15
|
+
schemaVersion: string;
|
|
15
16
|
purpose: string;
|
|
16
17
|
resourceType: string;
|
|
17
18
|
resourceId: string;
|
|
18
|
-
schemaVersion: string;
|
|
19
19
|
teamId?: string | undefined;
|
|
20
|
+
assignmentId?: string | undefined;
|
|
20
21
|
topicId?: string | undefined;
|
|
21
22
|
sendId?: string | undefined;
|
|
22
23
|
invocationId?: string | undefined;
|
|
23
|
-
assignmentId?: string | undefined;
|
|
24
24
|
sequence?: number | undefined;
|
|
25
25
|
eventType?: string | undefined;
|
|
26
26
|
}, {
|
|
27
|
+
schemaVersion: string;
|
|
27
28
|
purpose: string;
|
|
28
29
|
resourceType: string;
|
|
29
30
|
resourceId: string;
|
|
30
|
-
schemaVersion: string;
|
|
31
31
|
teamId?: string | undefined;
|
|
32
|
+
assignmentId?: string | undefined;
|
|
32
33
|
topicId?: string | undefined;
|
|
33
34
|
sendId?: string | undefined;
|
|
34
35
|
invocationId?: string | undefined;
|
|
35
|
-
assignmentId?: string | undefined;
|
|
36
36
|
sequence?: number | undefined;
|
|
37
37
|
eventType?: string | undefined;
|
|
38
38
|
}>;
|
|
@@ -81,27 +81,27 @@ export declare const encryptedEnvelopeSchema: z.ZodObject<{
|
|
|
81
81
|
eventType: z.ZodOptional<z.ZodString>;
|
|
82
82
|
schemaVersion: z.ZodString;
|
|
83
83
|
}, "strict", z.ZodTypeAny, {
|
|
84
|
+
schemaVersion: string;
|
|
84
85
|
purpose: string;
|
|
85
86
|
resourceType: string;
|
|
86
87
|
resourceId: string;
|
|
87
|
-
schemaVersion: string;
|
|
88
88
|
teamId?: string | undefined;
|
|
89
|
+
assignmentId?: string | undefined;
|
|
89
90
|
topicId?: string | undefined;
|
|
90
91
|
sendId?: string | undefined;
|
|
91
92
|
invocationId?: string | undefined;
|
|
92
|
-
assignmentId?: string | undefined;
|
|
93
93
|
sequence?: number | undefined;
|
|
94
94
|
eventType?: string | undefined;
|
|
95
95
|
}, {
|
|
96
|
+
schemaVersion: string;
|
|
96
97
|
purpose: string;
|
|
97
98
|
resourceType: string;
|
|
98
99
|
resourceId: string;
|
|
99
|
-
schemaVersion: string;
|
|
100
100
|
teamId?: string | undefined;
|
|
101
|
+
assignmentId?: string | undefined;
|
|
101
102
|
topicId?: string | undefined;
|
|
102
103
|
sendId?: string | undefined;
|
|
103
104
|
invocationId?: string | undefined;
|
|
104
|
-
assignmentId?: string | undefined;
|
|
105
105
|
sequence?: number | undefined;
|
|
106
106
|
eventType?: string | undefined;
|
|
107
107
|
}>;
|
|
@@ -110,6 +110,7 @@ export declare const encryptedEnvelopeSchema: z.ZodObject<{
|
|
|
110
110
|
createdAt: z.ZodString;
|
|
111
111
|
}, "strict", z.ZodTypeAny, {
|
|
112
112
|
schemaVersion: "treeseed.encrypted-envelope/v1";
|
|
113
|
+
createdAt: string;
|
|
113
114
|
algorithm: "aes-256-gcm";
|
|
114
115
|
keyProvider: string;
|
|
115
116
|
keyId: string;
|
|
@@ -125,23 +126,23 @@ export declare const encryptedEnvelopeSchema: z.ZodObject<{
|
|
|
125
126
|
ciphertext: string;
|
|
126
127
|
};
|
|
127
128
|
aad: {
|
|
129
|
+
schemaVersion: string;
|
|
128
130
|
purpose: string;
|
|
129
131
|
resourceType: string;
|
|
130
132
|
resourceId: string;
|
|
131
|
-
schemaVersion: string;
|
|
132
133
|
teamId?: string | undefined;
|
|
134
|
+
assignmentId?: string | undefined;
|
|
133
135
|
topicId?: string | undefined;
|
|
134
136
|
sendId?: string | undefined;
|
|
135
137
|
invocationId?: string | undefined;
|
|
136
|
-
assignmentId?: string | undefined;
|
|
137
138
|
sequence?: number | undefined;
|
|
138
139
|
eventType?: string | undefined;
|
|
139
140
|
};
|
|
140
141
|
aadDigest: string;
|
|
141
142
|
ciphertextDigest: string;
|
|
142
|
-
createdAt: string;
|
|
143
143
|
}, {
|
|
144
144
|
schemaVersion: "treeseed.encrypted-envelope/v1";
|
|
145
|
+
createdAt: string;
|
|
145
146
|
algorithm: "aes-256-gcm";
|
|
146
147
|
keyProvider: string;
|
|
147
148
|
keyId: string;
|
|
@@ -157,21 +158,20 @@ export declare const encryptedEnvelopeSchema: z.ZodObject<{
|
|
|
157
158
|
ciphertext: string;
|
|
158
159
|
};
|
|
159
160
|
aad: {
|
|
161
|
+
schemaVersion: string;
|
|
160
162
|
purpose: string;
|
|
161
163
|
resourceType: string;
|
|
162
164
|
resourceId: string;
|
|
163
|
-
schemaVersion: string;
|
|
164
165
|
teamId?: string | undefined;
|
|
166
|
+
assignmentId?: string | undefined;
|
|
165
167
|
topicId?: string | undefined;
|
|
166
168
|
sendId?: string | undefined;
|
|
167
169
|
invocationId?: string | undefined;
|
|
168
|
-
assignmentId?: string | undefined;
|
|
169
170
|
sequence?: number | undefined;
|
|
170
171
|
eventType?: string | undefined;
|
|
171
172
|
};
|
|
172
173
|
aadDigest: string;
|
|
173
174
|
ciphertextDigest: string;
|
|
174
|
-
createdAt: string;
|
|
175
175
|
}>;
|
|
176
176
|
export type EncryptedEnvelopeAad = z.infer<typeof encryptedEnvelopeAadSchema>;
|
|
177
177
|
export type EncryptedEnvelope = z.infer<typeof encryptedEnvelopeSchema>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@treeseed/sdk",
|
|
3
|
-
"version": "0.13.0-rc.
|
|
3
|
+
"version": "0.13.0-rc.62",
|
|
4
4
|
"description": "Portable TreeSeed contracts, standards, and typed remote control-plane clients.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -62,6 +62,10 @@
|
|
|
62
62
|
"vitest": "^4.1.2"
|
|
63
63
|
},
|
|
64
64
|
"exports": {
|
|
65
|
+
"./platform": {
|
|
66
|
+
"types": "./dist/platform/index.d.ts",
|
|
67
|
+
"default": "./dist/platform/index.js"
|
|
68
|
+
},
|
|
65
69
|
"./security": {
|
|
66
70
|
"types": "./dist/security/index.d.ts",
|
|
67
71
|
"default": "./dist/security/index.js"
|