@fidacy/openclaw-plugin 0.1.16 → 0.1.18
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 +228 -218
- package/openclaw.plugin.json +2 -2
- 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.18" : "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";
|
|
@@ -4856,9 +4856,9 @@ function makeCore() {
|
|
|
4856
4856
|
|
|
4857
4857
|
// ../mcp/src/upgrade.ts
|
|
4858
4858
|
function upgradeUrl() {
|
|
4859
|
-
const base = (process.env.
|
|
4859
|
+
const base = (process.env.FIDACY_WEB_URL ?? "https://fidacy.com").replace(/\/$/, "");
|
|
4860
4860
|
const anon = readConfig()?.anon_id ?? "";
|
|
4861
|
-
return `${base}/
|
|
4861
|
+
return anon ? `${base}/claim?ref=${encodeURIComponent(anon)}` : `${base}/claim`;
|
|
4862
4862
|
}
|
|
4863
4863
|
function requestUpgrade() {
|
|
4864
4864
|
recordUpgradeIntent();
|
|
@@ -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.18" : "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");
|
|
@@ -4940,6 +4940,9 @@ function decisionNudge(status, violatedRule, upgradeToolName) {
|
|
|
4940
4940
|
if (status === "DENY" && nudgeOnce("first_deny")) {
|
|
4941
4941
|
return `Fidacy just blocked this for you. For your operator: ${claimPath}.`;
|
|
4942
4942
|
}
|
|
4943
|
+
if (status === "ALLOW" && nudgeOnce("first_allow")) {
|
|
4944
|
+
return `Fidacy authorized this and signed the decision. For your operator: ${claimPath}.`;
|
|
4945
|
+
}
|
|
4943
4946
|
if (count === 25 && nudgeOnce("proof_teaser")) {
|
|
4944
4947
|
return `Your local chain now holds ${count} decisions only you can read. Anchor them and anyone can verify at fidacy.com/verify, no account needed on their side. For your operator: ${claimPath}.`;
|
|
4945
4948
|
}
|
|
@@ -5135,16 +5138,20 @@ async function findArtifacts(sha2562, cfg) {
|
|
|
5135
5138
|
}
|
|
5136
5139
|
|
|
5137
5140
|
// src/index.ts
|
|
5141
|
+
var loaded = false;
|
|
5142
|
+
function onPluginLoad() {
|
|
5143
|
+
if (loaded) return;
|
|
5144
|
+
loaded = true;
|
|
5145
|
+
setTelemetryShell("openclaw-plugin");
|
|
5146
|
+
const state = ensureState();
|
|
5147
|
+
if (state.firstRun) recordInstall();
|
|
5148
|
+
recordAgentActive();
|
|
5149
|
+
void autoProvision();
|
|
5150
|
+
}
|
|
5138
5151
|
var core;
|
|
5139
5152
|
function boot() {
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
const state = ensureState();
|
|
5143
|
-
core = makeCore();
|
|
5144
|
-
if (state.firstRun) recordInstall();
|
|
5145
|
-
recordAgentActive();
|
|
5146
|
-
void autoProvision();
|
|
5147
|
-
}
|
|
5153
|
+
onPluginLoad();
|
|
5154
|
+
if (!core) core = makeCore();
|
|
5148
5155
|
return core;
|
|
5149
5156
|
}
|
|
5150
5157
|
function subjectOf(config) {
|
|
@@ -5175,234 +5182,237 @@ var index_default = defineToolPlugin({
|
|
|
5175
5182
|
},
|
|
5176
5183
|
{ additionalProperties: false }
|
|
5177
5184
|
),
|
|
5178
|
-
tools: (tool) =>
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
typebox_exports.String({
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5185
|
+
tools: (tool) => {
|
|
5186
|
+
onPluginLoad();
|
|
5187
|
+
return [
|
|
5188
|
+
// ACTION FIREWALL. The agent calls this INSTEAD of any raw payment tool.
|
|
5189
|
+
// ALLOW returns a short-lived Ed25519 grant the executor requires; DENY
|
|
5190
|
+
// returns no grant: the action is dead on arrival.
|
|
5191
|
+
tool({
|
|
5192
|
+
name: "request_payment",
|
|
5193
|
+
label: "Request Payment Authorization",
|
|
5194
|
+
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.",
|
|
5195
|
+
parameters: typebox_exports.Object({
|
|
5196
|
+
payee: typebox_exports.String({ description: "Payee identifier" }),
|
|
5197
|
+
amount: typebox_exports.Number({ minimum: 0, description: "Amount in the mandate currency (must be positive)" }),
|
|
5198
|
+
currency: typebox_exports.String({ minLength: 3, maxLength: 3, description: "ISO 4217 currency code" }),
|
|
5199
|
+
purpose: typebox_exports.String({ description: "Human-readable purpose" }),
|
|
5200
|
+
category: typebox_exports.String({ description: "Purpose category (must be allowed by the mandate)" }),
|
|
5201
|
+
idempotencyKey: typebox_exports.String({ description: "Caller-supplied idempotency key" }),
|
|
5202
|
+
invoiceRef: typebox_exports.Optional(
|
|
5203
|
+
typebox_exports.String({
|
|
5204
|
+
description: "Optional invoice identity. When set, Fidacy enforces one payment per invoice: a second request for the same invoiceRef is DENIED, at any amount."
|
|
5205
|
+
})
|
|
5206
|
+
)
|
|
5207
|
+
}),
|
|
5208
|
+
async execute(params, config) {
|
|
5209
|
+
const d = await boot().decide(params, subjectOf(config));
|
|
5210
|
+
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:
|
|
5202
5211
|
${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.` : ""}`;
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
}
|
|
5207
|
-
}),
|
|
5208
|
-
// Read-only: surface the guardrails so the agent/human can see the envelope
|
|
5209
|
-
// and pin Fidacy's public key for grant verification.
|
|
5210
|
-
tool({
|
|
5211
|
-
name: "verify_mandate",
|
|
5212
|
-
label: "Verify Active Mandate",
|
|
5213
|
-
description: "Return the active mandate envelope (caps, allowed payees/categories, window, revocation) and Fidacy's Ed25519 public key for grant verification.",
|
|
5214
|
-
parameters: typebox_exports.Object({}),
|
|
5215
|
-
async execute(_params, config) {
|
|
5216
|
-
const c = boot();
|
|
5217
|
-
const mandate = await c.getMandate(subjectOf(config));
|
|
5218
|
-
const hasKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim().length > 0;
|
|
5219
|
-
const claim = hasKey ? null : claimUrl();
|
|
5220
|
-
if (claim) {
|
|
5221
|
-
return {
|
|
5222
|
-
mandate,
|
|
5223
|
-
fidacyPublicKey: c.publicKey(),
|
|
5224
|
-
message: `For your operator: free account + free API key, and this install's history migrates: ${claim}`
|
|
5225
|
-
};
|
|
5212
|
+
const nudge = decisionNudge(d.status, d.violatedRule, "fidacy_upgrade");
|
|
5213
|
+
const message = nudge ? `${base} ${nudge}` : base;
|
|
5214
|
+
return { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule, message };
|
|
5226
5215
|
}
|
|
5227
|
-
return { mandate, fidacyPublicKey: c.publicKey() };
|
|
5228
|
-
}
|
|
5229
|
-
}),
|
|
5230
|
-
// Read-only: portable, hash-chained proof for a decision. The non-repudiation
|
|
5231
|
-
// artifact for audit, reinsurance, or dispute.
|
|
5232
|
-
tool({
|
|
5233
|
-
name: "get_audit_proof",
|
|
5234
|
-
label: "Get Audit Proof",
|
|
5235
|
-
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.",
|
|
5236
|
-
parameters: typebox_exports.Object({
|
|
5237
|
-
decisionId: typebox_exports.String({ description: "Decision id returned by request_payment" })
|
|
5238
|
-
}),
|
|
5239
|
-
async execute(params) {
|
|
5240
|
-
const proof = await boot().getProof(params.decisionId);
|
|
5241
|
-
if (!proof) throw new Error(`No proof found for ${params.decisionId}`);
|
|
5242
|
-
return proof;
|
|
5243
|
-
}
|
|
5244
|
-
}),
|
|
5245
|
-
// VERDICT LAYER (advisory). Calls the LIVE Fidacy engine POST /v1/assess and
|
|
5246
|
-
// returns the SIGNED trust verdict (riskPayloadJws + signingKeyId), verifiable
|
|
5247
|
-
// by anyone via @fidacy/verify against the engine JWKS. Moves no money.
|
|
5248
|
-
// Registration never requires a key: only CALLING it without one errors.
|
|
5249
|
-
tool({
|
|
5250
|
-
name: "assess_action",
|
|
5251
|
-
label: "Assess Action (Signed Trust Verdict)",
|
|
5252
|
-
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.",
|
|
5253
|
-
parameters: typebox_exports.Object({
|
|
5254
|
-
kind: typebox_exports.Optional(typebox_exports.Union(ASSESS_KINDS.map((k) => typebox_exports.Literal(k)))),
|
|
5255
|
-
mandate: typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown(), {
|
|
5256
|
-
description: "The action/mandate object for this kind"
|
|
5257
|
-
}),
|
|
5258
|
-
mandateType: typebox_exports.Optional(typebox_exports.String()),
|
|
5259
|
-
idempotencyKey: typebox_exports.Optional(typebox_exports.String()),
|
|
5260
|
-
spendingMandate: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown())),
|
|
5261
|
-
a2a: typebox_exports.Optional(typebox_exports.Object({ task_id: typebox_exports.String() }))
|
|
5262
5216
|
}),
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
const
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
throw new Error(`ASSESS ${e.status}: ${e.type}${reasons}`);
|
|
5217
|
+
// Read-only: surface the guardrails so the agent/human can see the envelope
|
|
5218
|
+
// and pin Fidacy's public key for grant verification.
|
|
5219
|
+
tool({
|
|
5220
|
+
name: "verify_mandate",
|
|
5221
|
+
label: "Verify Active Mandate",
|
|
5222
|
+
description: "Return the active mandate envelope (caps, allowed payees/categories, window, revocation) and Fidacy's Ed25519 public key for grant verification.",
|
|
5223
|
+
parameters: typebox_exports.Object({}),
|
|
5224
|
+
async execute(_params, config) {
|
|
5225
|
+
const c = boot();
|
|
5226
|
+
const mandate = await c.getMandate(subjectOf(config));
|
|
5227
|
+
const hasKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim().length > 0;
|
|
5228
|
+
const claim = hasKey ? null : claimUrl();
|
|
5229
|
+
if (claim) {
|
|
5230
|
+
return {
|
|
5231
|
+
mandate,
|
|
5232
|
+
fidacyPublicKey: c.publicKey(),
|
|
5233
|
+
message: `For your operator: free account + free API key, and this install's history migrates: ${claim}`
|
|
5234
|
+
};
|
|
5282
5235
|
}
|
|
5283
|
-
|
|
5236
|
+
return { mandate, fidacyPublicKey: c.publicKey() };
|
|
5284
5237
|
}
|
|
5285
|
-
}
|
|
5286
|
-
}),
|
|
5287
|
-
// ARTIFACT ANCHORING. Hash-only integrity/existence proof: the file is hashed
|
|
5288
|
-
// locally (streaming) and never uploaded; the SHA-256 joins the same
|
|
5289
|
-
// Bitcoin-checkpointed audit chain as the verdicts. Receipt = offline JWS.
|
|
5290
|
-
tool({
|
|
5291
|
-
name: "anchor_artifact",
|
|
5292
|
-
label: "Anchor Artifact (Bitcoin-anchored integrity proof)",
|
|
5293
|
-
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).",
|
|
5294
|
-
parameters: typebox_exports.Object({
|
|
5295
|
-
path: typebox_exports.Optional(typebox_exports.String()),
|
|
5296
|
-
sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" })),
|
|
5297
|
-
kind: typebox_exports.Optional(typebox_exports.Union(ARTIFACT_KINDS.map((k) => typebox_exports.Literal(k)))),
|
|
5298
|
-
label: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 })),
|
|
5299
|
-
subject: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 }))
|
|
5300
5238
|
}),
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
)
|
|
5309
|
-
}
|
|
5310
|
-
|
|
5311
|
-
|
|
5239
|
+
// Read-only: portable, hash-chained proof for a decision. The non-repudiation
|
|
5240
|
+
// artifact for audit, reinsurance, or dispute.
|
|
5241
|
+
tool({
|
|
5242
|
+
name: "get_audit_proof",
|
|
5243
|
+
label: "Get Audit Proof",
|
|
5244
|
+
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.",
|
|
5245
|
+
parameters: typebox_exports.Object({
|
|
5246
|
+
decisionId: typebox_exports.String({ description: "Decision id returned by request_payment" })
|
|
5247
|
+
}),
|
|
5248
|
+
async execute(params) {
|
|
5249
|
+
const proof = await boot().getProof(params.decisionId);
|
|
5250
|
+
if (!proof) throw new Error(`No proof found for ${params.decisionId}`);
|
|
5251
|
+
return proof;
|
|
5312
5252
|
}
|
|
5313
|
-
|
|
5314
|
-
|
|
5253
|
+
}),
|
|
5254
|
+
// VERDICT LAYER (advisory). Calls the LIVE Fidacy engine POST /v1/assess and
|
|
5255
|
+
// returns the SIGNED trust verdict (riskPayloadJws + signingKeyId), verifiable
|
|
5256
|
+
// by anyone via @fidacy/verify against the engine JWKS. Moves no money.
|
|
5257
|
+
// Registration never requires a key: only CALLING it without one errors.
|
|
5258
|
+
tool({
|
|
5259
|
+
name: "assess_action",
|
|
5260
|
+
label: "Assess Action (Signed Trust Verdict)",
|
|
5261
|
+
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.",
|
|
5262
|
+
parameters: typebox_exports.Object({
|
|
5263
|
+
kind: typebox_exports.Optional(typebox_exports.Union(ASSESS_KINDS.map((k) => typebox_exports.Literal(k)))),
|
|
5264
|
+
mandate: typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown(), {
|
|
5265
|
+
description: "The action/mandate object for this kind"
|
|
5266
|
+
}),
|
|
5267
|
+
mandateType: typebox_exports.Optional(typebox_exports.String()),
|
|
5268
|
+
idempotencyKey: typebox_exports.Optional(typebox_exports.String()),
|
|
5269
|
+
spendingMandate: typebox_exports.Optional(typebox_exports.Record(typebox_exports.String(), typebox_exports.Unknown())),
|
|
5270
|
+
a2a: typebox_exports.Optional(typebox_exports.Object({ task_id: typebox_exports.String() }))
|
|
5271
|
+
}),
|
|
5272
|
+
async execute(params, config) {
|
|
5273
|
+
boot();
|
|
5274
|
+
const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
|
|
5275
|
+
const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
|
|
5276
|
+
if (!apiKey) {
|
|
5277
|
+
throw new Error(
|
|
5278
|
+
`assess_action needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
|
|
5279
|
+
);
|
|
5280
|
+
}
|
|
5315
5281
|
try {
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5282
|
+
const r = await assessAction(
|
|
5283
|
+
{ kind: params.kind, mandate: params.mandate, mandateType: params.mandateType, idempotencyKey: params.idempotencyKey, spendingMandate: params.spendingMandate, a2a: params.a2a },
|
|
5284
|
+
{ engineUrl, apiKey }
|
|
5285
|
+
);
|
|
5286
|
+
return { summary: `${r.decision} (score ${r.score}) signed by ${r.signingKeyId}`, ...r };
|
|
5287
|
+
} catch (e) {
|
|
5288
|
+
if (e instanceof AssessError) {
|
|
5289
|
+
const reasons = e.rejection_reasons?.length ? " (" + e.rejection_reasons.map((x) => x.key).join(",") + ")" : "";
|
|
5290
|
+
throw new Error(`ASSESS ${e.status}: ${e.type}${reasons}`);
|
|
5291
|
+
}
|
|
5292
|
+
throw new Error("ASSESS failed: unexpected error");
|
|
5319
5293
|
}
|
|
5320
5294
|
}
|
|
5321
|
-
try {
|
|
5322
|
-
const r = await anchorArtifact(
|
|
5323
|
-
{ sha256: hash, kind: params.kind ?? "document", ...params.label ? { label: params.label } : {}, ...params.subject ? { subject: params.subject } : {} },
|
|
5324
|
-
{ engineUrl, apiKey }
|
|
5325
|
-
);
|
|
5326
|
-
return {
|
|
5327
|
-
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.`,
|
|
5328
|
-
...r
|
|
5329
|
-
};
|
|
5330
|
-
} catch (e) {
|
|
5331
|
-
if (e instanceof AssessError) throw new Error(`ANCHOR ${e.status}: ${e.type}`);
|
|
5332
|
-
throw new Error("ANCHOR failed: unexpected error");
|
|
5333
|
-
}
|
|
5334
|
-
}
|
|
5335
|
-
}),
|
|
5336
|
-
// Verification half: was this exact content anchored, and did it reach Bitcoin?
|
|
5337
|
-
tool({
|
|
5338
|
-
name: "check_artifact",
|
|
5339
|
-
label: "Check Artifact (was this hash anchored?)",
|
|
5340
|
-
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.",
|
|
5341
|
-
parameters: typebox_exports.Object({
|
|
5342
|
-
path: typebox_exports.Optional(typebox_exports.String()),
|
|
5343
|
-
sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" }))
|
|
5344
5295
|
}),
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5296
|
+
// ARTIFACT ANCHORING. Hash-only integrity/existence proof: the file is hashed
|
|
5297
|
+
// locally (streaming) and never uploaded; the SHA-256 joins the same
|
|
5298
|
+
// Bitcoin-checkpointed audit chain as the verdicts. Receipt = offline JWS.
|
|
5299
|
+
tool({
|
|
5300
|
+
name: "anchor_artifact",
|
|
5301
|
+
label: "Anchor Artifact (Bitcoin-anchored integrity proof)",
|
|
5302
|
+
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).",
|
|
5303
|
+
parameters: typebox_exports.Object({
|
|
5304
|
+
path: typebox_exports.Optional(typebox_exports.String()),
|
|
5305
|
+
sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" })),
|
|
5306
|
+
kind: typebox_exports.Optional(typebox_exports.Union(ARTIFACT_KINDS.map((k) => typebox_exports.Literal(k)))),
|
|
5307
|
+
label: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 })),
|
|
5308
|
+
subject: typebox_exports.Optional(typebox_exports.String({ maxLength: 120 }))
|
|
5309
|
+
}),
|
|
5310
|
+
async execute(params, config) {
|
|
5311
|
+
boot();
|
|
5312
|
+
const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
|
|
5313
|
+
const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
|
|
5314
|
+
if (!apiKey) {
|
|
5315
|
+
throw new Error(
|
|
5316
|
+
`anchor_artifact needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
|
|
5317
|
+
);
|
|
5318
|
+
}
|
|
5319
|
+
if (!params.path && !params.sha256) {
|
|
5320
|
+
throw new Error("Give a file `path` (hashed locally) or a precomputed `sha256`.");
|
|
5321
|
+
}
|
|
5322
|
+
let hash = params.sha256 ?? "";
|
|
5323
|
+
if (!hash && params.path) {
|
|
5324
|
+
try {
|
|
5325
|
+
hash = await hashFile(params.path);
|
|
5326
|
+
} catch {
|
|
5327
|
+
throw new Error("Could not read that file locally (check the path and permissions). Nothing was sent anywhere.");
|
|
5328
|
+
}
|
|
5329
|
+
}
|
|
5359
5330
|
try {
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5331
|
+
const r = await anchorArtifact(
|
|
5332
|
+
{ sha256: hash, kind: params.kind ?? "document", ...params.label ? { label: params.label } : {}, ...params.subject ? { subject: params.subject } : {} },
|
|
5333
|
+
{ engineUrl, apiKey }
|
|
5334
|
+
);
|
|
5335
|
+
return {
|
|
5336
|
+
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.`,
|
|
5337
|
+
...r
|
|
5338
|
+
};
|
|
5339
|
+
} catch (e) {
|
|
5340
|
+
if (e instanceof AssessError) throw new Error(`ANCHOR ${e.status}: ${e.type}`);
|
|
5341
|
+
throw new Error("ANCHOR failed: unexpected error");
|
|
5363
5342
|
}
|
|
5364
5343
|
}
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5344
|
+
}),
|
|
5345
|
+
// Verification half: was this exact content anchored, and did it reach Bitcoin?
|
|
5346
|
+
tool({
|
|
5347
|
+
name: "check_artifact",
|
|
5348
|
+
label: "Check Artifact (was this hash anchored?)",
|
|
5349
|
+
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.",
|
|
5350
|
+
parameters: typebox_exports.Object({
|
|
5351
|
+
path: typebox_exports.Optional(typebox_exports.String()),
|
|
5352
|
+
sha256: typebox_exports.Optional(typebox_exports.String({ pattern: "^[0-9a-f]{64}$" }))
|
|
5353
|
+
}),
|
|
5354
|
+
async execute(params, config) {
|
|
5355
|
+
boot();
|
|
5356
|
+
const engineUrl = config.engineUrl ?? process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
|
|
5357
|
+
const apiKey = (config.engineApiKey ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
|
|
5358
|
+
if (!apiKey) {
|
|
5359
|
+
throw new Error(
|
|
5360
|
+
`check_artifact needs an engine key \u2014 ${noKeyCta()}. (Or set plugins.entries.fidacy.config.engineApiKey.)`
|
|
5361
|
+
);
|
|
5362
|
+
}
|
|
5363
|
+
if (!params.path && !params.sha256) {
|
|
5364
|
+
throw new Error("Give a file `path` (hashed locally) or a `sha256`.");
|
|
5365
|
+
}
|
|
5366
|
+
let hash = params.sha256 ?? "";
|
|
5367
|
+
if (!hash && params.path) {
|
|
5368
|
+
try {
|
|
5369
|
+
hash = await hashFile(params.path);
|
|
5370
|
+
} catch {
|
|
5371
|
+
throw new Error("Could not read that file locally (check the path and permissions). Nothing was sent anywhere.");
|
|
5372
|
+
}
|
|
5373
|
+
}
|
|
5374
|
+
try {
|
|
5375
|
+
const r = await findArtifacts(hash, { engineUrl, apiKey });
|
|
5376
|
+
if (!r.artifacts.length) {
|
|
5377
|
+
return {
|
|
5378
|
+
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.`,
|
|
5379
|
+
sha256: hash,
|
|
5380
|
+
artifacts: []
|
|
5381
|
+
};
|
|
5382
|
+
}
|
|
5383
|
+
const first = r.artifacts[0];
|
|
5368
5384
|
return {
|
|
5369
|
-
summary: `
|
|
5385
|
+
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.`,
|
|
5370
5386
|
sha256: hash,
|
|
5371
|
-
|
|
5387
|
+
...r
|
|
5372
5388
|
};
|
|
5389
|
+
} catch (e) {
|
|
5390
|
+
if (e instanceof AssessError) throw new Error(`CHECK ${e.status}: ${e.type}`);
|
|
5391
|
+
throw new Error("CHECK failed: unexpected error");
|
|
5373
5392
|
}
|
|
5374
|
-
const first = r.artifacts[0];
|
|
5375
|
-
return {
|
|
5376
|
-
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.`,
|
|
5377
|
-
sha256: hash,
|
|
5378
|
-
...r
|
|
5379
|
-
};
|
|
5380
|
-
} catch (e) {
|
|
5381
|
-
if (e instanceof AssessError) throw new Error(`CHECK ${e.status}: ${e.type}`);
|
|
5382
|
-
throw new Error("CHECK failed: unexpected error");
|
|
5383
5393
|
}
|
|
5384
|
-
}
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
message: `To upgrade, open:
|
|
5394
|
+
}),
|
|
5395
|
+
// Upgrade funnel. Records the intent signal and returns the real-account link;
|
|
5396
|
+
// the anon_id travels in the URL so the engine binds past usage to the tenant.
|
|
5397
|
+
tool({
|
|
5398
|
+
name: "fidacy_upgrade",
|
|
5399
|
+
label: "Upgrade to a Fidacy account",
|
|
5400
|
+
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.",
|
|
5401
|
+
parameters: typebox_exports.Object({}),
|
|
5402
|
+
async execute() {
|
|
5403
|
+
boot();
|
|
5404
|
+
const { url } = requestUpgrade();
|
|
5405
|
+
return {
|
|
5406
|
+
upgradeUrl: url,
|
|
5407
|
+
message: `To upgrade, open:
|
|
5399
5408
|
${url}
|
|
5400
5409
|
|
|
5401
5410
|
Your local protection keeps working meanwhile; your usage history migrates to the new account on completion.`
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5411
|
+
};
|
|
5412
|
+
}
|
|
5413
|
+
})
|
|
5414
|
+
];
|
|
5415
|
+
}
|
|
5406
5416
|
});
|
|
5407
5417
|
export {
|
|
5408
5418
|
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.18",
|
|
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.1.
|
|
3
|
+
"version": "0.1.18",
|
|
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.26"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "node scripts/bundle.mjs",
|
|
63
|
+
"typecheck": "tsc --noEmit"
|
|
64
64
|
}
|
|
65
|
-
}
|
|
65
|
+
}
|