@sly_ai/cli 0.1.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.
@@ -0,0 +1,134 @@
1
+ import { Command } from 'commander';
2
+ import { createClient } from '../utils/auth.js';
3
+ import { output, error } from '../utils/output.js';
4
+
5
+ export function registerAP2Commands(program: Command) {
6
+ const cmd = program.command('ap2').description('AP2 (Agent-to-Agent Protocol) mandate-based payments');
7
+
8
+ cmd
9
+ .command('mandates')
10
+ .description('List mandates')
11
+ .option('--status <status>', 'Filter by status (active, completed, cancelled, expired)')
12
+ .option('--agent-id <agentId>', 'Filter by agent ID')
13
+ .option('--account-id <accountId>', 'Filter by account ID')
14
+ .option('--limit <limit>', 'Max results', parseInt)
15
+ .action(async (opts) => {
16
+ try {
17
+ const sly = createClient();
18
+ const result = await sly.ap2.listMandates({
19
+ status: opts.status,
20
+ agent_id: opts.agentId,
21
+ account_id: opts.accountId,
22
+ limit: opts.limit,
23
+ });
24
+ output(result);
25
+ } catch (e: unknown) {
26
+ error((e as Error).message);
27
+ }
28
+ });
29
+
30
+ cmd
31
+ .command('get <mandateId>')
32
+ .description('Get mandate details with execution history')
33
+ .action(async (mandateId) => {
34
+ try {
35
+ const sly = createClient();
36
+ const result = await sly.ap2.getMandate(mandateId);
37
+ output(result);
38
+ } catch (e: unknown) {
39
+ error((e as Error).message);
40
+ }
41
+ });
42
+
43
+ cmd
44
+ .command('create')
45
+ .description('Create a spending mandate for an agent')
46
+ .requiredOption('--mandate-id <mandateId>', 'Unique mandate identifier')
47
+ .requiredOption('--agent-id <agentId>', 'UUID of the agent')
48
+ .requiredOption('--account-id <accountId>', 'UUID of the funding account')
49
+ .requiredOption('--authorized-amount <amount>', 'Maximum spending amount', parseFloat)
50
+ .option('--currency <currency>', 'Currency (default: USD)')
51
+ .option('--mandate-type <type>', 'Type: intent, cart, or payment (default: payment)')
52
+ .option('--description <description>', 'Human-readable description')
53
+ .option('--expires-at <date>', 'ISO 8601 expiration timestamp')
54
+ .option('--metadata <json>', 'Metadata as JSON string')
55
+ .action(async (opts) => {
56
+ try {
57
+ const sly = createClient();
58
+ const body: Record<string, unknown> = {
59
+ mandate_id: opts.mandateId,
60
+ agent_id: opts.agentId,
61
+ account_id: opts.accountId,
62
+ authorized_amount: opts.authorizedAmount,
63
+ };
64
+ if (opts.currency) body.currency = opts.currency;
65
+ if (opts.mandateType) body.mandate_type = opts.mandateType;
66
+ if (opts.description) body.description = opts.description;
67
+ if (opts.expiresAt) body.expires_at = opts.expiresAt;
68
+ if (opts.metadata) body.metadata = JSON.parse(opts.metadata);
69
+ const result = await sly.ap2.createMandate(body as Parameters<typeof sly.ap2.createMandate>[0]);
70
+ output(result);
71
+ } catch (e: unknown) {
72
+ error((e as Error).message);
73
+ }
74
+ });
75
+
76
+ cmd
77
+ .command('execute <mandateId>')
78
+ .description('Execute a payment against a mandate')
79
+ .requiredOption('--amount <amount>', 'Amount to pay', parseFloat)
80
+ .option('--currency <currency>', 'Currency')
81
+ .option('--description <description>', 'Description of this payment')
82
+ .action(async (mandateId, opts) => {
83
+ try {
84
+ const sly = createClient();
85
+ const body: Record<string, unknown> = { amount: opts.amount };
86
+ if (opts.currency) body.currency = opts.currency;
87
+ if (opts.description) body.description = opts.description;
88
+ const result = await sly.ap2.executeMandate(mandateId, body as Parameters<typeof sly.ap2.executeMandate>[1]);
89
+ output(result);
90
+ } catch (e: unknown) {
91
+ error((e as Error).message);
92
+ }
93
+ });
94
+
95
+ cmd
96
+ .command('cancel <mandateId>')
97
+ .description('Cancel an active mandate')
98
+ .action(async (mandateId) => {
99
+ try {
100
+ const sly = createClient();
101
+ const result = await sly.ap2.cancelMandate(mandateId);
102
+ output(result);
103
+ } catch (e: unknown) {
104
+ error((e as Error).message);
105
+ }
106
+ });
107
+
108
+ cmd
109
+ .command('update <mandateId>')
110
+ .description('Update a mandate')
111
+ .option('--authorized-amount <amount>', 'New authorized amount', parseFloat)
112
+ .option('--status <status>', 'New status')
113
+ .option('--expires-at <date>', 'New expiration (ISO 8601)')
114
+ .option('--description <description>', 'Updated description')
115
+ .option('--metadata <json>', 'Updated metadata as JSON string')
116
+ .action(async (mandateId, opts) => {
117
+ try {
118
+ const sly = createClient();
119
+ const body: Record<string, unknown> = {};
120
+ if (opts.authorizedAmount !== undefined) body.authorized_amount = opts.authorizedAmount;
121
+ if (opts.status) body.status = opts.status;
122
+ if (opts.expiresAt) body.expires_at = opts.expiresAt;
123
+ if (opts.description) body.description = opts.description;
124
+ if (opts.metadata) body.metadata = JSON.parse(opts.metadata);
125
+ const result = await sly.request(`/v1/ap2/mandates/${mandateId}`, {
126
+ method: 'PATCH',
127
+ body: JSON.stringify(body),
128
+ });
129
+ output(result);
130
+ } catch (e: unknown) {
131
+ error((e as Error).message);
132
+ }
133
+ });
134
+ }
@@ -0,0 +1,66 @@
1
+ import { Command } from 'commander';
2
+ import { output, error } from '../utils/output.js';
3
+
4
+ export function registerEnvCommands(program: Command) {
5
+ const cmd = program.command('env').description('Environment management');
6
+
7
+ cmd
8
+ .command('get')
9
+ .description('Show current environment info')
10
+ .action(() => {
11
+ try {
12
+ const apiKey = process.env.SLY_API_KEY;
13
+ if (!apiKey) {
14
+ error('SLY_API_KEY environment variable is not set');
15
+ }
16
+
17
+ const environment = apiKey.startsWith('pk_live_') ? 'production' : 'sandbox';
18
+ const masked = apiKey.slice(0, 12) + '***';
19
+
20
+ output({
21
+ environment,
22
+ apiKeyPrefix: masked,
23
+ apiUrl: process.env.SLY_API_URL || (environment === 'production' ? 'https://api.getsly.ai' : 'https://sandbox.getsly.ai'),
24
+ });
25
+ } catch (e: unknown) {
26
+ error((e as Error).message);
27
+ }
28
+ });
29
+
30
+ cmd
31
+ .command('switch <environment>')
32
+ .description('Show instructions for switching between sandbox and production')
33
+ .action((environment) => {
34
+ if (environment !== 'sandbox' && environment !== 'production') {
35
+ error('Environment must be "sandbox" or "production"');
36
+ }
37
+
38
+ const prefix = environment === 'production' ? 'pk_live_' : 'pk_test_';
39
+ console.log(`To switch to ${environment}, set your API key:`);
40
+ console.log(` export SLY_API_KEY=${prefix}your_key_here`);
41
+ if (environment === 'production') {
42
+ console.log(` export SLY_API_URL=https://api.getsly.ai`);
43
+ } else {
44
+ console.log(` export SLY_API_URL=https://sandbox.getsly.ai`);
45
+ }
46
+ });
47
+
48
+ cmd
49
+ .command('whoami')
50
+ .description('Get information about the current tenant/organization')
51
+ .action(async () => {
52
+ try {
53
+ const apiKey = process.env.SLY_API_KEY;
54
+ if (!apiKey) {
55
+ error('SLY_API_KEY environment variable is not set');
56
+ }
57
+ // Lazy import to avoid requiring SLY_API_KEY for non-API commands
58
+ const { createClient } = await import('../utils/auth.js');
59
+ const sly = createClient();
60
+ const result = await sly.request('/v1/context/whoami');
61
+ output(result);
62
+ } catch (e: unknown) {
63
+ error((e as Error).message);
64
+ }
65
+ });
66
+ }
@@ -0,0 +1,43 @@
1
+ import { Command } from 'commander';
2
+ import { createClient } from '../utils/auth.js';
3
+ import { output, error } from '../utils/output.js';
4
+
5
+ export function registerMerchantsCommands(program: Command) {
6
+ const cmd = program.command('merchants').description('Browse merchant catalogs');
7
+
8
+ cmd
9
+ .command('list')
10
+ .description('List merchants with product catalogs')
11
+ .option('--type <type>', 'Merchant type filter (e.g., restaurant, bar, hotel, retail)')
12
+ .option('--country <country>', 'Country code filter (e.g., PA, CR)')
13
+ .option('--search <search>', 'Search merchants by name')
14
+ .option('--limit <limit>', 'Max results (default 50, max 100)', parseInt)
15
+ .action(async (opts) => {
16
+ try {
17
+ const sly = createClient();
18
+ const params = new URLSearchParams();
19
+ if (opts.type) params.set('type', opts.type);
20
+ if (opts.country) params.set('country', opts.country);
21
+ if (opts.search) params.set('search', opts.search);
22
+ if (opts.limit) params.set('limit', String(opts.limit));
23
+ const qs = params.toString();
24
+ const result = await sly.request(`/v1/ucp/merchants${qs ? `?${qs}` : ''}`);
25
+ output(result);
26
+ } catch (e: unknown) {
27
+ error((e as Error).message);
28
+ }
29
+ });
30
+
31
+ cmd
32
+ .command('get <merchantId>')
33
+ .description('Get a merchant\'s full product catalog')
34
+ .action(async (merchantId) => {
35
+ try {
36
+ const sly = createClient();
37
+ const result = await sly.request(`/v1/ucp/merchants/${merchantId}`);
38
+ output(result);
39
+ } catch (e: unknown) {
40
+ error((e as Error).message);
41
+ }
42
+ });
43
+ }
@@ -0,0 +1,144 @@
1
+ import { Command } from 'commander';
2
+ import { createClient } from '../utils/auth.js';
3
+ import { output, error } from '../utils/output.js';
4
+
5
+ export function registerMPPCommands(program: Command) {
6
+ const cmd = program.command('mpp').description('MPP (Machine Payments Protocol) sessions and payments');
7
+
8
+ cmd
9
+ .command('sessions')
10
+ .description('List MPP sessions')
11
+ .option('--agent-id <agentId>', 'Filter by agent ID')
12
+ .option('--status <status>', 'Filter by status (active, closed, expired, exhausted)')
13
+ .option('--limit <limit>', 'Results per page', parseInt)
14
+ .option('--offset <offset>', 'Offset for pagination', parseInt)
15
+ .action(async (opts) => {
16
+ try {
17
+ const sly = createClient();
18
+ const result = await sly.mpp.listSessions({
19
+ agent_id: opts.agentId,
20
+ status: opts.status,
21
+ limit: opts.limit,
22
+ offset: opts.offset,
23
+ });
24
+ output(result);
25
+ } catch (e: unknown) {
26
+ error((e as Error).message);
27
+ }
28
+ });
29
+
30
+ cmd
31
+ .command('get-session <id>')
32
+ .description('Get MPP session details with voucher history')
33
+ .action(async (id) => {
34
+ try {
35
+ const sly = createClient();
36
+ const result = await sly.mpp.getSession(id);
37
+ output(result);
38
+ } catch (e: unknown) {
39
+ error((e as Error).message);
40
+ }
41
+ });
42
+
43
+ cmd
44
+ .command('open')
45
+ .description('Open a streaming MPP payment session')
46
+ .requiredOption('--service-url <url>', 'URL of the service')
47
+ .requiredOption('--deposit-amount <amount>', 'Initial deposit amount', parseFloat)
48
+ .requiredOption('--agent-id <agentId>', 'UUID of the agent')
49
+ .requiredOption('--wallet-id <walletId>', 'UUID of the wallet')
50
+ .option('--max-budget <amount>', 'Maximum total budget', parseFloat)
51
+ .option('--currency <currency>', 'Currency (default: USDC)')
52
+ .action(async (opts) => {
53
+ try {
54
+ const sly = createClient();
55
+ const body: Record<string, unknown> = {
56
+ service_url: opts.serviceUrl,
57
+ deposit_amount: opts.depositAmount,
58
+ agent_id: opts.agentId,
59
+ wallet_id: opts.walletId,
60
+ };
61
+ if (opts.maxBudget !== undefined) body.max_budget = opts.maxBudget;
62
+ if (opts.currency) body.currency = opts.currency;
63
+ const result = await sly.mpp.openSession(body as Parameters<typeof sly.mpp.openSession>[0]);
64
+ output(result);
65
+ } catch (e: unknown) {
66
+ error((e as Error).message);
67
+ }
68
+ });
69
+
70
+ cmd
71
+ .command('pay')
72
+ .description('Make a one-shot MPP payment to a service')
73
+ .requiredOption('--service-url <url>', 'URL of the service to pay')
74
+ .requiredOption('--amount <amount>', 'Payment amount', parseFloat)
75
+ .requiredOption('--agent-id <agentId>', 'UUID of the agent making the payment')
76
+ .option('--wallet-id <walletId>', 'UUID of the wallet to pay from')
77
+ .option('--currency <currency>', 'Currency (default: USDC)')
78
+ .option('--intent <intent>', 'Description of what the payment is for')
79
+ .action(async (opts) => {
80
+ try {
81
+ const sly = createClient();
82
+ const body: Record<string, unknown> = {
83
+ service_url: opts.serviceUrl,
84
+ amount: opts.amount,
85
+ agent_id: opts.agentId,
86
+ };
87
+ if (opts.walletId) body.wallet_id = opts.walletId;
88
+ if (opts.currency) body.currency = opts.currency;
89
+ if (opts.intent) body.intent = opts.intent;
90
+ const result = await sly.mpp.pay(body as Parameters<typeof sly.mpp.pay>[0]);
91
+ output(result);
92
+ } catch (e: unknown) {
93
+ error((e as Error).message);
94
+ }
95
+ });
96
+
97
+ cmd
98
+ .command('close <sessionId>')
99
+ .description('Close an active MPP session')
100
+ .action(async (sessionId) => {
101
+ try {
102
+ const sly = createClient();
103
+ const result = await sly.mpp.closeSession(sessionId);
104
+ output(result);
105
+ } catch (e: unknown) {
106
+ error((e as Error).message);
107
+ }
108
+ });
109
+
110
+ cmd
111
+ .command('transfers')
112
+ .description('List MPP payment transfers')
113
+ .option('--service-url <url>', 'Filter by service URL')
114
+ .option('--session-id <id>', 'Filter by session ID')
115
+ .option('--limit <limit>', 'Results per page', parseInt)
116
+ .option('--offset <offset>', 'Offset for pagination', parseInt)
117
+ .action(async (opts) => {
118
+ try {
119
+ const sly = createClient();
120
+ const result = await sly.mpp.listTransfers({
121
+ service_url: opts.serviceUrl,
122
+ session_id: opts.sessionId,
123
+ limit: opts.limit,
124
+ offset: opts.offset,
125
+ });
126
+ output(result);
127
+ } catch (e: unknown) {
128
+ error((e as Error).message);
129
+ }
130
+ });
131
+
132
+ cmd
133
+ .command('verify-receipt <receiptId>')
134
+ .description('Verify an MPP payment receipt')
135
+ .action(async (receiptId) => {
136
+ try {
137
+ const sly = createClient();
138
+ const result = await sly.mpp.verifyReceipt(receiptId);
139
+ output(result);
140
+ } catch (e: unknown) {
141
+ error((e as Error).message);
142
+ }
143
+ });
144
+ }
@@ -0,0 +1,63 @@
1
+ import { Command } from 'commander';
2
+ import { createClient } from '../utils/auth.js';
3
+ import { output, error } from '../utils/output.js';
4
+
5
+ export function registerSettlementCommands(program: Command) {
6
+ const cmd = program.command('settlement').description('Manage settlements and FX quotes');
7
+
8
+ cmd
9
+ .command('quote')
10
+ .description('Get a settlement quote with FX rates and fees')
11
+ .requiredOption('--from-currency <currency>', 'Source currency (USD, BRL, MXN, USDC)')
12
+ .requiredOption('--to-currency <currency>', 'Destination currency (USD, BRL, MXN, USDC)')
13
+ .requiredOption('--amount <amount>', 'Amount to convert')
14
+ .option('--rail <rail>', 'Settlement rail (pix, spei, wire, usdc)')
15
+ .action(async (opts) => {
16
+ try {
17
+ const sly = createClient();
18
+ const quote = await sly.getSettlementQuote({
19
+ fromCurrency: opts.fromCurrency,
20
+ toCurrency: opts.toCurrency,
21
+ amount: opts.amount,
22
+ rail: opts.rail,
23
+ });
24
+ output(quote);
25
+ } catch (e: unknown) {
26
+ error((e as Error).message);
27
+ }
28
+ });
29
+
30
+ cmd
31
+ .command('create')
32
+ .description('Execute a settlement using a quote')
33
+ .requiredOption('--quote-id <quoteId>', 'Quote ID from settlement quote')
34
+ .requiredOption('--destination-account-id <accountId>', 'Destination account ID')
35
+ .option('--metadata <json>', 'Optional metadata as JSON string')
36
+ .action(async (opts) => {
37
+ try {
38
+ const sly = createClient();
39
+ const body: Record<string, unknown> = {
40
+ quoteId: opts.quoteId,
41
+ destinationAccountId: opts.destinationAccountId,
42
+ };
43
+ if (opts.metadata) body.metadata = JSON.parse(opts.metadata);
44
+ const settlement = await sly.createSettlement(body as Parameters<typeof sly.createSettlement>[0]);
45
+ output(settlement);
46
+ } catch (e: unknown) {
47
+ error((e as Error).message);
48
+ }
49
+ });
50
+
51
+ cmd
52
+ .command('status <id>')
53
+ .description('Check the status of a settlement')
54
+ .action(async (id) => {
55
+ try {
56
+ const sly = createClient();
57
+ const settlement = await sly.getSettlement(id);
58
+ output(settlement);
59
+ } catch (e: unknown) {
60
+ error((e as Error).message);
61
+ }
62
+ });
63
+ }
@@ -0,0 +1,110 @@
1
+ import { Command } from 'commander';
2
+ import { createClient } from '../utils/auth.js';
3
+ import { output, error } from '../utils/output.js';
4
+
5
+ export function registerSupportCommands(program: Command) {
6
+ const cmd = program.command('support').description('Support and dispute management');
7
+
8
+ cmd
9
+ .command('explain-rejection')
10
+ .description('Explain why a transaction was rejected')
11
+ .option('--error-code <code>', 'Error code from the rejection')
12
+ .option('--transaction-id <id>', 'UUID of the rejected transaction')
13
+ .option('--agent-id <id>', 'UUID of the agent')
14
+ .action(async (opts) => {
15
+ try {
16
+ const sly = createClient();
17
+ const body: Record<string, unknown> = {};
18
+ if (opts.errorCode) body.error_code = opts.errorCode;
19
+ if (opts.transactionId) body.transaction_id = opts.transactionId;
20
+ if (opts.agentId) body.agent_id = opts.agentId;
21
+ const result = await sly.request('/v1/support/explain-rejection', {
22
+ method: 'POST',
23
+ body: JSON.stringify(body),
24
+ });
25
+ output(result);
26
+ } catch (e: unknown) {
27
+ error((e as Error).message);
28
+ }
29
+ });
30
+
31
+ cmd
32
+ .command('request-limit-increase')
33
+ .description('Submit a request to increase an agent\'s spending limit')
34
+ .requiredOption('--agent-id <agentId>', 'UUID of the agent')
35
+ .requiredOption('--limit-type <type>', 'Which limit to increase (per_transaction, daily, monthly)')
36
+ .requiredOption('--requested-amount <amount>', 'The new desired limit amount (USD)', parseFloat)
37
+ .requiredOption('--reason <reason>', 'Business justification')
38
+ .option('--duration <duration>', 'How long the increase should last (temporary_24h, temporary_7d, permanent)')
39
+ .action(async (opts) => {
40
+ try {
41
+ const sly = createClient();
42
+ const body: Record<string, unknown> = {
43
+ agent_id: opts.agentId,
44
+ limit_type: opts.limitType,
45
+ requested_amount: opts.requestedAmount,
46
+ reason: opts.reason,
47
+ };
48
+ if (opts.duration) body.duration = opts.duration;
49
+ const result = await sly.request('/v1/support/request-limit-increase', {
50
+ method: 'POST',
51
+ body: JSON.stringify(body),
52
+ });
53
+ output(result);
54
+ } catch (e: unknown) {
55
+ error((e as Error).message);
56
+ }
57
+ });
58
+
59
+ cmd
60
+ .command('open-dispute')
61
+ .description('Open a dispute for a completed transaction')
62
+ .requiredOption('--transaction-id <id>', 'UUID of the transaction')
63
+ .requiredOption('--reason <reason>', 'Reason (service_not_received, duplicate_charge, unauthorized, amount_incorrect, quality_issue, other)')
64
+ .requiredOption('--description <description>', 'Detailed description of the issue')
65
+ .option('--requested-resolution <resolution>', 'Requested resolution (full_refund, partial_refund, credit, other)')
66
+ .action(async (opts) => {
67
+ try {
68
+ const sly = createClient();
69
+ const body: Record<string, unknown> = {
70
+ transaction_id: opts.transactionId,
71
+ reason: opts.reason,
72
+ description: opts.description,
73
+ };
74
+ if (opts.requestedResolution) body.requested_resolution = opts.requestedResolution;
75
+ const result = await sly.request('/v1/support/open-dispute', {
76
+ method: 'POST',
77
+ body: JSON.stringify(body),
78
+ });
79
+ output(result);
80
+ } catch (e: unknown) {
81
+ error((e as Error).message);
82
+ }
83
+ });
84
+
85
+ cmd
86
+ .command('escalate')
87
+ .description('Escalate an issue to a human support operator')
88
+ .requiredOption('--reason <reason>', 'Why this is being escalated (complex_issue, agent_requested, security_concern, policy_exception, bug_report)')
89
+ .requiredOption('--summary <summary>', 'Summary of the issue')
90
+ .option('--agent-id <agentId>', 'UUID of the agent')
91
+ .option('--priority <priority>', 'Priority level (low, medium, high, critical)')
92
+ .action(async (opts) => {
93
+ try {
94
+ const sly = createClient();
95
+ const body: Record<string, unknown> = {
96
+ reason: opts.reason,
97
+ summary: opts.summary,
98
+ };
99
+ if (opts.agentId) body.agent_id = opts.agentId;
100
+ if (opts.priority) body.priority = opts.priority;
101
+ const result = await sly.request('/v1/support/escalate', {
102
+ method: 'POST',
103
+ body: JSON.stringify(body),
104
+ });
105
+ output(result);
106
+ } catch (e: unknown) {
107
+ error((e as Error).message);
108
+ }
109
+ });
110
+ }