@forgezero/agent 0.1.29 → 0.1.31
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/README.md +52 -4
- package/dist/agent-heartbeat.js +1 -1
- package/dist/cli/agent-install.d.ts +1 -1
- package/dist/definition.d.ts +19 -10
- package/dist/definition.js +77 -31
- package/dist/deploy-file.d.ts +40 -0
- package/dist/deploy-file.js +378 -0
- package/dist/deployment-pull.d.ts +3 -3
- package/dist/deployment.d.ts +9 -5
- package/dist/fz-agent.js +123 -58
- package/dist/fz.js +729 -19
- package/dist/index.d.ts +2 -2
- package/dist/project-context.d.ts +37 -0
- package/dist/project-context.js +266 -0
- package/dist/provision.d.ts +1 -1
- package/dist/provision.js +19 -4
- package/dist/software-helper.js +17 -2
- package/dist/software.d.ts +24 -2
- package/dist/software.js +20 -3
- package/dist/version.d.ts +1 -1
- package/package.json +12 -2
- package/schema/deploy-v2.json +70 -0
package/README.md
CHANGED
|
@@ -118,20 +118,68 @@ root-only control socket or signed outbound claim path, then the deployment
|
|
|
118
118
|
manager validates the checked-in definition and submits it to the common keyed
|
|
119
119
|
queue.
|
|
120
120
|
|
|
121
|
+
## Project context for any repository
|
|
122
|
+
|
|
123
|
+
Conversation memory is not a project database. Initialize one vendor-neutral,
|
|
124
|
+
Git-owned context and generate the small files each AI product discovers:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
fz project init
|
|
128
|
+
# edit .forgezero/project.json and create its named truth sources
|
|
129
|
+
fz project sync
|
|
130
|
+
fz project check
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, Copilot instructions and the Cursor rule
|
|
134
|
+
are generated adapters. They contain no independent architecture or progress.
|
|
135
|
+
`fz project check` fails when a truth source is missing or an adapter was edited
|
|
136
|
+
by hand. Tools and skills remain optional execution aids; accepted decisions and
|
|
137
|
+
status live in the repository and therefore survive switching AI agents.
|
|
138
|
+
|
|
121
139
|
## Deployment definitions
|
|
122
140
|
|
|
123
|
-
A repository may commit `.fz/deploy.
|
|
141
|
+
A repository may commit `.fz/deploy.json` with its own profiles, prerequisite
|
|
124
142
|
checks, commands, and the exact secret names each step needs. ForgeZero does not
|
|
125
143
|
choose a tenant's database, framework, or deploy shape. The agent validates the
|
|
126
|
-
file before executing any command, prepares only the selected
|
|
144
|
+
file before executing any command, prepares only the selected profile, and gives a
|
|
127
145
|
step only the vault values it explicitly names. `await` returns the complete
|
|
128
146
|
pipeline result; no polling service or persistent queue is required.
|
|
129
147
|
|
|
148
|
+
Create and validate that file with the same public package that executes it:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
fz deploy catalog --channel production
|
|
152
|
+
fz deploy init --profile app --software bun@1.3.14
|
|
153
|
+
# Replace the explicit safe blockers with this project's release and health commands.
|
|
154
|
+
fz deploy check
|
|
155
|
+
fz deploy sync
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`init` refuses to invent a generic release or health check: both generated steps
|
|
159
|
+
exit non-zero until the project replaces them. `check` validates the published
|
|
160
|
+
v2 schema and active software coordinates, then prints a formatting-independent
|
|
161
|
+
semantic SHA-256 digest. The schema ships at
|
|
162
|
+
`@forgezero/agent/schema/deploy-v2.json` and is served from
|
|
163
|
+
`https://www.forgezero.net/schemas/deploy-v2.json`.
|
|
164
|
+
|
|
165
|
+
Git is the only local-to-live synchronization mechanism. `sync` validates and
|
|
166
|
+
prints that rule; it does not create a second mutable command copy in the API.
|
|
167
|
+
The verified webhook identifies one exact commit, and a successful Agent result
|
|
168
|
+
persists the definition digest so the Applications screen can prove which local
|
|
169
|
+
contract became live.
|
|
170
|
+
|
|
171
|
+
Catalog status is an admission boundary, not a suggestion. `testing` coordinates
|
|
172
|
+
are visible in the development catalog for ForgeZero qualification but cannot be
|
|
173
|
+
selected by any deploy file. A repository cannot promote software by calling
|
|
174
|
+
itself development; only a reviewed catalog change to `active` unlocks it.
|
|
175
|
+
|
|
130
176
|
The daemon owns source checkout and command execution. Bootstrap explicitly
|
|
131
177
|
awaits release one because the API does not exist yet, then the Agent consumes a
|
|
132
178
|
one-use platform enrolment capability. There is no branch watcher. Every normal
|
|
133
179
|
platform or tenant release is one durable API delivery atomically expanded to
|
|
134
|
-
one row for every
|
|
180
|
+
one row for every independently attached compute target. Release-scoped steps
|
|
181
|
+
run on one target elected by stable target ordering, never on a user-declared
|
|
182
|
+
coordinator:
|
|
135
183
|
`pending` is written before dispatch, `running` and a fenced lease before project
|
|
136
184
|
code, and only an awaited successful pipeline writes `deployed`. An expired
|
|
137
185
|
claim can be recovered; its stale token cannot renew or finish.
|
|
@@ -188,7 +236,7 @@ An operator may also force a deployment and await the complete result over the
|
|
|
188
236
|
private control socket:
|
|
189
237
|
|
|
190
238
|
```bash
|
|
191
|
-
fz-agent deploy --revision=<full-40-character-commit> --
|
|
239
|
+
fz-agent deploy --revision=<full-40-character-commit> --release-executor
|
|
192
240
|
fz-agent status
|
|
193
241
|
fz-agent pause
|
|
194
242
|
fz-agent pause-key --key=project:production
|
package/dist/agent-heartbeat.js
CHANGED
package/dist/definition.d.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import type { Pipeline, PipelineStep } from './pipeline';
|
|
2
|
-
import { type SoftwareRequirement } from './software';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { type DeploymentChannel, type SoftwareRequirement } from './software';
|
|
3
|
+
/**
|
|
4
|
+
* Version two separates a repository's deploy recipe from the computes that use
|
|
5
|
+
* it. A target chooses one named profile in the control plane; compute names,
|
|
6
|
+
* counts and cluster leadership never belong in Git.
|
|
7
|
+
*/
|
|
8
|
+
export declare const PIPELINE_VERSION: 2;
|
|
9
|
+
export declare const DEPLOY_SCHEMA_URL = "https://www.forgezero.net/schemas/deploy-v2.json";
|
|
10
|
+
export interface PipelineProfile {
|
|
6
11
|
software: readonly SoftwareRequirement[];
|
|
7
12
|
}
|
|
8
13
|
export interface DeployStep extends PipelineStep {
|
|
9
14
|
phase: 'build' | 'release' | 'migrate' | 'health';
|
|
10
|
-
/**
|
|
11
|
-
|
|
15
|
+
/** Run on this target, or on the one deterministic release executor. */
|
|
16
|
+
scope: 'target' | 'release';
|
|
17
|
+
/** Optional profile filter. An omitted list applies to every profile. */
|
|
18
|
+
profiles?: readonly string[];
|
|
12
19
|
/** Run only when every named non-secret deployment coordinate has this value. */
|
|
13
20
|
when?: Readonly<Record<string, string>>;
|
|
14
21
|
}
|
|
@@ -16,12 +23,14 @@ export interface DeployDefinition {
|
|
|
16
23
|
version: typeof PIPELINE_VERSION;
|
|
17
24
|
name: string;
|
|
18
25
|
requireAttestation?: boolean;
|
|
19
|
-
|
|
26
|
+
profiles: Readonly<Record<string, PipelineProfile>>;
|
|
20
27
|
steps: readonly DeployStep[];
|
|
21
28
|
}
|
|
22
29
|
export declare class DefinitionError extends Error {
|
|
23
30
|
constructor(message: string);
|
|
24
31
|
}
|
|
25
|
-
/** Validate parsed
|
|
26
|
-
export declare function parseDeployDefinition(value: unknown
|
|
27
|
-
|
|
32
|
+
/** Validate parsed JSON before any command from it is allowed to run. */
|
|
33
|
+
export declare function parseDeployDefinition(value: unknown, options?: {
|
|
34
|
+
channel?: DeploymentChannel;
|
|
35
|
+
}): DeployDefinition;
|
|
36
|
+
export declare function phasePipeline(definition: DeployDefinition, phase: DeployStep['phase'], profile: string, executeRelease?: boolean): Pipeline;
|
package/dist/definition.js
CHANGED
|
@@ -3,6 +3,16 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
var BUN_INSTALLER_SHA256 = "bab8acfb046aac8c72407bdcce903957665d655d7acaa3e11c7c4616beae68dd";
|
|
4
4
|
var ARANGO_SHA256 = "b5a9197b4343f2ed554e1ebc1ef8e6529c7c39cde0035cdc311a4747a3355066";
|
|
5
5
|
var CLOUDFLARED_SHA256 = "9d71c677db00134c1bd4144b7783486b654ad281b1ea62b4972098d19f770f17";
|
|
6
|
+
var OS_CATALOG = [
|
|
7
|
+
{ id: "ubuntu", version: "26.04", architecture: "x64", status: "active" }
|
|
8
|
+
];
|
|
9
|
+
var SOFTWARE_CATALOG = [
|
|
10
|
+
{ id: "bun", version: "1.3.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
11
|
+
{ id: "nginx", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
12
|
+
{ id: "arangodb", version: "3.11.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
13
|
+
{ id: "cloudflared", version: "2026.7.3", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
14
|
+
{ id: "ufw", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" }
|
|
15
|
+
];
|
|
6
16
|
var UBUNTU_2604_X64 = [
|
|
7
17
|
{
|
|
8
18
|
requirement: { id: "bun", version: "1.3.14" },
|
|
@@ -41,7 +51,7 @@ function observeSoftwareHost(osRelease = readFileSync("/etc/os-release", "utf8")
|
|
|
41
51
|
architecture
|
|
42
52
|
};
|
|
43
53
|
}
|
|
44
|
-
function validateSoftwareRequirements(value) {
|
|
54
|
+
function validateSoftwareRequirements(value, _options = {}) {
|
|
45
55
|
if (!Array.isArray(value) || value.length > 32)
|
|
46
56
|
throw new Error("software requirements must be an array of at most 32 entries");
|
|
47
57
|
const seen = new Set;
|
|
@@ -59,13 +69,18 @@ function validateSoftwareRequirements(value) {
|
|
|
59
69
|
if (seen.has(requirement.id))
|
|
60
70
|
throw new Error(`duplicate software requirement: ${requirement.id}`);
|
|
61
71
|
seen.add(requirement.id);
|
|
72
|
+
const catalog = SOFTWARE_CATALOG.find((candidate) => candidate.id === requirement.id && candidate.version === requirement.version);
|
|
73
|
+
if (!catalog || catalog.status !== "active") {
|
|
74
|
+
throw new Error(`software requirement is not active: ${requirement.id}@${requirement.version}`);
|
|
75
|
+
}
|
|
62
76
|
return requirement;
|
|
63
77
|
});
|
|
64
78
|
}
|
|
65
79
|
async function ensureSoftwareRequirements(requirementsInput, options) {
|
|
66
80
|
const requirements = validateSoftwareRequirements(requirementsInput);
|
|
67
81
|
const observation = options.observation ?? observeSoftwareHost();
|
|
68
|
-
|
|
82
|
+
const os = OS_CATALOG.find((candidate) => candidate.id === observation.os.id && candidate.version === observation.os.versionId && candidate.architecture === observation.architecture);
|
|
83
|
+
if (!os || os.status !== "active") {
|
|
69
84
|
throw new Error(`unsupported software strategy: ${observation.os.id} ${observation.os.versionId} ${observation.architecture}`);
|
|
70
85
|
}
|
|
71
86
|
const results = [];
|
|
@@ -90,7 +105,8 @@ async function ensureSoftwareRequirements(requirementsInput, options) {
|
|
|
90
105
|
}
|
|
91
106
|
|
|
92
107
|
// src/definition.ts
|
|
93
|
-
var PIPELINE_VERSION =
|
|
108
|
+
var PIPELINE_VERSION = 2;
|
|
109
|
+
var DEPLOY_SCHEMA_URL = "https://www.forgezero.net/schemas/deploy-v2.json";
|
|
94
110
|
|
|
95
111
|
class DefinitionError extends Error {
|
|
96
112
|
constructor(message) {
|
|
@@ -115,6 +131,7 @@ var exactKeys = (value, allowed, where) => {
|
|
|
115
131
|
if (unknown.length > 0)
|
|
116
132
|
throw new DefinitionError(`${where} contains unknown field(s): ${unknown.join(", ")}.`);
|
|
117
133
|
};
|
|
134
|
+
var NAME = /^[a-z][a-z0-9-]{0,62}$/;
|
|
118
135
|
var RESERVED_STEP_ENV = new Set([
|
|
119
136
|
"PATH",
|
|
120
137
|
"HOME",
|
|
@@ -127,46 +144,67 @@ var RESERVED_STEP_ENV = new Set([
|
|
|
127
144
|
"GIT_SSH",
|
|
128
145
|
"GIT_SSH_COMMAND"
|
|
129
146
|
]);
|
|
130
|
-
function parseDeployDefinition(value) {
|
|
147
|
+
function parseDeployDefinition(value, options = {}) {
|
|
131
148
|
const root = record(value, "pipeline");
|
|
132
|
-
exactKeys(root, ["version", "name", "requireAttestation", "
|
|
149
|
+
exactKeys(root, ["$schema", "version", "name", "requireAttestation", "profiles", "steps"], "pipeline");
|
|
150
|
+
if (root.$schema !== undefined && root.$schema !== DEPLOY_SCHEMA_URL) {
|
|
151
|
+
throw new DefinitionError(`pipeline.$schema must be ${DEPLOY_SCHEMA_URL}.`);
|
|
152
|
+
}
|
|
133
153
|
if (root.version !== PIPELINE_VERSION) {
|
|
134
154
|
throw new DefinitionError(`pipeline.version must be ${PIPELINE_VERSION}.`);
|
|
135
155
|
}
|
|
136
|
-
if (
|
|
137
|
-
throw new DefinitionError("pipeline.
|
|
156
|
+
if (root.requireAttestation !== undefined && typeof root.requireAttestation !== "boolean") {
|
|
157
|
+
throw new DefinitionError("pipeline.requireAttestation must be a boolean.");
|
|
158
|
+
}
|
|
159
|
+
const rawProfiles = record(root.profiles, "pipeline.profiles");
|
|
160
|
+
const profileEntries = Object.entries(rawProfiles);
|
|
161
|
+
if (profileEntries.length === 0 || profileEntries.length > 32) {
|
|
162
|
+
throw new DefinitionError("pipeline.profiles must contain from 1 to 32 named profiles.");
|
|
138
163
|
}
|
|
139
164
|
if (!Array.isArray(root.steps) || root.steps.length === 0) {
|
|
140
165
|
throw new DefinitionError("pipeline.steps must contain at least one step.");
|
|
141
166
|
}
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
167
|
+
const profiles = {};
|
|
168
|
+
for (const [name2, raw] of profileEntries) {
|
|
169
|
+
if (!NAME.test(name2))
|
|
170
|
+
throw new DefinitionError(`pipeline profile name is invalid: ${name2}.`);
|
|
171
|
+
const profile = record(raw, `profiles.${name2}`);
|
|
172
|
+
exactKeys(profile, ["software"], `profiles.${name2}`);
|
|
173
|
+
if (!Array.isArray(profile.software)) {
|
|
174
|
+
throw new DefinitionError(`profiles.${name2}.software must be an array.`);
|
|
147
175
|
}
|
|
148
|
-
|
|
149
|
-
name: text(role.name, `roles[${index}].name`),
|
|
150
|
-
software: validateSoftwareRequirements(role.software)
|
|
151
|
-
};
|
|
152
|
-
});
|
|
153
|
-
if (new Set(roles.map((role) => role.name)).size !== roles.length) {
|
|
154
|
-
throw new DefinitionError("pipeline.roles must have unique names.");
|
|
176
|
+
profiles[name2] = { software: validateSoftwareRequirements(profile.software, options) };
|
|
155
177
|
}
|
|
156
178
|
const phases = new Set(["build", "release", "migrate", "health"]);
|
|
157
179
|
const steps = root.steps.map((raw, index) => {
|
|
158
180
|
const step = record(raw, `steps[${index}]`);
|
|
159
|
-
exactKeys(step, ["name", "run", "phase", "
|
|
181
|
+
exactKeys(step, ["name", "run", "phase", "scope", "profiles", "secrets", "always", "timeoutMs", "when"], `steps[${index}]`);
|
|
160
182
|
const phase = text(step.phase, `steps[${index}].phase`);
|
|
161
183
|
if (!phases.has(phase))
|
|
162
184
|
throw new DefinitionError(`steps[${index}].phase is not supported.`);
|
|
163
|
-
if (step.
|
|
185
|
+
if (step.scope !== "target" && step.scope !== "release") {
|
|
186
|
+
throw new DefinitionError(`steps[${index}].scope must be target or release.`);
|
|
187
|
+
}
|
|
188
|
+
if (step.always !== undefined && typeof step.always !== "boolean") {
|
|
189
|
+
throw new DefinitionError(`steps[${index}].always must be a boolean.`);
|
|
190
|
+
}
|
|
191
|
+
let selectedProfiles;
|
|
192
|
+
if (step.profiles !== undefined) {
|
|
193
|
+
if (!Array.isArray(step.profiles) || step.profiles.length === 0 || step.profiles.some((name2) => typeof name2 !== "string" || !Object.hasOwn(profiles, name2))) {
|
|
194
|
+
throw new DefinitionError(`steps[${index}].profiles must name existing profiles.`);
|
|
195
|
+
}
|
|
196
|
+
selectedProfiles = [...step.profiles];
|
|
197
|
+
if (new Set(selectedProfiles).size !== selectedProfiles.length) {
|
|
198
|
+
throw new DefinitionError(`steps[${index}].profiles must not contain duplicates.`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (step.secrets !== undefined && (!Array.isArray(step.secrets) || step.secrets.some((name2) => typeof name2 !== "string" || !/^[A-Z_][A-Z0-9_]*$/.test(name2)))) {
|
|
164
202
|
throw new DefinitionError(`steps[${index}].secrets must contain names only.`);
|
|
165
203
|
}
|
|
166
204
|
if (Array.isArray(step.secrets) && new Set(step.secrets).size !== step.secrets.length) {
|
|
167
205
|
throw new DefinitionError(`steps[${index}].secrets must not contain duplicates.`);
|
|
168
206
|
}
|
|
169
|
-
if (Array.isArray(step.secrets) && step.secrets.some((
|
|
207
|
+
if (Array.isArray(step.secrets) && step.secrets.some((name2) => RESERVED_STEP_ENV.has(String(name2)))) {
|
|
170
208
|
throw new DefinitionError(`steps[${index}].secrets may not replace process-control environment variables.`);
|
|
171
209
|
}
|
|
172
210
|
const timeoutMs = step.timeoutMs === undefined ? undefined : Number(step.timeoutMs);
|
|
@@ -177,11 +215,11 @@ function parseDeployDefinition(value) {
|
|
|
177
215
|
if (step.when !== undefined) {
|
|
178
216
|
const conditions = record(step.when, `steps[${index}].when`);
|
|
179
217
|
when = {};
|
|
180
|
-
for (const [
|
|
181
|
-
if (!/^[A-Z_][A-Z0-9_]*$/.test(
|
|
218
|
+
for (const [name2, expected] of Object.entries(conditions)) {
|
|
219
|
+
if (!/^[A-Z_][A-Z0-9_]*$/.test(name2) || typeof expected !== "string" || expected.length === 0) {
|
|
182
220
|
throw new DefinitionError(`steps[${index}].when must map environment names to non-empty strings.`);
|
|
183
221
|
}
|
|
184
|
-
when[
|
|
222
|
+
when[name2] = expected;
|
|
185
223
|
}
|
|
186
224
|
if (Object.keys(when).length === 0)
|
|
187
225
|
throw new DefinitionError(`steps[${index}].when must not be empty.`);
|
|
@@ -190,8 +228,9 @@ function parseDeployDefinition(value) {
|
|
|
190
228
|
name: text(step.name, `steps[${index}].name`),
|
|
191
229
|
run: text(step.run, `steps[${index}].run`),
|
|
192
230
|
phase,
|
|
231
|
+
scope: step.scope,
|
|
232
|
+
profiles: selectedProfiles,
|
|
193
233
|
secrets: step.secrets,
|
|
194
|
-
once: step.once === true,
|
|
195
234
|
always: step.always === true,
|
|
196
235
|
timeoutMs,
|
|
197
236
|
when
|
|
@@ -200,24 +239,31 @@ function parseDeployDefinition(value) {
|
|
|
200
239
|
if (new Set(steps.map((step) => step.name)).size !== steps.length) {
|
|
201
240
|
throw new DefinitionError("pipeline.steps must have unique names.");
|
|
202
241
|
}
|
|
242
|
+
const name = text(root.name, "pipeline.name");
|
|
243
|
+
if (name.length > 120)
|
|
244
|
+
throw new DefinitionError("pipeline.name must be at most 120 characters.");
|
|
203
245
|
return {
|
|
204
246
|
version: PIPELINE_VERSION,
|
|
205
|
-
name
|
|
247
|
+
name,
|
|
206
248
|
requireAttestation: root.requireAttestation === true,
|
|
207
|
-
|
|
249
|
+
profiles,
|
|
208
250
|
steps
|
|
209
251
|
};
|
|
210
252
|
}
|
|
211
|
-
function phasePipeline(definition, phase) {
|
|
253
|
+
function phasePipeline(definition, phase, profile, executeRelease = false) {
|
|
254
|
+
if (!Object.hasOwn(definition.profiles, profile)) {
|
|
255
|
+
throw new DefinitionError(`pipeline profile does not exist: ${profile}.`);
|
|
256
|
+
}
|
|
212
257
|
return {
|
|
213
258
|
name: `${definition.name}:${phase}`,
|
|
214
259
|
requireAttestation: definition.requireAttestation,
|
|
215
|
-
steps: definition.steps.filter((step) => step.phase === phase)
|
|
260
|
+
steps: definition.steps.filter((step) => step.phase === phase && (!step.profiles || step.profiles.includes(profile)) && (step.scope === "target" || executeRelease))
|
|
216
261
|
};
|
|
217
262
|
}
|
|
218
263
|
export {
|
|
219
264
|
phasePipeline,
|
|
220
265
|
parseDeployDefinition,
|
|
221
266
|
PIPELINE_VERSION,
|
|
222
|
-
DefinitionError
|
|
267
|
+
DefinitionError,
|
|
268
|
+
DEPLOY_SCHEMA_URL
|
|
223
269
|
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type DeployDefinition } from './definition';
|
|
2
|
+
import { type DeploymentChannel, type SoftwareRequirement } from './software';
|
|
3
|
+
export declare const DEPLOY_FILE = ".fz/deploy.json";
|
|
4
|
+
export { DEPLOY_SCHEMA_URL } from './definition';
|
|
5
|
+
/**
|
|
6
|
+
* An initialized file must fail safely until its project-specific promotion and
|
|
7
|
+
* health commands are supplied. A generic initializer cannot guess how a
|
|
8
|
+
* tenant starts a service, which systemd unit it owns, or what "healthy" means.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEPLOY_TODO_PREFIX = "ForgeZero pipeline TODO:";
|
|
11
|
+
export interface DeployFileSummary {
|
|
12
|
+
path: string;
|
|
13
|
+
digest: string;
|
|
14
|
+
version: number;
|
|
15
|
+
name: string;
|
|
16
|
+
profiles: readonly string[];
|
|
17
|
+
software: Readonly<Record<string, readonly SoftwareRequirement[]>>;
|
|
18
|
+
ready: boolean;
|
|
19
|
+
problems: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
export interface InitializedDeployFile {
|
|
22
|
+
path: string;
|
|
23
|
+
definition: DeployDefinition;
|
|
24
|
+
summary: DeployFileSummary;
|
|
25
|
+
}
|
|
26
|
+
/** Semantic digest: formatting and object-key order cannot create false drift. */
|
|
27
|
+
export declare function deployDefinitionDigest(definition: DeployDefinition): string;
|
|
28
|
+
export declare function defaultDeployFile(root: string, options?: {
|
|
29
|
+
name?: string;
|
|
30
|
+
profile?: string;
|
|
31
|
+
software?: readonly SoftwareRequirement[];
|
|
32
|
+
requireAttestation?: boolean;
|
|
33
|
+
channel?: DeploymentChannel;
|
|
34
|
+
}): Record<string, unknown>;
|
|
35
|
+
export declare function inspectDeployFile(root: string, options?: {
|
|
36
|
+
channel?: DeploymentChannel;
|
|
37
|
+
}): InitializedDeployFile;
|
|
38
|
+
export declare function initializeDeployFile(root: string, options?: Parameters<typeof defaultDeployFile>[1] & {
|
|
39
|
+
force?: boolean;
|
|
40
|
+
}): InitializedDeployFile;
|