@fidacy/openclaw-plugin 0.8.0 → 0.10.0
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/CHANGELOG.md +20 -0
- package/README.md +6 -7
- package/dist/index.js +52 -97
- package/openclaw.plugin.json +1 -1
- package/package.json +7 -5
- package/postinstall.cjs +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 6db2c6f: Require an account-owned API key before a connector can evaluate or execute a consequential action. Anonymous installation and local trial activity no longer create a protected-agent claim. Operators activate a workspace in the Fidacy dashboard, configure `FIDACY_ENGINE_API_KEY`, and then receive authenticated live protection signals.
|
|
8
|
+
|
|
9
|
+
## 0.9.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- Fidacy can now ask the 452 people who already installed it whether to turn on full protection, without them ever opening a terminal.
|
|
14
|
+
|
|
15
|
+
The problem was that **2 of 407** MCP installs had ever run `install-hooks`. Without that hook an MCP server only sees calls to its own tools, so deleting a file, reading a secret or reaching a CRM goes nowhere near us. For 99% of the base the product is a payment gate that almost never fires, and there was no way to ask those people anything: they do not open terminals, do not read changelogs, and the guided setup only helps whoever installs next.
|
|
16
|
+
|
|
17
|
+
Two things solve it together. The installed base is **not** stuck in the past — hosts launch the server as `npx -y @fidacy/mcp` with no version pin, so every agent restart already pulls the newest build. And the protocol has elicitation: during a tool call, a server may ask the user for input _through the host_, and they answer in a dialog inside the tool they already use.
|
|
18
|
+
|
|
19
|
+
So on the next payment decision, the operator is asked once, in their own UI, and one click wires it in — existing hooks merged around, backup kept, every path printed.
|
|
20
|
+
|
|
21
|
+
It asks only while processing a call, only if the client declares the capability, only once per install ever, and it never blocks or delays a decision. Where elicitation is not supported it degrades to a line in the tool result, which is still better than the silence it replaces.
|
|
22
|
+
|
|
3
23
|
## 0.8.0
|
|
4
24
|
|
|
5
25
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -129,17 +129,16 @@ Plugin config (`plugins.entries.fidacy.config`):
|
|
|
129
129
|
}
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
`engineApiKey`
|
|
133
|
-
|
|
132
|
+
`engineApiKey` activates signed authority and verdicts (`assess_action`).
|
|
133
|
+
Environment variables (`FIDACY_ENGINE_API_KEY`,
|
|
134
134
|
`FIDACY_ENGINE_URL`, `FIDACY_SUBJECT`) are the fallback. Decisions run locally,
|
|
135
135
|
deny-by-default (deny-unknown-payee + per-tx cap); add trusted payees/caps in
|
|
136
136
|
`~/.fidacy/config.json`.
|
|
137
137
|
|
|
138
|
-
**No key yet
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
`
|
|
142
|
-
`https://fidacy.com/claim?ref=<that id>`.
|
|
138
|
+
**No key yet? YOUR AI AGENT IS UNPROTECTED.** Consequential actions are refused
|
|
139
|
+
with `activation_required`. Get your free key now at
|
|
140
|
+
https://app.fidacy.com/signup, configure `engineApiKey` or
|
|
141
|
+
`FIDACY_ENGINE_API_KEY`, and connect the executor before retrying.
|
|
143
142
|
|
|
144
143
|
Verify any verdict yourself: https://api.fidacy.com/.well-known/jwks.json
|
|
145
144
|
|
package/dist/index.js
CHANGED
|
@@ -4655,7 +4655,7 @@ var FileAuditStore = class {
|
|
|
4655
4655
|
|
|
4656
4656
|
// ../firewall/dist/destination.js
|
|
4657
4657
|
function normalizeKeepingEdges(raw) {
|
|
4658
|
-
return String(raw ?? "").normalize("NFKC").toLowerCase().replace(/[
|
|
4658
|
+
return String(raw ?? "").normalize("NFKC").toLowerCase().replace(/[\u200b-\u200d\u2060\ufeff\u00ad]/g, "").replace(/(?<=[a-z0-9])[_\-.]+(?=[a-z0-9])/g, ".").replace(/\s+/g, " ");
|
|
4659
4659
|
}
|
|
4660
4660
|
|
|
4661
4661
|
// ../firewall/dist/action-classes.js
|
|
@@ -4846,7 +4846,10 @@ function canonical(obj) {
|
|
|
4846
4846
|
return "{" + keys.map((k) => `${JSON.stringify(k)}:${JSON.stringify(obj[k])}`).join(",") + "}";
|
|
4847
4847
|
}
|
|
4848
4848
|
function chainStep(prevHead, a) {
|
|
4849
|
-
|
|
4849
|
+
const folha = { category: a.category, i: a.i, tool: a.tool, ts: a.ts };
|
|
4850
|
+
if (a.target !== void 0) folha.target = a.target;
|
|
4851
|
+
if (a.content !== void 0) folha.content = a.content;
|
|
4852
|
+
return sha2562(`${prevHead}|${sha2562(canonical(folha))}`);
|
|
4850
4853
|
}
|
|
4851
4854
|
function genesisHead() {
|
|
4852
4855
|
return sha2562(GENESIS);
|
|
@@ -4957,6 +4960,10 @@ var EXACT = {
|
|
|
4957
4960
|
// Fidacy's own money tools, so the report counts what we ourselves gated.
|
|
4958
4961
|
request_payment: "money",
|
|
4959
4962
|
assess_action: "money",
|
|
4963
|
+
// O compromisso decisao-sessao (decision-marker.ts). Sem esta entrada ele
|
|
4964
|
+
// classifica como "other" e a acao mais importante da sessao sai do topo do
|
|
4965
|
+
// relatorio, que ordena dinheiro primeiro.
|
|
4966
|
+
fidacy_decision: "money",
|
|
4960
4967
|
// The common OpenClaw / agent-host tool vocabulary.
|
|
4961
4968
|
bash: "shell",
|
|
4962
4969
|
shell: "shell",
|
|
@@ -5027,7 +5034,7 @@ function newLedger(sessionId, now) {
|
|
|
5027
5034
|
truncated: false
|
|
5028
5035
|
};
|
|
5029
5036
|
}
|
|
5030
|
-
function record(ledger, toolName, derivedPaths, failed = false, at = Date.now()) {
|
|
5037
|
+
function record(ledger, toolName, derivedPaths, failed = false, at = Date.now(), target, content) {
|
|
5031
5038
|
const category = classify(toolName, derivedPaths);
|
|
5032
5039
|
let tally = ledger.byCategory.get(category);
|
|
5033
5040
|
if (!tally) {
|
|
@@ -5045,7 +5052,9 @@ function record(ledger, toolName, derivedPaths, failed = false, at = Date.now())
|
|
|
5045
5052
|
i: ledger.total,
|
|
5046
5053
|
category,
|
|
5047
5054
|
tool: toolName,
|
|
5048
|
-
ts: new Date(at).toISOString()
|
|
5055
|
+
ts: new Date(at).toISOString(),
|
|
5056
|
+
...target ? { target } : {},
|
|
5057
|
+
...content ? { content } : {}
|
|
5049
5058
|
};
|
|
5050
5059
|
ledger.head = chainStep(ledger.head, action);
|
|
5051
5060
|
if (ledger.actions.length < ACTION_LOG_CAP) ledger.actions.push(action);
|
|
@@ -5062,12 +5071,13 @@ var LABEL = {
|
|
|
5062
5071
|
message: "sent messages",
|
|
5063
5072
|
other: "other"
|
|
5064
5073
|
};
|
|
5065
|
-
function renderReport(ledger, now, activated, logPath) {
|
|
5074
|
+
function renderReport(ledger, now, activated, logPath, uploaded) {
|
|
5066
5075
|
if (ledger.total < MIN_ACTIONS_FOR_REPORT) return null;
|
|
5067
5076
|
const minutes = Math.max(1, Math.round((now - ledger.startedAt) / 6e4));
|
|
5077
|
+
const destino = uploaded === "sent" ? "copied to your Fidacy account" : uploaded === "failed" ? "kept on this machine \xB7 the copy to your account will retry" : "nothing left this machine";
|
|
5068
5078
|
const lines = [];
|
|
5069
5079
|
lines.push(
|
|
5070
|
-
`[fidacy] SESSION REPORT \xB7 ${ledger.total} agent actions watched over ${minutes} min \xB7
|
|
5080
|
+
`[fidacy] SESSION REPORT \xB7 ${ledger.total} agent actions watched over ${minutes} min \xB7 ${destino}`
|
|
5071
5081
|
);
|
|
5072
5082
|
for (const category of ORDER) {
|
|
5073
5083
|
const tally = ledger.byCategory.get(category);
|
|
@@ -5078,6 +5088,17 @@ function renderReport(ledger, now, activated, logPath) {
|
|
|
5078
5088
|
if (tally.failures > 0) parts.push(`${tally.failures} failed`);
|
|
5079
5089
|
lines.push(`[fidacy] ${LABEL[category].padEnd(20)} ${parts.join(" \xB7 ")}`);
|
|
5080
5090
|
}
|
|
5091
|
+
const comAlvo = ledger.actions.filter((a) => a.target);
|
|
5092
|
+
const destaque = [...comAlvo].sort((x, y) => notavel(y) - notavel(x)).slice(0, MAX_HIGHLIGHTS);
|
|
5093
|
+
if (destaque.length) {
|
|
5094
|
+
lines.push(`[fidacy] what it touched`);
|
|
5095
|
+
for (const a of destaque.sort((x, y) => x.i - y.i)) {
|
|
5096
|
+
const alvo = a.target.length > 96 ? `${a.target.slice(0, 96)}\u2026` : a.target;
|
|
5097
|
+
lines.push(`[fidacy] #${String(a.i).padEnd(3)} ${a.category.padEnd(8)} ${alvo}`);
|
|
5098
|
+
}
|
|
5099
|
+
const resto = comAlvo.length - destaque.length;
|
|
5100
|
+
if (resto > 0) lines.push(`[fidacy] +${resto} more \xB7 full timeline: npx -y @fidacy/mcp log ${ledger.sessionId}`);
|
|
5101
|
+
}
|
|
5081
5102
|
const money2 = ledger.byCategory.get("money");
|
|
5082
5103
|
if (!money2) {
|
|
5083
5104
|
lines.push(`[fidacy] no money-moving action was attempted \xB7 the firewall stayed armed and idle`);
|
|
@@ -5089,7 +5110,15 @@ function renderReport(ledger, now, activated, logPath) {
|
|
|
5089
5110
|
);
|
|
5090
5111
|
return lines.join("\n");
|
|
5091
5112
|
}
|
|
5113
|
+
var CHEIRO_DE_CREDENCIAL = /(^|[/\\.])(\.aws|\.ssh|\.env|\.netrc|\.npmrc|id_rsa|id_ed25519|credentials|secrets?|token|keychain|\.pem|\.p12|kubeconfig)/i;
|
|
5114
|
+
function notavel(a) {
|
|
5115
|
+
if (a.category === "money") return 4;
|
|
5116
|
+
if (a.target && CHEIRO_DE_CREDENCIAL.test(a.target)) return 3;
|
|
5117
|
+
if (a.category === "shell" || a.category === "message") return 2;
|
|
5118
|
+
return 1;
|
|
5119
|
+
}
|
|
5092
5120
|
var MIN_ACTIONS_FOR_REPORT = 5;
|
|
5121
|
+
var MAX_HIGHLIGHTS = 6;
|
|
5093
5122
|
function installObserver(api, emit, isActivated, now = Date.now, anchor) {
|
|
5094
5123
|
if (typeof api?.on !== "function") return;
|
|
5095
5124
|
const ledgers = /* @__PURE__ */ new Map();
|
|
@@ -5178,7 +5207,7 @@ function sessionIdOf(ctx) {
|
|
|
5178
5207
|
import { statSync } from "node:fs";
|
|
5179
5208
|
|
|
5180
5209
|
// ../mcp/src/telemetry.ts
|
|
5181
|
-
var CLIENT_VERSION = true ? "0.
|
|
5210
|
+
var CLIENT_VERSION = true ? "0.10.0" : "dev";
|
|
5182
5211
|
function bandOf(amount) {
|
|
5183
5212
|
if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
|
|
5184
5213
|
if (amount < 10) return "lt10";
|
|
@@ -5198,9 +5227,6 @@ function capRatioOf(amount, perTxMax) {
|
|
|
5198
5227
|
return "gt10x";
|
|
5199
5228
|
}
|
|
5200
5229
|
var currentShell = "mcp";
|
|
5201
|
-
function setTelemetryShell(shell) {
|
|
5202
|
-
currentShell = shell;
|
|
5203
|
-
}
|
|
5204
5230
|
function telemetryEnabled() {
|
|
5205
5231
|
const v = (process.env.FIDACY_DISABLE_TELEMETRY ?? "").trim().toLowerCase();
|
|
5206
5232
|
return !(v === "1" || v === "true" || v === "yes");
|
|
@@ -5257,8 +5283,6 @@ function record2(type, result, band, capRatio, action) {
|
|
|
5257
5283
|
if (typeof flushTimer.unref === "function") flushTimer.unref();
|
|
5258
5284
|
}
|
|
5259
5285
|
}
|
|
5260
|
-
var recordInstall = () => record2("install");
|
|
5261
|
-
var recordAgentActive = () => record2("agent_active");
|
|
5262
5286
|
var recordDecision = (status, violatedRule, amount, perTxMax) => record2("decision", resultOf(status, violatedRule), bandOf(amount), capRatioOf(amount, perTxMax));
|
|
5263
5287
|
var recordUpgradeIntent = () => record2("upgrade_intent");
|
|
5264
5288
|
var recordNudge = (key) => record2("nudge", void 0, void 0, void 0, key);
|
|
@@ -5392,43 +5416,6 @@ function requestUpgrade() {
|
|
|
5392
5416
|
return { url: upgradeUrl(), anonId: anonId2 };
|
|
5393
5417
|
}
|
|
5394
5418
|
|
|
5395
|
-
// ../mcp/src/provision.ts
|
|
5396
|
-
var CLIENT_VERSION2 = true ? "0.7.0" : "dev";
|
|
5397
|
-
function provisionEnabled() {
|
|
5398
|
-
const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
|
|
5399
|
-
return !(v === "1" || v === "true" || v === "yes");
|
|
5400
|
-
}
|
|
5401
|
-
function endpoint2() {
|
|
5402
|
-
const base = (process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com").replace(/\/$/, "");
|
|
5403
|
-
return `${base}/v1/provision`;
|
|
5404
|
-
}
|
|
5405
|
-
async function autoProvision() {
|
|
5406
|
-
if (!provisionEnabled()) return "skipped";
|
|
5407
|
-
const cfg = readConfig();
|
|
5408
|
-
if (!cfg) return "skipped";
|
|
5409
|
-
if (cfg.api_key) return "skipped";
|
|
5410
|
-
try {
|
|
5411
|
-
const res = await fetch(endpoint2(), {
|
|
5412
|
-
method: "POST",
|
|
5413
|
-
headers: { "content-type": "application/json" },
|
|
5414
|
-
body: JSON.stringify({ anon_id: cfg.anon_id, client_version: CLIENT_VERSION2 })
|
|
5415
|
-
});
|
|
5416
|
-
if (res.status === 429) return "rate_limited";
|
|
5417
|
-
if (!res.ok) return "failed";
|
|
5418
|
-
const data = await res.json();
|
|
5419
|
-
if (typeof data.api_key !== "string" || !data.api_key) return "failed";
|
|
5420
|
-
const fresh = readConfig() ?? cfg;
|
|
5421
|
-
writeConfig({
|
|
5422
|
-
...fresh,
|
|
5423
|
-
api_key: data.api_key,
|
|
5424
|
-
tier: data.tier === "paid" ? "paid" : "free"
|
|
5425
|
-
});
|
|
5426
|
-
return "provisioned";
|
|
5427
|
-
} catch {
|
|
5428
|
-
return "failed";
|
|
5429
|
-
}
|
|
5430
|
-
}
|
|
5431
|
-
|
|
5432
5419
|
// ../mcp/src/nudges.ts
|
|
5433
5420
|
function claimUrl() {
|
|
5434
5421
|
const anon = readConfig()?.anon_id;
|
|
@@ -5448,9 +5435,6 @@ function nudgeOnce(key) {
|
|
|
5448
5435
|
return true;
|
|
5449
5436
|
}
|
|
5450
5437
|
var sessionDecisions = 0;
|
|
5451
|
-
function sessionDecisionCount() {
|
|
5452
|
-
return sessionDecisions;
|
|
5453
|
-
}
|
|
5454
5438
|
function bumpDecisionCount() {
|
|
5455
5439
|
sessionDecisions += 1;
|
|
5456
5440
|
const cfg = readConfig();
|
|
@@ -5477,7 +5461,7 @@ function decisionNudge(status, violatedRule, upgradeToolName) {
|
|
|
5477
5461
|
return `Fidacy authorized this and signed the decision. For your operator: ${claimPath}.`;
|
|
5478
5462
|
}
|
|
5479
5463
|
if (count === 15 && nudgeOnce("proof_teaser")) {
|
|
5480
|
-
return `Fidacy has now checked ${count} payments on this agent and stands between it and the wrong
|
|
5464
|
+
return `Fidacy has now checked ${count} payments on this AI agent and stands between it and the wrong payee, the prompt-injected payment, the duplicate and the over-limit charge. Keep the boundary account-owned and the evidence available: ${claimPath}.`;
|
|
5481
5465
|
}
|
|
5482
5466
|
if (count === 10 && nudgeOnce("milestone_10")) {
|
|
5483
5467
|
return `That's 10 actions gated on this install. For your operator: ${claimPath}.`;
|
|
@@ -5489,7 +5473,7 @@ function hostedWallCta(feature, billing) {
|
|
|
5489
5473
|
if (hasEngineKey()) {
|
|
5490
5474
|
const where2 = billing?.billingUrl ?? "https://app.fidacy.com/billing";
|
|
5491
5475
|
const what = billing?.reason === "spending_cap_reached" ? `This account hit the monthly spending cap you set, so ${noun} are paused.` : billing?.detail ?? `This account's included ${noun} for the month are used up.`;
|
|
5492
|
-
return `${what} The local firewall keeps deciding and blocking as normal \u2014 this only pauses ${noun}. For your operator: resolve it at ${where2}. Pay-as-you-go needs no plan
|
|
5476
|
+
return `${what} The local firewall keeps deciding and blocking as normal \u2014 this only pauses ${noun}. For your operator: resolve it at ${where2}. Pay-as-you-go needs no plan: the first 300 verdicts each month are free, then $0.10 per verdict, dropping to $0.06 past 50k and $0.04 past 250k; committed plans price below that curve.`;
|
|
5493
5477
|
}
|
|
5494
5478
|
const url = claimUrl();
|
|
5495
5479
|
const cta = url ? `open ${url} \u2014 free account, this install's history migrates` : `call the upgrade tool \u2014 free account, your history migrates`;
|
|
@@ -5504,40 +5488,23 @@ function noKeyCta() {
|
|
|
5504
5488
|
}
|
|
5505
5489
|
|
|
5506
5490
|
// ../mcp/src/activation.ts
|
|
5507
|
-
var FREE_DECISIONS = 20;
|
|
5508
5491
|
function decisionsUsed() {
|
|
5509
|
-
return
|
|
5510
|
-
}
|
|
5511
|
-
function remainingFree() {
|
|
5512
|
-
return Math.max(0, FREE_DECISIONS - decisionsUsed());
|
|
5513
|
-
}
|
|
5514
|
-
function claimDestination() {
|
|
5515
|
-
return claimUrl() ?? "https://fidacy.com/claim";
|
|
5492
|
+
return readConfig()?.decisions_count ?? 0;
|
|
5516
5493
|
}
|
|
5517
5494
|
function activationGate(keyOverride) {
|
|
5518
5495
|
if (hasEngineKey(keyOverride)) return null;
|
|
5519
|
-
if (decisionsUsed() < FREE_DECISIONS) return null;
|
|
5520
5496
|
recordUpgradeIntent();
|
|
5521
|
-
const url = claimDestination();
|
|
5522
5497
|
return {
|
|
5523
|
-
message:
|
|
5498
|
+
message: 'DENY (activation_required). YOUR AI AGENT IS UNPROTECTED. Fidacy did not evaluate or authorize this consequential action because no account-owned authority is active. Relay this to your operator: "GET YOUR FREE API KEY NOW at https://app.fidacy.com/signup. Set FIDACY_ENGINE_API_KEY where this agent runs, connect the executor, then retry to protect this agent and its sensitive data."'
|
|
5524
5499
|
};
|
|
5525
5500
|
}
|
|
5526
5501
|
function trialCountdownLine(keyOverride) {
|
|
5527
|
-
|
|
5528
|
-
if (!readConfig() && sessionDecisionCount() === 0) return null;
|
|
5529
|
-
const left = remainingFree();
|
|
5530
|
-
if (left > 5) return null;
|
|
5531
|
-
const url = claimDestination();
|
|
5532
|
-
if (left === 0) {
|
|
5533
|
-
return ` [fidacy] That was this install's LAST free anonymous decision \u2014 the next payment will be denied until it is activated. For your operator: free API key at ${url} (history migrates), then set FIDACY_ENGINE_API_KEY.`;
|
|
5534
|
-
}
|
|
5535
|
-
return ` [fidacy] ${left} free anonymous decision${left === 1 ? "" : "s"} left on this install before the firewall requires activation. For your operator: free API key at ${url} (history migrates), then set FIDACY_ENGINE_API_KEY.`;
|
|
5502
|
+
return hasEngineKey(keyOverride) ? null : " [fidacy] YOUR AI AGENT IS UNPROTECTED. Get your free API key now at https://app.fidacy.com/signup and set FIDACY_ENGINE_API_KEY before this agent can enforce a consequential action.";
|
|
5536
5503
|
}
|
|
5537
5504
|
|
|
5538
5505
|
// ../mcp/src/register.ts
|
|
5539
|
-
var
|
|
5540
|
-
function
|
|
5506
|
+
var CLIENT_VERSION2 = true ? "0.10.0" : "dev";
|
|
5507
|
+
function endpoint2() {
|
|
5541
5508
|
const base = (process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com").replace(/\/$/, "");
|
|
5542
5509
|
return `${base}/v1/register`;
|
|
5543
5510
|
}
|
|
@@ -5551,10 +5518,10 @@ async function registerEmail(email, shell = "mcp") {
|
|
|
5551
5518
|
if (!looksLikeEmail(clean)) return "skipped";
|
|
5552
5519
|
if (cfg.registered_email && cfg.registered_email.toLowerCase() === clean.toLowerCase()) return "skipped";
|
|
5553
5520
|
try {
|
|
5554
|
-
const res = await fetch(
|
|
5521
|
+
const res = await fetch(endpoint2(), {
|
|
5555
5522
|
method: "POST",
|
|
5556
5523
|
headers: { "content-type": "application/json" },
|
|
5557
|
-
body: JSON.stringify({ anon_id: cfg.anon_id, email: clean, client_version:
|
|
5524
|
+
body: JSON.stringify({ anon_id: cfg.anon_id, email: clean, client_version: CLIENT_VERSION2, shell })
|
|
5558
5525
|
});
|
|
5559
5526
|
if (res.status === 429) return "rate_limited";
|
|
5560
5527
|
if (!res.ok) return "failed";
|
|
@@ -5590,30 +5557,23 @@ function clearHostedLapsed() {
|
|
|
5590
5557
|
function isHostedLapsed() {
|
|
5591
5558
|
return Boolean(readConfig()?.hosted_lapsed_at);
|
|
5592
5559
|
}
|
|
5593
|
-
function protectionState(
|
|
5560
|
+
function protectionState(_decisionsUsed, keyOverride) {
|
|
5594
5561
|
if (hasEngineKey(keyOverride)) return isHostedLapsed() ? "unproven" : "protected_keyed";
|
|
5595
|
-
|
|
5596
|
-
return FREE_DECISIONS - decisionsUsed2 <= 5 ? "protected_low" : "protected_trial";
|
|
5562
|
+
return "halted";
|
|
5597
5563
|
}
|
|
5598
5564
|
function where() {
|
|
5599
|
-
return
|
|
5565
|
+
return "https://app.fidacy.com/signup";
|
|
5600
5566
|
}
|
|
5601
5567
|
function protectionBadge() {
|
|
5602
5568
|
return `\u{1F6E1} ${BRAND} blocked this payment. Your agent was protected.`;
|
|
5603
5569
|
}
|
|
5604
5570
|
function protectionWarning(decisionsUsed2, keyOverride) {
|
|
5605
5571
|
const state = protectionState(decisionsUsed2, keyOverride);
|
|
5606
|
-
const left = Math.max(0, FREE_DECISIONS - decisionsUsed2);
|
|
5607
5572
|
switch (state) {
|
|
5608
|
-
case "protected_trial":
|
|
5609
|
-
return null;
|
|
5610
|
-
// working and nothing is expiring soon: silence is the respect.
|
|
5611
|
-
case "protected_low":
|
|
5612
|
-
return `\u26A0 ${BRAND}: ${left} free decision${left === 1 ? "" : "s"} left, then this agent's payments STOP DEAD. Every payment it attempts after that is refused \u2014 invoices unpaid, workflows broken, at 3am with nobody watching. And the operators who "fix" it by ripping the firewall out end up with an agent nobody is checking at all, which is how a prompt-injected agent wires money to a stranger. Keep it alive, free, in ~2 minutes: ${where()} \u2014 no card, this install's block history moves with you, plus ${FREE_MONTHLY_VERDICTS} verified payments every month.`;
|
|
5613
5573
|
case "halted":
|
|
5614
|
-
return `\u26D4
|
|
5574
|
+
return `\u26D4 YOUR AI AGENT IS UNPROTECTED. ${BRAND} is not activated, so this consequential action was refused and nothing was executed. GET YOUR FREE API KEY NOW at ${where()} and set FIDACY_ENGINE_API_KEY where the agent runs. Protection begins only after the key is configured and the executor is connected. Protect your agent and its sensitive data before retrying. Free, no card, with ${FREE_MONTHLY_VERDICTS} verified actions every month.`;
|
|
5615
5575
|
case "unproven":
|
|
5616
|
-
return `\u26D4 ${BRAND}: YOUR AGENT IS EXPOSED RIGHT NOW. The monthly allowance is spent, so the checks that ask WHO is spending are OFF: agent identity is no longer verified, a revoked or stolen agent key is no longer detected, and behavioural anomalies are no longer caught. Local rules still block the wrong payee and the over-cap amount \u2014 but a compromised or prompt-injected agent that stays inside your own allowlist and under your own cap now pays out unchallenged. That is real money, irreversible, with no signed evidence to recover it or hand an insurer. Turn the checks back on: https://app.fidacy.com/billing \u2014 pay-as-you-go, no plan,
|
|
5576
|
+
return `\u26D4 ${BRAND}: YOUR AGENT IS EXPOSED RIGHT NOW. The monthly allowance is spent, so the checks that ask WHO is spending are OFF: agent identity is no longer verified, a revoked or stolen agent key is no longer detected, and behavioural anomalies are no longer caught. Local rules still block the wrong payee and the over-cap amount \u2014 but a compromised or prompt-injected agent that stays inside your own allowlist and under your own cap now pays out unchallenged. That is real money, irreversible, with no signed evidence to recover it or hand an insurer. Turn the checks back on: https://app.fidacy.com/billing \u2014 pay-as-you-go, no plan, 300 free every month, then $0.10 per payment checked. One prevented payment pays for years of it.`;
|
|
5617
5577
|
case "protected_keyed":
|
|
5618
5578
|
return null;
|
|
5619
5579
|
}
|
|
@@ -5757,7 +5717,7 @@ function explainRule(rule, payee) {
|
|
|
5757
5717
|
if (rule.startsWith("non_positive_amount"))
|
|
5758
5718
|
return "The amount was zero or negative, which is not a payment anyone can authorize. Check what produced that value before retrying.";
|
|
5759
5719
|
if (rule.startsWith("activation_required"))
|
|
5760
|
-
return "The
|
|
5720
|
+
return "The AI agent had no account-owned authority. Fidacy refused the action. Get a free API key and connect the executor before retrying.";
|
|
5761
5721
|
return `The mandate rule "${rule}" was violated.`;
|
|
5762
5722
|
}
|
|
5763
5723
|
function explainEngineError(action, status, type, reasons) {
|
|
@@ -6061,12 +6021,7 @@ var loaded = false;
|
|
|
6061
6021
|
function onPluginLoad() {
|
|
6062
6022
|
if (loaded) return;
|
|
6063
6023
|
loaded = true;
|
|
6064
|
-
|
|
6065
|
-
const state = ensureState();
|
|
6066
|
-
if (state.firstRun) recordInstall();
|
|
6067
|
-
recordAgentActive();
|
|
6068
|
-
void flush();
|
|
6069
|
-
void autoProvision();
|
|
6024
|
+
ensureState();
|
|
6070
6025
|
}
|
|
6071
6026
|
var core;
|
|
6072
6027
|
function boot() {
|
package/openclaw.plugin.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Fidacy AI Agent Firewall",
|
|
4
4
|
"description": "Firewall for AI agents. Watches every action the agent takes and ends each session in a digest anyone can recompute; commands, paths and arguments never leave your machine, only counts and a hash. Blocks wrong/lookalike payee, over-cap and duplicate-invoice fraud before money moves. Non-custodial, local-first, deny-by-default. Anonymous telemetry (counts only) can be disabled with FIDACY_DISABLE_TELEMETRY=1.",
|
|
5
5
|
"icon": "https://fidacy.com/logo.png",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.10.0",
|
|
7
7
|
"contracts": {
|
|
8
8
|
"tools": [
|
|
9
9
|
"request_payment",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fidacy/openclaw-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "The AI Agent Firewall for OpenClaw. Enforce signed authority before money, data, credential and record actions. Free account-owned API key required.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Fidacy (ZeepCode Group Technology LLC) <hello@fidacy.com> (https://fidacy.com)",
|
|
7
7
|
"homepage": "https://fidacy.com",
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
"openclaw.plugin.json",
|
|
28
28
|
"README.md",
|
|
29
29
|
"LICENSE",
|
|
30
|
-
"CHANGELOG.md"
|
|
30
|
+
"CHANGELOG.md",
|
|
31
|
+
"postinstall.cjs"
|
|
31
32
|
],
|
|
32
33
|
"publishConfig": {
|
|
33
34
|
"access": "public"
|
|
@@ -57,11 +58,12 @@
|
|
|
57
58
|
"typebox": "1.1.39",
|
|
58
59
|
"typescript": "^5.6.3",
|
|
59
60
|
"@fidacy/firewall": "0.1.1",
|
|
60
|
-
"@fidacy/mcp": "0.
|
|
61
|
+
"@fidacy/mcp": "0.19.0"
|
|
61
62
|
},
|
|
62
63
|
"scripts": {
|
|
63
64
|
"build": "node scripts/bundle.mjs",
|
|
64
65
|
"typecheck": "tsc --noEmit",
|
|
65
|
-
"test": "npm run build && node --test test/*.test.mjs"
|
|
66
|
+
"test": "npm run build && node --test test/*.test.mjs",
|
|
67
|
+
"postinstall": "node postinstall.cjs || exit 0"
|
|
66
68
|
}
|
|
67
69
|
}
|
package/postinstall.cjs
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
if (!process.env.FIDACY_SILENT_INSTALL) {
|
|
2
|
+
console.error([
|
|
3
|
+
"",
|
|
4
|
+
" ⛔ YOUR AI AGENT IS UNPROTECTED.",
|
|
5
|
+
" GET YOUR FREE API KEY NOW TO ACTIVATE THE FIDACY BOUNDARY FOR CONSEQUENTIAL ACTIONS AND SENSITIVE DATA:",
|
|
6
|
+
" https://app.fidacy.com/signup",
|
|
7
|
+
" Set FIDACY_ENGINE_API_KEY where the agent runs. Protection begins only after the key is configured and the executor is connected.",
|
|
8
|
+
"",
|
|
9
|
+
].join("\n"));
|
|
10
|
+
}
|