witnora 0.18.5 → 0.18.6
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 +15 -2
- package/dist/onboard.js +5 -3
- package/dist/real-path-activation.js +18 -7
- package/package.json +1 -1
package/dist/gateway.js
CHANGED
|
@@ -1260,20 +1260,33 @@ export async function activateManagedWorkflowHarness(options) {
|
|
|
1260
1260
|
const path = join(directory, "workflow-harness.json");
|
|
1261
1261
|
const serialized = `${JSON.stringify(config, null, 2)}\n`;
|
|
1262
1262
|
let created = false;
|
|
1263
|
+
let changed = false;
|
|
1263
1264
|
if (await exists(path)) {
|
|
1264
1265
|
const current = await readFile(path, "utf8");
|
|
1265
1266
|
if (current !== serialized) {
|
|
1266
1267
|
const parsed = parseManagedWorkflowHarnessConfig(current);
|
|
1267
|
-
if (parsed.schemaVersion !== MANAGED_WORKFLOW_HARNESS_SCHEMA
|
|
1268
|
+
if (parsed.schemaVersion !== MANAGED_WORKFLOW_HARNESS_SCHEMA
|
|
1269
|
+
|| (canonicalHarnessConfig(parsed) !== canonicalHarnessConfig(config)
|
|
1270
|
+
&& !isExactGeneratedHarnessUpgrade(parsed, config, options.previousGeneratedModuleSha256))) {
|
|
1268
1271
|
throw new Error("Existing workflow-harness.json differs from the generated Assurance Harness binding; refusing to overwrite customer configuration.");
|
|
1272
|
+
}
|
|
1269
1273
|
await writeFile(path, serialized, { encoding: "utf8", mode: 0o600 });
|
|
1274
|
+
changed = true;
|
|
1270
1275
|
}
|
|
1271
1276
|
}
|
|
1272
1277
|
else {
|
|
1273
1278
|
await writeFile(path, serialized, { encoding: "utf8", mode: 0o600, flag: "wx" });
|
|
1274
1279
|
created = true;
|
|
1280
|
+
changed = true;
|
|
1275
1281
|
}
|
|
1276
|
-
return { state: "READY_TO_START", path, modulePath: evaluatorModulePath, config, created };
|
|
1282
|
+
return { state: "READY_TO_START", path, modulePath: evaluatorModulePath, config, created, changed };
|
|
1283
|
+
}
|
|
1284
|
+
function isExactGeneratedHarnessUpgrade(current, next, previousGeneratedModuleSha256) {
|
|
1285
|
+
if (!previousGeneratedModuleSha256 || current.evaluatorModuleSha256 !== previousGeneratedModuleSha256 || current.evaluatorContractSha256 !== previousGeneratedModuleSha256)
|
|
1286
|
+
return false;
|
|
1287
|
+
const { realPathActivations: _currentActivations, evaluatorModuleSha256: _currentModule, evaluatorContractSha256: _currentContract, ...currentStable } = current;
|
|
1288
|
+
const { realPathActivations: _nextActivations, evaluatorModuleSha256: _nextModule, evaluatorContractSha256: _nextContract, ...nextStable } = next;
|
|
1289
|
+
return JSON.stringify(currentStable) === JSON.stringify(nextStable);
|
|
1277
1290
|
}
|
|
1278
1291
|
function canonicalHarnessConfig(value) {
|
|
1279
1292
|
const { realPathActivations: _activations, ...stable } = value;
|
package/dist/onboard.js
CHANGED
|
@@ -88,6 +88,7 @@ export async function runOnboard(options) {
|
|
|
88
88
|
let gatewayMigration;
|
|
89
89
|
let managedGateway;
|
|
90
90
|
let runtimeUpgrade;
|
|
91
|
+
let realPathActivation;
|
|
91
92
|
let assuranceHarnessReadiness = {
|
|
92
93
|
state: "WAITING_FOR_CUSTOMER_HARNESS",
|
|
93
94
|
limitation: "No approved customer Harness module is bound to this Agent repository.",
|
|
@@ -196,13 +197,13 @@ export async function runOnboard(options) {
|
|
|
196
197
|
}
|
|
197
198
|
const runtimeConfigured = await gatewayHasRuntimeWorker(repositoryPath);
|
|
198
199
|
generatedFiles.push(...await generateAutopilotFiles(repositoryPath, repository.name, runtimeConfigured));
|
|
199
|
-
|
|
200
|
+
realPathActivation = await activateRealPathIntegrations({ repository: repositoryPath, server, projectId: token.projectId, apiKey: token.apiKey, env: options.env, fetch: requestFetch });
|
|
200
201
|
generatedFiles.push(...realPathActivation.generatedFiles);
|
|
201
202
|
try {
|
|
202
|
-
const activation = await activateManagedWorkflowHarness({ repository: repositoryPath, realPathActivations: realPathActivation.activations });
|
|
203
|
+
const activation = await activateManagedWorkflowHarness({ repository: repositoryPath, realPathActivations: realPathActivation.activations, previousGeneratedModuleSha256: realPathActivation.previousGeneratedModuleSha256 });
|
|
203
204
|
if (activation.created)
|
|
204
205
|
generatedFiles.push(activation.path);
|
|
205
|
-
assuranceHarnessChanged = activation.
|
|
206
|
+
assuranceHarnessChanged = activation.changed;
|
|
206
207
|
assuranceHarnessReadiness = { state: "ACTIVE" };
|
|
207
208
|
}
|
|
208
209
|
catch (error) {
|
|
@@ -266,6 +267,7 @@ export async function runOnboard(options) {
|
|
|
266
267
|
if (managedGateway?.started) {
|
|
267
268
|
await gatewayLifecycle.stop({ repository: repositoryPath, configHome: options.configHome, fetch: requestFetch }).catch(() => undefined);
|
|
268
269
|
}
|
|
270
|
+
await realPathActivation?.rollback().catch(() => undefined);
|
|
269
271
|
await rollbackGeneratedFiles(generatedFiles);
|
|
270
272
|
await runtimeUpgrade?.rollback().catch(() => undefined);
|
|
271
273
|
if (gatewayMigration)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
-
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
export async function activateRealPathIntegrations(options) {
|
|
5
5
|
const repository = resolve(options.repository ?? process.cwd());
|
|
@@ -7,14 +7,14 @@ export async function activateRealPathIntegrations(options) {
|
|
|
7
7
|
const base = options.server.replace(/\/$/, "");
|
|
8
8
|
const response = await request(`${base}/v1/projects/${encodeURIComponent(options.projectId)}/real-path-integrations`, { headers: { authorization: `Bearer ${options.apiKey}` } });
|
|
9
9
|
if (response.status === 404)
|
|
10
|
-
return { state: "NOT_CONFIGURED", created: false, activations: [], generatedFiles: [] };
|
|
10
|
+
return { state: "NOT_CONFIGURED", created: false, activations: [], generatedFiles: [], rollback: async () => undefined };
|
|
11
11
|
if (!response.ok)
|
|
12
12
|
throw new Error(`Could not load approved real-path integrations (${response.status}).`);
|
|
13
13
|
const body = await boundedJson(response);
|
|
14
14
|
const approved = Array.isArray(body.integrations) ? body.integrations.map(parsePlan).filter((plan) => plan.status === "READY_TO_ACTIVATE") : [];
|
|
15
15
|
const plans = approved.filter((plan) => ["STRIPE_REFUND", "SHOPIFY_DISPUTE", "ZENDESK_TICKET", "SALESFORCE_RECORD", "HUBSPOT_CRM_RECORD", "POSTGRES_RECORD"].includes(plan.generated.providerPackId) && plan.environment === "sandbox" && plan.customerSummary.evaluationMode === "SHADOW");
|
|
16
16
|
if (!plans.length)
|
|
17
|
-
return { state: "NOT_CONFIGURED", created: false, activations: [], generatedFiles: [] };
|
|
17
|
+
return { state: "NOT_CONFIGURED", created: false, activations: [], generatedFiles: [], rollback: async () => undefined };
|
|
18
18
|
const environment = options.env ?? process.env;
|
|
19
19
|
const activations = [];
|
|
20
20
|
const scenarios = [];
|
|
@@ -32,7 +32,9 @@ export async function activateRealPathIntegrations(options) {
|
|
|
32
32
|
let created = false;
|
|
33
33
|
const generatedFiles = [];
|
|
34
34
|
const current = await readFile(modulePath, "utf8").catch((error) => error.code === "ENOENT" ? undefined : Promise.reject(error));
|
|
35
|
-
const
|
|
35
|
+
const priorSource = await readFile(manifestPath, "utf8").catch((error) => error.code === "ENOENT" ? undefined : Promise.reject(error));
|
|
36
|
+
const prior = priorSource === undefined ? undefined : JSON.parse(priorSource);
|
|
37
|
+
const previousGeneratedModuleSha256 = current !== undefined && sha(current) === prior?.moduleDigestSha256 ? prior.moduleDigestSha256 : undefined;
|
|
36
38
|
if (current === undefined) {
|
|
37
39
|
await writeFile(modulePath, source, { encoding: "utf8", mode: 0o600, flag: "wx" });
|
|
38
40
|
created = true;
|
|
@@ -44,12 +46,12 @@ export async function activateRealPathIntegrations(options) {
|
|
|
44
46
|
const temporary = `${modulePath}.${randomUUID()}.tmp`;
|
|
45
47
|
await writeFile(temporary, source, { encoding: "utf8", mode: 0o600, flag: "wx" });
|
|
46
48
|
await rename(temporary, modulePath);
|
|
47
|
-
generatedFiles.push(modulePath);
|
|
48
49
|
}
|
|
49
50
|
const manifest = { schemaVersion: "witnora.real_path_activation_manifest.v0.1", projectId: options.projectId, moduleDigestSha256, activations };
|
|
50
51
|
await mkdir(dirname(manifestPath), { recursive: true });
|
|
51
52
|
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
52
|
-
|
|
53
|
+
if (priorSource === undefined)
|
|
54
|
+
generatedFiles.push(manifestPath);
|
|
53
55
|
for (const item of scenarios) {
|
|
54
56
|
const path = join(repository, ".witnora", "scenarios", `${item.plan.taskContractId}.json`);
|
|
55
57
|
await mkdir(dirname(path), { recursive: true });
|
|
@@ -62,7 +64,16 @@ export async function activateRealPathIntegrations(options) {
|
|
|
62
64
|
throw error;
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
|
-
return { state: "READY_TO_START", created, modulePath: "witnora.assurance-harness.mjs", activations, generatedFiles }
|
|
67
|
+
return { state: "READY_TO_START", created, modulePath: "witnora.assurance-harness.mjs", activations, generatedFiles, ...(previousGeneratedModuleSha256 ? { previousGeneratedModuleSha256 } : {}), rollback: async () => {
|
|
68
|
+
if (current === undefined)
|
|
69
|
+
await rm(modulePath, { force: true });
|
|
70
|
+
else
|
|
71
|
+
await writeFile(modulePath, current, { encoding: "utf8", mode: 0o600 });
|
|
72
|
+
if (priorSource === undefined)
|
|
73
|
+
await rm(manifestPath, { force: true });
|
|
74
|
+
else
|
|
75
|
+
await writeFile(manifestPath, priorSource, { encoding: "utf8", mode: 0o600 });
|
|
76
|
+
} };
|
|
66
77
|
}
|
|
67
78
|
async function providerPreflight(request, env, plan, now, postgresClientFactory) {
|
|
68
79
|
if (plan.generated.providerPackId === "STRIPE_REFUND") {
|