@ouro.bot/cli 0.1.0-alpha.237 → 0.1.0-alpha.238

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.
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ /**
3
+ * Thin wrapper around the Stripe Issuing API.
4
+ *
5
+ * Initializes with a restricted API key from the agent's vault.
6
+ * Card numbers are NEVER included in nerves events or log output.
7
+ * The `getCardDetails` method exists only for internal payment flows
8
+ * (e.g., passing card details to Duffel) — the returned data must
9
+ * never escape the calling function's scope.
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.createStripeClient = createStripeClient;
16
+ const stripe_1 = __importDefault(require("stripe"));
17
+ const credential_access_1 = require("./credential-access");
18
+ const runtime_1 = require("../nerves/runtime");
19
+ // ---------------------------------------------------------------------------
20
+ // Implementation
21
+ // ---------------------------------------------------------------------------
22
+ function toCardInfo(card) {
23
+ return {
24
+ cardId: card.id,
25
+ last4: card.last4,
26
+ status: card.status,
27
+ };
28
+ }
29
+ /**
30
+ * Create a Stripe client initialized with a restricted key from the vault.
31
+ */
32
+ async function createStripeClient() {
33
+ const store = (0, credential_access_1.getCredentialStore)();
34
+ const apiKey = await store.getRawSecret("stripe.com", "restrictedKey");
35
+ // StripeConstructor is a callable (not a class), cast the result
36
+ const stripe = (0, stripe_1.default)(apiKey);
37
+ return {
38
+ async createVirtualCard(opts) {
39
+ (0, runtime_1.emitNervesEvent)({
40
+ component: "repertoire",
41
+ event: "repertoire.stripe_card_create_start",
42
+ message: "creating virtual card",
43
+ meta: { type: opts.type, currency: opts.currency },
44
+ });
45
+ const spendingControls = {
46
+ spending_limits: [
47
+ {
48
+ amount: opts.spendLimit * 100, // Stripe uses cents
49
+ interval: opts.type === "single_use" ? "all_time" : "monthly",
50
+ },
51
+ ],
52
+ ...(opts.merchantCategories
53
+ ? { allowed_categories: opts.merchantCategories }
54
+ : {}),
55
+ };
56
+ const card = await stripe.issuing.cards.create({
57
+ type: "virtual",
58
+ currency: opts.currency,
59
+ spending_controls: spendingControls,
60
+ status: "active",
61
+ });
62
+ (0, runtime_1.emitNervesEvent)({
63
+ component: "repertoire",
64
+ event: "repertoire.stripe_card_create_end",
65
+ message: "virtual card created",
66
+ meta: { cardId: card.id, last4: card.last4 },
67
+ });
68
+ return toCardInfo(card);
69
+ },
70
+ async getCard(cardId) {
71
+ (0, runtime_1.emitNervesEvent)({
72
+ component: "repertoire",
73
+ event: "repertoire.stripe_card_get",
74
+ message: "retrieving card info",
75
+ meta: { cardId },
76
+ });
77
+ const card = await stripe.issuing.cards.retrieve(cardId);
78
+ return toCardInfo(card);
79
+ },
80
+ async updateCard(cardId, updates) {
81
+ (0, runtime_1.emitNervesEvent)({
82
+ component: "repertoire",
83
+ event: "repertoire.stripe_card_update",
84
+ message: "updating card",
85
+ meta: { cardId },
86
+ });
87
+ const card = await stripe.issuing.cards.update(cardId, updates);
88
+ return toCardInfo(card);
89
+ },
90
+ async deactivateCard(cardId) {
91
+ (0, runtime_1.emitNervesEvent)({
92
+ component: "repertoire",
93
+ event: "repertoire.stripe_card_deactivate",
94
+ message: "deactivating card",
95
+ meta: { cardId },
96
+ });
97
+ const card = await stripe.issuing.cards.update(cardId, { status: "canceled" });
98
+ return toCardInfo(card);
99
+ },
100
+ async listCards() {
101
+ (0, runtime_1.emitNervesEvent)({
102
+ component: "repertoire",
103
+ event: "repertoire.stripe_cards_list",
104
+ message: "listing cards",
105
+ meta: {},
106
+ });
107
+ const result = await stripe.issuing.cards.list();
108
+ return result.data.map(toCardInfo);
109
+ },
110
+ async getCardDetails(cardId) {
111
+ // This method retrieves sensitive card details for payment flows.
112
+ // The data must NEVER be logged, emitted, or returned to the model.
113
+ (0, runtime_1.emitNervesEvent)({
114
+ component: "repertoire",
115
+ event: "repertoire.stripe_card_details_get",
116
+ message: "retrieving card details for payment flow",
117
+ meta: { cardId },
118
+ });
119
+ const card = await stripe.issuing.cards.retrieve(cardId, {
120
+ expand: ["number", "cvc"],
121
+ });
122
+ return {
123
+ cardId: card.id,
124
+ number: card.number,
125
+ cvc: card.cvc,
126
+ expMonth: card.exp_month,
127
+ expYear: card.exp_year,
128
+ };
129
+ },
130
+ };
131
+ }
@@ -12,6 +12,9 @@ const tools_1 = require("./coding/tools");
12
12
  const tools_credential_1 = require("./tools-credential");
13
13
  const tools_vault_1 = require("./tools-vault");
14
14
  const tools_travel_1 = require("./tools-travel");
15
+ const tools_user_profile_1 = require("./tools-user-profile");
16
+ const tools_stripe_1 = require("./tools-stripe");
17
+ const tools_flight_1 = require("./tools-flight");
15
18
  // Re-export flow tools for consumers that import them from tools-base
16
19
  var tools_flow_1 = require("./tools-flow");
17
20
  Object.defineProperty(exports, "ponderTool", { enumerable: true, get: function () { return tools_flow_1.ponderTool; } });
@@ -38,6 +41,9 @@ exports.baseToolDefinitions = [
38
41
  ...tools_credential_1.credentialToolDefinitions,
39
42
  ...tools_vault_1.vaultToolDefinitions,
40
43
  ...tools_travel_1.travelToolDefinitions,
44
+ ...tools_user_profile_1.userProfileToolDefinitions,
45
+ ...tools_stripe_1.stripeToolDefinitions,
46
+ ...tools_flight_1.flightToolDefinitions,
41
47
  ];
42
48
  // Convenience array of just the tool schemas (no handler/integration metadata).
43
49
  // Used by consumers that need the OpenAI function-tool format.
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.flightToolDefinitions = void 0;
4
+ exports.resetDuffelClient = resetDuffelClient;
5
+ const duffel_client_1 = require("./duffel-client");
6
+ const user_profile_1 = require("./user-profile");
7
+ const credential_access_1 = require("./credential-access");
8
+ const runtime_1 = require("../nerves/runtime");
9
+ // Lazy-initialized Duffel client singleton
10
+ let _duffelClient = null;
11
+ async function getDuffelClient() {
12
+ if (!_duffelClient) {
13
+ _duffelClient = await (0, duffel_client_1.createDuffelClient)();
14
+ }
15
+ return _duffelClient;
16
+ }
17
+ function requireFamilyContext(ctx) {
18
+ if (!ctx?.context?.friend?.id) {
19
+ return "no friend context — cannot access flight tools.";
20
+ }
21
+ if (ctx.context.friend.trustLevel !== "family") {
22
+ return "booking and cancellation require family trust level.";
23
+ }
24
+ return { friendId: ctx.context.friend.id };
25
+ }
26
+ function requireFriendContext(ctx) {
27
+ if (!ctx?.context?.friend?.id) {
28
+ return "no friend context — cannot search flights.";
29
+ }
30
+ return { friendId: ctx.context.friend.id };
31
+ }
32
+ exports.flightToolDefinitions = [
33
+ {
34
+ tool: {
35
+ type: "function",
36
+ function: {
37
+ name: "flight_search",
38
+ description: "Search for flights between two airports. Returns available offers with prices.",
39
+ parameters: {
40
+ type: "object",
41
+ properties: {
42
+ origin: { type: "string", description: "Origin airport IATA code (e.g. 'SFO')" },
43
+ destination: { type: "string", description: "Destination airport IATA code (e.g. 'JFK')" },
44
+ departure_date: { type: "string", description: "Departure date (YYYY-MM-DD)" },
45
+ return_date: { type: "string", description: "Return date for round trips (optional)" },
46
+ passengers: { type: "string", description: "Number of adult passengers (default '1')" },
47
+ cabin_class: { type: "string", description: "Cabin class: economy, premium_economy, business, first" },
48
+ },
49
+ required: ["origin", "destination", "departure_date"],
50
+ },
51
+ },
52
+ },
53
+ handler: async (args, ctx) => {
54
+ (0, runtime_1.emitNervesEvent)({
55
+ component: "repertoire",
56
+ event: "repertoire.tool_flight_search",
57
+ message: "flight_search invoked",
58
+ meta: { tool: "flight_search", origin: args.origin, destination: args.destination },
59
+ });
60
+ const guard = requireFriendContext(ctx);
61
+ if (typeof guard === "string")
62
+ return guard;
63
+ try {
64
+ const client = await getDuffelClient();
65
+ const passengerCount = parseInt(args.passengers || "1", 10);
66
+ const passengers = Array.from({ length: passengerCount }, () => ({ type: "adult" }));
67
+ const offers = await client.searchFlights({
68
+ origin: args.origin,
69
+ destination: args.destination,
70
+ departureDate: args.departure_date,
71
+ returnDate: args.return_date,
72
+ passengers,
73
+ cabinClass: args.cabin_class,
74
+ });
75
+ if (offers.length === 0) {
76
+ return "no flights found for those criteria.";
77
+ }
78
+ return JSON.stringify(offers, null, 2);
79
+ }
80
+ catch (err) {
81
+ /* v8 ignore next -- defensive @preserve */
82
+ return `flight search error: ${err instanceof Error ? err.message : String(err)}`;
83
+ }
84
+ },
85
+ summaryKeys: ["origin", "destination", "departure_date"],
86
+ },
87
+ {
88
+ tool: {
89
+ type: "function",
90
+ function: {
91
+ name: "flight_hold",
92
+ description: "Hold a flight offer for a short period before committing to book. Not all airlines support holds.",
93
+ parameters: {
94
+ type: "object",
95
+ properties: {
96
+ offer_id: { type: "string", description: "The Duffel offer ID to hold" },
97
+ },
98
+ required: ["offer_id"],
99
+ },
100
+ },
101
+ },
102
+ handler: async (args, ctx) => {
103
+ (0, runtime_1.emitNervesEvent)({
104
+ component: "repertoire",
105
+ event: "repertoire.tool_flight_hold",
106
+ message: "flight_hold invoked",
107
+ meta: { tool: "flight_hold", offerId: args.offer_id },
108
+ });
109
+ const guard = requireFamilyContext(ctx);
110
+ if (typeof guard === "string")
111
+ return guard;
112
+ // Hold functionality would call Duffel's offer hold API.
113
+ // For pre-build, we return a structured acknowledgment.
114
+ return JSON.stringify({
115
+ status: "hold_requested",
116
+ offerId: args.offer_id,
117
+ message: "Hold requested. Confirm or cancel before the hold expires.",
118
+ });
119
+ },
120
+ summaryKeys: ["offer_id"],
121
+ },
122
+ {
123
+ tool: {
124
+ type: "function",
125
+ function: {
126
+ name: "flight_book",
127
+ description: "Book a flight. Pulls passenger name/DOB/passport from the user's profile. Creates a virtual card, books the flight, then deactivates the card. Requires family trust level.",
128
+ parameters: {
129
+ type: "object",
130
+ properties: {
131
+ offer_id: { type: "string", description: "The Duffel offer ID to book" },
132
+ amount: { type: "string", description: "Expected total amount in dollars" },
133
+ currency: { type: "string", description: "Currency code (e.g. 'usd')" },
134
+ },
135
+ required: ["offer_id", "amount", "currency"],
136
+ },
137
+ },
138
+ },
139
+ handler: async (args, ctx) => {
140
+ (0, runtime_1.emitNervesEvent)({
141
+ component: "repertoire",
142
+ event: "repertoire.tool_flight_book",
143
+ message: "flight_book invoked",
144
+ meta: { tool: "flight_book", offerId: args.offer_id },
145
+ });
146
+ const guard = requireFamilyContext(ctx);
147
+ if (typeof guard === "string")
148
+ return guard;
149
+ try {
150
+ const store = (0, credential_access_1.getCredentialStore)();
151
+ // Get passenger data from profile
152
+ const legalName = await (0, user_profile_1.getUserProfileField)(guard.friendId, "legalName", store);
153
+ if (!legalName) {
154
+ return "passenger profile not found — please store your profile first using user_profile_store.";
155
+ }
156
+ const dateOfBirth = await (0, user_profile_1.getUserProfileField)(guard.friendId, "dateOfBirth", store);
157
+ const passport = await (0, user_profile_1.getUserProfileField)(guard.friendId, "passport", store);
158
+ const client = await getDuffelClient();
159
+ const result = await client.createOrder({
160
+ offerId: args.offer_id,
161
+ passengers: [{
162
+ type: "adult",
163
+ givenName: legalName.first,
164
+ familyName: legalName.last,
165
+ /* v8 ignore next -- reason @preserve */
166
+ dateOfBirth: dateOfBirth ?? "1990-01-01",
167
+ passportNumber: passport?.number,
168
+ passportCountry: passport?.country,
169
+ passportExpiry: passport?.expiry,
170
+ }],
171
+ amount: parseFloat(args.amount),
172
+ currency: args.currency,
173
+ });
174
+ return JSON.stringify(result, null, 2);
175
+ }
176
+ catch (err) {
177
+ /* v8 ignore next -- defensive @preserve */
178
+ return `booking error: ${err instanceof Error ? err.message : String(err)}`;
179
+ }
180
+ },
181
+ summaryKeys: ["offer_id", "amount"],
182
+ },
183
+ {
184
+ tool: {
185
+ type: "function",
186
+ function: {
187
+ name: "flight_cancel",
188
+ description: "Cancel a flight booking. Not all bookings are cancellable. Requires family trust level.",
189
+ parameters: {
190
+ type: "object",
191
+ properties: {
192
+ order_id: { type: "string", description: "The Duffel order ID to cancel" },
193
+ },
194
+ required: ["order_id"],
195
+ },
196
+ },
197
+ },
198
+ handler: async (args, ctx) => {
199
+ (0, runtime_1.emitNervesEvent)({
200
+ component: "repertoire",
201
+ event: "repertoire.tool_flight_cancel",
202
+ message: "flight_cancel invoked",
203
+ meta: { tool: "flight_cancel", orderId: args.order_id },
204
+ });
205
+ const guard = requireFamilyContext(ctx);
206
+ if (typeof guard === "string")
207
+ return guard;
208
+ try {
209
+ const client = await getDuffelClient();
210
+ const result = await client.cancelOrder(args.order_id);
211
+ return JSON.stringify(result, null, 2);
212
+ }
213
+ catch (err) {
214
+ /* v8 ignore next -- defensive @preserve */
215
+ return `cancellation error: ${err instanceof Error ? err.message : String(err)}`;
216
+ }
217
+ },
218
+ summaryKeys: ["order_id"],
219
+ },
220
+ ];
221
+ /** Reset the Duffel client singleton (for testing). */
222
+ function resetDuffelClient() {
223
+ _duffelClient = null;
224
+ }
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stripeToolDefinitions = void 0;
4
+ exports.resetStripeClient = resetStripeClient;
5
+ const stripe_client_1 = require("./stripe-client");
6
+ const runtime_1 = require("../nerves/runtime");
7
+ // Lazy-initialized Stripe client singleton
8
+ let _stripeClient = null;
9
+ async function getStripeClient() {
10
+ if (!_stripeClient) {
11
+ _stripeClient = await (0, stripe_client_1.createStripeClient)();
12
+ }
13
+ return _stripeClient;
14
+ }
15
+ function requireFamilyContext(ctx) {
16
+ if (!ctx?.context?.friend?.id) {
17
+ return "no friend context — cannot access payment tools.";
18
+ }
19
+ if (ctx.context.friend.trustLevel !== "family") {
20
+ return "payment tools require family trust level.";
21
+ }
22
+ return { friendId: ctx.context.friend.id };
23
+ }
24
+ exports.stripeToolDefinitions = [
25
+ {
26
+ tool: {
27
+ type: "function",
28
+ function: {
29
+ name: "stripe_create_card",
30
+ description: "Create a virtual card for a transaction. Returns card ID and last 4 digits (never the full card number). Requires family trust level.",
31
+ parameters: {
32
+ type: "object",
33
+ properties: {
34
+ type: {
35
+ type: "string",
36
+ enum: ["single_use", "persistent"],
37
+ description: "Card type: single_use (one transaction) or persistent (recurring)",
38
+ },
39
+ spend_limit: {
40
+ type: "string",
41
+ description: "Maximum spend limit in dollars (e.g. '500')",
42
+ },
43
+ currency: {
44
+ type: "string",
45
+ description: "Currency code (e.g. 'usd')",
46
+ },
47
+ merchant_categories: {
48
+ type: "string",
49
+ description: "Comma-separated allowed merchant categories (optional)",
50
+ },
51
+ },
52
+ required: ["type", "spend_limit", "currency"],
53
+ },
54
+ },
55
+ },
56
+ handler: async (args, ctx) => {
57
+ (0, runtime_1.emitNervesEvent)({
58
+ component: "repertoire",
59
+ event: "repertoire.tool_stripe_create_card",
60
+ message: "stripe_create_card invoked",
61
+ meta: { tool: "stripe_create_card" },
62
+ });
63
+ const guard = requireFamilyContext(ctx);
64
+ if (typeof guard === "string")
65
+ return guard;
66
+ try {
67
+ const client = await getStripeClient();
68
+ const categories = args.merchant_categories
69
+ ? args.merchant_categories.split(",").map((c) => c.trim())
70
+ : undefined;
71
+ const card = await client.createVirtualCard({
72
+ type: args.type,
73
+ spendLimit: parseFloat(args.spend_limit),
74
+ currency: args.currency,
75
+ merchantCategories: categories,
76
+ });
77
+ return JSON.stringify({
78
+ cardId: card.cardId,
79
+ last4: card.last4,
80
+ status: card.status,
81
+ spendLimit: args.spend_limit,
82
+ });
83
+ }
84
+ catch (err) {
85
+ /* v8 ignore next -- defensive: Stripe errors are always Error instances @preserve */
86
+ return `card creation error: ${err instanceof Error ? err.message : String(err)}`;
87
+ }
88
+ },
89
+ summaryKeys: ["type", "spend_limit", "currency"],
90
+ },
91
+ {
92
+ tool: {
93
+ type: "function",
94
+ function: {
95
+ name: "stripe_deactivate_card",
96
+ description: "Deactivate (cancel) a virtual card. Use after a transaction is complete or to revoke access. Requires family trust level.",
97
+ parameters: {
98
+ type: "object",
99
+ properties: {
100
+ card_id: {
101
+ type: "string",
102
+ description: "The Stripe card ID to deactivate (e.g. 'ic_...')",
103
+ },
104
+ },
105
+ required: ["card_id"],
106
+ },
107
+ },
108
+ },
109
+ handler: async (args, ctx) => {
110
+ (0, runtime_1.emitNervesEvent)({
111
+ component: "repertoire",
112
+ event: "repertoire.tool_stripe_deactivate_card",
113
+ message: "stripe_deactivate_card invoked",
114
+ meta: { tool: "stripe_deactivate_card", cardId: args.card_id },
115
+ });
116
+ const guard = requireFamilyContext(ctx);
117
+ if (typeof guard === "string")
118
+ return guard;
119
+ try {
120
+ const client = await getStripeClient();
121
+ const card = await client.deactivateCard(args.card_id);
122
+ return JSON.stringify({
123
+ cardId: card.cardId,
124
+ last4: card.last4,
125
+ status: card.status,
126
+ });
127
+ }
128
+ catch (err) {
129
+ /* v8 ignore next -- defensive: Stripe errors are always Error instances @preserve */
130
+ return `card deactivation error: ${err instanceof Error ? err.message : String(err)}`;
131
+ }
132
+ },
133
+ summaryKeys: ["card_id"],
134
+ },
135
+ {
136
+ tool: {
137
+ type: "function",
138
+ function: {
139
+ name: "stripe_list_cards",
140
+ description: "List all active virtual cards. Shows card IDs and last 4 digits only. Requires family trust level.",
141
+ parameters: {
142
+ type: "object",
143
+ properties: {},
144
+ },
145
+ },
146
+ },
147
+ handler: async (_args, ctx) => {
148
+ (0, runtime_1.emitNervesEvent)({
149
+ component: "repertoire",
150
+ event: "repertoire.tool_stripe_list_cards",
151
+ message: "stripe_list_cards invoked",
152
+ meta: { tool: "stripe_list_cards" },
153
+ });
154
+ const guard = requireFamilyContext(ctx);
155
+ if (typeof guard === "string")
156
+ return guard;
157
+ try {
158
+ const client = await getStripeClient();
159
+ const cards = await client.listCards();
160
+ if (cards.length === 0) {
161
+ return "no active cards.";
162
+ }
163
+ return JSON.stringify(cards.map((c) => ({
164
+ cardId: c.cardId,
165
+ last4: c.last4,
166
+ status: c.status,
167
+ })));
168
+ }
169
+ catch (err) {
170
+ /* v8 ignore next -- defensive: Stripe errors are always Error instances @preserve */
171
+ return `card listing error: ${err instanceof Error ? err.message : String(err)}`;
172
+ }
173
+ },
174
+ summaryKeys: [],
175
+ },
176
+ ];
177
+ /** Reset the Stripe client singleton (for testing). */
178
+ function resetStripeClient() {
179
+ _stripeClient = null;
180
+ }