witnora 0.20.1 → 0.20.2

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/onboard.js CHANGED
@@ -335,7 +335,9 @@ export async function runOnboard(options) {
335
335
  else if (actionTransportTest) {
336
336
  output(`Action transport: ${actionTransportTest.transport.toUpperCase()} is waiting for one exact sandbox Task/action path. ${actionTransportTest.limitation}\n`);
337
337
  }
338
- output("Connected. Nora is monitoring this Agent. Run it normally whenever it is ready; the first source-signed activity will appear automatically without blocking setup.\n");
338
+ output(actionTransportTest?.state === "WAITING_FOR_ACTION_PATH"
339
+ ? `Base Gateway connected, but ${actionTransportTest.transport.toUpperCase()} Action is not verified yet. Go to Overview, run the Agent once, confirm one exact sandbox Business Task/action path, then rerun this same command.\n`
340
+ : "Connected. Nora is monitoring this Agent. Run it normally whenever it is ready; the first source-signed activity will appear automatically without blocking setup.\n");
339
341
  return { projectId: token.projectId, connectionName: token.connectionName, credentialsPath, template: repository.template,
340
342
  repositoryKind: repository.kind, generatedFiles, receiptPath, discovery, setupPlanId: setupPlan.id,
341
343
  gatewayDirectory: join(repositoryPath, ".witnora", "gateway"), gatewayArchiveDirectory: gatewayMigration?.archiveDirectory,
@@ -84,9 +84,7 @@ export async function activateRealPathIntegrations(options) {
84
84
  }
85
85
  async function providerPreflight(request, env, plan, now, postgresClientFactory) {
86
86
  if (plan.generated.providerPackId === "STRIPE_REFUND") {
87
- const secret = env.STRIPE_SECRET_KEY;
88
- if (!secret?.startsWith("sk_test_") || secret.length < 12)
89
- throw new Error("Stripe test-mode activation requires STRIPE_SECRET_KEY to reference an sk_test_ credential in the customer environment.");
87
+ const secret = stripeTestCredential(env);
90
88
  return stripePreflight(request, secret, plan, now);
91
89
  }
92
90
  if (plan.generated.providerPackId === "SHOPIFY_DISPUTE")
@@ -252,7 +250,7 @@ import {join} from "node:path";
252
250
  const contracts=${JSON.stringify(contracts)};
253
251
  const sha=(value)=>createHash("sha256").update(typeof value==="string"?value:JSON.stringify(value)).digest("hex");
254
252
  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();};
255
- 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.");};
253
+ 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("rk_test_")&&!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.");};
256
254
  export function createWitnoraBusinessTaskEvaluatorOptions(context){return {
257
255
  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.map((item)=>({...item,source:"LIVE_SHADOW"}));},
258
256
  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}]};}
@@ -265,11 +263,14 @@ async function persistProviderCredential(repository, plan, environment) {
265
263
  return;
266
264
  if (plan.generated.providerPackId === "QUEUE_JOB" && plan.environment === "sandbox")
267
265
  return;
268
- const name = providerCredentialEnvironmentName(plan.generated.providerPackId);
269
- const value = environment[name];
270
266
  const directory = join(repository, ".witnora", "provider-credentials");
271
267
  const target = join(directory, `${plan.id}.secret`);
272
268
  const current = await readFile(target, "utf8").catch((error) => error.code === "ENOENT" ? undefined : Promise.reject(error));
269
+ const name = providerCredentialEnvironmentName(plan.generated.providerPackId);
270
+ const stripeCredentialSupplied = Boolean(environment.STRIPE_RESTRICTED_TEST_KEY?.trim() || environment.STRIPE_SECRET_KEY?.trim());
271
+ const value = plan.generated.providerPackId === "STRIPE_REFUND"
272
+ ? stripeCredentialSupplied ? stripeTestCredential(environment) : undefined
273
+ : environment[name];
273
274
  if (!value) {
274
275
  if (current !== undefined)
275
276
  return;
@@ -294,7 +295,7 @@ function sameActivationPlans(plans, activations) {
294
295
  }
295
296
  function providerCredentialEnvironmentName(packId) {
296
297
  if (packId === "STRIPE_REFUND")
297
- return "STRIPE_SECRET_KEY";
298
+ return "STRIPE_RESTRICTED_TEST_KEY";
298
299
  if (packId === "SHOPIFY_DISPUTE")
299
300
  return "SHOPIFY_READ_ACCESS_TOKEN";
300
301
  if (packId === "ZENDESK_TICKET")
@@ -307,6 +308,18 @@ function providerCredentialEnvironmentName(packId) {
307
308
  return "WITNORA_POSTGRES_READ_URL";
308
309
  throw new Error(`Provider credential mapping is not supported for ${packId}.`);
309
310
  }
311
+ function stripeTestCredential(environment) {
312
+ const restricted = environment.STRIPE_RESTRICTED_TEST_KEY?.trim();
313
+ if (restricted) {
314
+ if (!restricted.startsWith("rk_test_") || restricted.length < 12)
315
+ throw new Error("Stripe sandbox activation requires STRIPE_RESTRICTED_TEST_KEY to contain an rk_test_ restricted test key.");
316
+ return restricted;
317
+ }
318
+ const legacy = environment.STRIPE_SECRET_KEY?.trim();
319
+ if (!legacy?.startsWith("sk_test_") || legacy.length < 12)
320
+ throw new Error("Stripe sandbox activation requires a read-only rk_test_ key in STRIPE_RESTRICTED_TEST_KEY. STRIPE_SECRET_KEY remains a legacy sk_test_ fallback only.");
321
+ return legacy;
322
+ }
310
323
  function parsePlan(value) { if (!value || typeof value !== "object" || Array.isArray(value))
311
324
  throw new Error("Real-path integration response is invalid."); const plan = value; if (plan.schemaVersion !== "witnora.real_path_integration.v0.1" || !plan.id || !plan.taskContractId || !plan.subject?.agentId || !plan.subject.agentVersion || !plan.generated || plan.generated.boundaries?.rawPayloadUpload !== false || plan.generated.boundaries.rawCredentialUpload !== false || plan.generated.boundaries.evaluatorWrites !== false || plan.generated.boundaries.firstAcceptanceProductionWrites !== 0 || !digest(plan.digestSha256) || !digest(plan.taskContractDigestSha256) || !digest(plan.generated.providerContractDigestSha256))
312
325
  throw new Error("Real-path integration response failed its safety contract."); return plan; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "witnora",
3
- "version": "0.20.1",
3
+ "version": "0.20.2",
4
4
  "description": "Independent assurance for covered agent action paths across models and frameworks.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",