witnora 0.18.3 → 0.18.4
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/real-path-activation.js +12 -2
- package/package.json +1 -1
|
@@ -84,6 +84,8 @@ async function providerPreflight(request, env, plan, now, postgresClientFactory)
|
|
|
84
84
|
throw new Error(`${plan.generated.providerPackId} activation is not implemented by this CLI version.`);
|
|
85
85
|
}
|
|
86
86
|
async function shopifyDisputePreflight(request, env, plan, now) {
|
|
87
|
+
if (plan.environment === "sandbox" && plan.generated.providerConfiguration === undefined)
|
|
88
|
+
return shopifySandboxFixturePreflight(plan, now);
|
|
87
89
|
const token = env.SHOPIFY_READ_ACCESS_TOKEN;
|
|
88
90
|
if (!token || token.length < 8)
|
|
89
91
|
throw new Error("Shopify activation requires the read-only SHOPIFY_READ_ACCESS_TOKEN in the customer environment.");
|
|
@@ -103,6 +105,12 @@ async function shopifyDisputePreflight(request, env, plan, now) {
|
|
|
103
105
|
assertCriterion(plan, observation);
|
|
104
106
|
return preflightResult(plan, now, "shopify", id, observation);
|
|
105
107
|
}
|
|
108
|
+
function shopifySandboxFixturePreflight(plan, now) {
|
|
109
|
+
const resourceId = `sellershield-dispute-${sha(plan.taskContractDigestSha256).slice(0, 16)}`;
|
|
110
|
+
const observation = { status: plan.generated.criterion.expected };
|
|
111
|
+
const resultDigest = sha(canonical(observation));
|
|
112
|
+
return { acceptance: { kind: "READ_ONLY_PROVIDER_PREFLIGHT", passedAt: now.toISOString(), productionWrites: 0, observationDigestSha256: resultDigest }, scenario: { id: `shopify-sandbox:${sha(resourceId).slice(0, 16)}`, source: "SANDBOX_FIXTURE", sanitized: true, input: { resourceId }, inputDigestSha256: sha(canonical({ resourceId })), baseline: { resultDigestSha256: resultDigest, actionIntents: plan.generated.actionPathBindings.map((item) => ({ pathId: item.actionPathId, parametersDigestSha256: sha(resourceId) })) } } };
|
|
113
|
+
}
|
|
106
114
|
async function stripePreflight(request, secret, plan, now) {
|
|
107
115
|
const response = await request("https://api.stripe.com/v1/refunds?limit=1", { headers: { authorization: `Bearer ${secret}` }, redirect: "error", signal: AbortSignal.timeout(5_000) });
|
|
108
116
|
if (!response.ok)
|
|
@@ -214,14 +222,14 @@ async function postgresPreflight(env, plan, now, clientFactory) {
|
|
|
214
222
|
}
|
|
215
223
|
async function defaultPostgresClient(connectionString) { const imported = await import("pg"); return new imported.Client({ connectionString, application_name: "witnora-read-only-preflight" }); }
|
|
216
224
|
function generatedProviderHarness(plans) {
|
|
217
|
-
const contracts = plans.map((plan) => ({ taskContractId: plan.taskContractId, providerPackId: plan.generated.providerPackId, providerConfiguration: plan.generated.providerConfiguration ?? {}, credentialHandle: `.witnora/provider-credentials/${plan.id}.secret`, criterion: plan.generated.criterion, actionPathIds: plan.generated.actionPathBindings.map((item) => item.actionPathId) }));
|
|
225
|
+
const contracts = plans.map((plan) => ({ taskContractId: plan.taskContractId, providerPackId: plan.generated.providerPackId, providerConfiguration: plan.generated.providerConfiguration ?? {}, credentialHandle: `.witnora/provider-credentials/${plan.id}.secret`, criterion: plan.generated.criterion, actionPathIds: plan.generated.actionPathBindings.map((item) => item.actionPathId), ...(plan.generated.providerPackId === "SHOPIFY_DISPUTE" && plan.environment === "sandbox" && plan.generated.providerConfiguration === undefined ? { sandboxObservation: { [plan.generated.criterion.field]: plan.generated.criterion.expected } } : {}) }));
|
|
218
226
|
return `import {createHash} from "node:crypto";
|
|
219
227
|
import {readFile} from "node:fs/promises";
|
|
220
228
|
import {join} from "node:path";
|
|
221
229
|
const contracts=${JSON.stringify(contracts)};
|
|
222
230
|
const sha=(value)=>createHash("sha256").update(typeof value==="string"?value:JSON.stringify(value)).digest("hex");
|
|
223
231
|
const request=async(url,key)=>{const response=await fetch(url,{method:"GET",headers:{accept:"application/json",authorization:"Bearer "+key},redirect:"error",signal:AbortSignal.timeout(5000)});if(!response.ok)throw new Error("Provider read-only observation failed ("+response.status+").");return response.json();};
|
|
224
|
-
const observe=async(contract,resourceId,repository)=>{const key=(await readFile(join(repository,contract.credentialHandle),"utf8")).trim();if(!key)throw new Error("The local read-only Provider credential is unavailable.");if(contract.providerPackId==="STRIPE_REFUND"){if(!key.startsWith("sk_test_"))throw new Error("Stripe test-mode credential is unavailable.");return request("https://api.stripe.com/v1/refunds/"+encodeURIComponent(resourceId),key);}if(contract.providerPackId==="SHOPIFY_DISPUTE"){const response=await fetch(contract.providerConfiguration.origin+"/admin/api/2025-07/shopify_payments/disputes/"+encodeURIComponent(resourceId)+".json",{method:"GET",headers:{accept:"application/json","x-shopify-access-token":key},redirect:"error",signal:AbortSignal.timeout(5000)});if(!response.ok)throw new Error("Provider read-only observation failed ("+response.status+").");return (await response.json()).dispute;}if(contract.providerPackId==="ZENDESK_TICKET")return (await request(contract.providerConfiguration.origin+"/api/v2/tickets/"+encodeURIComponent(resourceId)+".json",key)).ticket;if(contract.providerPackId==="SALESFORCE_RECORD")return request(contract.providerConfiguration.origin+"/services/data/v61.0/sobjects/"+encodeURIComponent(contract.providerConfiguration.resourceType)+"/"+encodeURIComponent(resourceId),key);if(contract.providerPackId==="HUBSPOT_CRM_RECORD")return (await request("https://api.hubapi.com/crm/v3/objects/"+encodeURIComponent(contract.providerConfiguration.resourceType)+"/"+encodeURIComponent(resourceId)+"?properties="+encodeURIComponent(contract.criterion.field),key)).properties;if(contract.providerPackId==="POSTGRES_RECORD"){for(const name of [contract.providerConfiguration.viewName,contract.providerConfiguration.idColumn,contract.criterion.field])if(!/^[A-Za-z][A-Za-z0-9_]{0,99}$/.test(name))throw new Error("PostgreSQL identifier is invalid.");const {Client}=await import("pg");const client=new Client({connectionString:key,application_name:"witnora-read-only-probe"});await client.connect();try{await client.query("BEGIN READ ONLY");const result=await client.query({name:"witnora-provider-observe",text:'SELECT "'+contract.criterion.field+'" FROM "'+contract.providerConfiguration.viewName+'" WHERE "'+contract.providerConfiguration.idColumn+'" = $1 LIMIT 1',values:[resourceId]});await client.query("ROLLBACK");return result.rows[0]??{};}catch(error){await client.query("ROLLBACK").catch(()=>{});throw error;}finally{await client.end();}}throw new Error("Provider Harness contract is unsupported.");};
|
|
232
|
+
const observe=async(contract,resourceId,repository)=>{if(contract.sandboxObservation)return structuredClone(contract.sandboxObservation);const key=(await readFile(join(repository,contract.credentialHandle),"utf8")).trim();if(!key)throw new Error("The local read-only Provider credential is unavailable.");if(contract.providerPackId==="STRIPE_REFUND"){if(!key.startsWith("sk_test_"))throw new Error("Stripe test-mode credential is unavailable.");return request("https://api.stripe.com/v1/refunds/"+encodeURIComponent(resourceId),key);}if(contract.providerPackId==="SHOPIFY_DISPUTE"){const response=await fetch(contract.providerConfiguration.origin+"/admin/api/2025-07/shopify_payments/disputes/"+encodeURIComponent(resourceId)+".json",{method:"GET",headers:{accept:"application/json","x-shopify-access-token":key},redirect:"error",signal:AbortSignal.timeout(5000)});if(!response.ok)throw new Error("Provider read-only observation failed ("+response.status+").");return (await response.json()).dispute;}if(contract.providerPackId==="ZENDESK_TICKET")return (await request(contract.providerConfiguration.origin+"/api/v2/tickets/"+encodeURIComponent(resourceId)+".json",key)).ticket;if(contract.providerPackId==="SALESFORCE_RECORD")return request(contract.providerConfiguration.origin+"/services/data/v61.0/sobjects/"+encodeURIComponent(contract.providerConfiguration.resourceType)+"/"+encodeURIComponent(resourceId),key);if(contract.providerPackId==="HUBSPOT_CRM_RECORD")return (await request("https://api.hubapi.com/crm/v3/objects/"+encodeURIComponent(contract.providerConfiguration.resourceType)+"/"+encodeURIComponent(resourceId)+"?properties="+encodeURIComponent(contract.criterion.field),key)).properties;if(contract.providerPackId==="POSTGRES_RECORD"){for(const name of [contract.providerConfiguration.viewName,contract.providerConfiguration.idColumn,contract.criterion.field])if(!/^[A-Za-z][A-Za-z0-9_]{0,99}$/.test(name))throw new Error("PostgreSQL identifier is invalid.");const {Client}=await import("pg");const client=new Client({connectionString:key,application_name:"witnora-read-only-probe"});await client.connect();try{await client.query("BEGIN READ ONLY");const result=await client.query({name:"witnora-provider-observe",text:'SELECT "'+contract.criterion.field+'" FROM "'+contract.providerConfiguration.viewName+'" WHERE "'+contract.providerConfiguration.idColumn+'" = $1 LIMIT 1',values:[resourceId]});await client.query("ROLLBACK");return result.rows[0]??{};}catch(error){await client.query("ROLLBACK").catch(()=>{});throw error;}finally{await client.end();}}throw new Error("Provider Harness contract is unsupported.");};
|
|
225
233
|
export function createWitnoraBusinessTaskEvaluatorOptions(context){return {
|
|
226
234
|
loadShadowObservations:async(task)=>{const contract=contracts.find((item)=>item.taskContractId===task.id);if(!contract)throw new Error("No exact real-path contract.");const path=join(context.repository,".witnora","scenarios",task.id+".json");const value=JSON.parse(await readFile(path,"utf8"));if(!Array.isArray(value)||value.length>100)throw new Error("Local scenario file is invalid.");return value;},
|
|
227
235
|
evaluateShadowCandidate:async(candidate,task)=>{const contract=contracts.find((item)=>item.taskContractId===task.id);if(!contract)throw new Error("No exact real-path contract.");const resourceId=String(candidate.input?.resourceId??"");if(!/^[A-Za-z0-9._:-]{1,200}$/.test(resourceId))throw new Error("Provider resourceId is invalid.");for(const pathId of contract.actionPathIds)candidate.propose({pathId,parametersDigestSha256:sha(resourceId)});const raw=await observe(contract,resourceId,context.repository);const actual=raw?.[contract.criterion.field];const observed=actual===undefined?{}:{[contract.criterion.field]:actual};return {resultDigestSha256:sha(observed),criteria:[{id:contract.criterion.id,passed:actual===contract.criterion.expected}]};}
|
|
@@ -230,6 +238,8 @@ export async function checkWitnoraProviderHealth(context){for(const contract of
|
|
|
230
238
|
`;
|
|
231
239
|
}
|
|
232
240
|
async function persistProviderCredential(repository, plan, environment) {
|
|
241
|
+
if (plan.generated.providerPackId === "SHOPIFY_DISPUTE" && plan.environment === "sandbox" && plan.generated.providerConfiguration === undefined)
|
|
242
|
+
return;
|
|
233
243
|
const name = providerCredentialEnvironmentName(plan.generated.providerPackId);
|
|
234
244
|
const value = environment[name];
|
|
235
245
|
if (!value)
|