@funyard-org/cli 0.0.0-stage → 0.6.2
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/README.md +141 -2
- package/package.json +35 -4
- package/src/agent-selection.js +74 -0
- package/src/agent-setup.js +41 -0
- package/src/agent-skill.md +21 -0
- package/src/agents.js +14 -0
- package/src/browser-login.js +107 -0
- package/src/capabilities.js +3 -0
- package/src/cli-loopback.js +37 -0
- package/src/cli.js +324 -0
- package/src/client.d.ts +97 -0
- package/src/client.js +462 -0
- package/src/config.js +81 -0
- package/src/mcp.js +400 -0
- package/src/protocol/index.d.ts +5598 -0
- package/src/protocol/index.js +828 -0
package/src/cli.js
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { agentFlags } from './agents.js';
|
|
3
|
+
import { randomBytes } from 'node:crypto';
|
|
4
|
+
import { parseArgs } from 'node:util';
|
|
5
|
+
import { FunyardClient, FunyardError, DEFAULT_BASE_URL, validateBaseURL, redact, safeError } from './client.js';
|
|
6
|
+
import { readConfig, writeConfig, readSecret } from './config.js';
|
|
7
|
+
import { browserLogin } from './browser-login.js';
|
|
8
|
+
import { setupAgent } from './agent-setup.js';
|
|
9
|
+
import { selectAgent } from './agent-selection.js';
|
|
10
|
+
import { runMCP } from './mcp.js';
|
|
11
|
+
|
|
12
|
+
const HELP = `funyard — independent client (Node >=22)
|
|
13
|
+
Usage: funyard <command> [options]
|
|
14
|
+
Commands:
|
|
15
|
+
setup [--agent ${agentFlags}] Detect your Agent, install Agent instructions, authorize in browser and verify access
|
|
16
|
+
login | auth login Authorize through your browser (use --key for a hidden API key prompt)
|
|
17
|
+
agent setup [--agent ${agentFlags}] Detect your Agent, install FunYard instructions for your Agent
|
|
18
|
+
logout Remove the stored API key (not environment variables)
|
|
19
|
+
config [--base-url ORIGIN] Show safe config or set API origin
|
|
20
|
+
me Show account
|
|
21
|
+
explore [DOMAIN] [--capability ID | --group ID | --search TEXT]
|
|
22
|
+
Browse what you can call: domains -> capabilities -> field groups -> fields
|
|
23
|
+
call CAPABILITY --arg NAME=VALUE ... [--fields A,B]
|
|
24
|
+
Call any listed capability, including ones newer than this CLI
|
|
25
|
+
capabilities [ID] List capabilities or show one
|
|
26
|
+
fields [--category NAME] List field ids, their billing group and group price
|
|
27
|
+
fields-quote --fields A,B Dry-run price for a field list (nothing reserved)
|
|
28
|
+
quote SYMBOL | --symbol SYMBOL finance.quote
|
|
29
|
+
get --symbol SYMBOL --fields A,B finance.get: fields by id, charged per field group
|
|
30
|
+
crypto --coin ID --fields A,B crypto.get: e.g. --coin bitcoin --fields crypto.market.price
|
|
31
|
+
economy --series ID --fields A,B economy.get: e.g. --series UNRATE --fields economy.latest.value
|
|
32
|
+
tiktok|instagram|youtube --handle NAME --fields A,B public social profiles, e.g. tiktok.profile.followers
|
|
33
|
+
linkedin --company SLUG --fields A,B linkedin.get: company pages only, e.g. --company stripe
|
|
34
|
+
trending --platform weibo social.trending: current hot-search list
|
|
35
|
+
search --query TEXT web.search
|
|
36
|
+
news --query TEXT news.search
|
|
37
|
+
read --url URL web.read
|
|
38
|
+
company --domain DOMAIN company.discover
|
|
39
|
+
research --query TEXT research.search
|
|
40
|
+
status --request-id ID | --call-id ID
|
|
41
|
+
usage Show latest 100 account ledger entries
|
|
42
|
+
cancel --call-id ID
|
|
43
|
+
mcp Serve newline-delimited stdio MCP JSON-RPC
|
|
44
|
+
Options: --base-url ORIGIN, --request-id ID (required for paid calls), --json, --help, --version
|
|
45
|
+
Search/news: --language LANGUAGE --region REGION; research: --language LANGUAGE
|
|
46
|
+
Start with \`funyard explore\`; every level shows prices, arguments and whether this key may call it.
|
|
47
|
+
MCP: one tool per available capability (finance_get, crypto_get, web_search, ...) plus explore, get_call, cancel_call, me, capabilities, fields, fields_quote.
|
|
48
|
+
Paid calls require an explicit stable request_id; no client generates one silently.
|
|
49
|
+
Authentication: FUNYARD_API_KEY or a permission-600 config file.
|
|
50
|
+
Do not pass API keys as command-line arguments or URL parameters.
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
let activeKey;
|
|
54
|
+
let jsonOutput = process.argv.includes('--json');
|
|
55
|
+
function readable(value, prefix = '') {
|
|
56
|
+
if (Array.isArray(value))
|
|
57
|
+
return value.length
|
|
58
|
+
? value.map((item, index) => readable(item, `${prefix}${index + 1}. `)).join('\n')
|
|
59
|
+
: `${prefix}(none)`;
|
|
60
|
+
if (value && typeof value === 'object') {
|
|
61
|
+
return Object.entries(value)
|
|
62
|
+
.map(([key, item]) =>
|
|
63
|
+
item && typeof item === 'object'
|
|
64
|
+
? `${prefix}${key}:\n${readable(item, `${prefix} `)}`
|
|
65
|
+
: `${prefix}${key}: ${item === null ? '—' : String(item)}`,
|
|
66
|
+
)
|
|
67
|
+
.join('\n');
|
|
68
|
+
}
|
|
69
|
+
return `${prefix}${String(value)}`;
|
|
70
|
+
}
|
|
71
|
+
async function main() {
|
|
72
|
+
let parsed;
|
|
73
|
+
try {
|
|
74
|
+
parsed = parseArgs({
|
|
75
|
+
allowPositionals: true,
|
|
76
|
+
strict: true,
|
|
77
|
+
options: {
|
|
78
|
+
'base-url': { type: 'string' },
|
|
79
|
+
key: { type: 'boolean' },
|
|
80
|
+
'no-browser': { type: 'boolean' },
|
|
81
|
+
agent: { type: 'string' },
|
|
82
|
+
'request-id': { type: 'string' },
|
|
83
|
+
'call-id': { type: 'string' },
|
|
84
|
+
capability: { type: 'string' },
|
|
85
|
+
group: { type: 'string' },
|
|
86
|
+
search: { type: 'string' },
|
|
87
|
+
arg: { type: 'string', multiple: true },
|
|
88
|
+
symbol: { type: 'string' },
|
|
89
|
+
coin: { type: 'string' },
|
|
90
|
+
series: { type: 'string' },
|
|
91
|
+
handle: { type: 'string' },
|
|
92
|
+
company: { type: 'string' },
|
|
93
|
+
platform: { type: 'string' },
|
|
94
|
+
fields: { type: 'string' },
|
|
95
|
+
category: { type: 'string' },
|
|
96
|
+
query: { type: 'string' },
|
|
97
|
+
url: { type: 'string' },
|
|
98
|
+
domain: { type: 'string' },
|
|
99
|
+
language: { type: 'string' },
|
|
100
|
+
region: { type: 'string' },
|
|
101
|
+
start: { type: 'string' },
|
|
102
|
+
end: { type: 'string' },
|
|
103
|
+
page: { type: 'string' },
|
|
104
|
+
'page-size': { type: 'string' },
|
|
105
|
+
help: { type: 'boolean', short: 'h' },
|
|
106
|
+
version: { type: 'boolean' },
|
|
107
|
+
json: { type: 'boolean' },
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
} catch {
|
|
111
|
+
throw new FunyardError('Invalid command or options. Run funyard --help. API keys are never accepted as arguments.');
|
|
112
|
+
}
|
|
113
|
+
const { values: flags, positionals } = parsed;
|
|
114
|
+
jsonOutput = Boolean(flags.json);
|
|
115
|
+
if (flags.help || (!positionals.length && !flags.version))
|
|
116
|
+
return process.stdout.write(jsonOutput ? `${JSON.stringify({ help: HELP })}\n` : HELP);
|
|
117
|
+
if (flags.version) return process.stdout.write(jsonOutput ? '{"version":"0.6.2"}\n' : '0.6.2\n');
|
|
118
|
+
let [command, ...rest] = positionals;
|
|
119
|
+
if (command === 'auth' && rest[0] === 'login') {
|
|
120
|
+
command = 'login';
|
|
121
|
+
rest = rest.slice(1);
|
|
122
|
+
}
|
|
123
|
+
const allowed = {
|
|
124
|
+
setup: ['agent', 'no-browser'],
|
|
125
|
+
login: ['key', 'no-browser'],
|
|
126
|
+
agent: ['agent'],
|
|
127
|
+
logout: [],
|
|
128
|
+
config: [],
|
|
129
|
+
me: [],
|
|
130
|
+
capabilities: [],
|
|
131
|
+
fields: ['category'],
|
|
132
|
+
explore: ['domain', 'capability', 'group', 'search'],
|
|
133
|
+
call: ['arg', 'fields', 'request-id'],
|
|
134
|
+
'fields-quote': ['fields'],
|
|
135
|
+
usage: [],
|
|
136
|
+
mcp: [],
|
|
137
|
+
quote: ['symbol', 'request-id'],
|
|
138
|
+
get: ['symbol', 'fields', 'request-id'],
|
|
139
|
+
crypto: ['coin', 'fields', 'request-id'],
|
|
140
|
+
economy: ['series', 'fields', 'request-id'],
|
|
141
|
+
tiktok: ['handle', 'fields', 'request-id'],
|
|
142
|
+
instagram: ['handle', 'fields', 'request-id'],
|
|
143
|
+
youtube: ['handle', 'fields', 'request-id'],
|
|
144
|
+
linkedin: ['company', 'fields', 'request-id'],
|
|
145
|
+
trending: ['platform', 'request-id'],
|
|
146
|
+
search: ['query', 'request-id', 'language', 'region'],
|
|
147
|
+
news: ['query', 'request-id', 'language', 'region'],
|
|
148
|
+
read: ['url', 'request-id'],
|
|
149
|
+
company: ['domain', 'request-id'],
|
|
150
|
+
research: ['query', 'request-id', 'language'],
|
|
151
|
+
status: ['call-id', 'request-id'],
|
|
152
|
+
cancel: ['call-id'],
|
|
153
|
+
};
|
|
154
|
+
if (
|
|
155
|
+
!Object.hasOwn(allowed, command) ||
|
|
156
|
+
rest.length > (['capabilities', 'quote', 'agent', 'explore', 'call'].includes(command) ? 1 : 0) ||
|
|
157
|
+
Object.keys(flags).some(flag => !['base-url', 'json', ...allowed[command]].includes(flag))
|
|
158
|
+
)
|
|
159
|
+
throw new FunyardError('Invalid command or options. Run funyard --help.');
|
|
160
|
+
if (command === 'quote' && rest[0]) {
|
|
161
|
+
if (flags.symbol !== undefined) throw new FunyardError('Provide a positional symbol or --symbol, not both.');
|
|
162
|
+
flags.symbol = rest[0];
|
|
163
|
+
}
|
|
164
|
+
if (command === 'agent') {
|
|
165
|
+
if (rest[0] !== 'setup') throw new FunyardError(`Use funyard agent setup [--agent ${agentFlags}].`);
|
|
166
|
+
const installed = await setupAgent(await selectAgent(flags.agent));
|
|
167
|
+
return process.stdout.write(JSON.stringify(installed) + '\n');
|
|
168
|
+
}
|
|
169
|
+
const config = await readConfig();
|
|
170
|
+
const baseURL = validateBaseURL(
|
|
171
|
+
flags['base-url'] || process.env.FUNYARD_BASE_URL || config.baseURL || DEFAULT_BASE_URL,
|
|
172
|
+
);
|
|
173
|
+
const credentialOrigin = Object.hasOwn(config, 'credentialOrigin') ? config.credentialOrigin : config.baseURL;
|
|
174
|
+
const print = value => {
|
|
175
|
+
const safe = redact(value, activeKey);
|
|
176
|
+
process.stdout.write(`${jsonOutput ? safe : readable(JSON.parse(safe))}\n`);
|
|
177
|
+
};
|
|
178
|
+
if (command === 'config') {
|
|
179
|
+
if (flags['base-url'])
|
|
180
|
+
await writeConfig({
|
|
181
|
+
...config,
|
|
182
|
+
...(config.apiKey ? { credentialOrigin: credentialOrigin || null } : {}),
|
|
183
|
+
baseURL,
|
|
184
|
+
});
|
|
185
|
+
return print({ baseURL, hasStoredKey: Boolean(config.apiKey), credentialOrigin: credentialOrigin || null });
|
|
186
|
+
}
|
|
187
|
+
if (command === 'logout') {
|
|
188
|
+
const { apiKey, ...remaining } = config;
|
|
189
|
+
await writeConfig(remaining);
|
|
190
|
+
return print({ loggedOut: true, environmentKeyStillSet: Boolean(process.env.FUNYARD_API_KEY) });
|
|
191
|
+
}
|
|
192
|
+
if (command === 'login' || command === 'setup') {
|
|
193
|
+
// Refuse conflicting Agent instructions before starting an authorization request.
|
|
194
|
+
const installed = command === 'setup' ? await setupAgent(await selectAgent(flags.agent)) : undefined;
|
|
195
|
+
// Per-origin installation secret survives logout and failed grants; never print it or put it in a URL.
|
|
196
|
+
const installations =
|
|
197
|
+
config.installations && typeof config.installations === 'object' && !Array.isArray(config.installations)
|
|
198
|
+
? config.installations
|
|
199
|
+
: {};
|
|
200
|
+
const installationSecret =
|
|
201
|
+
typeof installations[baseURL] === 'string' && /^[A-Za-z0-9_-]{43}$/.test(installations[baseURL])
|
|
202
|
+
? installations[baseURL]
|
|
203
|
+
: randomBytes(32).toString('base64url');
|
|
204
|
+
if (!flags.key) {
|
|
205
|
+
config.installations = { ...installations, [baseURL]: installationSecret };
|
|
206
|
+
await writeConfig(config);
|
|
207
|
+
}
|
|
208
|
+
activeKey = flags.key
|
|
209
|
+
? await readSecret()
|
|
210
|
+
: (await browserLogin(baseURL, { noBrowser: flags['no-browser'], installationSecret })).apiKey;
|
|
211
|
+
// Persist a rotated credential before read-only verification; a transient /me failure must not lose it.
|
|
212
|
+
if (!flags.key) await writeConfig({ ...config, baseURL, credentialOrigin: baseURL, apiKey: activeKey });
|
|
213
|
+
const client = new FunyardClient({ apiKey: activeKey, baseURL });
|
|
214
|
+
await client.me();
|
|
215
|
+
await writeConfig({ ...config, baseURL, credentialOrigin: baseURL, apiKey: activeKey });
|
|
216
|
+
if (installed) {
|
|
217
|
+
await client.capabilities();
|
|
218
|
+
return print({
|
|
219
|
+
loggedIn: true,
|
|
220
|
+
baseURL,
|
|
221
|
+
agent: installed.agent,
|
|
222
|
+
skill: installed.path,
|
|
223
|
+
verified: true,
|
|
224
|
+
next: installed.next,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return print({ loggedIn: true, baseURL, environmentKeyStillSet: Boolean(process.env.FUNYARD_API_KEY) });
|
|
228
|
+
}
|
|
229
|
+
if (
|
|
230
|
+
process.env.FUNYARD_API_KEY &&
|
|
231
|
+
!flags['base-url'] &&
|
|
232
|
+
!process.env.FUNYARD_BASE_URL &&
|
|
233
|
+
baseURL !== DEFAULT_BASE_URL
|
|
234
|
+
)
|
|
235
|
+
throw new FunyardError(
|
|
236
|
+
'An environment API key cannot implicitly use a non-default origin from config. Set FUNYARD_BASE_URL or pass --base-url explicitly, or unset FUNYARD_API_KEY to use the origin-bound stored key.',
|
|
237
|
+
{ code: 'EXPLICIT_ORIGIN_REQUIRED' },
|
|
238
|
+
);
|
|
239
|
+
if (!process.env.FUNYARD_API_KEY && config.apiKey && credentialOrigin !== baseURL)
|
|
240
|
+
throw new FunyardError(
|
|
241
|
+
'Stored API key belongs to a different or unknown origin. Run funyard login --base-url with the intended origin; legacy credentials.json is never imported automatically.',
|
|
242
|
+
);
|
|
243
|
+
activeKey = process.env.FUNYARD_API_KEY || config.apiKey;
|
|
244
|
+
const client = new FunyardClient({ apiKey: activeKey, baseURL });
|
|
245
|
+
if (command === 'mcp') return runMCP(client);
|
|
246
|
+
if (command === 'me') return print(await client.me());
|
|
247
|
+
if (command === 'capabilities') return print(await client.capabilities(rest[0], { raw: jsonOutput }));
|
|
248
|
+
if (command === 'usage') return print(await client.usage());
|
|
249
|
+
if (command === 'fields') return print(await client.fields(flags.category));
|
|
250
|
+
if (command === 'explore') {
|
|
251
|
+
if (rest[0] !== undefined && flags.domain !== undefined)
|
|
252
|
+
throw new FunyardError('Provide a positional domain or --domain, not both.');
|
|
253
|
+
return print(
|
|
254
|
+
await client.explore({
|
|
255
|
+
domain: rest[0] ?? flags.domain,
|
|
256
|
+
capability: flags.capability,
|
|
257
|
+
group: flags.group,
|
|
258
|
+
q: flags.search,
|
|
259
|
+
}),
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
if (command === 'call') {
|
|
263
|
+
// Any capability the catalog lists, including ones added after this release: --arg name=value.
|
|
264
|
+
if (!rest[0]) throw new FunyardError('Missing capability, e.g. funyard call crypto.get --arg coin=bitcoin.');
|
|
265
|
+
const args = {};
|
|
266
|
+
for (const pair of flags.arg ?? []) {
|
|
267
|
+
const at = pair.indexOf('=');
|
|
268
|
+
if (at < 1 || Object.hasOwn(args, pair.slice(0, at)))
|
|
269
|
+
throw new FunyardError('Each --arg is name=value, once per name.', { code: 'INVALID_ARGUMENTS' });
|
|
270
|
+
args[pair.slice(0, at)] = pair.slice(at + 1);
|
|
271
|
+
}
|
|
272
|
+
if (flags.fields !== undefined) {
|
|
273
|
+
if (Object.hasOwn(args, 'fields')) throw new FunyardError('Provide fields once.');
|
|
274
|
+
args.fields = flags.fields;
|
|
275
|
+
}
|
|
276
|
+
return print(await client.call(rest[0], args, { requestId: flags['request-id'] }));
|
|
277
|
+
}
|
|
278
|
+
if (command === 'fields-quote') {
|
|
279
|
+
if (!flags.fields?.trim()) throw new FunyardError('Missing --fields.');
|
|
280
|
+
return print(await client.fieldsQuote(flags.fields));
|
|
281
|
+
}
|
|
282
|
+
if (command === 'status')
|
|
283
|
+
return print(await client.status({ callId: flags['call-id'], requestId: flags['request-id'] }));
|
|
284
|
+
if (command === 'cancel') return print(await client.cancel(flags['call-id']));
|
|
285
|
+
const mapping = {
|
|
286
|
+
quote: ['finance.quote', 'symbol'],
|
|
287
|
+
get: ['finance.get', 'symbol'],
|
|
288
|
+
crypto: ['crypto.get', 'coin'],
|
|
289
|
+
economy: ['economy.get', 'series'],
|
|
290
|
+
tiktok: ['tiktok.get', 'handle'],
|
|
291
|
+
instagram: ['instagram.get', 'handle'],
|
|
292
|
+
youtube: ['youtube.get', 'handle'],
|
|
293
|
+
linkedin: ['linkedin.get', 'company'],
|
|
294
|
+
trending: ['social.trending', 'platform'],
|
|
295
|
+
search: ['web.search', 'query'],
|
|
296
|
+
news: ['news.search', 'query'],
|
|
297
|
+
read: ['web.read', 'url'],
|
|
298
|
+
company: ['company.discover', 'domain'],
|
|
299
|
+
research: ['research.search', 'query'],
|
|
300
|
+
};
|
|
301
|
+
const [capability, field] = mapping[command];
|
|
302
|
+
if (!flags[field]?.trim()) throw new FunyardError(`Missing --${field}.`);
|
|
303
|
+
const fielded = ['get', 'crypto', 'economy', 'tiktok', 'instagram', 'youtube', 'linkedin'].includes(command);
|
|
304
|
+
if (fielded && !flags.fields?.trim()) throw new FunyardError('Missing --fields.');
|
|
305
|
+
const requestId = flags['request-id'];
|
|
306
|
+
const response = await client.call(
|
|
307
|
+
capability,
|
|
308
|
+
{
|
|
309
|
+
[field]: flags[field],
|
|
310
|
+
...(fielded ? { fields: flags.fields } : {}),
|
|
311
|
+
...(flags.language === undefined ? {} : { language: flags.language }),
|
|
312
|
+
...(flags.region === undefined ? {} : { region: flags.region }),
|
|
313
|
+
},
|
|
314
|
+
{ requestId },
|
|
315
|
+
);
|
|
316
|
+
print(response);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
main().catch(error => {
|
|
320
|
+
const safe = redact(safeError(error), activeKey);
|
|
321
|
+
// Stderr remains structured even when stdout uses the human-readable view.
|
|
322
|
+
process.stderr.write(`${safe}\n`);
|
|
323
|
+
process.exitCode = 1;
|
|
324
|
+
});
|
package/src/client.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Arguments,
|
|
3
|
+
CapabilityId,
|
|
4
|
+
CallResponse,
|
|
5
|
+
CatalogResponse,
|
|
6
|
+
FieldsCatalogResponse,
|
|
7
|
+
FieldsQuoteResponse,
|
|
8
|
+
MeResponse,
|
|
9
|
+
UsageResponse,
|
|
10
|
+
} from './protocol/index.js';
|
|
11
|
+
export type {
|
|
12
|
+
Arguments,
|
|
13
|
+
BillingLine,
|
|
14
|
+
CapabilityId,
|
|
15
|
+
Result,
|
|
16
|
+
EmptyResult,
|
|
17
|
+
CallResponse,
|
|
18
|
+
CallSubmission,
|
|
19
|
+
CatalogResponse,
|
|
20
|
+
FieldsCatalogResponse,
|
|
21
|
+
FieldsQuoteResponse,
|
|
22
|
+
MeResponse,
|
|
23
|
+
UsageResponse,
|
|
24
|
+
ErrorResponse,
|
|
25
|
+
} from './protocol/index.js';
|
|
26
|
+
export { REQUEST_ID, CALL_ID } from './protocol/index.js';
|
|
27
|
+
export const DEFAULT_BASE_URL: string;
|
|
28
|
+
export interface ClientOptions {
|
|
29
|
+
apiKey?: string;
|
|
30
|
+
FUNYARD_API_KEY?: string;
|
|
31
|
+
baseURL?: string;
|
|
32
|
+
FUNYARD_BASE_URL?: string;
|
|
33
|
+
timeoutMs?: number;
|
|
34
|
+
fetch?: typeof fetch;
|
|
35
|
+
onDiagnostic?: (event: Record<string, unknown>) => void;
|
|
36
|
+
}
|
|
37
|
+
export class FunyardError extends Error {
|
|
38
|
+
status?: number;
|
|
39
|
+
code?: string;
|
|
40
|
+
requestId?: string;
|
|
41
|
+
callId?: string;
|
|
42
|
+
reason?: string;
|
|
43
|
+
retryAfter?: number;
|
|
44
|
+
constructor(
|
|
45
|
+
message: string,
|
|
46
|
+
options?: {
|
|
47
|
+
status?: number;
|
|
48
|
+
code?: string;
|
|
49
|
+
requestId?: string;
|
|
50
|
+
callId?: string;
|
|
51
|
+
reason?: string;
|
|
52
|
+
retryAfter?: number;
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
export class FunyardClient {
|
|
57
|
+
constructor(options?: ClientOptions, extra?: Pick<ClientOptions, 'timeoutMs' | 'fetch' | 'onDiagnostic'>);
|
|
58
|
+
readonly apiKey: string;
|
|
59
|
+
readonly baseURL: string;
|
|
60
|
+
me(signal?: AbortSignal): Promise<MeResponse>;
|
|
61
|
+
capabilities(id?: CapabilityId, options?: { signal?: AbortSignal }): Promise<CatalogResponse>;
|
|
62
|
+
usage(options?: Record<string, never>): Promise<UsageResponse>;
|
|
63
|
+
fields(category?: string, options?: { signal?: AbortSignal }): Promise<FieldsCatalogResponse>;
|
|
64
|
+
fieldsQuote(fields: string | readonly string[], options?: { signal?: AbortSignal }): Promise<FieldsQuoteResponse>;
|
|
65
|
+
call<C extends CapabilityId>(
|
|
66
|
+
capability: C,
|
|
67
|
+
args: Arguments<C>,
|
|
68
|
+
options: { requestId: string; signal?: AbortSignal },
|
|
69
|
+
): Promise<CallResponse<C>>;
|
|
70
|
+
create<C extends CapabilityId>(
|
|
71
|
+
capability: C,
|
|
72
|
+
requestId: string,
|
|
73
|
+
args: Arguments<C>,
|
|
74
|
+
signal?: AbortSignal,
|
|
75
|
+
): Promise<CallResponse<C>>;
|
|
76
|
+
status(
|
|
77
|
+
ids: { callId: string; requestId?: never } | { callId?: never; requestId: string },
|
|
78
|
+
signal?: AbortSignal,
|
|
79
|
+
): Promise<CallResponse>;
|
|
80
|
+
getCall(
|
|
81
|
+
ids: { call_id: string; request_id?: never } | { call_id?: never; request_id: string },
|
|
82
|
+
signal?: AbortSignal,
|
|
83
|
+
): Promise<CallResponse>;
|
|
84
|
+
cancel(callId: string, signal?: AbortSignal): Promise<CallResponse>;
|
|
85
|
+
request(
|
|
86
|
+
path: string,
|
|
87
|
+
options?: { method?: 'GET' | 'POST'; body?: unknown; requestId?: string; callId?: string; signal?: AbortSignal },
|
|
88
|
+
): Promise<unknown>;
|
|
89
|
+
request(
|
|
90
|
+
method: 'GET' | 'POST',
|
|
91
|
+
route: string,
|
|
92
|
+
options?: { body?: unknown; requestID?: string; callID?: string; signal?: AbortSignal },
|
|
93
|
+
): Promise<unknown>;
|
|
94
|
+
}
|
|
95
|
+
export function validateBaseURL(value: string): string;
|
|
96
|
+
export function safeError(error: unknown): Record<string, unknown>;
|
|
97
|
+
export function redact(value: unknown, secret?: string): string;
|