agentbnb 5.1.10 → 6.0.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/dist/{card-RSGDCHCV.js → card-REW7BSWW.js} +1 -1
- package/dist/{chunk-EPIWHNB2.js → chunk-2TLZ6G2B.js} +446 -373
- package/dist/{chunk-WGZ5AGOX.js → chunk-3CIMVISQ.js} +24 -1
- package/dist/{chunk-NH2FIERR.js → chunk-574W3HHE.js} +1 -1
- package/dist/{chunk-B5FTAGFN.js → chunk-7XHDSWRD.js} +75 -75
- package/dist/{chunk-NLAWT4DT.js → chunk-7YLFLC5C.js} +6 -6
- package/dist/chunk-BP3L2TET.js +148 -0
- package/dist/{chunk-EGUOAHCW.js → chunk-C2T4BMRW.js} +12 -12
- package/dist/{chunk-5KFI5X7B.js → chunk-F53QQIM2.js} +1 -1
- package/dist/chunk-JR6TJDIF.js +425 -0
- package/dist/{chunk-DFBX3BBD.js → chunk-KA2VIEGM.js} +211 -16
- package/dist/chunk-NQTE577Q.js +159 -0
- package/dist/{chunk-WTXRY7R2.js → chunk-NYV3NE5Z.js} +157 -9
- package/dist/{chunk-UKT6H7YT.js → chunk-OZXCRLP3.js} +1 -1
- package/dist/{chunk-QITOPASZ.js → chunk-PSQHUZ7X.js} +1 -1
- package/dist/{chunk-EANI2N2V.js → chunk-RVYQSC6L.js} +2 -99
- package/dist/{chunk-MLS6IGGG.js → chunk-TQDV254A.js} +1 -1
- package/dist/{chunk-QQFBFV4V.js → chunk-TR6UZDNX.js} +57 -18
- package/dist/{chunk-ZX5623ER.js → chunk-VMH2YS2I.js} +1 -1
- package/dist/{chunk-XND2DWTZ.js → chunk-VPQ44XKE.js} +2 -2
- package/dist/{chunk-CSATDXZC.js → chunk-Y7T6IMM3.js} +1 -1
- package/dist/{chunk-FLY3WIQR.js → chunk-YRRVFTDR.js} +3 -3
- package/dist/cli/index.js +261 -125
- package/dist/{client-T5MTY3CS.js → client-HRYRJKSA.js} +3 -3
- package/dist/{conduct-WU3VEXB6.js → conduct-LF6FYPLD.js} +11 -11
- package/dist/conduct-QAFZIEY6.js +21 -0
- package/dist/{conductor-mode-ZMTFZGJP.js → conductor-mode-NUDQLZFM.js} +309 -13
- package/dist/conductor-mode-YQ6QSPPT.js +275 -0
- package/dist/{execute-4D4ITQCL.js → execute-ITHIYYOX.js} +4 -3
- package/dist/execute-PNJFABVJ.js +14 -0
- package/dist/index.d.ts +555 -0
- package/dist/index.js +592 -83
- package/dist/{process-guard-CC7CNRQJ.js → process-guard-QCCBGILS.js} +1 -1
- package/dist/publish-capability-TS6CNR5G.js +12 -0
- package/dist/{request-VOXBFUOG.js → request-P6QCTCCG.js} +14 -14
- package/dist/{serve-skill-IH7UAJNR.js → serve-skill-EZOL7UYN.js} +10 -9
- package/dist/{server-JVQW2TID.js → server-3G6ZTASA.js} +16 -16
- package/dist/{service-coordinator-EYRDTHL5.js → service-coordinator-CRSE4GWC.js} +174 -242
- package/dist/skill-config-4W5W5O6T.js +22 -0
- package/dist/skills/agentbnb/bootstrap.js +227 -67
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/agentbnb/SKILL.md +50 -1
- package/skills/agentbnb/bootstrap.ts +126 -8
- package/skills/agentbnb/install.sh +49 -9
- package/dist/chunk-CRFCWD6V.js +0 -366
- package/dist/conduct-6LKIJJKQ.js +0 -21
- package/dist/conductor-mode-Q4IIDY5E.js +0 -123
- package/dist/execute-T7Y6RKSW.js +0 -13
|
@@ -9,7 +9,40 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { join } from 'node:path';
|
|
12
|
+
import { dirname, basename } from 'node:path';
|
|
13
|
+
import { existsSync } from 'node:fs';
|
|
14
|
+
import { homedir } from 'node:os';
|
|
12
15
|
import { spawnSync } from 'node:child_process';
|
|
16
|
+
import { randomUUID } from 'node:crypto';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Walks up from `startDir` looking for a SOUL.md file.
|
|
20
|
+
* Returns the absolute path to the first SOUL.md found, or null.
|
|
21
|
+
*/
|
|
22
|
+
function findSoulMd(startDir: string): string | null {
|
|
23
|
+
let dir = startDir;
|
|
24
|
+
while (true) {
|
|
25
|
+
const candidate = join(dir, 'SOUL.md');
|
|
26
|
+
if (existsSync(candidate)) return candidate;
|
|
27
|
+
const parent = dirname(dir);
|
|
28
|
+
if (parent === dir) return null; // filesystem root
|
|
29
|
+
dir = parent;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Derives a workspace-specific AGENTBNB_DIR from SOUL.md location.
|
|
35
|
+
* Uses the name of the directory containing SOUL.md as the workspace identifier.
|
|
36
|
+
* Falls back to the global ~/.agentbnb if SOUL.md is not found.
|
|
37
|
+
*/
|
|
38
|
+
function resolveWorkspaceDir(): string {
|
|
39
|
+
const soulPath = findSoulMd(process.cwd());
|
|
40
|
+
if (soulPath) {
|
|
41
|
+
const workspaceName = basename(dirname(soulPath));
|
|
42
|
+
return join(homedir(), '.agentbnb', workspaceName);
|
|
43
|
+
}
|
|
44
|
+
return join(homedir(), '.agentbnb');
|
|
45
|
+
}
|
|
13
46
|
|
|
14
47
|
import { getConfigDir, loadConfig } from '../../src/cli/config.js';
|
|
15
48
|
import { AgentBnBError } from '../../src/types/index.js';
|
|
@@ -17,6 +50,7 @@ import { ProcessGuard } from '../../src/runtime/process-guard.js';
|
|
|
17
50
|
import { ServiceCoordinator } from '../../src/runtime/service-coordinator.js';
|
|
18
51
|
import type { ServiceOptions, ServiceStatus } from '../../src/runtime/service-coordinator.js';
|
|
19
52
|
import { AgentBnBService } from '../../src/app/agentbnb-service.js';
|
|
53
|
+
import { openDatabase } from '../../src/registry/store.js';
|
|
20
54
|
|
|
21
55
|
/** Configuration for bringing an AgentBnB agent online via OpenClaw. */
|
|
22
56
|
export interface BootstrapConfig {
|
|
@@ -44,6 +78,83 @@ export interface BootstrapContext {
|
|
|
44
78
|
_removeSignalHandlers: () => void;
|
|
45
79
|
}
|
|
46
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Idempotently registers a task_decomposition Capability Card for this agent.
|
|
83
|
+
* Uses raw SQL upsert to accept v2.0 card shape (same pattern as websocket-relay.ts).
|
|
84
|
+
* No-op if a task_decomposition card for this owner already exists.
|
|
85
|
+
* Non-fatal: failure is logged to stderr and does not prevent agent startup.
|
|
86
|
+
*
|
|
87
|
+
* @param configDir - Agent config directory (used to locate registry.db).
|
|
88
|
+
* @param owner - Agent owner identifier from AgentBnBConfig.
|
|
89
|
+
*/
|
|
90
|
+
function registerDecomposerCard(configDir: string, owner: string): void {
|
|
91
|
+
try {
|
|
92
|
+
const db = openDatabase(join(configDir, 'registry.db'));
|
|
93
|
+
|
|
94
|
+
// Idempotency: skip if a task_decomposition card for this owner already exists
|
|
95
|
+
const existing = db
|
|
96
|
+
.prepare(
|
|
97
|
+
"SELECT id FROM capability_cards WHERE owner = ? AND json_extract(data, '$.capability_type') = ?"
|
|
98
|
+
)
|
|
99
|
+
.get(owner, 'task_decomposition') as { id: string } | undefined;
|
|
100
|
+
|
|
101
|
+
if (existing) return;
|
|
102
|
+
|
|
103
|
+
const cardId = randomUUID();
|
|
104
|
+
const now = new Date().toISOString();
|
|
105
|
+
const card = {
|
|
106
|
+
spec_version: '2.0' as const,
|
|
107
|
+
id: cardId,
|
|
108
|
+
owner,
|
|
109
|
+
agent_name: `${owner}-decomposer`,
|
|
110
|
+
capability_type: 'task_decomposition',
|
|
111
|
+
skills: [
|
|
112
|
+
{
|
|
113
|
+
id: 'task-decomposition',
|
|
114
|
+
name: 'Task Decomposition',
|
|
115
|
+
description:
|
|
116
|
+
'Decomposes natural-language tasks into executable sub-task DAGs using the AgentBnB Rule Engine.',
|
|
117
|
+
level: 1 as const,
|
|
118
|
+
category: 'task_decomposition',
|
|
119
|
+
inputs: [
|
|
120
|
+
{
|
|
121
|
+
name: 'task',
|
|
122
|
+
type: 'text' as const,
|
|
123
|
+
description: 'Natural language task description',
|
|
124
|
+
required: true,
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
outputs: [
|
|
128
|
+
{
|
|
129
|
+
name: 'subtasks',
|
|
130
|
+
type: 'json' as const,
|
|
131
|
+
description: 'Array of SubTask objects with id, role, description, dependencies',
|
|
132
|
+
required: true,
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
pricing: { credits_per_call: 1 },
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
availability: { online: true },
|
|
139
|
+
created_at: now,
|
|
140
|
+
updated_at: now,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
db.prepare(
|
|
144
|
+
'INSERT INTO capability_cards (id, owner, data, created_at, updated_at) VALUES (?, ?, ?, ?, ?)'
|
|
145
|
+
).run(cardId, owner, JSON.stringify(card), now, now);
|
|
146
|
+
|
|
147
|
+
process.stderr.write(
|
|
148
|
+
`[agentbnb] registered task_decomposition card: ${cardId} (owner=${owner})\n`
|
|
149
|
+
);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
// Non-fatal: log and continue. The agent still starts without decomposer card.
|
|
152
|
+
process.stderr.write(
|
|
153
|
+
`[agentbnb] WARNING: failed to register task_decomposition card: ${String(err)}\n`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
47
158
|
/**
|
|
48
159
|
* Brings an AgentBnB node online (idempotent — safe to call when already running).
|
|
49
160
|
* Registers SIGTERM/SIGINT handlers that conditionally stop the node on process exit.
|
|
@@ -54,17 +165,20 @@ export interface BootstrapContext {
|
|
|
54
165
|
* registered here to avoid double-handler conflicts. Track in Layer A implementation.
|
|
55
166
|
*/
|
|
56
167
|
export async function activate(config: BootstrapConfig = {}): Promise<BootstrapContext> {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
//
|
|
60
|
-
// If AGENTBNB_DIR is not set, auto-set it from configDir so all child processes
|
|
61
|
-
// spawned by this OpenClaw session (e.g. `agentbnb request` CLI calls) inherit it.
|
|
168
|
+
// Per-workspace isolation: if AGENTBNB_DIR is not already set, derive it from
|
|
169
|
+
// the SOUL.md location in the current working directory. This ensures each
|
|
170
|
+
// OpenClaw workspace uses its own isolated ~/.agentbnb/<workspace-name>/ directory.
|
|
62
171
|
if (!process.env['AGENTBNB_DIR']) {
|
|
63
|
-
|
|
172
|
+
const workspaceDir = resolveWorkspaceDir();
|
|
173
|
+
process.env['AGENTBNB_DIR'] = workspaceDir;
|
|
64
174
|
process.stderr.write(
|
|
65
|
-
`[agentbnb] AGENTBNB_DIR
|
|
175
|
+
`[agentbnb] AGENTBNB_DIR auto-configured to ${workspaceDir} for workspace isolation.\n`
|
|
66
176
|
);
|
|
67
|
-
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const configDir = getConfigDir();
|
|
180
|
+
|
|
181
|
+
if (process.env['AGENTBNB_DIR'] !== configDir) {
|
|
68
182
|
process.stderr.write(
|
|
69
183
|
`[agentbnb] WARNING: AGENTBNB_DIR (${process.env['AGENTBNB_DIR']}) differs from resolved configDir (${configDir}).\n`
|
|
70
184
|
);
|
|
@@ -106,6 +220,10 @@ export async function activate(config: BootstrapConfig = {}): Promise<BootstrapC
|
|
|
106
220
|
};
|
|
107
221
|
|
|
108
222
|
const startDisposition = await service.ensureRunning(opts);
|
|
223
|
+
|
|
224
|
+
// Auto-register task_decomposition card so this agent is discoverable as a decomposer peer.
|
|
225
|
+
registerDecomposerCard(configDir, agentConfig.owner);
|
|
226
|
+
|
|
109
227
|
const status = await service.getNodeStatus();
|
|
110
228
|
|
|
111
229
|
// Register signal handlers.
|
|
@@ -80,12 +80,12 @@ ok "Node.js ${NODE_VERSION_FULL} confirmed"
|
|
|
80
80
|
# node_version — full version string (e.g. "v20.11.0")
|
|
81
81
|
# source — how it was resolved: "OPENCLAW_NODE_EXEC" | "shell"
|
|
82
82
|
# detected_at — ISO 8601 UTC timestamp
|
|
83
|
-
|
|
84
|
-
mkdir -p "$
|
|
83
|
+
_EARLY_DIR="$HOME/.agentbnb"
|
|
84
|
+
mkdir -p "$_EARLY_DIR"
|
|
85
85
|
DETECTED_AT="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
|
86
86
|
printf '{"node_exec":"%s","node_version":"%s","source":"%s","detected_at":"%s"}\n' \
|
|
87
87
|
"$NODE_EXEC" "$NODE_VERSION_FULL" "$NODE_SOURCE" "$DETECTED_AT" \
|
|
88
|
-
> "$
|
|
88
|
+
> "$_EARLY_DIR/runtime.json"
|
|
89
89
|
ok "Runtime persisted to ~/.agentbnb/runtime.json (source: ${NODE_SOURCE})"
|
|
90
90
|
|
|
91
91
|
# pnpm (attempt install if missing)
|
|
@@ -177,13 +177,53 @@ fi
|
|
|
177
177
|
# ---------------------------------------------------------------------------
|
|
178
178
|
step "Step 4/6 — Initializing AgentBnB config"
|
|
179
179
|
|
|
180
|
+
# Per-workspace isolation: detect SOUL.md to derive workspace-specific AGENTBNB_DIR.
|
|
181
|
+
# This ensures each OpenClaw workspace has its own isolated data directory
|
|
182
|
+
# (~/.agentbnb/<workspace-name>/) so multiple agents never share the same registry,
|
|
183
|
+
# credits, or config.
|
|
184
|
+
#
|
|
185
|
+
# Walk up from CWD looking for SOUL.md; use containing directory's name as workspace ID.
|
|
186
|
+
_find_soul_dir() {
|
|
187
|
+
local d="$1"
|
|
188
|
+
while [ "$d" != "/" ]; do
|
|
189
|
+
if [ -f "$d/SOUL.md" ]; then
|
|
190
|
+
echo "$d"
|
|
191
|
+
return
|
|
192
|
+
fi
|
|
193
|
+
d=$(dirname "$d")
|
|
194
|
+
done
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if [ -z "${AGENTBNB_DIR:-}" ]; then
|
|
198
|
+
WORKSPACE_DIR=$(_find_soul_dir "$(pwd)")
|
|
199
|
+
if [ -n "$WORKSPACE_DIR" ]; then
|
|
200
|
+
WORKSPACE_NAME=$(basename "$WORKSPACE_DIR")
|
|
201
|
+
export AGENTBNB_DIR="$HOME/.agentbnb/$WORKSPACE_NAME"
|
|
202
|
+
ok "Workspace detected: $WORKSPACE_NAME"
|
|
203
|
+
ok "AGENTBNB_DIR=$AGENTBNB_DIR (isolated from other agents)"
|
|
204
|
+
else
|
|
205
|
+
export AGENTBNB_DIR="$HOME/.agentbnb"
|
|
206
|
+
warn "No SOUL.md found — using shared config at ~/.agentbnb/"
|
|
207
|
+
warn "For isolation, set AGENTBNB_DIR manually or run from your agent's workspace directory."
|
|
208
|
+
fi
|
|
209
|
+
else
|
|
210
|
+
ok "AGENTBNB_DIR already set: $AGENTBNB_DIR"
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
# Update runtime.json path to match workspace-specific dir
|
|
214
|
+
mkdir -p "$AGENTBNB_DIR"
|
|
215
|
+
DETECTED_AT="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
|
216
|
+
printf '{"node_exec":"%s","node_version":"%s","source":"%s","detected_at":"%s"}\n' \
|
|
217
|
+
"$NODE_EXEC" "$NODE_VERSION_FULL" "$NODE_SOURCE" "$DETECTED_AT" \
|
|
218
|
+
> "$AGENTBNB_DIR/runtime.json"
|
|
219
|
+
|
|
180
220
|
# agentbnb init is idempotent — safe to run on existing installs
|
|
181
221
|
if agentbnb init --yes 2>/dev/null; then
|
|
182
|
-
ok "Config initialized at
|
|
222
|
+
ok "Config initialized at $AGENTBNB_DIR/"
|
|
183
223
|
else
|
|
184
224
|
# May already be initialized — check if directory exists
|
|
185
|
-
if [ -d "$
|
|
186
|
-
ok "Config already exists at
|
|
225
|
+
if [ -d "$AGENTBNB_DIR" ] && [ -f "$AGENTBNB_DIR/config.json" ]; then
|
|
226
|
+
ok "Config already exists at $AGENTBNB_DIR/ (skipping re-init)"
|
|
187
227
|
else
|
|
188
228
|
err "Failed to initialize AgentBnB config. Run 'agentbnb init' manually."
|
|
189
229
|
exit 1
|
|
@@ -239,7 +279,7 @@ echo "${GREEN}${BOLD}AgentBnB skill installed successfully!${RESET}"
|
|
|
239
279
|
echo ""
|
|
240
280
|
echo "What was set up:"
|
|
241
281
|
ok "AgentBnB CLI available as 'agentbnb'"
|
|
242
|
-
ok "Config directory:
|
|
282
|
+
ok "Config directory: $AGENTBNB_DIR"
|
|
243
283
|
ok "Node runtime: ${NODE_EXEC} (${NODE_VERSION_FULL}, source: ${NODE_SOURCE})"
|
|
244
284
|
ok "Runtime persisted to: ~/.agentbnb/runtime.json"
|
|
245
285
|
ok "Registry: https://agentbnb.fly.dev (public network)"
|
|
@@ -247,8 +287,8 @@ ok "Default autonomy tier: Tier 3 (ask before all transactions)"
|
|
|
247
287
|
ok "Default credit reserve: 20 credits"
|
|
248
288
|
|
|
249
289
|
# Verify identity.json was created (v4.0+ feature)
|
|
250
|
-
if [ -f "$
|
|
251
|
-
ok "Agent identity:
|
|
290
|
+
if [ -f "$AGENTBNB_DIR/identity.json" ]; then
|
|
291
|
+
ok "Agent identity: $AGENTBNB_DIR/identity.json"
|
|
252
292
|
else
|
|
253
293
|
warn "identity.json not found — will be created on next agentbnb init"
|
|
254
294
|
fi
|
package/dist/chunk-CRFCWD6V.js
DELETED
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AgentBnBError
|
|
3
|
-
} from "./chunk-WGZ5AGOX.js";
|
|
4
|
-
|
|
5
|
-
// src/credit/ledger.ts
|
|
6
|
-
import Database from "better-sqlite3";
|
|
7
|
-
import { randomUUID } from "crypto";
|
|
8
|
-
var CREDIT_SCHEMA = `
|
|
9
|
-
CREATE TABLE IF NOT EXISTS credit_balances (
|
|
10
|
-
owner TEXT PRIMARY KEY,
|
|
11
|
-
balance INTEGER NOT NULL DEFAULT 0,
|
|
12
|
-
updated_at TEXT NOT NULL
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
CREATE TABLE IF NOT EXISTS credit_transactions (
|
|
16
|
-
id TEXT PRIMARY KEY,
|
|
17
|
-
owner TEXT NOT NULL,
|
|
18
|
-
amount INTEGER NOT NULL,
|
|
19
|
-
reason TEXT NOT NULL,
|
|
20
|
-
reference_id TEXT,
|
|
21
|
-
created_at TEXT NOT NULL
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
CREATE TABLE IF NOT EXISTS credit_escrow (
|
|
25
|
-
id TEXT PRIMARY KEY,
|
|
26
|
-
owner TEXT NOT NULL,
|
|
27
|
-
amount INTEGER NOT NULL,
|
|
28
|
-
card_id TEXT NOT NULL,
|
|
29
|
-
status TEXT NOT NULL DEFAULT 'held',
|
|
30
|
-
created_at TEXT NOT NULL,
|
|
31
|
-
settled_at TEXT
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
CREATE INDEX IF NOT EXISTS idx_transactions_owner ON credit_transactions(owner, created_at);
|
|
35
|
-
CREATE INDEX IF NOT EXISTS idx_escrow_owner ON credit_escrow(owner);
|
|
36
|
-
`;
|
|
37
|
-
function openCreditDb(path = ":memory:") {
|
|
38
|
-
const db = new Database(path);
|
|
39
|
-
db.pragma("journal_mode = WAL");
|
|
40
|
-
db.pragma("foreign_keys = ON");
|
|
41
|
-
db.exec(CREDIT_SCHEMA);
|
|
42
|
-
return db;
|
|
43
|
-
}
|
|
44
|
-
function bootstrapAgent(db, owner, amount = 100) {
|
|
45
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
46
|
-
db.transaction(() => {
|
|
47
|
-
const result = db.prepare("INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, ?, ?)").run(owner, amount, now);
|
|
48
|
-
if (result.changes > 0) {
|
|
49
|
-
db.prepare(
|
|
50
|
-
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
51
|
-
).run(randomUUID(), owner, amount, "bootstrap", null, now);
|
|
52
|
-
}
|
|
53
|
-
})();
|
|
54
|
-
}
|
|
55
|
-
function getBalance(db, owner) {
|
|
56
|
-
const row = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(owner);
|
|
57
|
-
return row?.balance ?? 0;
|
|
58
|
-
}
|
|
59
|
-
function getTransactions(db, owner, limit = 100) {
|
|
60
|
-
return db.prepare(
|
|
61
|
-
"SELECT id, owner, amount, reason, reference_id, created_at FROM credit_transactions WHERE owner = ? ORDER BY created_at DESC LIMIT ?"
|
|
62
|
-
).all(owner, limit);
|
|
63
|
-
}
|
|
64
|
-
function recordEarning(db, owner, amount, _cardId, receiptNonce) {
|
|
65
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
66
|
-
db.transaction(() => {
|
|
67
|
-
const existing = db.prepare(
|
|
68
|
-
"SELECT id FROM credit_transactions WHERE reference_id = ? AND reason = 'remote_earning'"
|
|
69
|
-
).get(receiptNonce);
|
|
70
|
-
if (existing) return;
|
|
71
|
-
db.prepare(
|
|
72
|
-
"INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
|
|
73
|
-
).run(owner, now);
|
|
74
|
-
db.prepare(
|
|
75
|
-
"UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
|
|
76
|
-
).run(amount, now, owner);
|
|
77
|
-
db.prepare(
|
|
78
|
-
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
79
|
-
).run(randomUUID(), owner, amount, "remote_earning", receiptNonce, now);
|
|
80
|
-
})();
|
|
81
|
-
}
|
|
82
|
-
function migrateOwner(db, oldOwner, newOwner) {
|
|
83
|
-
if (oldOwner === newOwner) return;
|
|
84
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
85
|
-
db.transaction(() => {
|
|
86
|
-
const oldRow = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(oldOwner);
|
|
87
|
-
if (!oldRow) return;
|
|
88
|
-
const newRow = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(newOwner);
|
|
89
|
-
if (newRow) {
|
|
90
|
-
db.prepare("UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?").run(oldRow.balance, now, newOwner);
|
|
91
|
-
} else {
|
|
92
|
-
db.prepare("UPDATE credit_balances SET owner = ?, updated_at = ? WHERE owner = ?").run(newOwner, now, oldOwner);
|
|
93
|
-
}
|
|
94
|
-
if (newRow) {
|
|
95
|
-
db.prepare("DELETE FROM credit_balances WHERE owner = ?").run(oldOwner);
|
|
96
|
-
}
|
|
97
|
-
db.prepare("UPDATE credit_transactions SET owner = ? WHERE owner = ?").run(newOwner, oldOwner);
|
|
98
|
-
db.prepare("UPDATE credit_escrow SET owner = ? WHERE owner = ?").run(newOwner, oldOwner);
|
|
99
|
-
})();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// src/credit/escrow.ts
|
|
103
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
104
|
-
function holdEscrow(db, owner, amount, cardId) {
|
|
105
|
-
const escrowId = randomUUID2();
|
|
106
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
107
|
-
const hold = db.transaction(() => {
|
|
108
|
-
const row = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(owner);
|
|
109
|
-
if (!row || row.balance < amount) {
|
|
110
|
-
throw new AgentBnBError("Insufficient credits", "INSUFFICIENT_CREDITS");
|
|
111
|
-
}
|
|
112
|
-
db.prepare(
|
|
113
|
-
"UPDATE credit_balances SET balance = balance - ?, updated_at = ? WHERE owner = ? AND balance >= ?"
|
|
114
|
-
).run(amount, now, owner, amount);
|
|
115
|
-
db.prepare(
|
|
116
|
-
"INSERT INTO credit_escrow (id, owner, amount, card_id, status, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
117
|
-
).run(escrowId, owner, amount, cardId, "held", now);
|
|
118
|
-
db.prepare(
|
|
119
|
-
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
120
|
-
).run(randomUUID2(), owner, -amount, "escrow_hold", escrowId, now);
|
|
121
|
-
});
|
|
122
|
-
hold();
|
|
123
|
-
return escrowId;
|
|
124
|
-
}
|
|
125
|
-
function settleEscrow(db, escrowId, recipientOwner) {
|
|
126
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
127
|
-
const settle = db.transaction(() => {
|
|
128
|
-
const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
129
|
-
if (!escrow) {
|
|
130
|
-
throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
|
|
131
|
-
}
|
|
132
|
-
if (escrow.status !== "held") {
|
|
133
|
-
throw new AgentBnBError(
|
|
134
|
-
`Escrow ${escrowId} is already ${escrow.status}`,
|
|
135
|
-
"ESCROW_ALREADY_SETTLED"
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
db.prepare(
|
|
139
|
-
"INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
|
|
140
|
-
).run(recipientOwner, now);
|
|
141
|
-
db.prepare(
|
|
142
|
-
"UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
|
|
143
|
-
).run(escrow.amount, now, recipientOwner);
|
|
144
|
-
db.prepare(
|
|
145
|
-
"UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
|
|
146
|
-
).run("settled", now, escrowId);
|
|
147
|
-
db.prepare(
|
|
148
|
-
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
149
|
-
).run(randomUUID2(), recipientOwner, escrow.amount, "settlement", escrowId, now);
|
|
150
|
-
});
|
|
151
|
-
settle();
|
|
152
|
-
}
|
|
153
|
-
function releaseEscrow(db, escrowId) {
|
|
154
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
155
|
-
const release = db.transaction(() => {
|
|
156
|
-
const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
157
|
-
if (!escrow) {
|
|
158
|
-
throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
|
|
159
|
-
}
|
|
160
|
-
if (escrow.status !== "held") {
|
|
161
|
-
throw new AgentBnBError(
|
|
162
|
-
`Escrow ${escrowId} is already ${escrow.status}`,
|
|
163
|
-
"ESCROW_ALREADY_SETTLED"
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
db.prepare(
|
|
167
|
-
"UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
|
|
168
|
-
).run(escrow.amount, now, escrow.owner);
|
|
169
|
-
db.prepare(
|
|
170
|
-
"UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
|
|
171
|
-
).run("released", now, escrowId);
|
|
172
|
-
db.prepare(
|
|
173
|
-
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
174
|
-
).run(randomUUID2(), escrow.owner, escrow.amount, "refund", escrowId, now);
|
|
175
|
-
});
|
|
176
|
-
release();
|
|
177
|
-
}
|
|
178
|
-
function confirmEscrowDebit(db, escrowId) {
|
|
179
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
180
|
-
const confirm = db.transaction(() => {
|
|
181
|
-
const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
182
|
-
if (!escrow) {
|
|
183
|
-
throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
|
|
184
|
-
}
|
|
185
|
-
if (escrow.status !== "held") {
|
|
186
|
-
throw new AgentBnBError(
|
|
187
|
-
`Escrow ${escrowId} is already ${escrow.status}`,
|
|
188
|
-
"ESCROW_ALREADY_SETTLED"
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
db.prepare(
|
|
192
|
-
"UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
|
|
193
|
-
).run("settled", now, escrowId);
|
|
194
|
-
db.prepare(
|
|
195
|
-
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
196
|
-
).run(randomUUID2(), escrow.owner, 0, "remote_settlement_confirmed", escrowId, now);
|
|
197
|
-
});
|
|
198
|
-
confirm();
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// src/credit/signing.ts
|
|
202
|
-
import { generateKeyPairSync, sign, verify, createPublicKey, createPrivateKey } from "crypto";
|
|
203
|
-
import { writeFileSync, readFileSync, existsSync, chmodSync } from "fs";
|
|
204
|
-
import { join } from "path";
|
|
205
|
-
function generateKeyPair() {
|
|
206
|
-
const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
|
|
207
|
-
publicKeyEncoding: { type: "spki", format: "der" },
|
|
208
|
-
privateKeyEncoding: { type: "pkcs8", format: "der" }
|
|
209
|
-
});
|
|
210
|
-
return {
|
|
211
|
-
publicKey: Buffer.from(publicKey),
|
|
212
|
-
privateKey: Buffer.from(privateKey)
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
function saveKeyPair(configDir, keys) {
|
|
216
|
-
const privatePath = join(configDir, "private.key");
|
|
217
|
-
const publicPath = join(configDir, "public.key");
|
|
218
|
-
writeFileSync(privatePath, keys.privateKey);
|
|
219
|
-
chmodSync(privatePath, 384);
|
|
220
|
-
writeFileSync(publicPath, keys.publicKey);
|
|
221
|
-
}
|
|
222
|
-
function loadKeyPair(configDir) {
|
|
223
|
-
const privatePath = join(configDir, "private.key");
|
|
224
|
-
const publicPath = join(configDir, "public.key");
|
|
225
|
-
if (!existsSync(privatePath) || !existsSync(publicPath)) {
|
|
226
|
-
throw new AgentBnBError("Keypair not found. Run `agentbnb init` to generate one.", "KEYPAIR_NOT_FOUND");
|
|
227
|
-
}
|
|
228
|
-
return {
|
|
229
|
-
publicKey: readFileSync(publicPath),
|
|
230
|
-
privateKey: readFileSync(privatePath)
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
function canonicalJson(data) {
|
|
234
|
-
return JSON.stringify(data, Object.keys(data).sort());
|
|
235
|
-
}
|
|
236
|
-
function signEscrowReceipt(data, privateKey) {
|
|
237
|
-
const message = Buffer.from(canonicalJson(data), "utf-8");
|
|
238
|
-
const keyObject = createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
|
|
239
|
-
const signature = sign(null, message, keyObject);
|
|
240
|
-
return signature.toString("base64url");
|
|
241
|
-
}
|
|
242
|
-
function verifyEscrowReceipt(data, signature, publicKey) {
|
|
243
|
-
try {
|
|
244
|
-
const message = Buffer.from(canonicalJson(data), "utf-8");
|
|
245
|
-
const keyObject = createPublicKey({ key: publicKey, format: "der", type: "spki" });
|
|
246
|
-
const sigBuffer = Buffer.from(signature, "base64url");
|
|
247
|
-
return verify(null, message, keyObject, sigBuffer);
|
|
248
|
-
} catch {
|
|
249
|
-
return false;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// src/feedback/store.ts
|
|
254
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
255
|
-
function initFeedbackTable(db) {
|
|
256
|
-
db.exec(`
|
|
257
|
-
CREATE TABLE IF NOT EXISTS feedback (
|
|
258
|
-
id TEXT PRIMARY KEY,
|
|
259
|
-
transaction_id TEXT NOT NULL,
|
|
260
|
-
provider_agent TEXT NOT NULL,
|
|
261
|
-
skill_id TEXT NOT NULL,
|
|
262
|
-
requester_agent TEXT NOT NULL,
|
|
263
|
-
rating INTEGER NOT NULL,
|
|
264
|
-
latency_ms INTEGER NOT NULL,
|
|
265
|
-
result_quality TEXT NOT NULL,
|
|
266
|
-
quality_details TEXT,
|
|
267
|
-
would_reuse INTEGER NOT NULL,
|
|
268
|
-
cost_value_ratio TEXT NOT NULL,
|
|
269
|
-
timestamp TEXT NOT NULL,
|
|
270
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
271
|
-
);
|
|
272
|
-
|
|
273
|
-
CREATE INDEX IF NOT EXISTS feedback_provider_idx ON feedback(provider_agent);
|
|
274
|
-
CREATE INDEX IF NOT EXISTS feedback_skill_idx ON feedback(skill_id);
|
|
275
|
-
`);
|
|
276
|
-
}
|
|
277
|
-
function insertFeedback(db, feedback) {
|
|
278
|
-
const id = randomUUID3();
|
|
279
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
280
|
-
db.prepare(`
|
|
281
|
-
INSERT INTO feedback (
|
|
282
|
-
id, transaction_id, provider_agent, skill_id, requester_agent,
|
|
283
|
-
rating, latency_ms, result_quality, quality_details,
|
|
284
|
-
would_reuse, cost_value_ratio, timestamp, created_at
|
|
285
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
286
|
-
`).run(
|
|
287
|
-
id,
|
|
288
|
-
feedback.transaction_id,
|
|
289
|
-
feedback.provider_agent,
|
|
290
|
-
feedback.skill_id,
|
|
291
|
-
feedback.requester_agent,
|
|
292
|
-
feedback.rating,
|
|
293
|
-
feedback.latency_ms,
|
|
294
|
-
feedback.result_quality,
|
|
295
|
-
feedback.quality_details ?? null,
|
|
296
|
-
feedback.would_reuse ? 1 : 0,
|
|
297
|
-
feedback.cost_value_ratio,
|
|
298
|
-
feedback.timestamp,
|
|
299
|
-
now
|
|
300
|
-
);
|
|
301
|
-
return id;
|
|
302
|
-
}
|
|
303
|
-
function getFeedbackForSkill(db, skillId, limit = 20) {
|
|
304
|
-
const rows = db.prepare(`
|
|
305
|
-
SELECT * FROM feedback
|
|
306
|
-
WHERE skill_id = ?
|
|
307
|
-
ORDER BY timestamp DESC
|
|
308
|
-
LIMIT ?
|
|
309
|
-
`).all(skillId, limit);
|
|
310
|
-
return rows.map(rowToFeedback);
|
|
311
|
-
}
|
|
312
|
-
function getFeedbackForProvider(db, providerAgent, sinceDays) {
|
|
313
|
-
let rows;
|
|
314
|
-
if (sinceDays !== void 0) {
|
|
315
|
-
rows = db.prepare(`
|
|
316
|
-
SELECT * FROM feedback
|
|
317
|
-
WHERE provider_agent = ?
|
|
318
|
-
AND timestamp >= datetime('now', ? || ' days')
|
|
319
|
-
ORDER BY timestamp DESC
|
|
320
|
-
`).all(providerAgent, `-${sinceDays}`);
|
|
321
|
-
} else {
|
|
322
|
-
rows = db.prepare(`
|
|
323
|
-
SELECT * FROM feedback
|
|
324
|
-
WHERE provider_agent = ?
|
|
325
|
-
ORDER BY timestamp DESC
|
|
326
|
-
`).all(providerAgent);
|
|
327
|
-
}
|
|
328
|
-
return rows.map(rowToFeedback);
|
|
329
|
-
}
|
|
330
|
-
function rowToFeedback(row) {
|
|
331
|
-
return {
|
|
332
|
-
transaction_id: row["transaction_id"],
|
|
333
|
-
provider_agent: row["provider_agent"],
|
|
334
|
-
skill_id: row["skill_id"],
|
|
335
|
-
requester_agent: row["requester_agent"],
|
|
336
|
-
rating: row["rating"],
|
|
337
|
-
latency_ms: row["latency_ms"],
|
|
338
|
-
result_quality: row["result_quality"],
|
|
339
|
-
quality_details: row["quality_details"] ?? void 0,
|
|
340
|
-
would_reuse: row["would_reuse"] === 1,
|
|
341
|
-
cost_value_ratio: row["cost_value_ratio"],
|
|
342
|
-
timestamp: row["timestamp"]
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
export {
|
|
347
|
-
initFeedbackTable,
|
|
348
|
-
insertFeedback,
|
|
349
|
-
getFeedbackForSkill,
|
|
350
|
-
getFeedbackForProvider,
|
|
351
|
-
openCreditDb,
|
|
352
|
-
bootstrapAgent,
|
|
353
|
-
getBalance,
|
|
354
|
-
getTransactions,
|
|
355
|
-
recordEarning,
|
|
356
|
-
migrateOwner,
|
|
357
|
-
holdEscrow,
|
|
358
|
-
settleEscrow,
|
|
359
|
-
releaseEscrow,
|
|
360
|
-
confirmEscrowDebit,
|
|
361
|
-
generateKeyPair,
|
|
362
|
-
saveKeyPair,
|
|
363
|
-
loadKeyPair,
|
|
364
|
-
signEscrowReceipt,
|
|
365
|
-
verifyEscrowReceipt
|
|
366
|
-
};
|
package/dist/conduct-6LKIJJKQ.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
conductAction
|
|
3
|
-
} from "./chunk-NLAWT4DT.js";
|
|
4
|
-
import "./chunk-JOY533UH.js";
|
|
5
|
-
import "./chunk-QT7TEVNV.js";
|
|
6
|
-
import "./chunk-WTXRY7R2.js";
|
|
7
|
-
import "./chunk-3MJT4PZG.js";
|
|
8
|
-
import "./chunk-EGUOAHCW.js";
|
|
9
|
-
import "./chunk-CSATDXZC.js";
|
|
10
|
-
import "./chunk-ZX5623ER.js";
|
|
11
|
-
import "./chunk-NH2FIERR.js";
|
|
12
|
-
import "./chunk-XND2DWTZ.js";
|
|
13
|
-
import "./chunk-5AH3CMOX.js";
|
|
14
|
-
import "./chunk-75OC6E4F.js";
|
|
15
|
-
import "./chunk-DFBX3BBD.js";
|
|
16
|
-
import "./chunk-EANI2N2V.js";
|
|
17
|
-
import "./chunk-5KFI5X7B.js";
|
|
18
|
-
import "./chunk-WGZ5AGOX.js";
|
|
19
|
-
export {
|
|
20
|
-
conductAction
|
|
21
|
-
};
|