@ouro.bot/cli 0.1.0-alpha.488 → 0.1.0-alpha.489

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.json CHANGED
@@ -1,6 +1,12 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.489",
6
+ "changes": [
7
+ "Trip ledger Step 4 — harness-side trip tools land. Six new tools (`trip_ensure_ledger`, `trip_status`, `trip_get`, `trip_upsert`, `trip_attach_evidence`, `trip_new_id`) give the agent a private, encrypted, per-agent travel ledger backed by an RSA-OAEP-SHA256 + AES-256-GCM envelope. Ledger keypair lives at `state/trips/ledger.json`; encrypted records persist under `state/trips/records/<tripId>.json`. Tools are gated behind the same trust check as other private surfaces (only available to trusted callers) and validate `TripRecord` / `TripEvidence` shape before persisting. Vendor-copies the substrate trip-control types so the harness has no runtime dependency on a hosted ledger service yet, while keeping the on-disk format compatible for future migration."
8
+ ]
9
+ },
4
10
  {
5
11
  "version": "0.1.0-alpha.488",
6
12
  "changes": [
@@ -101,6 +101,10 @@ const DISPATCH_EXEMPT_PATTERNS = [
101
101
  // arithmetic). The caller (search-cache.ts searchMailSearchCache) owns
102
102
  // observability via senses.mail_search_cache_upserted and friends.
103
103
  "mailroom/search-relevance",
104
+ // Trip ledger crypto helpers: pure RSA/AES envelope construction + slug
105
+ // hashing. The caller (trips/store.ts) owns observability via
106
+ // trips.ledger_created and trips.evidence_attached.
107
+ "trips/core",
104
108
  // Outlook HTTP helper modules: route/static/transport/hook seams are
105
109
  // dispatched by outlook-http.ts, whose server lifecycle owns observability.
106
110
  "heart/outlook/outlook-http-transport",
@@ -17,6 +17,7 @@ const tools_stripe_1 = require("./tools-stripe");
17
17
  const tools_flight_1 = require("./tools-flight");
18
18
  const tools_attachments_1 = require("./tools-attachments");
19
19
  const tools_mail_1 = require("./tools-mail");
20
+ const tools_trip_1 = require("./tools-trip");
20
21
  // Re-export flow tools for consumers that import them from tools-base
21
22
  var tools_flow_1 = require("./tools-flow");
22
23
  Object.defineProperty(exports, "ponderTool", { enumerable: true, get: function () { return tools_flow_1.ponderTool; } });
@@ -48,6 +49,7 @@ exports.baseToolDefinitions = [
48
49
  ...tools_flight_1.flightToolDefinitions,
49
50
  ...tools_attachments_1.attachmentToolDefinitions,
50
51
  ...tools_mail_1.mailToolDefinitions,
52
+ ...tools_trip_1.tripToolDefinitions,
51
53
  ];
52
54
  // Convenience array of just the tool schemas (no handler/integration metadata).
53
55
  // Used by consumers that need the OpenAI function-tool format.
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tripToolDefinitions = void 0;
4
+ const types_1 = require("../mind/friends/types");
5
+ const runtime_1 = require("../nerves/runtime");
6
+ const store_1 = require("../trips/store");
7
+ const core_1 = require("../trips/core");
8
+ const identity_1 = require("../heart/identity");
9
+ function trustAllowsTripAccess(ctx) {
10
+ const trustLevel = ctx?.context?.friend?.trustLevel;
11
+ return trustLevel === undefined || (0, types_1.isTrustedLevel)(trustLevel);
12
+ }
13
+ function isRecord(value) {
14
+ return !!value && typeof value === "object" && !Array.isArray(value);
15
+ }
16
+ function parseJsonArg(raw, label) {
17
+ if (typeof raw !== "string")
18
+ throw new Error(`${label} must be a JSON string`);
19
+ try {
20
+ return JSON.parse(raw);
21
+ }
22
+ catch (error) {
23
+ throw new Error(`${label} is not valid JSON: ${error instanceof Error ? error.message : /* v8 ignore next -- JSON.parse only throws SyntaxError */ String(error)}`);
24
+ }
25
+ }
26
+ function validateTripRecord(value) {
27
+ if (!isRecord(value))
28
+ throw new Error("record must be a TripRecord object");
29
+ // Minimal structural validation — the agent is constructing the value but
30
+ // we still guard against the obvious shape mistakes that would break decrypt.
31
+ for (const field of ["tripId", "agentId", "ownerEmail", "name", "status", "createdAt", "updatedAt"]) {
32
+ if (typeof value[field] !== "string" || value[field].length === 0) {
33
+ throw new Error(`record.${field} must be a non-empty string`);
34
+ }
35
+ }
36
+ if (!Array.isArray(value.travellers))
37
+ throw new Error("record.travellers must be an array");
38
+ if (!Array.isArray(value.legs))
39
+ throw new Error("record.legs must be an array");
40
+ for (const leg of value.legs) {
41
+ if (!isRecord(leg))
42
+ throw new Error("each leg must be an object");
43
+ if (typeof leg.legId !== "string" || leg.legId.length === 0)
44
+ throw new Error("each leg requires a legId");
45
+ if (typeof leg.kind !== "string")
46
+ throw new Error("each leg requires a kind");
47
+ if (typeof leg.status !== "string")
48
+ throw new Error("each leg requires a status");
49
+ if (!Array.isArray(leg.evidence))
50
+ throw new Error(`leg ${leg.legId} requires an evidence array`);
51
+ for (const ev of leg.evidence) {
52
+ if (!isRecord(ev))
53
+ throw new Error(`leg ${leg.legId}: each evidence entry must be an object`);
54
+ if (typeof ev.messageId !== "string" || ev.messageId.length === 0)
55
+ throw new Error(`leg ${leg.legId}: evidence.messageId must be a non-empty string`);
56
+ if (typeof ev.discoveryMethod !== "string" || ev.discoveryMethod.length === 0)
57
+ throw new Error(`leg ${leg.legId}: evidence.discoveryMethod must be a non-empty string`);
58
+ }
59
+ }
60
+ return value;
61
+ }
62
+ function validateTripEvidence(value) {
63
+ if (!isRecord(value))
64
+ throw new Error("evidence must be a TripEvidence object");
65
+ for (const field of ["messageId", "reason", "recordedAt", "discoveryMethod"]) {
66
+ if (typeof value[field] !== "string" || value[field].length === 0) {
67
+ throw new Error(`evidence.${field} must be a non-empty string`);
68
+ }
69
+ }
70
+ return value;
71
+ }
72
+ function renderTripSummary(trip) {
73
+ const dateRange = trip.startDate && trip.endDate
74
+ ? `${trip.startDate} → ${trip.endDate}`
75
+ : trip.startDate ?? trip.endDate ?? "(no dates)";
76
+ const lines = [
77
+ `- ${trip.tripId} :: "${trip.name}" [${trip.status}; ${dateRange}; legs: ${trip.legs.length}]`,
78
+ ` travellers: ${trip.travellers.map((p) => p.name).join(", ") || "(none)"}`,
79
+ ];
80
+ if (trip.notes)
81
+ lines.push(` notes: ${trip.notes}`);
82
+ return lines.join("\n");
83
+ }
84
+ exports.tripToolDefinitions = [
85
+ {
86
+ tool: {
87
+ type: "function",
88
+ function: {
89
+ name: "trip_ensure_ledger",
90
+ description: "Idempotently ensure this agent has a trip ledger keypair. Safe to call multiple times. Required once before any other trip_ tool.",
91
+ parameters: { type: "object", properties: {} },
92
+ },
93
+ },
94
+ handler: async (_args, ctx) => {
95
+ if (!trustAllowsTripAccess(ctx))
96
+ return "trip ledger is private; this tool is only available in trusted contexts.";
97
+ const result = (0, store_1.ensureAgentTripLedger)({ agentName: (0, identity_1.getAgentName)() });
98
+ const verb = result.added ? "created" : "already present";
99
+ return `trip ledger ${verb}: ledgerId=${result.ledger.ledgerId}, keyId=${result.ledger.keyId}, createdAt=${result.ledger.createdAt}`;
100
+ },
101
+ summaryKeys: [],
102
+ },
103
+ {
104
+ tool: {
105
+ type: "function",
106
+ function: {
107
+ name: "trip_status",
108
+ description: "List the agent's trip ids in sorted order. Cheap overview before opening individual trips.",
109
+ parameters: { type: "object", properties: {} },
110
+ },
111
+ },
112
+ handler: async (_args, ctx) => {
113
+ if (!trustAllowsTripAccess(ctx))
114
+ return "trip ledger is private; this tool is only available in trusted contexts.";
115
+ const tripIds = (0, store_1.listTripIds)((0, identity_1.getAgentName)());
116
+ if (tripIds.length === 0)
117
+ return "no trips on the ledger yet.";
118
+ return `${tripIds.length} trip(s):\n${tripIds.map((id) => `- ${id}`).join("\n")}`;
119
+ },
120
+ summaryKeys: [],
121
+ },
122
+ {
123
+ tool: {
124
+ type: "function",
125
+ function: {
126
+ name: "trip_get",
127
+ description: "Read one trip record by id. Returns a structured summary plus the raw JSON for further reasoning.",
128
+ parameters: {
129
+ type: "object",
130
+ properties: {
131
+ tripId: { type: "string", description: "Canonical trip id (trip_<slug>_<fingerprint>)." },
132
+ },
133
+ required: ["tripId"],
134
+ },
135
+ },
136
+ },
137
+ handler: async (args, ctx) => {
138
+ if (!trustAllowsTripAccess(ctx))
139
+ return "trip ledger is private; this tool is only available in trusted contexts.";
140
+ const tripId = args.tripId;
141
+ if (typeof tripId !== "string" || tripId.length === 0)
142
+ return "tripId is required.";
143
+ try {
144
+ const trip = (0, store_1.readTripRecord)((0, identity_1.getAgentName)(), tripId);
145
+ return [
146
+ renderTripSummary(trip),
147
+ "",
148
+ "raw record (JSON):",
149
+ JSON.stringify(trip, null, 2),
150
+ ].join("\n");
151
+ }
152
+ catch (error) {
153
+ if (error instanceof store_1.TripNotFoundError)
154
+ return error.message;
155
+ throw error;
156
+ }
157
+ },
158
+ summaryKeys: ["tripId"],
159
+ },
160
+ {
161
+ tool: {
162
+ type: "function",
163
+ function: {
164
+ name: "trip_upsert",
165
+ description: "Create or replace a TripRecord. Pass the full record as a JSON string in `record`. Every leg requires a legId and an evidence array (each evidence entry requires messageId + discoveryMethod). Returns the persisted tripId.",
166
+ parameters: {
167
+ type: "object",
168
+ properties: {
169
+ record: { type: "string", description: "Full TripRecord JSON. Must include tripId, agentId, ownerEmail, name, status, travellers[], legs[], createdAt, updatedAt." },
170
+ },
171
+ required: ["record"],
172
+ },
173
+ },
174
+ },
175
+ handler: async (args, ctx) => {
176
+ if (!trustAllowsTripAccess(ctx))
177
+ return "trip ledger is private; this tool is only available in trusted contexts.";
178
+ try {
179
+ const parsed = parseJsonArg(args.record, "record");
180
+ const trip = validateTripRecord(parsed);
181
+ (0, store_1.ensureAgentTripLedger)({ agentName: (0, identity_1.getAgentName)() });
182
+ (0, store_1.upsertTripRecord)((0, identity_1.getAgentName)(), trip);
183
+ return `trip upserted: ${trip.tripId} (${trip.legs.length} leg(s), status=${trip.status})`;
184
+ }
185
+ catch (error) {
186
+ return `upsert failed: ${error instanceof Error ? error.message : /* v8 ignore next -- non-Error throw is unreachable from validateTripRecord/parseJsonArg */ String(error)}`;
187
+ }
188
+ },
189
+ summaryKeys: [],
190
+ },
191
+ {
192
+ tool: {
193
+ type: "function",
194
+ function: {
195
+ name: "trip_attach_evidence",
196
+ description: "Append a TripEvidence record to a specific leg's evidence array. Pass tripId, legId, and the evidence as a JSON string. Useful when extracting a fact from a single mail message and attaching it to an existing leg without re-uploading the whole record.",
197
+ parameters: {
198
+ type: "object",
199
+ properties: {
200
+ tripId: { type: "string", description: "Canonical trip id." },
201
+ legId: { type: "string", description: "Leg id within the trip." },
202
+ evidence: { type: "string", description: "TripEvidence JSON: { messageId, reason, recordedAt, discoveryMethod, excerpt? }." },
203
+ },
204
+ required: ["tripId", "legId", "evidence"],
205
+ },
206
+ },
207
+ },
208
+ handler: async (args, ctx) => {
209
+ if (!trustAllowsTripAccess(ctx))
210
+ return "trip ledger is private; this tool is only available in trusted contexts.";
211
+ const tripId = args.tripId;
212
+ const legId = args.legId;
213
+ if (typeof tripId !== "string" || tripId.length === 0)
214
+ return "tripId is required.";
215
+ if (typeof legId !== "string" || legId.length === 0)
216
+ return "legId is required.";
217
+ try {
218
+ const evidence = validateTripEvidence(parseJsonArg(args.evidence, "evidence"));
219
+ const trip = (0, store_1.readTripRecord)((0, identity_1.getAgentName)(), tripId);
220
+ const legIndex = trip.legs.findIndex((leg) => leg.legId === legId);
221
+ if (legIndex === -1)
222
+ return `leg ${legId} not found in trip ${tripId}.`;
223
+ const leg = trip.legs[legIndex];
224
+ const updatedLeg = {
225
+ ...leg,
226
+ evidence: [...leg.evidence, evidence],
227
+ updatedAt: evidence.recordedAt,
228
+ };
229
+ const updated = {
230
+ ...trip,
231
+ legs: [...trip.legs.slice(0, legIndex), updatedLeg, ...trip.legs.slice(legIndex + 1)],
232
+ updatedAt: evidence.recordedAt,
233
+ };
234
+ (0, store_1.upsertTripRecord)((0, identity_1.getAgentName)(), updated);
235
+ (0, runtime_1.emitNervesEvent)({
236
+ component: "trips",
237
+ event: "trips.evidence_attached",
238
+ message: "trip evidence attached to leg",
239
+ meta: { agentId: (0, identity_1.getAgentName)(), tripId, legId, discoveryMethod: evidence.discoveryMethod, messageId: evidence.messageId },
240
+ });
241
+ return `evidence attached to leg ${legId} in ${tripId}; leg now carries ${updatedLeg.evidence.length} evidence entries.`;
242
+ }
243
+ catch (error) {
244
+ if (error instanceof store_1.TripNotFoundError)
245
+ return error.message;
246
+ return `attach failed: ${error instanceof Error ? error.message : /* v8 ignore next -- non-Error throw is unreachable from validateTripEvidence/parseJsonArg/store */ String(error)}`;
247
+ }
248
+ },
249
+ summaryKeys: ["tripId", "legId"],
250
+ },
251
+ {
252
+ tool: {
253
+ type: "function",
254
+ function: {
255
+ name: "trip_new_id",
256
+ description: "Compute a deterministic trip id from agentId + name + createdAt. Useful before constructing a new TripRecord so the id is stable and reproducible.",
257
+ parameters: {
258
+ type: "object",
259
+ properties: {
260
+ name: { type: "string", description: "Human-friendly trip name (e.g. \"Europe summer 2026\")." },
261
+ createdAt: { type: "string", description: "ISO timestamp the trip was first conceived. Pass `now` if just creating it." },
262
+ },
263
+ required: ["name", "createdAt"],
264
+ },
265
+ },
266
+ },
267
+ handler: async (args, ctx) => {
268
+ if (!trustAllowsTripAccess(ctx))
269
+ return "trip ledger is private; this tool is only available in trusted contexts.";
270
+ const name = args.name;
271
+ const createdAt = args.createdAt;
272
+ if (typeof name !== "string" || name.length === 0)
273
+ return "name is required.";
274
+ if (typeof createdAt !== "string" || createdAt.length === 0)
275
+ return "createdAt is required.";
276
+ return (0, core_1.newTripId)((0, identity_1.getAgentName)(), name, createdAt);
277
+ },
278
+ summaryKeys: ["name"],
279
+ },
280
+ ];
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ // Harness-side trip ledger primitive.
3
+ //
4
+ // Mirrors the substrate's @ouro/work-protocol/src/trip.ts contract (vendored
5
+ // the same way mailroom/core.ts vendors mail). Slugger's framing: today,
6
+ // doc-edits-from-mail keep falling back on freeform parsing because there is
7
+ // no structured object between "mail body" and "travel doc". TripRecord +
8
+ // TripLeg are that object — every leg fact carries non-optional provenance
9
+ // (TripEvidence with discoveryMethod) so the ledger can be audited cleanly
10
+ // and reasoned about under conflict.
11
+ //
12
+ // Per-agent ledger keypair design: in v1 each agent has ONE ledger keypair.
13
+ // All TripRecord blobs are encrypted with that key. Cross-trip sharing
14
+ // (handing one trip's facts to another party without their owning the whole
15
+ // ledger) is a follow-on; it would shard to per-trip keys, which the
16
+ // substrate's TripLedgerRegistry can already represent.
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.generateTripKeyPair = generateTripKeyPair;
52
+ exports.encryptTripRecord = encryptTripRecord;
53
+ exports.decryptTripRecord = decryptTripRecord;
54
+ exports.newTripId = newTripId;
55
+ exports.newLegId = newLegId;
56
+ exports.newTripLedgerRecord = newTripLedgerRecord;
57
+ const crypto = __importStar(require("node:crypto"));
58
+ // ── Helpers: keys + crypto ─────────────────────────────────────────
59
+ function safeLabel(label) {
60
+ return label.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
61
+ }
62
+ function generateTripKeyPair(label) {
63
+ const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
64
+ modulusLength: 2048,
65
+ publicKeyEncoding: { type: "spki", format: "pem" },
66
+ privateKeyEncoding: { type: "pkcs8", format: "pem" },
67
+ });
68
+ const slug = safeLabel(label) || "ledger";
69
+ const fingerprint = crypto.createHash("sha256").update(publicKey).digest("hex").slice(0, 16);
70
+ return {
71
+ keyId: `trip_${slug}_${fingerprint}`,
72
+ publicKeyPem: publicKey,
73
+ privateKeyPem: privateKey,
74
+ };
75
+ }
76
+ function encryptTripRecord(trip, publicKeyPem, keyId) {
77
+ const contentKey = crypto.randomBytes(32);
78
+ const iv = crypto.randomBytes(12);
79
+ const cipher = crypto.createCipheriv("aes-256-gcm", contentKey, iv);
80
+ const ciphertext = Buffer.concat([cipher.update(Buffer.from(JSON.stringify(trip), "utf-8")), cipher.final()]);
81
+ const authTag = cipher.getAuthTag();
82
+ const wrappedKey = crypto.publicEncrypt({ key: publicKeyPem, oaepHash: "sha256" }, contentKey);
83
+ return {
84
+ algorithm: "RSA-OAEP-SHA256+A256GCM",
85
+ keyId,
86
+ wrappedKey: wrappedKey.toString("base64"),
87
+ iv: iv.toString("base64"),
88
+ authTag: authTag.toString("base64"),
89
+ ciphertext: ciphertext.toString("base64"),
90
+ };
91
+ }
92
+ function decryptTripRecord(payload, privateKeyPem) {
93
+ const contentKey = crypto.privateDecrypt({
94
+ key: privateKeyPem,
95
+ oaepHash: "sha256",
96
+ }, Buffer.from(payload.wrappedKey, "base64"));
97
+ const decipher = crypto.createDecipheriv("aes-256-gcm", contentKey, Buffer.from(payload.iv, "base64"));
98
+ decipher.setAuthTag(Buffer.from(payload.authTag, "base64"));
99
+ const plaintext = Buffer.concat([
100
+ decipher.update(Buffer.from(payload.ciphertext, "base64")),
101
+ decipher.final(),
102
+ ]);
103
+ return JSON.parse(plaintext.toString("utf-8"));
104
+ }
105
+ // ── Helpers: deterministic ids ─────────────────────────────────────
106
+ function newTripId(agentId, name, createdAt) {
107
+ const fingerprint = crypto.createHash("sha256")
108
+ .update(`${agentId}\n${name}\n${createdAt}`)
109
+ .digest("hex")
110
+ .slice(0, 16);
111
+ const slug = safeLabel(name) || "trip";
112
+ return `trip_${slug}_${fingerprint}`;
113
+ }
114
+ function newLegId(input) {
115
+ const distinguish = input.vendor || input.confirmationCode || crypto.randomUUID();
116
+ const fingerprint = crypto.createHash("sha256")
117
+ .update(`${input.tripId}\n${input.kind}\n${distinguish}\n${input.createdAt}`)
118
+ .digest("hex")
119
+ .slice(0, 16);
120
+ return `leg_${input.kind}_${fingerprint}`;
121
+ }
122
+ // ── Ledger record helpers ──────────────────────────────────────────
123
+ function newTripLedgerRecord(input) {
124
+ const now = (input.now ?? (() => new Date().toISOString()))();
125
+ const keypair = generateTripKeyPair(input.label ?? input.agentId);
126
+ const ledgerId = `ledger_${safeLabel(input.agentId) || "agent"}_${crypto.createHash("sha256").update(`${input.agentId}\n${now}\n${keypair.keyId}`).digest("hex").slice(0, 16)}`;
127
+ return {
128
+ ledger: {
129
+ schemaVersion: 1,
130
+ agentId: input.agentId,
131
+ ledgerId,
132
+ keyId: keypair.keyId,
133
+ publicKeyPem: keypair.publicKeyPem,
134
+ createdAt: now,
135
+ },
136
+ keypair,
137
+ };
138
+ }
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.TripNotFoundError = void 0;
37
+ exports.ensureAgentTripLedger = ensureAgentTripLedger;
38
+ exports.readAgentTripKeypair = readAgentTripKeypair;
39
+ exports.upsertTripRecord = upsertTripRecord;
40
+ exports.readTripRecord = readTripRecord;
41
+ exports.listTripIds = listTripIds;
42
+ const fs = __importStar(require("node:fs"));
43
+ const path = __importStar(require("node:path"));
44
+ const identity_1 = require("../heart/identity");
45
+ const runtime_1 = require("../nerves/runtime");
46
+ const core_1 = require("./core");
47
+ function tripsRoot(agentName) {
48
+ return path.join((0, identity_1.getAgentRoot)(agentName), "state", "trips");
49
+ }
50
+ function ledgerPath(agentName) {
51
+ return path.join(tripsRoot(agentName), "ledger.json");
52
+ }
53
+ function recordsDir(agentName) {
54
+ return path.join(tripsRoot(agentName), "records");
55
+ }
56
+ function recordPath(agentName, tripId) {
57
+ return path.join(recordsDir(agentName), `${tripId}.json`);
58
+ }
59
+ function readJsonFile(filePath) {
60
+ if (!fs.existsSync(filePath))
61
+ return null;
62
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
63
+ }
64
+ function writeJsonAtomic(filePath, value) {
65
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
66
+ const tmp = `${filePath}.tmp`;
67
+ fs.writeFileSync(tmp, JSON.stringify(value, null, 2), "utf-8");
68
+ fs.renameSync(tmp, filePath);
69
+ }
70
+ class TripNotFoundError extends Error {
71
+ statusCode = 404;
72
+ constructor(input) {
73
+ super(`trip not found: agent=${input.agentName} trip=${input.tripId}`);
74
+ }
75
+ }
76
+ exports.TripNotFoundError = TripNotFoundError;
77
+ /**
78
+ * Idempotent — if the agent already has a ledger on disk, return it; otherwise
79
+ * generate a fresh keypair and persist both halves.
80
+ */
81
+ function ensureAgentTripLedger(input) {
82
+ const existing = readJsonFile(ledgerPath(input.agentName));
83
+ if (existing) {
84
+ return { ledger: existing.ledger, added: false };
85
+ }
86
+ const created = (0, core_1.newTripLedgerRecord)({
87
+ agentId: input.agentName,
88
+ ...(input.label ? { label: input.label } : {}),
89
+ ...(input.now ? { now: input.now } : {}),
90
+ });
91
+ const stored = {
92
+ schemaVersion: 1,
93
+ ledger: created.ledger,
94
+ privateKeyPem: created.keypair.privateKeyPem,
95
+ };
96
+ writeJsonAtomic(ledgerPath(input.agentName), stored);
97
+ (0, runtime_1.emitNervesEvent)({
98
+ component: "trips",
99
+ event: "trips.ledger_created",
100
+ message: "agent trip ledger keypair created",
101
+ meta: { agentId: input.agentName, ledgerId: created.ledger.ledgerId, keyId: created.ledger.keyId },
102
+ });
103
+ return { ledger: created.ledger, added: true };
104
+ }
105
+ function readLedgerOrThrow(agentName) {
106
+ const stored = readJsonFile(ledgerPath(agentName));
107
+ if (!stored) {
108
+ throw new Error(`no trip ledger for agent ${agentName} — call ensureAgentTripLedger first`);
109
+ }
110
+ return stored;
111
+ }
112
+ function readAgentTripKeypair(agentName) {
113
+ const stored = readLedgerOrThrow(agentName);
114
+ return {
115
+ keyId: stored.ledger.keyId,
116
+ publicKeyPem: stored.ledger.publicKeyPem,
117
+ privateKeyPem: stored.privateKeyPem,
118
+ };
119
+ }
120
+ function upsertTripRecord(agentName, trip) {
121
+ const stored = readLedgerOrThrow(agentName);
122
+ const payload = (0, core_1.encryptTripRecord)(trip, stored.ledger.publicKeyPem, stored.ledger.keyId);
123
+ writeJsonAtomic(recordPath(agentName, trip.tripId), payload);
124
+ (0, runtime_1.emitNervesEvent)({
125
+ component: "trips",
126
+ event: "trips.record_upserted",
127
+ message: "trip record upserted",
128
+ meta: { agentId: agentName, tripId: trip.tripId, legCount: trip.legs.length, status: trip.status },
129
+ });
130
+ }
131
+ function readTripRecord(agentName, tripId) {
132
+ const payload = readJsonFile(recordPath(agentName, tripId));
133
+ if (!payload)
134
+ throw new TripNotFoundError({ agentName, tripId });
135
+ const stored = readLedgerOrThrow(agentName);
136
+ return (0, core_1.decryptTripRecord)(payload, stored.privateKeyPem);
137
+ }
138
+ function listTripIds(agentName) {
139
+ const dir = recordsDir(agentName);
140
+ if (!fs.existsSync(dir))
141
+ return [];
142
+ return fs.readdirSync(dir)
143
+ .filter((entry) => entry.endsWith(".json"))
144
+ .map((entry) => entry.slice(0, -".json".length))
145
+ .sort();
146
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.488",
3
+ "version": "0.1.0-alpha.489",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",