@fidacy/mcp 0.1.9 → 0.1.11
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 +21 -0
- package/dist/core.js +10 -4
- package/dist/index.js +77 -11
- package/dist/lib.js +148 -5
- package/package.json +9 -10
package/CHANGELOG.md
CHANGED
|
@@ -74,3 +74,24 @@ semantic versioning.
|
|
|
74
74
|
- One install delivers both the signed verdict (assess_action) and the
|
|
75
75
|
non-custodial payment firewall (request_payment), with the same signed,
|
|
76
76
|
publicly verifiable proof as the SDK.
|
|
77
|
+
|
|
78
|
+
## 0.1.10 — 2026-07-02
|
|
79
|
+
|
|
80
|
+
- Telemetry: `shell` channel attribution (mcp | openclaw-plugin | crabtrap | sdk)
|
|
81
|
+
and `client_version` injected from package.json at build time (0.1.5 had shipped
|
|
82
|
+
stale through 0.1.9 in telemetry AND provision).
|
|
83
|
+
- DENY responses now point to the `upgrade` tool (free account, signed anchored
|
|
84
|
+
proof of every block).
|
|
85
|
+
- Onboarding: first-run `~/.fidacy/config.json` now writes the mandate template
|
|
86
|
+
explicitly (payees/categories/currency/caps) — adding a trusted payee is a
|
|
87
|
+
visible one-line edit; friendlier first-boot message (no more "dev mode" scare).
|
|
88
|
+
|
|
89
|
+
## 0.1.11 — 2026-07-02
|
|
90
|
+
|
|
91
|
+
- Upgrade micro-triggers, elegant by doctrine: fire only at value-proven moments
|
|
92
|
+
(first block, first duplicate-invoice/BEC catch, 10th gated action, one week
|
|
93
|
+
protected), each ONCE per install ever (persisted in config.json) — the free
|
|
94
|
+
tier stays fully usable in silence. The 0.1.10 every-DENY nudge is replaced.
|
|
95
|
+
- verify_mandate/get_audit_proof gain one informational closing line each;
|
|
96
|
+
assess_action's missing-key error now offers the free-key path.
|
|
97
|
+
- config.json gains created_at + nudge/decision state (preserved on read).
|
package/dist/core.js
CHANGED
|
@@ -213,7 +213,7 @@ var DevFidacyCore = class {
|
|
|
213
213
|
this.priv = kp.privateKey;
|
|
214
214
|
this.pubPem = publicKeyPem(kp.publicKey);
|
|
215
215
|
if (kp.ephemeral)
|
|
216
|
-
console.error("[fidacy]
|
|
216
|
+
console.error("[fidacy] local firewall active, deny-by-default. Grants are signed with a per-session key (fine for local use); set FIDACY_SIGNING_KEY_B64 for a stable key, or FIDACY_MODE=http to use the hosted core.");
|
|
217
217
|
this.mandate = opts.mandate;
|
|
218
218
|
this.store = new FileAuditStore(opts.auditLogPath ?? "./fidacy-audit.log");
|
|
219
219
|
this.onDecision = opts.onDecision;
|
|
@@ -337,7 +337,12 @@ function readConfig() {
|
|
|
337
337
|
anon_id: raw.anon_id,
|
|
338
338
|
tier: raw.tier === "paid" ? "paid" : "free",
|
|
339
339
|
api_key: typeof raw.api_key === "string" ? raw.api_key : null,
|
|
340
|
-
mandate: raw.mandate
|
|
340
|
+
mandate: raw.mandate,
|
|
341
|
+
// Install-state passthrough: dropping these on a read→write cycle would
|
|
342
|
+
// reset every once-per-install nudge into an every-time nag.
|
|
343
|
+
created_at: typeof raw.created_at === "string" ? raw.created_at : void 0,
|
|
344
|
+
nudges: raw.nudges && typeof raw.nudges === "object" ? raw.nudges : void 0,
|
|
345
|
+
decisions_count: typeof raw.decisions_count === "number" ? raw.decisions_count : void 0
|
|
341
346
|
};
|
|
342
347
|
} catch {
|
|
343
348
|
return null;
|
|
@@ -357,7 +362,8 @@ function resolveMandateRules(cfg) {
|
|
|
357
362
|
}
|
|
358
363
|
|
|
359
364
|
// src/telemetry.ts
|
|
360
|
-
var CLIENT_VERSION = "0.1.
|
|
365
|
+
var CLIENT_VERSION = true ? "0.1.11" : "dev";
|
|
366
|
+
var currentShell = "mcp";
|
|
361
367
|
function telemetryEnabled() {
|
|
362
368
|
const v = (process.env.FIDACY_DISABLE_TELEMETRY ?? "").trim().toLowerCase();
|
|
363
369
|
return !(v === "1" || v === "true" || v === "yes");
|
|
@@ -383,7 +389,7 @@ function resultOf(status, violatedRule) {
|
|
|
383
389
|
}
|
|
384
390
|
function record(type, result) {
|
|
385
391
|
if (!telemetryEnabled()) return;
|
|
386
|
-
buffer.push({ type, ts: (/* @__PURE__ */ new Date()).toISOString(), client_version: CLIENT_VERSION, ...result ? { result } : {} });
|
|
392
|
+
buffer.push({ type, ts: (/* @__PURE__ */ new Date()).toISOString(), client_version: CLIENT_VERSION, shell: currentShell, ...result ? { result } : {} });
|
|
387
393
|
if (!flushTimer) {
|
|
388
394
|
flushTimer = setTimeout(() => {
|
|
389
395
|
void flush();
|
package/dist/index.js
CHANGED
|
@@ -220,7 +220,7 @@ var DevFidacyCore = class {
|
|
|
220
220
|
this.priv = kp.privateKey;
|
|
221
221
|
this.pubPem = publicKeyPem(kp.publicKey);
|
|
222
222
|
if (kp.ephemeral)
|
|
223
|
-
console.error("[fidacy]
|
|
223
|
+
console.error("[fidacy] local firewall active, deny-by-default. Grants are signed with a per-session key (fine for local use); set FIDACY_SIGNING_KEY_B64 for a stable key, or FIDACY_MODE=http to use the hosted core.");
|
|
224
224
|
this.mandate = opts.mandate;
|
|
225
225
|
this.store = new FileAuditStore(opts.auditLogPath ?? "./fidacy-audit.log");
|
|
226
226
|
this.onDecision = opts.onDecision;
|
|
@@ -345,7 +345,12 @@ function readConfig() {
|
|
|
345
345
|
anon_id: raw.anon_id,
|
|
346
346
|
tier: raw.tier === "paid" ? "paid" : "free",
|
|
347
347
|
api_key: typeof raw.api_key === "string" ? raw.api_key : null,
|
|
348
|
-
mandate: raw.mandate
|
|
348
|
+
mandate: raw.mandate,
|
|
349
|
+
// Install-state passthrough: dropping these on a read→write cycle would
|
|
350
|
+
// reset every once-per-install nudge into an every-time nag.
|
|
351
|
+
created_at: typeof raw.created_at === "string" ? raw.created_at : void 0,
|
|
352
|
+
nudges: raw.nudges && typeof raw.nudges === "object" ? raw.nudges : void 0,
|
|
353
|
+
decisions_count: typeof raw.decisions_count === "number" ? raw.decisions_count : void 0
|
|
349
354
|
};
|
|
350
355
|
} catch {
|
|
351
356
|
return null;
|
|
@@ -359,7 +364,13 @@ function writeConfig(cfg) {
|
|
|
359
364
|
function ensureState() {
|
|
360
365
|
const existing = readConfig();
|
|
361
366
|
if (existing) return { config: existing, firstRun: false };
|
|
362
|
-
const config = {
|
|
367
|
+
const config = {
|
|
368
|
+
anon_id: randomUUID2(),
|
|
369
|
+
tier: "free",
|
|
370
|
+
api_key: null,
|
|
371
|
+
mandate: { payees: [], categories: ["*"], currency: "USD", perTxMax: 2500, maxTotal: 1e4 },
|
|
372
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
373
|
+
};
|
|
363
374
|
try {
|
|
364
375
|
writeConfig(config);
|
|
365
376
|
} catch {
|
|
@@ -380,7 +391,8 @@ function resolveMandateRules(cfg) {
|
|
|
380
391
|
}
|
|
381
392
|
|
|
382
393
|
// src/telemetry.ts
|
|
383
|
-
var CLIENT_VERSION = "0.1.
|
|
394
|
+
var CLIENT_VERSION = true ? "0.1.11" : "dev";
|
|
395
|
+
var currentShell = "mcp";
|
|
384
396
|
function telemetryEnabled() {
|
|
385
397
|
const v = (process.env.FIDACY_DISABLE_TELEMETRY ?? "").trim().toLowerCase();
|
|
386
398
|
return !(v === "1" || v === "true" || v === "yes");
|
|
@@ -406,7 +418,7 @@ function resultOf(status, violatedRule) {
|
|
|
406
418
|
}
|
|
407
419
|
function record(type, result) {
|
|
408
420
|
if (!telemetryEnabled()) return;
|
|
409
|
-
buffer.push({ type, ts: (/* @__PURE__ */ new Date()).toISOString(), client_version: CLIENT_VERSION, ...result ? { result } : {} });
|
|
421
|
+
buffer.push({ type, ts: (/* @__PURE__ */ new Date()).toISOString(), client_version: CLIENT_VERSION, shell: currentShell, ...result ? { result } : {} });
|
|
410
422
|
if (!flushTimer) {
|
|
411
423
|
flushTimer = setTimeout(() => {
|
|
412
424
|
void flush();
|
|
@@ -603,7 +615,7 @@ async function postOnce(fetchImpl, url, headers, payload, timeoutMs) {
|
|
|
603
615
|
}
|
|
604
616
|
|
|
605
617
|
// src/provision.ts
|
|
606
|
-
var CLIENT_VERSION2 = "0.1.
|
|
618
|
+
var CLIENT_VERSION2 = true ? "0.1.11" : "dev";
|
|
607
619
|
function provisionEnabled() {
|
|
608
620
|
const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
|
|
609
621
|
return !(v === "1" || v === "true" || v === "yes");
|
|
@@ -650,6 +662,55 @@ function requestUpgrade() {
|
|
|
650
662
|
return { url: upgradeUrl(), anonId: anonId2 };
|
|
651
663
|
}
|
|
652
664
|
|
|
665
|
+
// src/nudges.ts
|
|
666
|
+
function nudgeOnce(key) {
|
|
667
|
+
const cfg = readConfig();
|
|
668
|
+
if (!cfg) return false;
|
|
669
|
+
if (cfg.nudges?.[key]) return false;
|
|
670
|
+
cfg.nudges = { ...cfg.nudges ?? {}, [key]: true };
|
|
671
|
+
try {
|
|
672
|
+
writeConfig(cfg);
|
|
673
|
+
} catch {
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
function bumpDecisionCount() {
|
|
679
|
+
const cfg = readConfig();
|
|
680
|
+
if (!cfg) return 0;
|
|
681
|
+
const n = (cfg.decisions_count ?? 0) + 1;
|
|
682
|
+
cfg.decisions_count = n;
|
|
683
|
+
try {
|
|
684
|
+
writeConfig(cfg);
|
|
685
|
+
} catch {
|
|
686
|
+
}
|
|
687
|
+
return n;
|
|
688
|
+
}
|
|
689
|
+
function installAgeDays() {
|
|
690
|
+
const t = Date.parse(readConfig()?.created_at ?? "");
|
|
691
|
+
if (Number.isNaN(t)) return 0;
|
|
692
|
+
return Math.floor((Date.now() - t) / 864e5);
|
|
693
|
+
}
|
|
694
|
+
function decisionNudge(status, violatedRule, upgradeToolName) {
|
|
695
|
+
const count = bumpDecisionCount();
|
|
696
|
+
if (status === "DENY" && violatedRule?.startsWith("duplicate_invoice") && nudgeOnce("bec_catch")) {
|
|
697
|
+
return `That was a duplicate-invoice (BEC) catch \u2014 the #1 payment fraud. On a free account every block like this is server-signed and anchored to Bitcoin: court-grade evidence. Call the ${upgradeToolName} tool to start.`;
|
|
698
|
+
}
|
|
699
|
+
if (status === "DENY" && nudgeOnce("first_deny")) {
|
|
700
|
+
return `Fidacy just blocked this for you \u2014 for server-signed, anchored proof of every block (free account, local history migrates), call the ${upgradeToolName} tool.`;
|
|
701
|
+
}
|
|
702
|
+
if (count === 10 && nudgeOnce("milestone_10")) {
|
|
703
|
+
return `That's 10 actions gated on this install. For a signed, Bitcoin-anchored history of all of them (free account), call the ${upgradeToolName} tool.`;
|
|
704
|
+
}
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
function weekOneBootNudge(upgradeToolName) {
|
|
708
|
+
if (installAgeDays() >= 7 && nudgeOnce("week_1")) {
|
|
709
|
+
return ` One week protected \u2014 anchor your history to Bitcoin with a free account: the ${upgradeToolName} tool starts it.`;
|
|
710
|
+
}
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
|
|
653
714
|
// src/index.ts
|
|
654
715
|
var state = ensureState();
|
|
655
716
|
var core = makeCore();
|
|
@@ -683,8 +744,10 @@ server.registerTool(
|
|
|
683
744
|
const req = args;
|
|
684
745
|
const d = await core.decide(req, subject);
|
|
685
746
|
const out = { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule };
|
|
686
|
-
const
|
|
747
|
+
const base = d.status === "ALLOW" ? `ALLOW (decision ${d.decisionId})${req.invoiceRef ? ` for invoice ${req.invoiceRef}` : ""}. To settle, call execute_payment with the SAME payee, amount, currency, and idempotencyKey, and set "grant" to EXACTLY this signed value:
|
|
687
748
|
${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.`;
|
|
749
|
+
const nudge = decisionNudge(d.status, d.violatedRule, "upgrade");
|
|
750
|
+
const human = nudge ? `${base} ${nudge}` : base;
|
|
688
751
|
return { content: [{ type: "text", text: human }], structuredContent: out };
|
|
689
752
|
}
|
|
690
753
|
);
|
|
@@ -699,7 +762,8 @@ server.registerTool(
|
|
|
699
762
|
async () => {
|
|
700
763
|
const m = await core.getMandate(subject);
|
|
701
764
|
const payload = { mandate: m, fidacyPublicKey: core.publicKey() };
|
|
702
|
-
|
|
765
|
+
const text = JSON.stringify(payload, null, 2) + "\n\nThis mandate is local (free tier). Hosted mandates add team governance and server-signed verdicts \u2014 the upgrade tool starts a free account.";
|
|
766
|
+
return { content: [{ type: "text", text }], structuredContent: payload };
|
|
703
767
|
}
|
|
704
768
|
);
|
|
705
769
|
server.registerTool(
|
|
@@ -713,7 +777,8 @@ server.registerTool(
|
|
|
713
777
|
async ({ decisionId }) => {
|
|
714
778
|
const proof = await core.getProof(decisionId);
|
|
715
779
|
if (!proof) return { content: [{ type: "text", text: `No proof found for ${decisionId}` }], isError: true };
|
|
716
|
-
|
|
780
|
+
const text = JSON.stringify(proof, null, 2) + "\n\nThis proof lives on this machine. On a free hosted account the chain is anchored to Bitcoin \u2014 verifiable by anyone, forever, even against Fidacy (the upgrade tool starts it).";
|
|
781
|
+
return { content: [{ type: "text", text }], structuredContent: proof };
|
|
717
782
|
}
|
|
718
783
|
);
|
|
719
784
|
server.registerTool(
|
|
@@ -739,7 +804,7 @@ server.registerTool(
|
|
|
739
804
|
content: [
|
|
740
805
|
{
|
|
741
806
|
type: "text",
|
|
742
|
-
text: "assess_action
|
|
807
|
+
text: "assess_action needs an engine key \u2014 free in about a minute: call the upgrade tool (your usage history migrates), or set FIDACY_ENGINE_API_KEY if you already have one."
|
|
743
808
|
}
|
|
744
809
|
],
|
|
745
810
|
isError: true
|
|
@@ -799,7 +864,8 @@ async function main() {
|
|
|
799
864
|
void autoProvision();
|
|
800
865
|
const verdictHint = process.env.FIDACY_ENGINE_API_KEY ? "" : " Set FIDACY_ENGINE_API_KEY to enable verdicts (assess_action).";
|
|
801
866
|
const tierHint = state.firstRun ? " First run: free local tier active (deny-unknown payee + cap). Add trusted payees in ~/.fidacy/config.json." : ` Tier: ${state.config.tier} (local-first).`;
|
|
802
|
-
|
|
867
|
+
const weekNudge = weekOneBootNudge("upgrade") ?? "";
|
|
868
|
+
console.error("[fidacy] @fidacy/mcp ready on stdio: signed verdict + payment firewall." + tierHint + verdictHint + weekNudge);
|
|
803
869
|
}
|
|
804
870
|
main().catch((e) => {
|
|
805
871
|
console.error("[fidacy] fatal:", e);
|
package/dist/lib.js
CHANGED
|
@@ -339,7 +339,7 @@ var DevFidacyCore = class {
|
|
|
339
339
|
this.priv = kp.privateKey;
|
|
340
340
|
this.pubPem = publicKeyPem(kp.publicKey);
|
|
341
341
|
if (kp.ephemeral)
|
|
342
|
-
console.error("[fidacy]
|
|
342
|
+
console.error("[fidacy] local firewall active, deny-by-default. Grants are signed with a per-session key (fine for local use); set FIDACY_SIGNING_KEY_B64 for a stable key, or FIDACY_MODE=http to use the hosted core.");
|
|
343
343
|
this.mandate = opts.mandate;
|
|
344
344
|
this.store = new FileAuditStore(opts.auditLogPath ?? "./fidacy-audit.log");
|
|
345
345
|
this.onDecision = opts.onDecision;
|
|
@@ -430,6 +430,7 @@ var HttpFidacyCore = class {
|
|
|
430
430
|
};
|
|
431
431
|
|
|
432
432
|
// src/config.ts
|
|
433
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
433
434
|
import { homedir } from "node:os";
|
|
434
435
|
import { join } from "node:path";
|
|
435
436
|
import {
|
|
@@ -463,12 +464,38 @@ function readConfig() {
|
|
|
463
464
|
anon_id: raw.anon_id,
|
|
464
465
|
tier: raw.tier === "paid" ? "paid" : "free",
|
|
465
466
|
api_key: typeof raw.api_key === "string" ? raw.api_key : null,
|
|
466
|
-
mandate: raw.mandate
|
|
467
|
+
mandate: raw.mandate,
|
|
468
|
+
// Install-state passthrough: dropping these on a read→write cycle would
|
|
469
|
+
// reset every once-per-install nudge into an every-time nag.
|
|
470
|
+
created_at: typeof raw.created_at === "string" ? raw.created_at : void 0,
|
|
471
|
+
nudges: raw.nudges && typeof raw.nudges === "object" ? raw.nudges : void 0,
|
|
472
|
+
decisions_count: typeof raw.decisions_count === "number" ? raw.decisions_count : void 0
|
|
467
473
|
};
|
|
468
474
|
} catch {
|
|
469
475
|
return null;
|
|
470
476
|
}
|
|
471
477
|
}
|
|
478
|
+
function writeConfig(cfg) {
|
|
479
|
+
const dir = configDir();
|
|
480
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
481
|
+
writeFileSync(configPath(), JSON.stringify(cfg, null, 2), { mode: 384 });
|
|
482
|
+
}
|
|
483
|
+
function ensureState() {
|
|
484
|
+
const existing = readConfig();
|
|
485
|
+
if (existing) return { config: existing, firstRun: false };
|
|
486
|
+
const config = {
|
|
487
|
+
anon_id: randomUUID3(),
|
|
488
|
+
tier: "free",
|
|
489
|
+
api_key: null,
|
|
490
|
+
mandate: { payees: [], categories: ["*"], currency: "USD", perTxMax: 2500, maxTotal: 1e4 },
|
|
491
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
492
|
+
};
|
|
493
|
+
try {
|
|
494
|
+
writeConfig(config);
|
|
495
|
+
} catch {
|
|
496
|
+
}
|
|
497
|
+
return { config, firstRun: true };
|
|
498
|
+
}
|
|
472
499
|
function resolveMandateRules(cfg) {
|
|
473
500
|
const m = cfg?.mandate ?? {};
|
|
474
501
|
const envList = (v) => v === void 0 ? void 0 : v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -483,7 +510,11 @@ function resolveMandateRules(cfg) {
|
|
|
483
510
|
}
|
|
484
511
|
|
|
485
512
|
// src/telemetry.ts
|
|
486
|
-
var CLIENT_VERSION = "0.1.
|
|
513
|
+
var CLIENT_VERSION = true ? "0.1.11" : "dev";
|
|
514
|
+
var currentShell = "mcp";
|
|
515
|
+
function setTelemetryShell(shell) {
|
|
516
|
+
currentShell = shell;
|
|
517
|
+
}
|
|
487
518
|
function telemetryEnabled() {
|
|
488
519
|
const v = (process.env.FIDACY_DISABLE_TELEMETRY ?? "").trim().toLowerCase();
|
|
489
520
|
return !(v === "1" || v === "true" || v === "yes");
|
|
@@ -509,7 +540,7 @@ function resultOf(status, violatedRule) {
|
|
|
509
540
|
}
|
|
510
541
|
function record(type, result) {
|
|
511
542
|
if (!telemetryEnabled()) return;
|
|
512
|
-
buffer.push({ type, ts: (/* @__PURE__ */ new Date()).toISOString(), client_version: CLIENT_VERSION, ...result ? { result } : {} });
|
|
543
|
+
buffer.push({ type, ts: (/* @__PURE__ */ new Date()).toISOString(), client_version: CLIENT_VERSION, shell: currentShell, ...result ? { result } : {} });
|
|
513
544
|
if (!flushTimer) {
|
|
514
545
|
flushTimer = setTimeout(() => {
|
|
515
546
|
void flush();
|
|
@@ -517,7 +548,10 @@ function record(type, result) {
|
|
|
517
548
|
if (typeof flushTimer.unref === "function") flushTimer.unref();
|
|
518
549
|
}
|
|
519
550
|
}
|
|
551
|
+
var recordInstall = () => record("install");
|
|
552
|
+
var recordAgentActive = () => record("agent_active");
|
|
520
553
|
var recordDecision = (status, violatedRule) => record("decision", resultOf(status, violatedRule));
|
|
554
|
+
var recordUpgradeIntent = () => record("upgrade_intent");
|
|
521
555
|
async function flush() {
|
|
522
556
|
if (flushTimer) {
|
|
523
557
|
clearTimeout(flushTimer);
|
|
@@ -585,24 +619,133 @@ function makeCore() {
|
|
|
585
619
|
onDecision: (d) => recordDecision(d.status, d.violatedRule)
|
|
586
620
|
});
|
|
587
621
|
}
|
|
622
|
+
|
|
623
|
+
// src/upgrade.ts
|
|
624
|
+
function upgradeUrl() {
|
|
625
|
+
const base = (process.env.FIDACY_APP_URL ?? "https://app.fidacy.com").replace(/\/$/, "");
|
|
626
|
+
const anon = readConfig()?.anon_id ?? "";
|
|
627
|
+
return `${base}/upgrade?anon=${encodeURIComponent(anon)}`;
|
|
628
|
+
}
|
|
629
|
+
function requestUpgrade() {
|
|
630
|
+
recordUpgradeIntent();
|
|
631
|
+
const anonId2 = readConfig()?.anon_id ?? "";
|
|
632
|
+
return { url: upgradeUrl(), anonId: anonId2 };
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// src/provision.ts
|
|
636
|
+
var CLIENT_VERSION2 = true ? "0.1.11" : "dev";
|
|
637
|
+
function provisionEnabled() {
|
|
638
|
+
const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
|
|
639
|
+
return !(v === "1" || v === "true" || v === "yes");
|
|
640
|
+
}
|
|
641
|
+
function endpoint2() {
|
|
642
|
+
const base = (process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com").replace(/\/$/, "");
|
|
643
|
+
return `${base}/v1/provision`;
|
|
644
|
+
}
|
|
645
|
+
async function autoProvision() {
|
|
646
|
+
if (!provisionEnabled()) return "skipped";
|
|
647
|
+
const cfg = readConfig();
|
|
648
|
+
if (!cfg) return "skipped";
|
|
649
|
+
if (cfg.api_key) return "skipped";
|
|
650
|
+
try {
|
|
651
|
+
const res = await fetch(endpoint2(), {
|
|
652
|
+
method: "POST",
|
|
653
|
+
headers: { "content-type": "application/json" },
|
|
654
|
+
body: JSON.stringify({ anon_id: cfg.anon_id, client_version: CLIENT_VERSION2 })
|
|
655
|
+
});
|
|
656
|
+
if (res.status === 429) return "rate_limited";
|
|
657
|
+
if (!res.ok) return "failed";
|
|
658
|
+
const data = await res.json();
|
|
659
|
+
if (typeof data.api_key !== "string" || !data.api_key) return "failed";
|
|
660
|
+
writeConfig({
|
|
661
|
+
...cfg,
|
|
662
|
+
api_key: data.api_key,
|
|
663
|
+
tier: data.tier === "paid" ? "paid" : "free"
|
|
664
|
+
});
|
|
665
|
+
return "provisioned";
|
|
666
|
+
} catch {
|
|
667
|
+
return "failed";
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// src/nudges.ts
|
|
672
|
+
function nudgeOnce(key) {
|
|
673
|
+
const cfg = readConfig();
|
|
674
|
+
if (!cfg) return false;
|
|
675
|
+
if (cfg.nudges?.[key]) return false;
|
|
676
|
+
cfg.nudges = { ...cfg.nudges ?? {}, [key]: true };
|
|
677
|
+
try {
|
|
678
|
+
writeConfig(cfg);
|
|
679
|
+
} catch {
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
return true;
|
|
683
|
+
}
|
|
684
|
+
function bumpDecisionCount() {
|
|
685
|
+
const cfg = readConfig();
|
|
686
|
+
if (!cfg) return 0;
|
|
687
|
+
const n = (cfg.decisions_count ?? 0) + 1;
|
|
688
|
+
cfg.decisions_count = n;
|
|
689
|
+
try {
|
|
690
|
+
writeConfig(cfg);
|
|
691
|
+
} catch {
|
|
692
|
+
}
|
|
693
|
+
return n;
|
|
694
|
+
}
|
|
695
|
+
function installAgeDays() {
|
|
696
|
+
const t = Date.parse(readConfig()?.created_at ?? "");
|
|
697
|
+
if (Number.isNaN(t)) return 0;
|
|
698
|
+
return Math.floor((Date.now() - t) / 864e5);
|
|
699
|
+
}
|
|
700
|
+
function decisionNudge(status, violatedRule, upgradeToolName) {
|
|
701
|
+
const count = bumpDecisionCount();
|
|
702
|
+
if (status === "DENY" && violatedRule?.startsWith("duplicate_invoice") && nudgeOnce("bec_catch")) {
|
|
703
|
+
return `That was a duplicate-invoice (BEC) catch \u2014 the #1 payment fraud. On a free account every block like this is server-signed and anchored to Bitcoin: court-grade evidence. Call the ${upgradeToolName} tool to start.`;
|
|
704
|
+
}
|
|
705
|
+
if (status === "DENY" && nudgeOnce("first_deny")) {
|
|
706
|
+
return `Fidacy just blocked this for you \u2014 for server-signed, anchored proof of every block (free account, local history migrates), call the ${upgradeToolName} tool.`;
|
|
707
|
+
}
|
|
708
|
+
if (count === 10 && nudgeOnce("milestone_10")) {
|
|
709
|
+
return `That's 10 actions gated on this install. For a signed, Bitcoin-anchored history of all of them (free account), call the ${upgradeToolName} tool.`;
|
|
710
|
+
}
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
function weekOneBootNudge(upgradeToolName) {
|
|
714
|
+
if (installAgeDays() >= 7 && nudgeOnce("week_1")) {
|
|
715
|
+
return ` One week protected \u2014 anchor your history to Bitcoin with a free account: the ${upgradeToolName} tool starts it.`;
|
|
716
|
+
}
|
|
717
|
+
return null;
|
|
718
|
+
}
|
|
588
719
|
export {
|
|
589
720
|
DevFidacyCore,
|
|
590
721
|
FileAuditStore,
|
|
591
722
|
GrantEnforcingExecutor,
|
|
592
723
|
HttpFidacyCore,
|
|
593
724
|
ReferenceRail,
|
|
725
|
+
autoProvision,
|
|
594
726
|
canonInvoice,
|
|
727
|
+
decisionNudge,
|
|
728
|
+
ensureState,
|
|
595
729
|
evaluate,
|
|
596
730
|
httpRedeem,
|
|
731
|
+
installAgeDays,
|
|
597
732
|
loadOrGenerateKeyPair,
|
|
598
733
|
lookalikePayee,
|
|
599
734
|
makeCore,
|
|
735
|
+
nudgeOnce,
|
|
600
736
|
publicKeyPem,
|
|
737
|
+
readConfig,
|
|
738
|
+
recordAgentActive,
|
|
739
|
+
recordInstall,
|
|
740
|
+
requestUpgrade,
|
|
601
741
|
requireHttpsBase,
|
|
742
|
+
setTelemetryShell,
|
|
602
743
|
sha256,
|
|
603
744
|
sign,
|
|
604
745
|
stableStringify,
|
|
746
|
+
upgradeUrl,
|
|
605
747
|
validateRequest,
|
|
606
748
|
verify,
|
|
607
|
-
verifyGrant
|
|
749
|
+
verifyGrant,
|
|
750
|
+
weekOneBootNudge
|
|
608
751
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fidacy/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Fidacy action firewall for AI agents. Mandate-gated payment authorization as an MCP server.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://fidacy.com",
|
|
@@ -36,12 +36,6 @@
|
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
|
-
"scripts": {
|
|
40
|
-
"build": "node scripts/bundle.mjs",
|
|
41
|
-
"dev": "tsx watch src/index.ts",
|
|
42
|
-
"start": "node dist/index.js",
|
|
43
|
-
"prepublishOnly": "npm run build"
|
|
44
|
-
},
|
|
45
39
|
"engines": {
|
|
46
40
|
"node": ">=18"
|
|
47
41
|
},
|
|
@@ -50,14 +44,19 @@
|
|
|
50
44
|
"zod": "^3.25.0"
|
|
51
45
|
},
|
|
52
46
|
"devDependencies": {
|
|
53
|
-
"@fidacy/firewall": "workspace:*",
|
|
54
47
|
"@types/node": "^22.10.0",
|
|
55
48
|
"tsx": "^4.19.2",
|
|
56
|
-
"typescript": "^5.7.2"
|
|
49
|
+
"typescript": "^5.7.2",
|
|
50
|
+
"@fidacy/firewall": "0.1.0"
|
|
57
51
|
},
|
|
58
52
|
"mcpName": "com.fidacy/mcp",
|
|
59
53
|
"repository": {
|
|
60
54
|
"type": "git",
|
|
61
55
|
"url": "git+https://github.com/lucaslubi/fidacy-mcp.git"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "node scripts/bundle.mjs",
|
|
59
|
+
"dev": "tsx watch src/index.ts",
|
|
60
|
+
"start": "node dist/index.js"
|
|
62
61
|
}
|
|
63
|
-
}
|
|
62
|
+
}
|