@fidacy/openclaw-plugin 0.4.0 → 0.4.1
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 +35 -0
- package/dist/index.js +82 -30
- package/openclaw.plugin.json +1 -1
- package/package.json +10 -9
package/README.md
CHANGED
|
@@ -94,6 +94,41 @@ This is a payment-control plugin, so audit it like one. What it actually does:
|
|
|
94
94
|
JWKS at `api.fidacy.com/.well-known/jwks.json` with the open-source
|
|
95
95
|
[`@fidacy/verify`](https://www.npmjs.com/package/@fidacy/verify).
|
|
96
96
|
|
|
97
|
+
## Security
|
|
98
|
+
|
|
99
|
+
This plugin sits in front of money-moving tool calls, so it should be precise about
|
|
100
|
+
what it touches.
|
|
101
|
+
|
|
102
|
+
**What it reads.** Your engine credential, from the plugin config field `engineApiKey`
|
|
103
|
+
or from `FIDACY_ENGINE_API_KEY`. Optionally `FIDACY_SIGNING_KEY_B64`, which is your own
|
|
104
|
+
Ed25519 key, so grants are signed with a stable key instead of a per-session one. Both
|
|
105
|
+
belong to you and both are optional. With neither set, the plugin runs the free
|
|
106
|
+
anonymous path and signs with an ephemeral key.
|
|
107
|
+
|
|
108
|
+
**No credential is embedded.** There is no API key, token, or private key literal in the
|
|
109
|
+
published package, in any version. Check it yourself:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
cd $(mktemp -d) && curl -sL $(npm view @fidacy/openclaw-plugin dist.tarball) | tar xz && grep -rE "fky_(live|test)_[A-Za-z0-9]{8,}|BEGIN [A-Z ]*PRIVATE KEY" package/ || echo "no credential literals found"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
A static scanner may flag the line that resolves your credential as a hardcoded secret,
|
|
116
|
+
because the identifier on the right of the assignment is named like one. It reads your
|
|
117
|
+
config and falls back to the environment, defaulting to the empty string. The command
|
|
118
|
+
above is what settles the question, and the test suite fails the build if a literal ever
|
|
119
|
+
does get in.
|
|
120
|
+
|
|
121
|
+
**Turning the network off.** `FIDACY_DISABLE_TELEMETRY=1` stops anonymous install and
|
|
122
|
+
usage telemetry. `FIDACY_DISABLE_PROVISION=1` stops the background free-key provisioning.
|
|
123
|
+
With both set and no engine credential, the plugin decides locally and contacts nothing.
|
|
124
|
+
|
|
125
|
+
**Where the history lives.** Decisions go to a hash-chained log under `~/.fidacy` on your
|
|
126
|
+
machine. That local chain is what makes `spend_summary`, `list_decisions` and
|
|
127
|
+
`get_audit_proof` work with no network. Delete the directory to clear it.
|
|
128
|
+
|
|
129
|
+
**Files are never uploaded.** `anchor_artifact` and `check_artifact` hash the file locally
|
|
130
|
+
with SHA-256 and send only the 64 hex characters.
|
|
131
|
+
|
|
97
132
|
## Prefer MCP instead?
|
|
98
133
|
|
|
99
134
|
If you'd rather run Fidacy as an MCP server (out-of-process), use
|
package/dist/index.js
CHANGED
|
@@ -4739,7 +4739,7 @@ function resolveMandateRules(cfg) {
|
|
|
4739
4739
|
}
|
|
4740
4740
|
|
|
4741
4741
|
// ../mcp/src/telemetry.ts
|
|
4742
|
-
var CLIENT_VERSION = true ? "0.4.
|
|
4742
|
+
var CLIENT_VERSION = true ? "0.4.1" : "dev";
|
|
4743
4743
|
function bandOf(amount) {
|
|
4744
4744
|
if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
|
|
4745
4745
|
if (amount < 10) return "lt10";
|
|
@@ -4927,7 +4927,7 @@ function requestUpgrade() {
|
|
|
4927
4927
|
}
|
|
4928
4928
|
|
|
4929
4929
|
// ../mcp/src/provision.ts
|
|
4930
|
-
var CLIENT_VERSION2 = true ? "0.4.
|
|
4930
|
+
var CLIENT_VERSION2 = true ? "0.4.1" : "dev";
|
|
4931
4931
|
function provisionEnabled() {
|
|
4932
4932
|
const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
|
|
4933
4933
|
return !(v === "1" || v === "true" || v === "yes");
|
|
@@ -5069,7 +5069,7 @@ function trialCountdownLine(keyOverride) {
|
|
|
5069
5069
|
}
|
|
5070
5070
|
|
|
5071
5071
|
// ../mcp/src/register.ts
|
|
5072
|
-
var CLIENT_VERSION3 = true ? "0.4.
|
|
5072
|
+
var CLIENT_VERSION3 = true ? "0.4.1" : "dev";
|
|
5073
5073
|
function endpoint3() {
|
|
5074
5074
|
const base = (process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com").replace(/\/$/, "");
|
|
5075
5075
|
return `${base}/v1/register`;
|
|
@@ -5211,7 +5211,7 @@ function renderSummary(s) {
|
|
|
5211
5211
|
];
|
|
5212
5212
|
if (s.denied > 0) {
|
|
5213
5213
|
const ranked = Object.entries(s.denied_by_rule).sort((a, b) => b[1] - a[1]);
|
|
5214
|
-
lines.push(`Blocked: ${money(s.blocked_totals)} in attempted value. By
|
|
5214
|
+
lines.push(`Blocked: ${money(s.blocked_totals)} in attempted value. By reason: ${ranked.map(([rule, n]) => `${ruleLabel(rule)} (${n})`).join(", ")}.`);
|
|
5215
5215
|
}
|
|
5216
5216
|
if (s.amount_unknown > 0) {
|
|
5217
5217
|
lines.push(`${s.amount_unknown} record(s) in this window predate amount logging, so the totals above are a floor, not the exact figure.`);
|
|
@@ -5238,32 +5238,82 @@ function renderRows(rows) {
|
|
|
5238
5238
|
return rows.map((r) => {
|
|
5239
5239
|
const amount = typeof r.amount === "number" ? `${r.amount.toLocaleString("en-US")} ${r.currency ?? ""}`.trim() : "amount not recorded";
|
|
5240
5240
|
const who = r.payee ?? "payee not recorded";
|
|
5241
|
-
const why = r.status === "DENY" ? `
|
|
5241
|
+
const why = r.status === "DENY" ? ` ${explainRule(r.violatedRule, r.payee)}` : "";
|
|
5242
5242
|
const said = r.purpose ? ` Agent's stated reason: "${r.purpose}".` : "";
|
|
5243
5243
|
return `#${r.seq} ${r.ts} ${r.status} ${amount} to ${who}.${why}${said} (decision ${r.decisionId})`;
|
|
5244
5244
|
}).join("\n");
|
|
5245
5245
|
}
|
|
5246
|
+
function ruleLabel(rule) {
|
|
5247
|
+
if (rule.startsWith("payee_lookalike")) return "lookalike payee";
|
|
5248
|
+
if (rule.startsWith("payee_not_in_allowlist")) return "payee not approved";
|
|
5249
|
+
if (rule.startsWith("category_not_allowed")) return "purpose not allowed";
|
|
5250
|
+
if (rule.startsWith("currency_not_allowed")) return "wrong currency";
|
|
5251
|
+
if (rule.startsWith("per_tx") || rule.includes("perTxMax")) return "over the per-payment cap";
|
|
5252
|
+
if (rule.includes("maxTotal") || rule.startsWith("total_cap") || rule.startsWith("budget")) return "over the total budget";
|
|
5253
|
+
if (rule.startsWith("invoice") || rule.includes("duplicate")) return "duplicate invoice";
|
|
5254
|
+
if (rule.startsWith("window") || rule.includes("mandate_window") || rule.includes("expired")) return "outside the mandate window";
|
|
5255
|
+
if (rule.startsWith("revoked") || rule.startsWith("mandate_revoked")) return "mandate revoked";
|
|
5256
|
+
if (rule.startsWith("invalid_mandate")) return "mandate misconfigured";
|
|
5257
|
+
if (rule.startsWith("non_positive_amount")) return "invalid amount";
|
|
5258
|
+
if (rule.startsWith("activation_required")) return "trial used up";
|
|
5259
|
+
if (rule === "not_recorded") return "rule not recorded";
|
|
5260
|
+
return rule;
|
|
5261
|
+
}
|
|
5246
5262
|
function explainRule(rule, payee) {
|
|
5247
5263
|
if (!rule) return "This entry predates readable rule logging, so the specific rule is not on the record. The decision payload is still covered by the chain digest.";
|
|
5264
|
+
if (rule.startsWith("invalid_mandate")) {
|
|
5265
|
+
const cap = rule.includes("perTxMax") ? "perTxMax" : rule.includes("maxTotal") ? "maxTotal" : null;
|
|
5266
|
+
if (cap)
|
|
5267
|
+
return `The mandate's \`${cap}\` is not a usable number, so the firewall denied rather than run without that cap. Values like "2,500", "$2500" or "2500 USD" all look right to a human and become NaN, and a cap that cannot be compared would silently let everything through. Write it as a bare number: ${cap}: 2500.`;
|
|
5268
|
+
return "The mandate is missing its `allow` block, so there is nothing to enforce and the firewall denied. Check ~/.fidacy/config.json or FIDACY_MANDATE_JSON.";
|
|
5269
|
+
}
|
|
5270
|
+
if (rule.startsWith("payee_lookalike")) {
|
|
5271
|
+
const approved = rule.includes("~") ? rule.split("~")[1] : "an approved payee";
|
|
5272
|
+
return `The payee${payee ? ` "${payee}"` : ""} is a lookalike of your approved payee "${approved}": same name to a human glance, different string. That is the signature of a payee swap, whether from a prompt injection or a spoofed invoice. If it really is a separate vendor, add it to \`payees\` explicitly; if it is not, this attempt is exactly what the firewall exists to stop.`;
|
|
5273
|
+
}
|
|
5248
5274
|
if (rule.startsWith("payee_not_in_allowlist"))
|
|
5249
|
-
return `The payee${payee ? ` "${payee}"` : ""} is not on the mandate's allowlist. This is the deny-by-default rule doing its job: an agent can only pay counterparties a human put on the list, which is what stops a swapped or lookalike vendor.`;
|
|
5275
|
+
return `The payee${payee ? ` "${payee}"` : ""} is not on the mandate's allowlist. This is the deny-by-default rule doing its job: an agent can only pay counterparties a human put on the list, which is what stops a swapped or lookalike vendor. To authorize this one, add it to \`payees\` in ~/.fidacy/config.json, or pass a full mandate through FIDACY_MANDATE_JSON.`;
|
|
5250
5276
|
if (rule.startsWith("category_not_allowed"))
|
|
5251
|
-
return "The stated purpose category is not one the mandate permits for this agent.";
|
|
5277
|
+
return "The stated purpose category is not one the mandate permits for this agent. Add it to `categories` in the mandate, or send the payment under a purpose the mandate already allows.";
|
|
5252
5278
|
if (rule.startsWith("currency_not_allowed"))
|
|
5253
|
-
return "The payment currency is not the mandate's currency.";
|
|
5279
|
+
return "The payment currency is not the mandate's currency. Set `currency` to the one you intend to pay in. A mandate authorizes one currency at a time on purpose, so a swapped currency cannot slip past a cap set in another.";
|
|
5254
5280
|
if (rule.startsWith("per_tx") || rule.includes("perTxMax"))
|
|
5255
|
-
return "The amount exceeds the per-transaction ceiling in the mandate.";
|
|
5256
|
-
if (rule.includes("maxTotal") || rule.startsWith("budget"))
|
|
5257
|
-
return "The payment would push cumulative spend past the mandate's total budget for the window.";
|
|
5281
|
+
return "The amount exceeds the per-transaction ceiling in the mandate. Raise `perTxMax` if payments this size are expected, or split the payment.";
|
|
5282
|
+
if (rule.includes("maxTotal") || rule.startsWith("budget") || rule.startsWith("total_cap"))
|
|
5283
|
+
return "The payment would push cumulative spend past the mandate's total budget for the window. Raise `maxTotal`, or wait for the window to roll over. Blocked attempts do not consume the budget; only payments that went through do.";
|
|
5258
5284
|
if (rule.startsWith("invoice") || rule.includes("duplicate"))
|
|
5259
|
-
return "This invoice was already paid once. One payment per invoice is enforced regardless of amount, which is the control that stops a re-presented invoice at a higher figure.";
|
|
5260
|
-
if (rule.startsWith("window") || rule.includes("expired"))
|
|
5261
|
-
return "The mandate was outside its validity window at the moment of the request.";
|
|
5262
|
-
if (rule.startsWith("revoked")
|
|
5285
|
+
return "This invoice was already paid once. One payment per invoice is enforced regardless of amount, which is the control that stops a re-presented invoice at a higher figure. If this genuinely is a second, separate payment, give it its own invoiceRef.";
|
|
5286
|
+
if (rule.startsWith("window") || rule.includes("mandate_window") || rule.includes("expired"))
|
|
5287
|
+
return "The mandate was outside its validity window at the moment of the request. Check `notBefore` and `notAfter`, and issue a mandate whose window covers now.";
|
|
5288
|
+
if (rule.startsWith("revoked") || rule.startsWith("mandate_revoked"))
|
|
5289
|
+
return "The mandate had been revoked. A revoked mandate cannot be un-revoked; issue a new one to authorize further payments.";
|
|
5290
|
+
if (rule.startsWith("non_positive_amount"))
|
|
5291
|
+
return "The amount was zero or negative, which is not a payment anyone can authorize. Check what produced that value before retrying.";
|
|
5263
5292
|
if (rule.startsWith("activation_required"))
|
|
5264
5293
|
return "The anonymous trial had been used up, so the firewall failed closed. No money can move until the install is activated with a free API key.";
|
|
5265
5294
|
return `The mandate rule "${rule}" was violated.`;
|
|
5266
5295
|
}
|
|
5296
|
+
function explainEngineError(action, status, type, reasons) {
|
|
5297
|
+
const noun = action === "verdict" ? "assess this action" : action === "anchor" ? "anchor this artifact" : "check this artifact";
|
|
5298
|
+
if (status === 401 || status === 403)
|
|
5299
|
+
return `Fidacy could not ${noun}: the engine rejected the credential (HTTP ${status}). This is not an outage. The key is missing, wrong, or lacks the scope this call needs \u2014 an anonymous provisioned key has no scopes at all. Get an account key at https://fidacy.com/claim and set FIDACY_ENGINE_API_KEY. Nothing was executed.`;
|
|
5300
|
+
if (status === 402)
|
|
5301
|
+
return `Fidacy could not ${noun}: the account is past a spending or quota limit (HTTP 402). The integration is fine; the limit needs settling at https://fidacy.com/claim. Nothing was executed.`;
|
|
5302
|
+
if (status === 429)
|
|
5303
|
+
return `Fidacy could not ${noun}: too many requests in a short window (HTTP 429). Retry in a few seconds. Nothing was executed, and no decision was recorded.`;
|
|
5304
|
+
if (status === 404 && action === "check")
|
|
5305
|
+
return "No anchor exists for that hash on this account. Either the artifact was never anchored, or its contents changed since it was \u2014 if you expected a match, the file is not the one that was anchored.";
|
|
5306
|
+
if (status === 422 || type === "mandate_violation") {
|
|
5307
|
+
const first = reasons?.[0]?.key;
|
|
5308
|
+
const why = first ? ` ${explainRule(first)}` : "";
|
|
5309
|
+
return `Fidacy denied this action: it falls outside the mandate in force.${why} Nothing was executed.`;
|
|
5310
|
+
}
|
|
5311
|
+
if (status >= 500)
|
|
5312
|
+
return `Fidacy could not ${noun}: the engine returned an error (HTTP ${status}). Nothing was executed, and the call was refused rather than allowed through. Retrying is safe.`;
|
|
5313
|
+
if (status === 400)
|
|
5314
|
+
return `Fidacy could not ${noun}: the request was malformed (HTTP 400${type ? `, ${type}` : ""}). This is a bug in how the call was built, not a policy decision. Nothing was executed.`;
|
|
5315
|
+
return `Fidacy could not ${noun} (HTTP ${status}${type ? `, ${type}` : ""}), so the call was refused rather than allowed through. Nothing was executed.`;
|
|
5316
|
+
}
|
|
5267
5317
|
|
|
5268
5318
|
// ../mcp/src/sentinel.ts
|
|
5269
5319
|
var SENTINEL = {
|
|
@@ -5537,6 +5587,9 @@ async function findArtifacts(sha2562, cfg) {
|
|
|
5537
5587
|
}
|
|
5538
5588
|
|
|
5539
5589
|
// src/index.ts
|
|
5590
|
+
function operatorEngineKey(config) {
|
|
5591
|
+
return (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
|
|
5592
|
+
}
|
|
5540
5593
|
var loaded = false;
|
|
5541
5594
|
function onPluginLoad() {
|
|
5542
5595
|
if (loaded) return;
|
|
@@ -5657,7 +5710,7 @@ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}
|
|
|
5657
5710
|
async execute(_params, config) {
|
|
5658
5711
|
const c = boot();
|
|
5659
5712
|
const mandate = await c.getMandate(subjectOf(config));
|
|
5660
|
-
const hasKey = (config
|
|
5713
|
+
const hasKey = operatorEngineKey(config).length > 0;
|
|
5661
5714
|
const claim = hasKey ? null : claimUrl();
|
|
5662
5715
|
if (claim) {
|
|
5663
5716
|
return {
|
|
@@ -5697,7 +5750,7 @@ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}
|
|
|
5697
5750
|
async execute(params, config) {
|
|
5698
5751
|
const records = await boot().history(5e3);
|
|
5699
5752
|
const summary = summarize(records, params.days ?? 7);
|
|
5700
|
-
const hasKey = (config
|
|
5753
|
+
const hasKey = operatorEngineKey(config).length > 0;
|
|
5701
5754
|
const claim = hasKey ? null : claimUrl();
|
|
5702
5755
|
return {
|
|
5703
5756
|
summary,
|
|
@@ -5776,8 +5829,8 @@ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}
|
|
|
5776
5829
|
async execute(params, config) {
|
|
5777
5830
|
boot();
|
|
5778
5831
|
const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
|
|
5779
|
-
const
|
|
5780
|
-
if (!
|
|
5832
|
+
const engineKey = operatorEngineKey(config);
|
|
5833
|
+
if (!engineKey) {
|
|
5781
5834
|
throw new Error(
|
|
5782
5835
|
`assess_action needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
|
|
5783
5836
|
);
|
|
@@ -5785,7 +5838,7 @@ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}
|
|
|
5785
5838
|
try {
|
|
5786
5839
|
const r = await assessAction(
|
|
5787
5840
|
{ kind: params.kind, mandate: params.mandate, mandateType: params.mandateType, idempotencyKey: params.idempotencyKey, spendingMandate: params.spendingMandate, a2a: params.a2a },
|
|
5788
|
-
{ engineUrl, apiKey }
|
|
5841
|
+
{ engineUrl, apiKey: engineKey }
|
|
5789
5842
|
);
|
|
5790
5843
|
clearHostedLapsed();
|
|
5791
5844
|
return { summary: `${r.decision} (score ${r.score}) signed by ${r.signingKeyId}`, ...r };
|
|
@@ -5795,8 +5848,7 @@ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}
|
|
|
5795
5848
|
markHostedLapsed();
|
|
5796
5849
|
throw new Error(hostedWallCta("verdict", e.billing));
|
|
5797
5850
|
}
|
|
5798
|
-
|
|
5799
|
-
throw new Error(`ASSESS ${e.status}: ${e.type}${reasons}`);
|
|
5851
|
+
throw new Error(explainEngineError("verdict", e.status, e.type, e.rejection_reasons));
|
|
5800
5852
|
}
|
|
5801
5853
|
throw new Error("ASSESS failed: unexpected error");
|
|
5802
5854
|
}
|
|
@@ -5819,8 +5871,8 @@ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}
|
|
|
5819
5871
|
async execute(params, config) {
|
|
5820
5872
|
boot();
|
|
5821
5873
|
const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
|
|
5822
|
-
const
|
|
5823
|
-
if (!
|
|
5874
|
+
const engineKey = operatorEngineKey(config);
|
|
5875
|
+
if (!engineKey) {
|
|
5824
5876
|
throw new Error(
|
|
5825
5877
|
`anchor_artifact needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
|
|
5826
5878
|
);
|
|
@@ -5839,7 +5891,7 @@ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}
|
|
|
5839
5891
|
try {
|
|
5840
5892
|
const r = await anchorArtifact(
|
|
5841
5893
|
{ sha256: hash, kind: params.kind ?? "document", ...params.label ? { label: params.label } : {}, ...params.subject ? { subject: params.subject } : {} },
|
|
5842
|
-
{ engineUrl, apiKey }
|
|
5894
|
+
{ engineUrl, apiKey: engineKey }
|
|
5843
5895
|
);
|
|
5844
5896
|
return {
|
|
5845
5897
|
summary: `ANCHORED ${r.kind} \xB7 sha256 ${hash.slice(0, 16)}\u2026 \xB7 audit seq ${r.audit.seq} \xB7 Bitcoin checkpoint: ${r.anchor.status}. The file never left this machine.`,
|
|
@@ -5851,7 +5903,7 @@ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}
|
|
|
5851
5903
|
markHostedLapsed();
|
|
5852
5904
|
throw new Error(hostedWallCta("anchor", e.billing));
|
|
5853
5905
|
}
|
|
5854
|
-
throw new Error(
|
|
5906
|
+
throw new Error(explainEngineError("anchor", e.status, e.type));
|
|
5855
5907
|
}
|
|
5856
5908
|
throw new Error("ANCHOR failed: unexpected error");
|
|
5857
5909
|
}
|
|
@@ -5869,8 +5921,8 @@ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}
|
|
|
5869
5921
|
async execute(params, config) {
|
|
5870
5922
|
boot();
|
|
5871
5923
|
const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
|
|
5872
|
-
const
|
|
5873
|
-
if (!
|
|
5924
|
+
const engineKey = operatorEngineKey(config);
|
|
5925
|
+
if (!engineKey) {
|
|
5874
5926
|
throw new Error(
|
|
5875
5927
|
`check_artifact needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
|
|
5876
5928
|
);
|
|
@@ -5887,7 +5939,7 @@ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}
|
|
|
5887
5939
|
}
|
|
5888
5940
|
}
|
|
5889
5941
|
try {
|
|
5890
|
-
const r = await findArtifacts(hash, { engineUrl, apiKey });
|
|
5942
|
+
const r = await findArtifacts(hash, { engineUrl, apiKey: engineKey });
|
|
5891
5943
|
if (!r.artifacts.length) {
|
|
5892
5944
|
return {
|
|
5893
5945
|
summary: `NOT FOUND \xB7 sha256 ${hash.slice(0, 16)}\u2026 has no anchored record in this account. If you expected a match, the file changed since anchoring.`,
|
|
@@ -5902,7 +5954,7 @@ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}
|
|
|
5902
5954
|
...r
|
|
5903
5955
|
};
|
|
5904
5956
|
} catch (e) {
|
|
5905
|
-
if (e instanceof AssessError) throw new Error(
|
|
5957
|
+
if (e instanceof AssessError) throw new Error(explainEngineError("check", e.status, e.type));
|
|
5906
5958
|
throw new Error("CHECK failed: unexpected error");
|
|
5907
5959
|
}
|
|
5908
5960
|
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Fidacy AI Agent Firewall",
|
|
4
4
|
"description": "A signed, independently-verifiable verdict on every money-moving agent action. Blocks wrong/lookalike payee, over-cap, and duplicate-invoice fraud before money moves. Non-custodial, local-first, deny-by-default.",
|
|
5
5
|
"icon": "https://fidacy.com/logo.png",
|
|
6
|
-
"version": "0.4.
|
|
6
|
+
"version": "0.4.1",
|
|
7
7
|
"contracts": {
|
|
8
8
|
"tools": [
|
|
9
9
|
"request_payment",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fidacy/openclaw-plugin",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Fidacy payment firewall as a native OpenClaw plugin: signed, verifiable verdicts on every money-moving agent action, in-process (no MCP subprocess).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Fidacy (ZeepCode Group Technology LLC) <hello@fidacy.com> (https://fidacy.com)",
|
|
@@ -47,20 +47,21 @@
|
|
|
47
47
|
"npmSpec": "@fidacy/openclaw-plugin"
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "node scripts/bundle.mjs",
|
|
52
|
+
"typecheck": "tsc --noEmit",
|
|
53
|
+
"test": "npm run build && node --test test/*.test.mjs"
|
|
54
|
+
},
|
|
50
55
|
"engines": {
|
|
51
56
|
"node": ">=20"
|
|
52
57
|
},
|
|
53
58
|
"devDependencies": {
|
|
59
|
+
"@fidacy/firewall": "workspace:*",
|
|
60
|
+
"@fidacy/mcp": "workspace:*",
|
|
54
61
|
"@types/node": "^22.10.0",
|
|
55
62
|
"esbuild": "^0.24.0",
|
|
56
63
|
"openclaw": "2026.6.11",
|
|
57
64
|
"typebox": "1.1.39",
|
|
58
|
-
"typescript": "^5.6.3"
|
|
59
|
-
"@fidacy/firewall": "0.1.1",
|
|
60
|
-
"@fidacy/mcp": "0.5.1"
|
|
61
|
-
},
|
|
62
|
-
"scripts": {
|
|
63
|
-
"build": "node scripts/bundle.mjs",
|
|
64
|
-
"typecheck": "tsc --noEmit"
|
|
65
|
+
"typescript": "^5.6.3"
|
|
65
66
|
}
|
|
66
|
-
}
|
|
67
|
+
}
|