@forgezero/agent 0.1.31 → 0.1.33
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 +60 -5
- package/dist/agent-heartbeat.d.ts +12 -2
- package/dist/agent-heartbeat.js +416 -60
- package/dist/agent-update-helper.d.ts +41 -2
- package/dist/agent-update-helper.js +361 -43
- package/dist/agent-update.d.ts +1 -0
- package/dist/agent-update.js +34 -2
- package/dist/attestation-client.d.ts +2 -0
- package/dist/cli/agent-install.d.ts +6 -0
- package/dist/cli/genesis.d.ts +7 -48
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/session-store.d.ts +22 -0
- package/dist/deployment-pull.d.ts +2 -0
- package/dist/deployment.d.ts +3 -0
- package/dist/egress-policy.d.ts +50 -0
- package/dist/fz-agent.js +5088 -1699
- package/dist/fz.js +10334 -7613
- package/dist/git-egress.d.ts +35 -0
- package/dist/index.d.ts +19 -2
- package/dist/metal-helper-socket.js +12 -1
- package/dist/metal-provision.d.ts +2 -0
- package/dist/metal-provision.js +12 -1
- package/dist/migration-pull.d.ts +2 -0
- package/dist/migration-pull.js +18 -6
- package/dist/node-vault.d.ts +2 -0
- package/dist/node-vault.js +2 -1
- package/dist/provision.d.ts +13 -1
- package/dist/provision.js +540 -52
- package/dist/provisioning-pull.d.ts +2 -0
- package/dist/provisioning-pull.js +20 -7
- package/dist/telemetry-runtime.d.ts +20 -0
- package/dist/telemetry.d.ts +56 -0
- package/dist/version.d.ts +1 -1
- package/package.json +10 -10
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { NodeKeyPair } from '@forgezero/runtime/identity';
|
|
2
2
|
import { type SignedNodeHttpOptions } from './signed-node-http';
|
|
3
|
+
import type { AgentOperationTelemetry } from './telemetry-runtime';
|
|
3
4
|
interface RemoteProvisionClaimBase {
|
|
4
5
|
computeKey: string;
|
|
5
6
|
claimToken: string;
|
|
@@ -73,6 +74,7 @@ export interface ProvisioningPullOptions extends SignedNodeHttpOptions {
|
|
|
73
74
|
kvm: boolean;
|
|
74
75
|
helper: boolean;
|
|
75
76
|
};
|
|
77
|
+
telemetry?: AgentOperationTelemetry;
|
|
76
78
|
}
|
|
77
79
|
export type ProvisionPullResult = {
|
|
78
80
|
status: 'idle';
|
|
@@ -70,6 +70,7 @@ async function postSignedNode(options, path, body) {
|
|
|
70
70
|
// src/provisioning-pull.ts
|
|
71
71
|
class ProvisionClaimLostError extends Error {
|
|
72
72
|
}
|
|
73
|
+
var remoteOutcome = (cause) => cause instanceof SignedNodeHttpError && cause.status < 500 ? "refused" : "retryable";
|
|
73
74
|
var post = (options, operation, body) => postSignedNode(options, `v1/metal/computes/${operation}`, body);
|
|
74
75
|
async function runClaim(options, claim) {
|
|
75
76
|
const setTimer = options.setTimer ?? ((callback, ms) => setTimeout(callback, ms));
|
|
@@ -88,10 +89,11 @@ async function runClaim(options, claim) {
|
|
|
88
89
|
if (stopped || renewal)
|
|
89
90
|
return;
|
|
90
91
|
let retry;
|
|
91
|
-
|
|
92
|
+
const renew = () => post(options, "renew", {
|
|
92
93
|
computeKey: claim.computeKey,
|
|
93
94
|
claimToken: claim.claimToken
|
|
94
|
-
})
|
|
95
|
+
});
|
|
96
|
+
renewal = (options.telemetry ? options.telemetry.observe("provisioning.renew", renew, () => "success", remoteOutcome) : renew()).then((response) => {
|
|
95
97
|
expires = response.claimExpiresAtTs;
|
|
96
98
|
options.onEvent?.("lease-renewed", { computeKey: claim.computeKey, claimExpiresAtTs: expires });
|
|
97
99
|
}).catch((cause) => {
|
|
@@ -112,7 +114,8 @@ async function runClaim(options, claim) {
|
|
|
112
114
|
schedule();
|
|
113
115
|
let result;
|
|
114
116
|
try {
|
|
115
|
-
|
|
117
|
+
const run = () => options.run(claim);
|
|
118
|
+
result = options.telemetry ? await options.telemetry.observe("provisioning.apply", run) : await run();
|
|
116
119
|
} finally {
|
|
117
120
|
stopped = true;
|
|
118
121
|
clearTimer(timer);
|
|
@@ -143,7 +146,8 @@ async function complete(options, body) {
|
|
|
143
146
|
async function pullProvisioningOnce(options) {
|
|
144
147
|
if (options.metalPreflight) {
|
|
145
148
|
const report = options.metalPreflight();
|
|
146
|
-
const
|
|
149
|
+
const preflight = () => postSignedNode(options, "v1/metal/preflight", report);
|
|
150
|
+
const accepted = options.telemetry ? await options.telemetry.observe("provisioning.preflight", preflight, (value) => value.ready ? "success" : "refused", remoteOutcome) : await preflight();
|
|
147
151
|
if (options.metalHostname && accepted.hostname !== options.metalHostname) {
|
|
148
152
|
throw new Error(`metal agent: configured inventory hostname ${options.metalHostname} is bound as ${accepted.hostname}; refusing work.`);
|
|
149
153
|
}
|
|
@@ -156,7 +160,8 @@ async function pullProvisioningOnce(options) {
|
|
|
156
160
|
if (!accepted.ready)
|
|
157
161
|
return { status: "idle" };
|
|
158
162
|
}
|
|
159
|
-
const
|
|
163
|
+
const claimWork = () => post(options, "claim", {});
|
|
164
|
+
const response = options.telemetry ? await options.telemetry.observe("provisioning.claim", claimWork, (value) => value.claim ? "success" : "idle", remoteOutcome) : await claimWork();
|
|
160
165
|
if (!response.claim)
|
|
161
166
|
return { status: "idle" };
|
|
162
167
|
const claim = response.claim;
|
|
@@ -167,20 +172,28 @@ async function pullProvisioningOnce(options) {
|
|
|
167
172
|
if (cause instanceof ProvisionClaimLostError)
|
|
168
173
|
throw cause;
|
|
169
174
|
const reason = (cause instanceof Error ? cause.message : String(cause)).slice(0, 2000);
|
|
170
|
-
|
|
175
|
+
const acknowledge2 = () => complete(options, {
|
|
171
176
|
computeKey: claim.computeKey,
|
|
172
177
|
claimToken: claim.claimToken,
|
|
173
178
|
ok: false,
|
|
174
179
|
detail: reason
|
|
175
180
|
});
|
|
181
|
+
if (options.telemetry) {
|
|
182
|
+
await options.telemetry.observe("provisioning.complete", acknowledge2, () => "success", remoteOutcome);
|
|
183
|
+
} else
|
|
184
|
+
await acknowledge2();
|
|
176
185
|
return { status: "failed", claim, reason };
|
|
177
186
|
}
|
|
178
|
-
|
|
187
|
+
const acknowledge = () => complete(options, {
|
|
179
188
|
computeKey: claim.computeKey,
|
|
180
189
|
claimToken: claim.claimToken,
|
|
181
190
|
ok: true,
|
|
182
191
|
...result.guestAddress ? { guestAddress: result.guestAddress } : {}
|
|
183
192
|
});
|
|
193
|
+
if (options.telemetry) {
|
|
194
|
+
await options.telemetry.observe("provisioning.complete", acknowledge, () => "success", remoteOutcome);
|
|
195
|
+
} else
|
|
196
|
+
await acknowledge();
|
|
184
197
|
return { status: claim.action === "delete" ? "terminated" : "running", claim, result };
|
|
185
198
|
}
|
|
186
199
|
function startProvisioningPull(options) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AgentTelemetry, AgentTelemetryEvent, AgentTelemetryOperation, AgentTelemetryOutcome } from './telemetry';
|
|
2
|
+
/**
|
|
3
|
+
* Process-wide operation/state accounting layered over the deliberately small
|
|
4
|
+
* OTLP emitter. Callers still cannot attach arbitrary attributes or payloads.
|
|
5
|
+
*/
|
|
6
|
+
export declare class AgentTelemetryRuntime {
|
|
7
|
+
private readonly telemetry;
|
|
8
|
+
private readonly now;
|
|
9
|
+
private active;
|
|
10
|
+
private completed;
|
|
11
|
+
private failed;
|
|
12
|
+
private draining;
|
|
13
|
+
constructor(telemetry: AgentTelemetry, now?: () => number);
|
|
14
|
+
event(event: AgentTelemetryEvent): void;
|
|
15
|
+
setDraining(draining: boolean): void;
|
|
16
|
+
observe<T>(operation: AgentTelemetryOperation, work: () => Promise<T>, outcome?: (value: T) => AgentTelemetryOutcome, errorOutcome?: (cause: unknown) => AgentTelemetryOutcome): Promise<T>;
|
|
17
|
+
close(): Promise<void>;
|
|
18
|
+
private recordState;
|
|
19
|
+
}
|
|
20
|
+
export type AgentOperationTelemetry = Pick<AgentTelemetryRuntime, 'observe'>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency-free OTLP/HTTP telemetry for the machine Agent.
|
|
3
|
+
*
|
|
4
|
+
* Callers can name only reviewed operations, outcomes and lifecycle events.
|
|
5
|
+
* There is deliberately no field for a URL, request/response body, header,
|
|
6
|
+
* credential, tenant coordinate, command, error detail or arbitrary label.
|
|
7
|
+
*/
|
|
8
|
+
export declare const AGENT_TELEMETRY_OPERATIONS: readonly ["agent.heartbeat", "agent.update", "attestation.refresh", "deployment.claim", "deployment.run", "deployment.renew", "deployment.complete", "migration.claim", "migration.run", "migration.renew", "migration.complete", "provisioning.preflight", "provisioning.claim", "provisioning.apply", "provisioning.renew", "provisioning.complete", "vault.sync", "agent.other"];
|
|
9
|
+
export type AgentTelemetryOperation = (typeof AGENT_TELEMETRY_OPERATIONS)[number];
|
|
10
|
+
export declare const AGENT_TELEMETRY_OUTCOMES: readonly ["success", "idle", "refused", "retryable", "failed"];
|
|
11
|
+
export type AgentTelemetryOutcome = (typeof AGENT_TELEMETRY_OUTCOMES)[number];
|
|
12
|
+
export declare const AGENT_TELEMETRY_EVENTS: readonly ["agent.started", "agent.draining", "agent.stopped", "agent.update_prepared", "agent.update_recovered", "telemetry.queue_overflow"];
|
|
13
|
+
export type AgentTelemetryEvent = (typeof AGENT_TELEMETRY_EVENTS)[number];
|
|
14
|
+
export interface AgentTelemetryConfig {
|
|
15
|
+
endpoint: string;
|
|
16
|
+
serviceName: string;
|
|
17
|
+
instanceId: string;
|
|
18
|
+
environment: 'production' | 'development';
|
|
19
|
+
flushIntervalMs: number;
|
|
20
|
+
traceSampleRatio: number;
|
|
21
|
+
}
|
|
22
|
+
export interface AgentOperationObservation {
|
|
23
|
+
operation: AgentTelemetryOperation;
|
|
24
|
+
outcome: AgentTelemetryOutcome;
|
|
25
|
+
startedAtMs: number;
|
|
26
|
+
durationMs: number;
|
|
27
|
+
}
|
|
28
|
+
export interface AgentTelemetryState {
|
|
29
|
+
active: number;
|
|
30
|
+
completed: number;
|
|
31
|
+
failed: number;
|
|
32
|
+
draining: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface AgentTelemetry {
|
|
35
|
+
recordOperation(observation: AgentOperationObservation): void;
|
|
36
|
+
recordEvent(event: AgentTelemetryEvent): void;
|
|
37
|
+
recordState(state: AgentTelemetryState): void;
|
|
38
|
+
flush(): Promise<void>;
|
|
39
|
+
close(): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
42
|
+
export declare const AGENT_OTLP_EXPORT_TIMEOUT_MS = 5000;
|
|
43
|
+
export declare const AGENT_OTLP_MAX_ATTEMPTS = 2;
|
|
44
|
+
export declare const AGENT_OTLP_MAX_RETRY_DELAY_MS = 1000;
|
|
45
|
+
export declare const AGENT_TELEMETRY_MAX_SPANS = 256;
|
|
46
|
+
export declare const AGENT_TELEMETRY_MAX_LOGS = 64;
|
|
47
|
+
/** Resolve once at process startup; production refuses to run without an exporter. */
|
|
48
|
+
export declare function resolveAgentTelemetryConfig(env?: Record<string, string | undefined>): AgentTelemetryConfig | null;
|
|
49
|
+
export declare function createAgentTelemetry(input: AgentTelemetryConfig | null, options?: {
|
|
50
|
+
fetch?: FetchLike;
|
|
51
|
+
warn?: (message: string) => void;
|
|
52
|
+
now?: () => number;
|
|
53
|
+
random?: () => number;
|
|
54
|
+
wait?: (milliseconds: number) => Promise<void>;
|
|
55
|
+
}): AgentTelemetry;
|
|
56
|
+
export {};
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** One package version shared by both public binaries. Pinned to package.json by tests. */
|
|
2
|
-
export declare const VERSION = "0.1.
|
|
2
|
+
export declare const VERSION = "0.1.33";
|
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"//": "Publishing happens from an operator's machine, not CI \u2014 CLAUDE.md records that the absence of CI is deliberate. npm's `provenance` attests a tarball was built by a recognised CI provider from a named commit, so it cannot be produced here: it was set, and the first publish failed with `Automatic provenance generation not supported for provider: null`. A setting that can never be satisfied is worse than none, because it reads as a guarantee nobody is getting. Restore it the day this publishes from CI, and not before.",
|
|
3
2
|
"name": "@forgezero/agent",
|
|
4
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.33",
|
|
5
4
|
"type": "module",
|
|
6
5
|
"scripts": {
|
|
7
6
|
"check": "tsc --noEmit",
|
|
@@ -18,19 +17,20 @@
|
|
|
18
17
|
"@noble/post-quantum": "^0.6.1"
|
|
19
18
|
},
|
|
20
19
|
"dependencies": {
|
|
21
|
-
"@forgezero/runtime": "
|
|
22
|
-
"@forgezero/vault": "
|
|
23
|
-
"@noble/curves": "
|
|
24
|
-
"@noble/hashes": "
|
|
25
|
-
"@noble/post-quantum": "
|
|
26
|
-
"@scure/bip39": "
|
|
20
|
+
"@forgezero/runtime": "0.1.5",
|
|
21
|
+
"@forgezero/vault": "0.1.8",
|
|
22
|
+
"@noble/curves": "2.2.0",
|
|
23
|
+
"@noble/hashes": "2.2.0",
|
|
24
|
+
"@noble/post-quantum": "0.6.1",
|
|
25
|
+
"@scure/bip39": "2.2.0"
|
|
27
26
|
},
|
|
28
27
|
"bin": {
|
|
29
28
|
"fz-agent": "dist/fz-agent.js",
|
|
30
29
|
"fz": "dist/fz.js"
|
|
31
30
|
},
|
|
32
31
|
"publishConfig": {
|
|
33
|
-
"access": "public"
|
|
32
|
+
"access": "public",
|
|
33
|
+
"provenance": true
|
|
34
34
|
},
|
|
35
35
|
"files": [
|
|
36
36
|
"dist",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"repository": {
|
|
54
54
|
"type": "git",
|
|
55
55
|
"url": "git+https://github.com/forgezero-net/packages.git",
|
|
56
|
-
"directory": "
|
|
56
|
+
"directory": "agent"
|
|
57
57
|
},
|
|
58
58
|
"bugs": "https://github.com/forgezero-net/packages/issues",
|
|
59
59
|
"exports": {
|