@unclick/mcp-server 0.3.20 → 0.3.22
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/__tests__/reliability.test.d.ts +2 -0
- package/dist/__tests__/reliability.test.d.ts.map +1 -0
- package/dist/__tests__/reliability.test.js +126 -0
- package/dist/__tests__/reliability.test.js.map +1 -0
- package/dist/reliability.d.ts +69 -0
- package/dist/reliability.d.ts.map +1 -0
- package/dist/reliability.js +106 -0
- package/dist/reliability.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reliability.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/reliability.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { createDispatchId, createHeartbeat, createReclaimSignal, createTimeBucket, decideStaleLease, } from "../reliability.js";
|
|
3
|
+
describe("reliability helpers", () => {
|
|
4
|
+
it("creates a stable dispatch ID from sorted payload data", () => {
|
|
5
|
+
const first = createDispatchId({
|
|
6
|
+
source: "fishbowl",
|
|
7
|
+
targetAgentId: "bailey",
|
|
8
|
+
taskRef: "chip-123",
|
|
9
|
+
timeBucket: "2026-04-30T11:00:00.000Z",
|
|
10
|
+
payload: { b: 2, a: 1 },
|
|
11
|
+
});
|
|
12
|
+
const second = createDispatchId({
|
|
13
|
+
source: "fishbowl",
|
|
14
|
+
targetAgentId: "bailey",
|
|
15
|
+
taskRef: "chip-123",
|
|
16
|
+
timeBucket: "2026-04-30T11:00:00.000Z",
|
|
17
|
+
payload: { a: 1, b: 2 },
|
|
18
|
+
});
|
|
19
|
+
expect(first).toBe(second);
|
|
20
|
+
expect(first).toMatch(/^dispatch_[a-f0-9]{32}$/);
|
|
21
|
+
});
|
|
22
|
+
it("creates different dispatch IDs when the task changes", () => {
|
|
23
|
+
const first = createDispatchId({
|
|
24
|
+
source: "fishbowl",
|
|
25
|
+
targetAgentId: "bailey",
|
|
26
|
+
taskRef: "chip-123",
|
|
27
|
+
});
|
|
28
|
+
const second = createDispatchId({
|
|
29
|
+
source: "fishbowl",
|
|
30
|
+
targetAgentId: "bailey",
|
|
31
|
+
taskRef: "chip-124",
|
|
32
|
+
});
|
|
33
|
+
expect(first).not.toBe(second);
|
|
34
|
+
});
|
|
35
|
+
it("buckets dispatch time into stable windows", () => {
|
|
36
|
+
expect(createTimeBucket(new Date("2026-04-30T11:00:04.999Z"), 5)).toBe("2026-04-30T11:00:00.000Z");
|
|
37
|
+
expect(createTimeBucket(new Date("2026-04-30T11:00:05.000Z"), 5)).toBe("2026-04-30T11:00:05.000Z");
|
|
38
|
+
});
|
|
39
|
+
it("detects expired leases", () => {
|
|
40
|
+
const decision = decideStaleLease({
|
|
41
|
+
status: "leased",
|
|
42
|
+
leaseExpiresAt: "2026-04-30T11:00:00.000Z",
|
|
43
|
+
}, new Date("2026-04-30T11:00:12.300Z"));
|
|
44
|
+
expect(decision).toEqual({
|
|
45
|
+
isStale: true,
|
|
46
|
+
reason: "lease_expired",
|
|
47
|
+
staleSeconds: 12,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
it("does not mark active or non-leased work as stale", () => {
|
|
51
|
+
expect(decideStaleLease({
|
|
52
|
+
status: "leased",
|
|
53
|
+
leaseExpiresAt: "2026-04-30T11:00:12.000Z",
|
|
54
|
+
}, new Date("2026-04-30T11:00:10.000Z"))).toMatchObject({ isStale: false, reason: "lease_active" });
|
|
55
|
+
expect(decideStaleLease({
|
|
56
|
+
status: "queued",
|
|
57
|
+
leaseExpiresAt: "2026-04-30T11:00:00.000Z",
|
|
58
|
+
}, new Date("2026-04-30T11:00:10.000Z"))).toMatchObject({ isStale: false, reason: "not_leased" });
|
|
59
|
+
});
|
|
60
|
+
it("creates compact heartbeat metadata", () => {
|
|
61
|
+
const heartbeat = createHeartbeat({
|
|
62
|
+
apiKeyHash: "hash_123",
|
|
63
|
+
agentId: "bailey",
|
|
64
|
+
dispatchId: "dispatch_abc",
|
|
65
|
+
state: "working",
|
|
66
|
+
currentTask: "write WakePass PRD",
|
|
67
|
+
nextAction: "open PR",
|
|
68
|
+
etaMinutes: 4,
|
|
69
|
+
createdAt: new Date("2026-04-30T11:00:00.000Z"),
|
|
70
|
+
lastRealActionAt: new Date("2026-04-30T10:59:30.000Z"),
|
|
71
|
+
});
|
|
72
|
+
expect(heartbeat).toEqual({
|
|
73
|
+
apiKeyHash: "hash_123",
|
|
74
|
+
agentId: "bailey",
|
|
75
|
+
dispatchId: "dispatch_abc",
|
|
76
|
+
state: "working",
|
|
77
|
+
currentTask: "write WakePass PRD",
|
|
78
|
+
nextAction: "open PR",
|
|
79
|
+
etaMinutes: 4,
|
|
80
|
+
createdAt: "2026-04-30T11:00:00.000Z",
|
|
81
|
+
lastRealActionAt: "2026-04-30T10:59:30.000Z",
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
it("marks missing-ack handoffs as a WakePass reliability miss", () => {
|
|
85
|
+
const signal = createReclaimSignal({
|
|
86
|
+
dispatchId: "dispatch_ack",
|
|
87
|
+
source: "fishbowl",
|
|
88
|
+
targetAgentId: "plex",
|
|
89
|
+
taskRef: "todo-123",
|
|
90
|
+
payload: { ack_required: true, handoff_message_id: "msg-abc" },
|
|
91
|
+
}, 95);
|
|
92
|
+
expect(signal).toEqual({
|
|
93
|
+
action: "handoff_ack_missing",
|
|
94
|
+
summary: "WakePass reliability miss: no ACK arrived before reclaim for plex",
|
|
95
|
+
payload: {
|
|
96
|
+
dispatch_id: "dispatch_ack",
|
|
97
|
+
source: "fishbowl",
|
|
98
|
+
target_agent_id: "plex",
|
|
99
|
+
task_ref: "todo-123",
|
|
100
|
+
stale_seconds: 95,
|
|
101
|
+
ack_required: true,
|
|
102
|
+
handoff_message_id: "msg-abc",
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
it("marks non-ack reclaim as a generic stale dispatch", () => {
|
|
107
|
+
const signal = createReclaimSignal({
|
|
108
|
+
dispatchId: "dispatch_generic",
|
|
109
|
+
source: "connectors",
|
|
110
|
+
targetAgentId: "bailey",
|
|
111
|
+
taskRef: "conn-42",
|
|
112
|
+
payload: { route: "oauth-health" },
|
|
113
|
+
}, 12);
|
|
114
|
+
expect(signal.action).toBe("stale_dispatch_reclaimed");
|
|
115
|
+
expect(signal.summary).toBe("Reclaimed stale connectors dispatch for bailey");
|
|
116
|
+
expect(signal.payload).toMatchObject({
|
|
117
|
+
dispatch_id: "dispatch_generic",
|
|
118
|
+
source: "connectors",
|
|
119
|
+
target_agent_id: "bailey",
|
|
120
|
+
task_ref: "conn-42",
|
|
121
|
+
stale_seconds: 12,
|
|
122
|
+
route: "oauth-health",
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
//# sourceMappingURL=reliability.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reliability.test.js","sourceRoot":"","sources":["../../src/__tests__/reliability.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export type DispatchSource = "fishbowl" | "connectors" | "wakepass" | "testpass" | "uxpass" | "flowpass" | "securitypass" | "manual";
|
|
2
|
+
export type DispatchStatus = "queued" | "leased" | "completed" | "failed" | "stale" | "cancelled";
|
|
3
|
+
export type HeartbeatState = "idle" | "received" | "accepted" | "working" | "blocked" | "completed";
|
|
4
|
+
export interface AgentDispatch {
|
|
5
|
+
apiKeyHash: string;
|
|
6
|
+
dispatchId: string;
|
|
7
|
+
source: DispatchSource;
|
|
8
|
+
targetAgentId: string;
|
|
9
|
+
taskRef?: string;
|
|
10
|
+
status: DispatchStatus;
|
|
11
|
+
leaseOwner?: string;
|
|
12
|
+
leaseExpiresAt?: string;
|
|
13
|
+
lastRealActionAt?: string;
|
|
14
|
+
createdAt: string;
|
|
15
|
+
updatedAt: string;
|
|
16
|
+
payload?: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
export interface AgentHeartbeat {
|
|
19
|
+
apiKeyHash: string;
|
|
20
|
+
agentId: string;
|
|
21
|
+
dispatchId?: string;
|
|
22
|
+
state: HeartbeatState;
|
|
23
|
+
currentTask?: string;
|
|
24
|
+
nextAction?: string;
|
|
25
|
+
etaMinutes?: number;
|
|
26
|
+
blocker?: string;
|
|
27
|
+
lastRealActionAt?: string;
|
|
28
|
+
createdAt: string;
|
|
29
|
+
}
|
|
30
|
+
export interface DispatchIdInput {
|
|
31
|
+
source: DispatchSource;
|
|
32
|
+
targetAgentId: string;
|
|
33
|
+
taskRef?: string;
|
|
34
|
+
promptHash?: string;
|
|
35
|
+
timeBucket?: string;
|
|
36
|
+
payload?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
export interface ReclaimSignalDescriptor {
|
|
39
|
+
action: "stale_dispatch_reclaimed" | "handoff_ack_missing";
|
|
40
|
+
summary: string;
|
|
41
|
+
payload: Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
export interface StaleLeaseInput {
|
|
44
|
+
status: DispatchStatus;
|
|
45
|
+
leaseExpiresAt?: string | null;
|
|
46
|
+
lastRealActionAt?: string | null;
|
|
47
|
+
}
|
|
48
|
+
export interface StaleLeaseDecision {
|
|
49
|
+
isStale: boolean;
|
|
50
|
+
reason: "not_leased" | "missing_lease_expiry" | "lease_active" | "lease_expired";
|
|
51
|
+
staleSeconds: number;
|
|
52
|
+
}
|
|
53
|
+
export declare function createDispatchId(input: DispatchIdInput): string;
|
|
54
|
+
export declare function createTimeBucket(date: Date, bucketSeconds?: number): string;
|
|
55
|
+
export declare function decideStaleLease(input: StaleLeaseInput, now?: Date): StaleLeaseDecision;
|
|
56
|
+
export declare function createHeartbeat(params: {
|
|
57
|
+
apiKeyHash: string;
|
|
58
|
+
agentId: string;
|
|
59
|
+
state: HeartbeatState;
|
|
60
|
+
createdAt?: Date;
|
|
61
|
+
dispatchId?: string;
|
|
62
|
+
currentTask?: string;
|
|
63
|
+
nextAction?: string;
|
|
64
|
+
etaMinutes?: number;
|
|
65
|
+
blocker?: string;
|
|
66
|
+
lastRealActionAt?: Date;
|
|
67
|
+
}): AgentHeartbeat;
|
|
68
|
+
export declare function createReclaimSignal(dispatch: Pick<AgentDispatch, "dispatchId" | "source" | "targetAgentId" | "taskRef" | "payload">, staleSeconds: number): ReclaimSignalDescriptor;
|
|
69
|
+
//# sourceMappingURL=reliability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reliability.d.ts","sourceRoot":"","sources":["../src/reliability.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GACtB,UAAU,GACV,YAAY,GACZ,UAAU,GACV,UAAU,GACV,QAAQ,GACR,UAAU,GACV,cAAc,GACd,QAAQ,CAAC;AAEb,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,OAAO,GACP,WAAW,CAAC;AAEhB,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,UAAU,GACV,UAAU,GACV,SAAS,GACT,SAAS,GACT,WAAW,CAAC;AAEhB,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,cAAc,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,cAAc,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,cAAc,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,cAAc,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,0BAA0B,GAAG,qBAAqB,CAAC;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,cAAc,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,YAAY,GAAG,sBAAsB,GAAG,cAAc,GAAG,eAAe,CAAC;IACjF,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAO/D;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,SAAI,GAAG,MAAM,CAQtE;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,eAAe,EACtB,GAAG,OAAa,GACf,kBAAkB,CAwBpB;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,cAAc,CAAC;IACtB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,IAAI,CAAC;CACzB,GAAG,cAAc,CAkBjB;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,GAAG,QAAQ,GAAG,eAAe,GAAG,SAAS,GAAG,SAAS,CAAC,EAChG,YAAY,EAAE,MAAM,GACnB,uBAAuB,CAmCzB"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export function createDispatchId(input) {
|
|
3
|
+
const hash = createHash("sha256")
|
|
4
|
+
.update(stableStringify(input))
|
|
5
|
+
.digest("hex")
|
|
6
|
+
.slice(0, 32);
|
|
7
|
+
return `dispatch_${hash}`;
|
|
8
|
+
}
|
|
9
|
+
export function createTimeBucket(date, bucketSeconds = 5) {
|
|
10
|
+
if (!Number.isFinite(bucketSeconds) || bucketSeconds <= 0) {
|
|
11
|
+
throw new Error("bucketSeconds must be a positive number");
|
|
12
|
+
}
|
|
13
|
+
const bucketMs = bucketSeconds * 1000;
|
|
14
|
+
const bucketStart = Math.floor(date.getTime() / bucketMs) * bucketMs;
|
|
15
|
+
return new Date(bucketStart).toISOString();
|
|
16
|
+
}
|
|
17
|
+
export function decideStaleLease(input, now = new Date()) {
|
|
18
|
+
if (input.status !== "leased") {
|
|
19
|
+
return { isStale: false, reason: "not_leased", staleSeconds: 0 };
|
|
20
|
+
}
|
|
21
|
+
if (!input.leaseExpiresAt) {
|
|
22
|
+
return { isStale: false, reason: "missing_lease_expiry", staleSeconds: 0 };
|
|
23
|
+
}
|
|
24
|
+
const leaseExpiresAtMs = Date.parse(input.leaseExpiresAt);
|
|
25
|
+
if (Number.isNaN(leaseExpiresAtMs)) {
|
|
26
|
+
return { isStale: false, reason: "missing_lease_expiry", staleSeconds: 0 };
|
|
27
|
+
}
|
|
28
|
+
const staleMs = now.getTime() - leaseExpiresAtMs;
|
|
29
|
+
if (staleMs <= 0) {
|
|
30
|
+
return { isStale: false, reason: "lease_active", staleSeconds: 0 };
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
isStale: true,
|
|
34
|
+
reason: "lease_expired",
|
|
35
|
+
staleSeconds: Math.floor(staleMs / 1000),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function createHeartbeat(params) {
|
|
39
|
+
const heartbeat = {
|
|
40
|
+
apiKeyHash: params.apiKeyHash,
|
|
41
|
+
agentId: params.agentId,
|
|
42
|
+
state: params.state,
|
|
43
|
+
createdAt: (params.createdAt ?? new Date()).toISOString(),
|
|
44
|
+
};
|
|
45
|
+
if (params.dispatchId)
|
|
46
|
+
heartbeat.dispatchId = params.dispatchId;
|
|
47
|
+
if (params.currentTask)
|
|
48
|
+
heartbeat.currentTask = params.currentTask;
|
|
49
|
+
if (params.nextAction)
|
|
50
|
+
heartbeat.nextAction = params.nextAction;
|
|
51
|
+
if (typeof params.etaMinutes === "number")
|
|
52
|
+
heartbeat.etaMinutes = params.etaMinutes;
|
|
53
|
+
if (params.blocker)
|
|
54
|
+
heartbeat.blocker = params.blocker;
|
|
55
|
+
if (params.lastRealActionAt) {
|
|
56
|
+
heartbeat.lastRealActionAt = params.lastRealActionAt.toISOString();
|
|
57
|
+
}
|
|
58
|
+
return heartbeat;
|
|
59
|
+
}
|
|
60
|
+
export function createReclaimSignal(dispatch, staleSeconds) {
|
|
61
|
+
const payload = dispatch.payload ?? {};
|
|
62
|
+
const expectsAck = payload.ack_required === true ||
|
|
63
|
+
payload.require_ack === true ||
|
|
64
|
+
typeof payload.handoff_message_id === "string" ||
|
|
65
|
+
typeof payload.handoff_thread_id === "string";
|
|
66
|
+
if (expectsAck) {
|
|
67
|
+
return {
|
|
68
|
+
action: "handoff_ack_missing",
|
|
69
|
+
summary: `WakePass reliability miss: no ACK arrived before reclaim for ${dispatch.targetAgentId}`,
|
|
70
|
+
payload: {
|
|
71
|
+
dispatch_id: dispatch.dispatchId,
|
|
72
|
+
source: dispatch.source,
|
|
73
|
+
target_agent_id: dispatch.targetAgentId,
|
|
74
|
+
task_ref: dispatch.taskRef ?? null,
|
|
75
|
+
stale_seconds: staleSeconds,
|
|
76
|
+
...payload,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
action: "stale_dispatch_reclaimed",
|
|
82
|
+
summary: `Reclaimed stale ${dispatch.source} dispatch for ${dispatch.targetAgentId}`,
|
|
83
|
+
payload: {
|
|
84
|
+
dispatch_id: dispatch.dispatchId,
|
|
85
|
+
source: dispatch.source,
|
|
86
|
+
target_agent_id: dispatch.targetAgentId,
|
|
87
|
+
task_ref: dispatch.taskRef ?? null,
|
|
88
|
+
stale_seconds: staleSeconds,
|
|
89
|
+
...payload,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function stableStringify(value) {
|
|
94
|
+
if (value === null || typeof value !== "object") {
|
|
95
|
+
return JSON.stringify(value);
|
|
96
|
+
}
|
|
97
|
+
if (Array.isArray(value)) {
|
|
98
|
+
return `[${value.map((item) => stableStringify(item)).join(",")}]`;
|
|
99
|
+
}
|
|
100
|
+
const record = value;
|
|
101
|
+
const keys = Object.keys(record).sort();
|
|
102
|
+
return `{${keys
|
|
103
|
+
.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`)
|
|
104
|
+
.join(",")}}`;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=reliability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reliability.js","sourceRoot":"","sources":["../src/reliability.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unclick/mcp-server",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.22",
|
|
4
4
|
"mcpName": "io.github.malamutemayhem/unclick-mcp-server",
|
|
5
5
|
"description": "MCP server for the UnClick tool marketplace — lets AI agents discover and use every UnClick tool",
|
|
6
6
|
"type": "module",
|