@fidacy/openclaw-plugin 0.1.15 → 0.1.17
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/index.js +229 -209
- package/openclaw.plugin.json +3 -3
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -4690,7 +4690,7 @@ function resolveMandateRules(cfg) {
|
|
|
4690
4690
|
}
|
|
4691
4691
|
|
|
4692
4692
|
// ../mcp/src/telemetry.ts
|
|
4693
|
-
var CLIENT_VERSION = true ? "0.1.
|
|
4693
|
+
var CLIENT_VERSION = true ? "0.1.17" : "dev";
|
|
4694
4694
|
function bandOf(amount) {
|
|
4695
4695
|
if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
|
|
4696
4696
|
if (amount < 10) return "lt10";
|
|
@@ -4867,7 +4867,7 @@ function requestUpgrade() {
|
|
|
4867
4867
|
}
|
|
4868
4868
|
|
|
4869
4869
|
// ../mcp/src/provision.ts
|
|
4870
|
-
var CLIENT_VERSION2 = true ? "0.1.
|
|
4870
|
+
var CLIENT_VERSION2 = true ? "0.1.17" : "dev";
|
|
4871
4871
|
function provisionEnabled() {
|
|
4872
4872
|
const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
|
|
4873
4873
|
return !(v === "1" || v === "true" || v === "yes");
|
|
@@ -4933,7 +4933,7 @@ function bumpDecisionCount() {
|
|
|
4933
4933
|
function decisionNudge(status, violatedRule, upgradeToolName) {
|
|
4934
4934
|
const count = bumpDecisionCount();
|
|
4935
4935
|
const url = claimUrl();
|
|
4936
|
-
const claimPath = url ? `
|
|
4936
|
+
const claimPath = url ? `free account + free API key at ${url} (this install's history migrates)` : `call the ${upgradeToolName} tool (free account + free API key, this history migrates)`;
|
|
4937
4937
|
if (status === "DENY" && violatedRule?.startsWith("duplicate_invoice") && nudgeOnce("bec_catch")) {
|
|
4938
4938
|
return `That was a duplicate-invoice (BEC) catch, the #1 payment fraud. For your operator: ${claimPath}. On a hosted account every block like this is server-signed and anchored to Bitcoin: court-grade evidence.`;
|
|
4939
4939
|
}
|
|
@@ -4948,6 +4948,10 @@ function decisionNudge(status, violatedRule, upgradeToolName) {
|
|
|
4948
4948
|
}
|
|
4949
4949
|
return null;
|
|
4950
4950
|
}
|
|
4951
|
+
function noKeyCta() {
|
|
4952
|
+
const url = claimUrl();
|
|
4953
|
+
return url ? `free in ~1 min for your operator: open ${url} (this install's history migrates), or set FIDACY_ENGINE_API_KEY if you already have one` : `free in ~1 min: call the upgrade tool (your history migrates), or set FIDACY_ENGINE_API_KEY if you already have one`;
|
|
4954
|
+
}
|
|
4951
4955
|
|
|
4952
4956
|
// ../mcp/src/assess.ts
|
|
4953
4957
|
var AssessError = class extends Error {
|
|
@@ -5131,16 +5135,20 @@ async function findArtifacts(sha2562, cfg) {
|
|
|
5131
5135
|
}
|
|
5132
5136
|
|
|
5133
5137
|
// src/index.ts
|
|
5138
|
+
var loaded = false;
|
|
5139
|
+
function onPluginLoad() {
|
|
5140
|
+
if (loaded) return;
|
|
5141
|
+
loaded = true;
|
|
5142
|
+
setTelemetryShell("openclaw-plugin");
|
|
5143
|
+
const state = ensureState();
|
|
5144
|
+
if (state.firstRun) recordInstall();
|
|
5145
|
+
recordAgentActive();
|
|
5146
|
+
void autoProvision();
|
|
5147
|
+
}
|
|
5134
5148
|
var core;
|
|
5135
5149
|
function boot() {
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
const state = ensureState();
|
|
5139
|
-
core = makeCore();
|
|
5140
|
-
if (state.firstRun) recordInstall();
|
|
5141
|
-
recordAgentActive();
|
|
5142
|
-
void autoProvision();
|
|
5143
|
-
}
|
|
5150
|
+
onPluginLoad();
|
|
5151
|
+
if (!core) core = makeCore();
|
|
5144
5152
|
return core;
|
|
5145
5153
|
}
|
|
5146
5154
|
function subjectOf(config) {
|
|
@@ -5171,225 +5179,237 @@ var index_default = defineToolPlugin({
|
|
|
5171
5179
|
},
|
|
5172
5180
|
{ additionalProperties: false }
|
|
5173
5181
|
),
|
|
5174
|
-
tools: (tool) =>
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
typebox_exports.String({
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
const d = await boot().decide(params, subjectOf(config));
|
|
5197
|
-
const base = d.status === "ALLOW" ? `ALLOW (decision ${d.decisionId})${params.invoiceRef ? ` for invoice ${params.invoiceRef}` : ""}. To settle, call the executor with the SAME payee, amount, currency, and idempotencyKey, and set "grant" to EXACTLY this signed value:
|
|
5198
|
-
${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}. No grant issued, this payment cannot proceed. The denial itself is recorded in the tamper-evident, hash-chained audit: call get_audit_proof with decisionId ${d.decisionId} for the proof of what was blocked.${d.violatedRule?.startsWith("payee_not_in_allowlist") ? ` If the user trusts this payee, add "${params.payee}" to mandate.payees in ~/.fidacy/config.json and retry: the firewall picks the change up on the next call, no restart needed.` : ""}`;
|
|
5199
|
-
const nudge = decisionNudge(d.status, d.violatedRule, "fidacy_upgrade");
|
|
5200
|
-
const message = nudge ? `${base} ${nudge}` : base;
|
|
5201
|
-
return { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule, message };
|
|
5202
|
-
}
|
|
5203
|
-
}),
|
|
5204
|
-
// Read-only: surface the guardrails so the agent/human can see the envelope
|
|
5205
|
-
// and pin Fidacy's public key for grant verification.
|
|
5206
|
-
tool({
|
|
5207
|
-
name: "verify_mandate",
|
|
5208
|
-
label: "Verify Active Mandate",
|
|
5209
|
-
description: "Return the active mandate envelope (caps, allowed payees/categories, window, revocation) and Fidacy's Ed25519 public key for grant verification.",
|
|
5210
|
-
parameters: typebox_exports.Object({}),
|
|
5211
|
-
async execute(_params, config) {
|
|
5212
|
-
const c = boot();
|
|
5213
|
-
const mandate = await c.getMandate(subjectOf(config));
|
|
5214
|
-
return { mandate, fidacyPublicKey: c.publicKey() };
|
|
5215
|
-
}
|
|
5216
|
-
}),
|
|
5217
|
-
// Read-only: portable, hash-chained proof for a decision. The non-repudiation
|
|
5218
|
-
// artifact for audit, reinsurance, or dispute.
|
|
5219
|
-
tool({
|
|
5220
|
-
name: "get_audit_proof",
|
|
5221
|
-
label: "Get Audit Proof",
|
|
5222
|
-
description: "Return the tamper-evident, hash-chained proof for a decision id, including whether the chain is intact and the public key it verifies against.",
|
|
5223
|
-
parameters: typebox_exports.Object({
|
|
5224
|
-
decisionId: typebox_exports.String({ description: "Decision id returned by request_payment" })
|
|
5225
|
-
}),
|
|
5226
|
-
async execute(params) {
|
|
5227
|
-
const proof = await boot().getProof(params.decisionId);
|
|
5228
|
-
if (!proof) throw new Error(`No proof found for ${params.decisionId}`);
|
|
5229
|
-
return proof;
|
|
5230
|
-
}
|
|
5231
|
-
}),
|
|
5232
|
-
// VERDICT LAYER (advisory). Calls the LIVE Fidacy engine POST /v1/assess and
|
|
5233
|
-
// returns the SIGNED trust verdict (riskPayloadJws + signingKeyId), verifiable
|
|
5234
|
-
// by anyone via @fidacy/verify against the engine JWKS. Moves no money.
|
|
5235
|
-
// Registration never requires a key: only CALLING it without one errors.
|
|
5236
|
-
tool({
|
|
5237
|
-
name: "assess_action",
|
|
5238
|
-
label: "Assess Action (Signed Trust Verdict)",
|
|
5239
|
-
description: "Return a SIGNED Fidacy trust verdict from the live engine (default https://api.fidacy.com) for a proposed action. The signed proof is `riskPayloadJws` + `signingKeyId`, verifiable by anyone via @fidacy/verify against the engine JWKS at /.well-known/jwks.json. `kind` is one of ap2_payment, message_send, voice_call, custom, claim_document; `mandate` is the action/mandate object for that kind. This is the verdict (advisory) layer and moves no money; it complements the payment-firewall tools (request_payment et al.) in the same install.",
|
|
5240
|
-
parameters: typebox_exports.Object({
|
|
5241
|
-
kind: typebox_exports.Optional(typebox_exports.Union(ASSESS_KINDS.map((k) => typebox_exports.Literal(k)))),
|
|
5242
|
-
mandate: typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown(), {
|
|
5243
|
-
description: "The action/mandate object for this kind"
|
|
5182
|
+
tools: (tool) => {
|
|
5183
|
+
onPluginLoad();
|
|
5184
|
+
return [
|
|
5185
|
+
// ACTION FIREWALL. The agent calls this INSTEAD of any raw payment tool.
|
|
5186
|
+
// ALLOW returns a short-lived Ed25519 grant the executor requires; DENY
|
|
5187
|
+
// returns no grant: the action is dead on arrival.
|
|
5188
|
+
tool({
|
|
5189
|
+
name: "request_payment",
|
|
5190
|
+
label: "Request Payment Authorization",
|
|
5191
|
+
description: "Authorize a payment action against the active Fidacy mandate. Returns an ALLOW with a signed grant, or a DENY with the violated rule. The downstream executor MUST require the grant. Call this before any payment; never pay without it.",
|
|
5192
|
+
parameters: typebox_exports.Object({
|
|
5193
|
+
payee: typebox_exports.String({ description: "Payee identifier" }),
|
|
5194
|
+
amount: typebox_exports.Number({ minimum: 0, description: "Amount in the mandate currency (must be positive)" }),
|
|
5195
|
+
currency: typebox_exports.String({ minLength: 3, maxLength: 3, description: "ISO 4217 currency code" }),
|
|
5196
|
+
purpose: typebox_exports.String({ description: "Human-readable purpose" }),
|
|
5197
|
+
category: typebox_exports.String({ description: "Purpose category (must be allowed by the mandate)" }),
|
|
5198
|
+
idempotencyKey: typebox_exports.String({ description: "Caller-supplied idempotency key" }),
|
|
5199
|
+
invoiceRef: typebox_exports.Optional(
|
|
5200
|
+
typebox_exports.String({
|
|
5201
|
+
description: "Optional invoice identity. When set, Fidacy enforces one payment per invoice: a second request for the same invoiceRef is DENIED, at any amount."
|
|
5202
|
+
})
|
|
5203
|
+
)
|
|
5244
5204
|
}),
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
|
|
5253
|
-
const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
|
|
5254
|
-
if (!apiKey) {
|
|
5255
|
-
throw new Error(
|
|
5256
|
-
"assess_action requires an engine API key (an fky_live_/fky_test_ key with assess:write). Set plugins.entries.fidacy.config.engineApiKey or FIDACY_ENGINE_API_KEY to enable signed verdicts."
|
|
5257
|
-
);
|
|
5205
|
+
async execute(params, config) {
|
|
5206
|
+
const d = await boot().decide(params, subjectOf(config));
|
|
5207
|
+
const base = d.status === "ALLOW" ? `ALLOW (decision ${d.decisionId})${params.invoiceRef ? ` for invoice ${params.invoiceRef}` : ""}. To settle, call the executor with the SAME payee, amount, currency, and idempotencyKey, and set "grant" to EXACTLY this signed value:
|
|
5208
|
+
${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}. No grant issued, this payment cannot proceed. The denial itself is recorded in the tamper-evident, hash-chained audit: call get_audit_proof with decisionId ${d.decisionId} for the proof of what was blocked.${d.violatedRule?.startsWith("payee_not_in_allowlist") ? ` If the user trusts this payee, add "${params.payee}" to mandate.payees in ~/.fidacy/config.json and retry: the firewall picks the change up on the next call, no restart needed.` : ""}`;
|
|
5209
|
+
const nudge = decisionNudge(d.status, d.violatedRule, "fidacy_upgrade");
|
|
5210
|
+
const message = nudge ? `${base} ${nudge}` : base;
|
|
5211
|
+
return { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule, message };
|
|
5258
5212
|
}
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5213
|
+
}),
|
|
5214
|
+
// Read-only: surface the guardrails so the agent/human can see the envelope
|
|
5215
|
+
// and pin Fidacy's public key for grant verification.
|
|
5216
|
+
tool({
|
|
5217
|
+
name: "verify_mandate",
|
|
5218
|
+
label: "Verify Active Mandate",
|
|
5219
|
+
description: "Return the active mandate envelope (caps, allowed payees/categories, window, revocation) and Fidacy's Ed25519 public key for grant verification.",
|
|
5220
|
+
parameters: typebox_exports.Object({}),
|
|
5221
|
+
async execute(_params, config) {
|
|
5222
|
+
const c = boot();
|
|
5223
|
+
const mandate = await c.getMandate(subjectOf(config));
|
|
5224
|
+
const hasKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim().length > 0;
|
|
5225
|
+
const claim = hasKey ? null : claimUrl();
|
|
5226
|
+
if (claim) {
|
|
5227
|
+
return {
|
|
5228
|
+
mandate,
|
|
5229
|
+
fidacyPublicKey: c.publicKey(),
|
|
5230
|
+
message: `For your operator: free account + free API key, and this install's history migrates: ${claim}`
|
|
5231
|
+
};
|
|
5269
5232
|
}
|
|
5270
|
-
|
|
5233
|
+
return { mandate, fidacyPublicKey: c.publicKey() };
|
|
5271
5234
|
}
|
|
5272
|
-
}
|
|
5273
|
-
}),
|
|
5274
|
-
// ARTIFACT ANCHORING. Hash-only integrity/existence proof: the file is hashed
|
|
5275
|
-
// locally (streaming) and never uploaded; the SHA-256 joins the same
|
|
5276
|
-
// Bitcoin-checkpointed audit chain as the verdicts. Receipt = offline JWS.
|
|
5277
|
-
tool({
|
|
5278
|
-
name: "anchor_artifact",
|
|
5279
|
-
label: "Anchor Artifact (Bitcoin-anchored integrity proof)",
|
|
5280
|
-
description: "Prove an artifact existed exactly as-is at this moment, and make any later tampering detectable. Give a file `path` (hashed locally with SHA-256 \u2014 the file itself is NEVER uploaded) or a precomputed `sha256`. The hash is registered on Fidacy's tamper-evident audit chain, which is checkpointed to the Bitcoin blockchain, and you get a signed receipt (JWS) verifiable offline against the engine JWKS. Use it for contracts, invoices, medical prescriptions, insurance claims, images, audio, video. `kind` defaults to document; optional `label` is a short reference (no PII).",
|
|
5281
|
-
parameters: typebox_exports.Object({
|
|
5282
|
-
path: typebox_exports.Optional(typebox_exports.String()),
|
|
5283
|
-
sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" })),
|
|
5284
|
-
kind: typebox_exports.Optional(typebox_exports.Union(ARTIFACT_KINDS.map((k) => typebox_exports.Literal(k)))),
|
|
5285
|
-
label: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 })),
|
|
5286
|
-
subject: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 }))
|
|
5287
5235
|
}),
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
)
|
|
5296
|
-
}
|
|
5297
|
-
|
|
5298
|
-
|
|
5236
|
+
// Read-only: portable, hash-chained proof for a decision. The non-repudiation
|
|
5237
|
+
// artifact for audit, reinsurance, or dispute.
|
|
5238
|
+
tool({
|
|
5239
|
+
name: "get_audit_proof",
|
|
5240
|
+
label: "Get Audit Proof",
|
|
5241
|
+
description: "Return the tamper-evident, hash-chained proof for a decision id, including whether the chain is intact and the public key it verifies against.",
|
|
5242
|
+
parameters: typebox_exports.Object({
|
|
5243
|
+
decisionId: typebox_exports.String({ description: "Decision id returned by request_payment" })
|
|
5244
|
+
}),
|
|
5245
|
+
async execute(params) {
|
|
5246
|
+
const proof = await boot().getProof(params.decisionId);
|
|
5247
|
+
if (!proof) throw new Error(`No proof found for ${params.decisionId}`);
|
|
5248
|
+
return proof;
|
|
5299
5249
|
}
|
|
5300
|
-
|
|
5301
|
-
|
|
5250
|
+
}),
|
|
5251
|
+
// VERDICT LAYER (advisory). Calls the LIVE Fidacy engine POST /v1/assess and
|
|
5252
|
+
// returns the SIGNED trust verdict (riskPayloadJws + signingKeyId), verifiable
|
|
5253
|
+
// by anyone via @fidacy/verify against the engine JWKS. Moves no money.
|
|
5254
|
+
// Registration never requires a key: only CALLING it without one errors.
|
|
5255
|
+
tool({
|
|
5256
|
+
name: "assess_action",
|
|
5257
|
+
label: "Assess Action (Signed Trust Verdict)",
|
|
5258
|
+
description: "Return a SIGNED Fidacy trust verdict from the live engine (default https://api.fidacy.com) for a proposed action. The signed proof is `riskPayloadJws` + `signingKeyId`, verifiable by anyone via @fidacy/verify against the engine JWKS at /.well-known/jwks.json. `kind` is one of ap2_payment, message_send, voice_call, custom, claim_document; `mandate` is the action/mandate object for that kind. This is the verdict (advisory) layer and moves no money; it complements the payment-firewall tools (request_payment et al.) in the same install.",
|
|
5259
|
+
parameters: typebox_exports.Object({
|
|
5260
|
+
kind: typebox_exports.Optional(typebox_exports.Union(ASSESS_KINDS.map((k) => typebox_exports.Literal(k)))),
|
|
5261
|
+
mandate: typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown(), {
|
|
5262
|
+
description: "The action/mandate object for this kind"
|
|
5263
|
+
}),
|
|
5264
|
+
mandateType: typebox_exports.Optional(typebox_exports.String()),
|
|
5265
|
+
idempotencyKey: typebox_exports.Optional(typebox_exports.String()),
|
|
5266
|
+
spendingMandate: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown())),
|
|
5267
|
+
a2a: typebox_exports.Optional(typebox_exports.Object({ task_id: typebox_exports.String() }))
|
|
5268
|
+
}),
|
|
5269
|
+
async execute(params, config) {
|
|
5270
|
+
boot();
|
|
5271
|
+
const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
|
|
5272
|
+
const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
|
|
5273
|
+
if (!apiKey) {
|
|
5274
|
+
throw new Error(
|
|
5275
|
+
`assess_action needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
|
|
5276
|
+
);
|
|
5277
|
+
}
|
|
5302
5278
|
try {
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5279
|
+
const r = await assessAction(
|
|
5280
|
+
{ kind: params.kind, mandate: params.mandate, mandateType: params.mandateType, idempotencyKey: params.idempotencyKey, spendingMandate: params.spendingMandate, a2a: params.a2a },
|
|
5281
|
+
{ engineUrl, apiKey }
|
|
5282
|
+
);
|
|
5283
|
+
return { summary: `${r.decision} (score ${r.score}) signed by ${r.signingKeyId}`, ...r };
|
|
5284
|
+
} catch (e) {
|
|
5285
|
+
if (e instanceof AssessError) {
|
|
5286
|
+
const reasons = e.rejection_reasons?.length ? " (" + e.rejection_reasons.map((x) => x.key).join(",") + ")" : "";
|
|
5287
|
+
throw new Error(`ASSESS ${e.status}: ${e.type}${reasons}`);
|
|
5288
|
+
}
|
|
5289
|
+
throw new Error("ASSESS failed: unexpected error");
|
|
5306
5290
|
}
|
|
5307
5291
|
}
|
|
5308
|
-
try {
|
|
5309
|
-
const r = await anchorArtifact(
|
|
5310
|
-
{ sha256: hash, kind: params.kind ?? "document", ...params.label ? { label: params.label } : {}, ...params.subject ? { subject: params.subject } : {} },
|
|
5311
|
-
{ engineUrl, apiKey }
|
|
5312
|
-
);
|
|
5313
|
-
return {
|
|
5314
|
-
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.`,
|
|
5315
|
-
...r
|
|
5316
|
-
};
|
|
5317
|
-
} catch (e) {
|
|
5318
|
-
if (e instanceof AssessError) throw new Error(`ANCHOR ${e.status}: ${e.type}`);
|
|
5319
|
-
throw new Error("ANCHOR failed: unexpected error");
|
|
5320
|
-
}
|
|
5321
|
-
}
|
|
5322
|
-
}),
|
|
5323
|
-
// Verification half: was this exact content anchored, and did it reach Bitcoin?
|
|
5324
|
-
tool({
|
|
5325
|
-
name: "check_artifact",
|
|
5326
|
-
label: "Check Artifact (was this hash anchored?)",
|
|
5327
|
-
description: "Check whether an artifact was anchored by this account and the state of its Bitcoin checkpoint. Give a file `path` (hashed locally, never uploaded) or a `sha256`. If the current hash of a file does NOT match an anchored record you expected, the file changed since anchoring \u2014 that is the tampering signal.",
|
|
5328
|
-
parameters: typebox_exports.Object({
|
|
5329
|
-
path: typebox_exports.Optional(typebox_exports.String()),
|
|
5330
|
-
sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" }))
|
|
5331
5292
|
}),
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5293
|
+
// ARTIFACT ANCHORING. Hash-only integrity/existence proof: the file is hashed
|
|
5294
|
+
// locally (streaming) and never uploaded; the SHA-256 joins the same
|
|
5295
|
+
// Bitcoin-checkpointed audit chain as the verdicts. Receipt = offline JWS.
|
|
5296
|
+
tool({
|
|
5297
|
+
name: "anchor_artifact",
|
|
5298
|
+
label: "Anchor Artifact (Bitcoin-anchored integrity proof)",
|
|
5299
|
+
description: "Prove an artifact existed exactly as-is at this moment, and make any later tampering detectable. Give a file `path` (hashed locally with SHA-256 \u2014 the file itself is NEVER uploaded) or a precomputed `sha256`. The hash is registered on Fidacy's tamper-evident audit chain, which is checkpointed to the Bitcoin blockchain, and you get a signed receipt (JWS) verifiable offline against the engine JWKS. Use it for contracts, invoices, medical prescriptions, insurance claims, images, audio, video. `kind` defaults to document; optional `label` is a short reference (no PII).",
|
|
5300
|
+
parameters: typebox_exports.Object({
|
|
5301
|
+
path: typebox_exports.Optional(typebox_exports.String()),
|
|
5302
|
+
sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" })),
|
|
5303
|
+
kind: typebox_exports.Optional(typebox_exports.Union(ARTIFACT_KINDS.map((k) => typebox_exports.Literal(k)))),
|
|
5304
|
+
label: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 })),
|
|
5305
|
+
subject: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 }))
|
|
5306
|
+
}),
|
|
5307
|
+
async execute(params, config) {
|
|
5308
|
+
boot();
|
|
5309
|
+
const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
|
|
5310
|
+
const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
|
|
5311
|
+
if (!apiKey) {
|
|
5312
|
+
throw new Error(
|
|
5313
|
+
`anchor_artifact needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
|
|
5314
|
+
);
|
|
5315
|
+
}
|
|
5316
|
+
if (!params.path && !params.sha256) {
|
|
5317
|
+
throw new Error("Give a file `path` (hashed locally) or a precomputed `sha256`.");
|
|
5318
|
+
}
|
|
5319
|
+
let hash = params.sha256 ?? "";
|
|
5320
|
+
if (!hash && params.path) {
|
|
5321
|
+
try {
|
|
5322
|
+
hash = await hashFile(params.path);
|
|
5323
|
+
} catch {
|
|
5324
|
+
throw new Error("Could not read that file locally (check the path and permissions). Nothing was sent anywhere.");
|
|
5325
|
+
}
|
|
5326
|
+
}
|
|
5346
5327
|
try {
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5328
|
+
const r = await anchorArtifact(
|
|
5329
|
+
{ sha256: hash, kind: params.kind ?? "document", ...params.label ? { label: params.label } : {}, ...params.subject ? { subject: params.subject } : {} },
|
|
5330
|
+
{ engineUrl, apiKey }
|
|
5331
|
+
);
|
|
5332
|
+
return {
|
|
5333
|
+
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.`,
|
|
5334
|
+
...r
|
|
5335
|
+
};
|
|
5336
|
+
} catch (e) {
|
|
5337
|
+
if (e instanceof AssessError) throw new Error(`ANCHOR ${e.status}: ${e.type}`);
|
|
5338
|
+
throw new Error("ANCHOR failed: unexpected error");
|
|
5350
5339
|
}
|
|
5351
5340
|
}
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5341
|
+
}),
|
|
5342
|
+
// Verification half: was this exact content anchored, and did it reach Bitcoin?
|
|
5343
|
+
tool({
|
|
5344
|
+
name: "check_artifact",
|
|
5345
|
+
label: "Check Artifact (was this hash anchored?)",
|
|
5346
|
+
description: "Check whether an artifact was anchored by this account and the state of its Bitcoin checkpoint. Give a file `path` (hashed locally, never uploaded) or a `sha256`. If the current hash of a file does NOT match an anchored record you expected, the file changed since anchoring \u2014 that is the tampering signal.",
|
|
5347
|
+
parameters: typebox_exports.Object({
|
|
5348
|
+
path: typebox_exports.Optional(typebox_exports.String()),
|
|
5349
|
+
sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" }))
|
|
5350
|
+
}),
|
|
5351
|
+
async execute(params, config) {
|
|
5352
|
+
boot();
|
|
5353
|
+
const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
|
|
5354
|
+
const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
|
|
5355
|
+
if (!apiKey) {
|
|
5356
|
+
throw new Error(
|
|
5357
|
+
`check_artifact needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
|
|
5358
|
+
);
|
|
5359
|
+
}
|
|
5360
|
+
if (!params.path && !params.sha256) {
|
|
5361
|
+
throw new Error("Give a file `path` (hashed locally) or a `sha256`.");
|
|
5362
|
+
}
|
|
5363
|
+
let hash = params.sha256 ?? "";
|
|
5364
|
+
if (!hash && params.path) {
|
|
5365
|
+
try {
|
|
5366
|
+
hash = await hashFile(params.path);
|
|
5367
|
+
} catch {
|
|
5368
|
+
throw new Error("Could not read that file locally (check the path and permissions). Nothing was sent anywhere.");
|
|
5369
|
+
}
|
|
5370
|
+
}
|
|
5371
|
+
try {
|
|
5372
|
+
const r = await findArtifacts(hash, { engineUrl, apiKey });
|
|
5373
|
+
if (!r.artifacts.length) {
|
|
5374
|
+
return {
|
|
5375
|
+
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.`,
|
|
5376
|
+
sha256: hash,
|
|
5377
|
+
artifacts: []
|
|
5378
|
+
};
|
|
5379
|
+
}
|
|
5380
|
+
const first = r.artifacts[0];
|
|
5355
5381
|
return {
|
|
5356
|
-
summary: `
|
|
5382
|
+
summary: `FOUND ${r.artifacts.length} record(s) \xB7 newest: ${String(first.kind)} anchored ${String(first.createdAt)} (audit seq ${String(first.auditSeq)}). Content matches the anchored hash byte for byte.`,
|
|
5357
5383
|
sha256: hash,
|
|
5358
|
-
|
|
5384
|
+
...r
|
|
5359
5385
|
};
|
|
5386
|
+
} catch (e) {
|
|
5387
|
+
if (e instanceof AssessError) throw new Error(`CHECK ${e.status}: ${e.type}`);
|
|
5388
|
+
throw new Error("CHECK failed: unexpected error");
|
|
5360
5389
|
}
|
|
5361
|
-
const first = r.artifacts[0];
|
|
5362
|
-
return {
|
|
5363
|
-
summary: `FOUND ${r.artifacts.length} record(s) \xB7 newest: ${String(first.kind)} anchored ${String(first.createdAt)} (audit seq ${String(first.auditSeq)}). Content matches the anchored hash byte for byte.`,
|
|
5364
|
-
sha256: hash,
|
|
5365
|
-
...r
|
|
5366
|
-
};
|
|
5367
|
-
} catch (e) {
|
|
5368
|
-
if (e instanceof AssessError) throw new Error(`CHECK ${e.status}: ${e.type}`);
|
|
5369
|
-
throw new Error("CHECK failed: unexpected error");
|
|
5370
5390
|
}
|
|
5371
|
-
}
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
message: `To upgrade, open:
|
|
5391
|
+
}),
|
|
5392
|
+
// Upgrade funnel. Records the intent signal and returns the real-account link;
|
|
5393
|
+
// the anon_id travels in the URL so the engine binds past usage to the tenant.
|
|
5394
|
+
tool({
|
|
5395
|
+
name: "fidacy_upgrade",
|
|
5396
|
+
label: "Upgrade to a Fidacy account",
|
|
5397
|
+
description: "Start upgrading this local install to a real Fidacy account (server-backed signed verdicts, anchored proof, higher volume). Returns a link to open; your anonymous usage is preserved and migrated to the new account.",
|
|
5398
|
+
parameters: typebox_exports.Object({}),
|
|
5399
|
+
async execute() {
|
|
5400
|
+
boot();
|
|
5401
|
+
const { url } = requestUpgrade();
|
|
5402
|
+
return {
|
|
5403
|
+
upgradeUrl: url,
|
|
5404
|
+
message: `To upgrade, open:
|
|
5386
5405
|
${url}
|
|
5387
5406
|
|
|
5388
5407
|
Your local protection keeps working meanwhile; your usage history migrates to the new account on completion.`
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5408
|
+
};
|
|
5409
|
+
}
|
|
5410
|
+
})
|
|
5411
|
+
];
|
|
5412
|
+
}
|
|
5393
5413
|
});
|
|
5394
5414
|
export {
|
|
5395
5415
|
index_default as default
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "fidacy",
|
|
3
|
-
"name": "Fidacy
|
|
3
|
+
"name": "Fidacy — Payment 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.1.
|
|
6
|
+
"version": "0.1.17",
|
|
7
7
|
"contracts": {
|
|
8
8
|
"tools": [
|
|
9
9
|
"request_payment",
|
|
@@ -51,4 +51,4 @@
|
|
|
51
51
|
"advanced": true
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
}
|
|
54
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fidacy/openclaw-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
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
|
"homepage": "https://fidacy.com",
|
|
@@ -46,20 +46,20 @@
|
|
|
46
46
|
"npmSpec": "@fidacy/openclaw-plugin"
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
|
-
"scripts": {
|
|
50
|
-
"build": "node scripts/bundle.mjs",
|
|
51
|
-
"typecheck": "tsc --noEmit"
|
|
52
|
-
},
|
|
53
49
|
"engines": {
|
|
54
50
|
"node": ">=20"
|
|
55
51
|
},
|
|
56
52
|
"devDependencies": {
|
|
57
|
-
"@fidacy/firewall": "workspace:*",
|
|
58
|
-
"@fidacy/mcp": "workspace:*",
|
|
59
53
|
"@types/node": "^22.10.0",
|
|
60
54
|
"esbuild": "^0.24.0",
|
|
61
55
|
"openclaw": "2026.6.11",
|
|
62
56
|
"typebox": "1.1.39",
|
|
63
|
-
"typescript": "^5.6.3"
|
|
57
|
+
"typescript": "^5.6.3",
|
|
58
|
+
"@fidacy/firewall": "0.1.1",
|
|
59
|
+
"@fidacy/mcp": "0.1.25"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "node scripts/bundle.mjs",
|
|
63
|
+
"typecheck": "tsc --noEmit"
|
|
64
64
|
}
|
|
65
|
-
}
|
|
65
|
+
}
|