witnora 0.18.11 → 0.18.12
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/gateway.js +25 -4
- package/dist/onboard.js +32 -6
- package/package.json +1 -1
package/dist/gateway.js
CHANGED
|
@@ -61,6 +61,7 @@ export async function initializeCustomerGateway(options) {
|
|
|
61
61
|
port: gatewayPort(process.env.WITNORA_GATEWAY_PORT, 8787),
|
|
62
62
|
storageDirectory: "data",
|
|
63
63
|
privacyMode: "metadata_only",
|
|
64
|
+
...(options.agentIdentity ? { agentIdentity: options.agentIdentity } : {}),
|
|
64
65
|
coverage: runtimeKit
|
|
65
66
|
? { recorded: "configured", enforced: "configured", outcomeVerified: "configured" }
|
|
66
67
|
: { recorded: "configured", enforced: "not_configured", outcomeVerified: "not_configured" },
|
|
@@ -136,14 +137,20 @@ export async function upgradeCustomerGatewayRuntime(options) {
|
|
|
136
137
|
if (current.projectId !== options.authorization.projectId || current.server !== options.authorization.server)
|
|
137
138
|
throw new Error("Existing Gateway project/server binding does not match this authorized Runtime upgrade.");
|
|
138
139
|
const generatedClient = clientRaw === gatewayClient(current)
|
|
139
|
-
|| (!current.runtimeWorker && clientRaw === recordedGatewayClient(current)
|
|
140
|
+
|| (!current.runtimeWorker && (clientRaw === recordedGatewayClient(current)
|
|
141
|
+
|| clientRaw === recordedGatewayClient({ ...current, agentIdentity: undefined })));
|
|
140
142
|
if (!generatedClient || readmeRaw !== gatewayReadme(current))
|
|
141
143
|
throw new Error("Existing Gateway files differ from the Witnora-generated version; refusing to overwrite them.");
|
|
142
144
|
if (current.runtimeWorker && current.runtimeWorker.adapterId !== LOCAL_SANDBOX_ADAPTER_ID) {
|
|
143
145
|
throw new Error("Existing Gateway already has a different Runtime worker; preserve it and reconfigure in Advanced mode.");
|
|
144
146
|
}
|
|
145
147
|
const runtimeKit = await prepareRuntimeSandboxKit(options.runtimeReferences, options.authorization, options.configHome, options.fetch ?? fetch, directory);
|
|
146
|
-
const next = {
|
|
148
|
+
const next = {
|
|
149
|
+
...current,
|
|
150
|
+
...(options.agentIdentity ? { agentIdentity: options.agentIdentity } : {}),
|
|
151
|
+
coverage: { recorded: "configured", enforced: "configured", outcomeVerified: "configured" },
|
|
152
|
+
runtimeWorker: runtimeKit.config,
|
|
153
|
+
};
|
|
147
154
|
next.runtimeWorker.configDigestSha256 = gatewayConfigDigest(next);
|
|
148
155
|
const replacingGeneratedRuntime = current.runtimeWorker?.adapterId === LOCAL_SANDBOX_ADAPTER_ID;
|
|
149
156
|
if (replacingGeneratedRuntime) {
|
|
@@ -155,7 +162,8 @@ export async function upgradeCustomerGatewayRuntime(options) {
|
|
|
155
162
|
|| createHash("sha256").update(probeBytes).digest("hex") !== current.runtimeWorker.probeModuleSha256) {
|
|
156
163
|
throw new Error("Existing generated Runtime modules were modified; refusing to overwrite them.");
|
|
157
164
|
}
|
|
158
|
-
if (sameRuntimeGeneration(current.runtimeWorker, next.runtimeWorker)
|
|
165
|
+
if (sameRuntimeGeneration(current.runtimeWorker, next.runtimeWorker)
|
|
166
|
+
&& JSON.stringify(current.agentIdentity) === JSON.stringify(next.agentIdentity)) {
|
|
159
167
|
return { config: current, generatedFiles: [], changed: false, rollback: async () => undefined };
|
|
160
168
|
}
|
|
161
169
|
}
|
|
@@ -1358,6 +1366,8 @@ function parseConfig(raw) {
|
|
|
1358
1366
|
}
|
|
1359
1367
|
if (value.privacyMode !== "metadata_only")
|
|
1360
1368
|
throw new Error("Gateway privacyMode must remain metadata_only.");
|
|
1369
|
+
if (value.agentIdentity)
|
|
1370
|
+
validateGatewayAgentIdentity(value.agentIdentity);
|
|
1361
1371
|
if (!value.host || !Number.isSafeInteger(value.port) || Number(value.port) < 1 || Number(value.port) > 65_535)
|
|
1362
1372
|
throw new Error("Gateway host and port are invalid.");
|
|
1363
1373
|
if (value.runtimeWorker)
|
|
@@ -1368,6 +1378,13 @@ function parseConfig(raw) {
|
|
|
1368
1378
|
}
|
|
1369
1379
|
return config;
|
|
1370
1380
|
}
|
|
1381
|
+
function validateGatewayAgentIdentity(value) {
|
|
1382
|
+
for (const [key, item] of Object.entries(value)) {
|
|
1383
|
+
if (typeof item !== "string" || item.trim().length === 0 || item.length > 200) {
|
|
1384
|
+
throw new Error(`Gateway agentIdentity.${key} must be a non-empty string no longer than 200 characters.`);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1371
1388
|
export function parseManagedWorkflowHarnessConfig(raw) {
|
|
1372
1389
|
const value = JSON.parse(raw);
|
|
1373
1390
|
const workflowIds = value.workflowIds;
|
|
@@ -1652,7 +1669,11 @@ async function ed25519PublicKeyPem(handle) {
|
|
|
1652
1669
|
return createPublicKey(key).export({ type: "spki", format: "pem" }).toString();
|
|
1653
1670
|
}
|
|
1654
1671
|
function recordedGatewayClient(config) {
|
|
1655
|
-
|
|
1672
|
+
const defaultAgent = config.agentIdentity ? JSON.stringify(config.agentIdentity) : undefined;
|
|
1673
|
+
const startPayload = defaultAgent
|
|
1674
|
+
? `const payload = { ...metadata, agent: metadata.agent ?? ${defaultAgent} };\n return post(runId, "start", { payload, idempotencyKey: "run-start" });`
|
|
1675
|
+
: `return post(runId, "start", { payload: metadata, idempotencyKey: "run-start" });`;
|
|
1676
|
+
return `import { readFile } from "node:fs/promises";\n\nconst baseUrl = "http://${config.host}:${config.port}";\nlet gatewayToken;\n\nasync function token() {\n if (gatewayToken) return gatewayToken;\n const secrets = JSON.parse(await readFile(new URL("./secrets.json", import.meta.url), "utf8"));\n if (typeof secrets.gatewayToken !== "string" || secrets.gatewayToken.length < 32) {\n throw new Error("Witnora local Gateway token is missing or invalid.");\n }\n gatewayToken = secrets.gatewayToken;\n return gatewayToken;\n}\n\nasync function post(runId, operation, body) {\n if (!/^[A-Za-z0-9._:-]+$/.test(runId)) throw new Error("Witnora runId contains unsupported characters.");\n const response = await fetch(\`\${baseUrl}/v1/runs/\${encodeURIComponent(runId)}/\${operation}\`, {\n method: "POST",\n headers: { authorization: \`Bearer \${await token()}\`, "content-type": "application/json" },\n body: JSON.stringify(body),\n });\n const result = await response.json().catch(() => ({}));\n if (!response.ok) throw new Error(result.error ?? \`Witnora Gateway returned HTTP \${response.status}.\`);\n return result;\n}\n\nexport const witnoraGateway = {\n start(runId, metadata = {}) {\n ${startPayload}\n },\n event(runId, type, metadata = {}, idempotencyKey = \`\${type}-\${crypto.randomUUID()}\`) {\n return post(runId, "events", { type, payload: metadata, idempotencyKey });\n },\n complete(runId, metadata = {}) {\n return post(runId, "complete", {\n payload: metadata,\n evidenceStrength: {\n schemaVersion: "agentcert.evidence_strength.v0.1",\n level: "recorded",\n claims: [],\n limitations: ["No write-credential mediation or independent outcome probe is configured."],\n },\n idempotencyKey: "run-complete",\n });\n },\n};\n`;
|
|
1656
1677
|
}
|
|
1657
1678
|
async function writeExclusive(path, content, force, mode) {
|
|
1658
1679
|
if (!force && await exists(path))
|
package/dist/onboard.js
CHANGED
|
@@ -19,6 +19,12 @@ export async function runOnboard(options) {
|
|
|
19
19
|
const server = normalizeServer(options.server ?? DEFAULT_WITNORA_SERVER);
|
|
20
20
|
const repositoryPath = resolve(options.repository ?? process.cwd());
|
|
21
21
|
const repository = await inspectRepository(repositoryPath, options.template);
|
|
22
|
+
const agentIdentity = {
|
|
23
|
+
externalId: repository.name,
|
|
24
|
+
name: humanizeAgentName(repository.name),
|
|
25
|
+
version: repository.version,
|
|
26
|
+
framework: frameworkForTemplate(repository.template),
|
|
27
|
+
};
|
|
22
28
|
const connectionName = options.name ?? repository.slug;
|
|
23
29
|
const token = await authorizeProjectConnection({
|
|
24
30
|
projectId: options.projectId,
|
|
@@ -95,6 +101,24 @@ export async function runOnboard(options) {
|
|
|
95
101
|
};
|
|
96
102
|
let assuranceHarnessChanged = false;
|
|
97
103
|
let continuousService;
|
|
104
|
+
const prepareRuntime = async (operation) => {
|
|
105
|
+
const delays = [250, 500, 1_000, 2_000, 4_000, 8_000];
|
|
106
|
+
let lastError;
|
|
107
|
+
for (let attempt = 0; attempt <= delays.length; attempt += 1) {
|
|
108
|
+
try {
|
|
109
|
+
return await operation();
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (!(error instanceof RuntimeSetupNotReadyError))
|
|
113
|
+
throw error;
|
|
114
|
+
lastError = error;
|
|
115
|
+
if (attempt === delays.length)
|
|
116
|
+
break;
|
|
117
|
+
await (options.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds))))(delays[attempt]);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
throw lastError;
|
|
121
|
+
};
|
|
98
122
|
await reportInstall(requestFetch, server, token.projectId, token.apiKey, setupPlan.id, { status: "installing", attemptId });
|
|
99
123
|
try {
|
|
100
124
|
generatedFiles.push(...await generateRepositoryConfig(repositoryPath, repository.template, repository.name));
|
|
@@ -137,11 +161,12 @@ export async function runOnboard(options) {
|
|
|
137
161
|
if (gatewayState.status === "absent") {
|
|
138
162
|
let gateway;
|
|
139
163
|
try {
|
|
140
|
-
gateway = await initializeCustomerGateway({
|
|
164
|
+
gateway = await prepareRuntime(() => initializeCustomerGateway({
|
|
141
165
|
projectId: token.projectId, server, repository: repositoryPath, authorization: token,
|
|
142
166
|
runtimeReferences: localRuntime.references,
|
|
167
|
+
agentIdentity,
|
|
143
168
|
fetch: requestFetch, configHome: options.configHome, output,
|
|
144
|
-
});
|
|
169
|
+
}));
|
|
145
170
|
}
|
|
146
171
|
catch (error) {
|
|
147
172
|
if (!(error instanceof RuntimeSetupNotReadyError) || !localRuntime.references)
|
|
@@ -157,7 +182,7 @@ export async function runOnboard(options) {
|
|
|
157
182
|
await saveConnection(binding.connectionName, { server, projectId: token.projectId, apiKey: token.apiKey }, { configHome: options.configHome });
|
|
158
183
|
if (localRuntime.references) {
|
|
159
184
|
try {
|
|
160
|
-
runtimeUpgrade = await upgradeCustomerGatewayRuntime({ repository: repositoryPath, authorization: token, runtimeReferences: localRuntime.references, configHome: options.configHome, fetch: requestFetch });
|
|
185
|
+
runtimeUpgrade = await prepareRuntime(() => upgradeCustomerGatewayRuntime({ repository: repositoryPath, authorization: token, runtimeReferences: localRuntime.references, agentIdentity, configHome: options.configHome, fetch: requestFetch }));
|
|
161
186
|
if (runtimeUpgrade.changed) {
|
|
162
187
|
const stopped = await gatewayLifecycle.stop({ repository: repositoryPath, configHome: options.configHome, fetch: requestFetch });
|
|
163
188
|
if (stopped.state !== "STOPPED" || stopped.healthy)
|
|
@@ -187,11 +212,12 @@ export async function runOnboard(options) {
|
|
|
187
212
|
await assertGatewayStopped(requestFetch, binding.host, binding.port, binding.projectId, token.projectId);
|
|
188
213
|
gatewayMigration = await archiveGateway(gatewayState.directory, repositoryPath, binding.projectId);
|
|
189
214
|
output(`\nExisting Gateway belongs to project ${binding.projectId}; archived it at ${gatewayMigration.archiveDirectory}.\n`);
|
|
190
|
-
const gateway = await initializeCustomerGateway({
|
|
215
|
+
const gateway = await prepareRuntime(() => initializeCustomerGateway({
|
|
191
216
|
projectId: token.projectId, server, repository: repositoryPath, authorization: token,
|
|
192
217
|
runtimeReferences: localRuntime.references,
|
|
218
|
+
agentIdentity,
|
|
193
219
|
fetch: requestFetch, configHome: options.configHome, output,
|
|
194
|
-
});
|
|
220
|
+
}));
|
|
195
221
|
generatedFiles.push(...gateway.generatedFiles);
|
|
196
222
|
}
|
|
197
223
|
}
|
|
@@ -252,7 +278,7 @@ export async function runOnboard(options) {
|
|
|
252
278
|
: { state: "RECORDED_ONLY", checkedAt: new Date().toISOString(), limitations: [runtimeLimitation ?? "The local sandbox Runtime worker has not established exact adapter, probe, source-key, identity, mandate, and readiness bindings."] };
|
|
253
279
|
await reportInstall(requestFetch, server, token.projectId, token.apiKey, setupPlan.id, {
|
|
254
280
|
status: "verified", attemptId, generatedFiles: relativeGeneratedFiles(repositoryPath, generatedFiles), runtimeReadiness,
|
|
255
|
-
connectedAgent:
|
|
281
|
+
connectedAgent: agentIdentity,
|
|
256
282
|
...(runtimeBinding ? { runtimeBinding } : {}),
|
|
257
283
|
});
|
|
258
284
|
output(`\nWitnora Setup Autopilot completed for ${repository.name}.\n`);
|