@eightstate/escli 0.5.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.
Files changed (54) hide show
  1. package/CONVENTIONS.md +59 -0
  2. package/LICENSE +21 -0
  3. package/README.md +106 -0
  4. package/RELEASE-NOTES-0.5.0.md +34 -0
  5. package/dist/base-command.js +166 -0
  6. package/dist/commands/audio/get.js +39 -0
  7. package/dist/commands/audio/index.js +18 -0
  8. package/dist/commands/audio/list.js +39 -0
  9. package/dist/commands/audio/status.js +34 -0
  10. package/dist/commands/audio/transcribe.js +99 -0
  11. package/dist/commands/auth/index.js +18 -0
  12. package/dist/commands/auth/login.js +38 -0
  13. package/dist/commands/auth/logout.js +27 -0
  14. package/dist/commands/auth/profiles.js +31 -0
  15. package/dist/commands/auth/status.js +27 -0
  16. package/dist/commands/auth/switch.js +24 -0
  17. package/dist/commands/docs/fetch.js +37 -0
  18. package/dist/commands/docs/get.js +47 -0
  19. package/dist/commands/docs/index.js +18 -0
  20. package/dist/commands/docs/search.js +55 -0
  21. package/dist/commands/fetch.js +55 -0
  22. package/dist/commands/image/edit.js +59 -0
  23. package/dist/commands/image/generate.js +67 -0
  24. package/dist/commands/image/index.js +18 -0
  25. package/dist/commands/models.js +27 -0
  26. package/dist/commands/research.js +92 -0
  27. package/dist/commands/search.js +54 -0
  28. package/dist/commands/social.js +69 -0
  29. package/dist/commands/usage.js +51 -0
  30. package/dist/commands/version.js +22 -0
  31. package/dist/entry.js +120 -0
  32. package/dist/io/io.js +322 -0
  33. package/dist/lib/build-flags.js +2 -0
  34. package/dist/lib/command-metadata.js +8 -0
  35. package/dist/lib/envelope.js +28 -0
  36. package/dist/lib/escli-error.js +20 -0
  37. package/dist/lib/global-flags.js +29 -0
  38. package/dist/lib/globals.js +2 -0
  39. package/dist/lib/manifest.js +67 -0
  40. package/dist/lib/oclif-manifest-check.js +11 -0
  41. package/dist/lib/registry.js +228 -0
  42. package/dist/services/audio.js +454 -0
  43. package/dist/services/auth.js +329 -0
  44. package/dist/services/credentials.js +137 -0
  45. package/dist/services/docs.js +303 -0
  46. package/dist/services/fetch.js +197 -0
  47. package/dist/services/image.js +297 -0
  48. package/dist/services/models.js +131 -0
  49. package/dist/services/research.js +504 -0
  50. package/dist/services/search.js +195 -0
  51. package/dist/services/social.js +224 -0
  52. package/dist/services/usage.js +165 -0
  53. package/oclif.manifest.json +3377 -0
  54. package/package.json +57 -0
@@ -0,0 +1,54 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { z } from 'zod';
3
+ import { BaseCommand } from '../base-command.js';
4
+ import { writeStderr } from '../io/io.js';
5
+ import { searchWeb } from '../services/search.js';
6
+ import { ErrorCode } from '@eightstate/contracts/errors';
7
+ export const SearchResultSchema = z.object({
8
+ title: z.string(),
9
+ url: z.string(),
10
+ excerpts: z.array(z.string()),
11
+ });
12
+ export const SearchDataSchema = z.object({
13
+ elapsed_seconds: z.number().nonnegative().optional(),
14
+ results: z.array(SearchResultSchema),
15
+ count: z.number().int().nonnegative(),
16
+ });
17
+ export default class Search extends BaseCommand {
18
+ static errors = [ErrorCode.UsageRequired, ErrorCode.ApiUnauthorized, ErrorCode.ApiForbidden, ErrorCode.ApiRateLimited, ErrorCode.ApiError, ErrorCode.GateInvalidResponse, ErrorCode.NetworkError, ErrorCode.NetworkTimeout, ErrorCode.ServiceUnavailable, ErrorCode.SearchFailed];
19
+ static summary = 'Search the web, return ranked excerpts';
20
+ static description = 'Search the web via Parallel Search.';
21
+ static examples = [
22
+ '<%= config.bin %> search "latest cloudflare workers features"',
23
+ '<%= config.bin %> search "react server components" --queries "RSC tutorial" "RSC vs SSR"',
24
+ '<%= config.bin %> --json search "python web frameworks 2026"',
25
+ ];
26
+ static aliases = ['s'];
27
+ static flags = {
28
+ queries: Flags.string({ description: 'Override queries (default: objective).', multiple: true }),
29
+ };
30
+ static args = {
31
+ objective: Args.string({ description: 'Search objective.', required: true, multiple: true }),
32
+ };
33
+ static enableJsonFlag = true;
34
+ static strict = true;
35
+ async execute() {
36
+ const { args, flags } = await this.parse(Search);
37
+ const objective = args.objective.join(' ');
38
+ if (!flags.quiet && !flags.json)
39
+ await writeStderr(` ▸ searching: ${objective}\n`);
40
+ const data = await searchWeb({
41
+ objective,
42
+ queries: flags.queries,
43
+ timeoutSeconds: flags.timeout,
44
+ });
45
+ if (!flags.quiet && !flags.json) {
46
+ if (data.count === 0)
47
+ await writeStderr(' No results found.\n');
48
+ else
49
+ await writeStderr(`\n ${data.count} results · ${data.elapsed_seconds}s\n`);
50
+ }
51
+ return data;
52
+ }
53
+ }
54
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1,69 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { z } from 'zod';
3
+ import { BaseCommand } from '../base-command.js';
4
+ import { writeStderr } from '../io/io.js';
5
+ import { searchSocial, validDepths, validTimeRanges } from '../services/social.js';
6
+ import { ErrorCode } from '@eightstate/contracts/errors';
7
+ export const SocialResultSchema = z.object({
8
+ title: z.string(),
9
+ url: z.string(),
10
+ content: z.string(),
11
+ score: z.number().nullable().optional(),
12
+ raw_content: z.string().nullable().optional(),
13
+ });
14
+ export const SocialDataSchema = z.object({
15
+ answer: z.string().nullable(),
16
+ results: z.array(SocialResultSchema),
17
+ platform: z.string(),
18
+ query: z.string(),
19
+ });
20
+ export default class Social extends BaseCommand {
21
+ static errors = [ErrorCode.UsageRequired, ErrorCode.UsageInvalid, ErrorCode.ValidationFailed, ErrorCode.AuthRequired, ErrorCode.ApiUnauthorized, ErrorCode.ApiForbidden, ErrorCode.ApiRateLimited, ErrorCode.ApiError, ErrorCode.GateInvalidResponse, ErrorCode.NetworkError, ErrorCode.NetworkTimeout, ErrorCode.ServiceUnavailable, ErrorCode.SocialFailed];
22
+ static summary = 'Search posts across social platforms';
23
+ static description = 'Search Reddit, X/Twitter, TikTok, LinkedIn, Instagram, Facebook, YouTube, or combined social domains via Tavily.';
24
+ static examples = [
25
+ '<%= config.bin %> social "what are people saying about Claude Code"',
26
+ '<%= config.bin %> social "AI trends 2026" --platform reddit',
27
+ '<%= config.bin %> social "product reviews" --platform tiktok,instagram --time week',
28
+ '<%= config.bin %> social "company sentiment" --platform linkedin,x --answer',
29
+ '<%= config.bin %> --json --quiet social "brand perception" --platform reddit,x',
30
+ ];
31
+ static aliases = [];
32
+ static flags = {
33
+ platform: Flags.string({ char: 'p', description: 'Platform(s), comma-separated: reddit,x,twitter,tiktok,linkedin,instagram,facebook,youtube,combined.' }),
34
+ time: Flags.option({ char: 't', description: 'Time range.', options: validTimeRanges() })(),
35
+ 'max-results': Flags.integer({ char: 'n', description: 'Max results.', default: 10 }),
36
+ depth: Flags.option({ description: 'Search depth.', options: validDepths(), default: 'advanced' })(),
37
+ answer: Flags.boolean({ char: 'a', description: 'Include AI-synthesized answer.', default: false }),
38
+ raw: Flags.boolean({ description: 'Include full post content.', default: false }),
39
+ images: Flags.boolean({ description: 'Include images.', default: false }),
40
+ country: Flags.string({ description: 'Country for geo-targeting.' }),
41
+ };
42
+ static args = {
43
+ query: Args.string({ description: 'Search query.', required: true, multiple: true }),
44
+ };
45
+ static enableJsonFlag = true;
46
+ static strict = true;
47
+ async execute() {
48
+ const { args, flags } = await this.parse(Social);
49
+ const query = args.query.join(' ');
50
+ if (!flags.quiet && !flags.json)
51
+ await writeStderr(` ▸ searching ${flags.platform ?? 'all platforms'}: ${query}\n`);
52
+ const data = await searchSocial({
53
+ query,
54
+ platform: flags.platform,
55
+ time: flags.time,
56
+ maxResults: flags['max-results'],
57
+ depth: flags.depth,
58
+ answer: flags.answer,
59
+ raw: flags.raw,
60
+ images: flags.images,
61
+ country: flags.country,
62
+ timeoutSeconds: flags.timeout,
63
+ });
64
+ if (!flags.quiet && !flags.json)
65
+ await writeStderr(` ${data.results.length} results · ? credit(s)\n`);
66
+ return data;
67
+ }
68
+ }
69
+ //# sourceMappingURL=social.js.map
@@ -0,0 +1,51 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { z } from 'zod';
3
+ import { BaseCommand } from '../base-command.js';
4
+ import { writeStderr } from '../io/io.js';
5
+ import { fetchUsage, normalizeDays } from '../services/usage.js';
6
+ import { ErrorCode } from '@eightstate/contracts/errors';
7
+ export const UsageDataSchema = z.record(z.string(), z.unknown());
8
+ export default class Usage extends BaseCommand {
9
+ static errors = [ErrorCode.AuthRequired, ErrorCode.AuthInvalid, ErrorCode.AuthForbidden, ErrorCode.ApiRateLimited, ErrorCode.ApiError, ErrorCode.GateUnavailable, ErrorCode.GateInvalidResponse, ErrorCode.NetworkError, ErrorCode.NetworkTimeout, ErrorCode.UsageFetchFailed];
10
+ static summary = 'Show API usage and spend';
11
+ static description = 'Show API usage and spend analytics from the eightstate gate service.';
12
+ static examples = [
13
+ '<%= config.bin %> usage',
14
+ '<%= config.bin %> usage --days 7',
15
+ '<%= config.bin %> usage --service parallel',
16
+ '<%= config.bin %> usage --daily',
17
+ '<%= config.bin %> usage --pricing',
18
+ '<%= config.bin %> --json usage',
19
+ ];
20
+ static aliases = ['u'];
21
+ static flags = {
22
+ days: Flags.integer({ description: 'Window size in days.', default: 30 }),
23
+ service: Flags.string({ description: 'Filter to one service.' }),
24
+ daily: Flags.boolean({ description: 'Day-by-day breakdown.', default: false }),
25
+ pricing: Flags.boolean({ description: 'Show current pricing rules.', default: false }),
26
+ };
27
+ static enableJsonFlag = true;
28
+ static strict = true;
29
+ async execute() {
30
+ const flags = await this.parseFlags(Usage);
31
+ const days = normalizeDays(flags.days);
32
+ if (!flags.quiet && !flags.json)
33
+ await writeStderr(progressLine({ days, service: flags.service, daily: flags.daily, pricing: flags.pricing }));
34
+ return fetchUsage({
35
+ days: flags.days,
36
+ service: flags.service,
37
+ daily: flags.daily,
38
+ pricing: flags.pricing,
39
+ });
40
+ }
41
+ }
42
+ function progressLine(options) {
43
+ if (options.pricing)
44
+ return ' ▸ fetching pricing rules...\n';
45
+ if (options.daily)
46
+ return ` ▸ fetching daily breakdown (${options.days} days)...\n`;
47
+ if (options.service)
48
+ return ` ▸ fetching usage for ${options.service} (${options.days} days)...\n`;
49
+ return ` ▸ fetching usage summary (${options.days} days)...\n`;
50
+ }
51
+ //# sourceMappingURL=usage.js.map
@@ -0,0 +1,22 @@
1
+ import { BaseCommand } from '../base-command.js';
2
+ import { z } from 'zod';
3
+ import { ErrorCode } from '@eightstate/contracts/errors';
4
+ export const VersionDataSchema = z.object({
5
+ version: z.string(),
6
+ endpoint: z.string(),
7
+ });
8
+ export default class Version extends BaseCommand {
9
+ static errors = [ErrorCode.Internal];
10
+ static summary = 'Show escli version';
11
+ static description = 'Print the escli version and active default endpoint.';
12
+ static examples = ['<%= config.bin %> version', '<%= config.bin %> --json version'];
13
+ static enableJsonFlag = true;
14
+ async execute() {
15
+ const flags = await this.parseFlags(Version);
16
+ return {
17
+ version: this.config.version,
18
+ endpoint: flags['base-url'] ?? process.env.ESCLI_BASE_URL ?? 'https://ai.eightstate.co/v1',
19
+ };
20
+ }
21
+ }
22
+ //# sourceMappingURL=version.js.map
package/dist/entry.js ADDED
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+ import { Config, Help } from '@oclif/core';
3
+ import { dirname } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { ErrorCode } from '@eightstate/contracts/errors';
6
+ import { buildErrorEnvelope, buildUsageError } from './lib/envelope.js';
7
+ import { GLOBAL_SHORT_FLAGS, globalFlagTakesValue, isGlobalFlagToken } from './lib/globals.js';
8
+ import { commandRegistry, commandsById } from './lib/registry.js';
9
+ import { writeStdout } from './io/io.js';
10
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
11
+ const pjson = {
12
+ name: '@eightstate/escli',
13
+ version: '0.5.0',
14
+ type: 'module',
15
+ oclif: {
16
+ bin: 'escli',
17
+ enableJsonFlag: true,
18
+ topicSeparator: ' ',
19
+ },
20
+ };
21
+ const plugin = { root: moduleDir, pjson, name: pjson.name, alias: pjson.name, type: 'core', valid: true, commands: [], topics: [], hooks: {}, commandIDs: [] };
22
+ const config = await Config.load({
23
+ root: moduleDir,
24
+ pjson,
25
+ plugins: new Map([[pjson.name, plugin]]),
26
+ });
27
+ const start = Date.now();
28
+ const argv = [...process.argv.slice(2)];
29
+ const globals = [];
30
+ while (argv[0] && isGlobalFlagToken(argv[0])) {
31
+ const flag = argv.shift();
32
+ globals.push(GLOBAL_SHORT_FLAGS.has(flag) ? `--${GLOBAL_SHORT_FLAGS.get(flag)}` : flag);
33
+ if (globalFlagTakesValue(flag) && !flag.includes('=') && argv[0] && !argv[0].startsWith('--')) {
34
+ globals.push(argv.shift());
35
+ }
36
+ }
37
+ const manifestRequested = globals.includes('--describe') || globals.includes('--schema');
38
+ let versionRequested = globals.includes('--version');
39
+ let helpRequested = globals.includes('--help');
40
+ const rootManifestRequested = manifestRequested && argv.length === 0;
41
+ const firstCommandToken = argv.shift() ?? (manifestRequested || versionRequested ? 'version' : undefined);
42
+ let commandId = firstCommandToken;
43
+ let registration = commandId ? commandsById.get(commandId) : undefined;
44
+ if (firstCommandToken && argv.length > 0) {
45
+ const nestedCommandId = `${firstCommandToken} ${argv[0]}`;
46
+ const nestedRegistration = commandsById.get(nestedCommandId);
47
+ if (nestedRegistration) {
48
+ commandId = nestedCommandId;
49
+ registration = nestedRegistration;
50
+ argv.shift();
51
+ }
52
+ }
53
+ if (argv.includes('--help') || argv.includes('-h'))
54
+ helpRequested = true;
55
+ if (argv.includes('--version') || argv.includes('-v'))
56
+ versionRequested = true;
57
+ if (helpRequested) {
58
+ await emitHelp(commandId, argv);
59
+ }
60
+ if (versionRequested) {
61
+ commandId = 'version';
62
+ registration = commandsById.get(commandId);
63
+ argv.length = 0;
64
+ }
65
+ else if (!registration && manifestRequested) {
66
+ commandId = 'version';
67
+ registration = commandsById.get(commandId);
68
+ }
69
+ else if (registration && manifestRequested) {
70
+ if (rootManifestRequested)
71
+ globals.push('--root-manifest');
72
+ globals.push(...argv.filter((token) => token === '--describe' || token === '--schema'));
73
+ argv.length = 0;
74
+ }
75
+ if (helpRequested) {
76
+ process.exitCode = 0;
77
+ }
78
+ else if (!registration) {
79
+ const error = buildUsageError(commandId ? `command ${commandId} not found` : 'command required', commandId ? ErrorCode.UsageUnknownCommand : ErrorCode.UsageRequired);
80
+ process.exitCode = 2;
81
+ await writeStdout(`${JSON.stringify(buildErrorEnvelope(commandId ?? 'unknown', config.version, error, Date.now() - start))}\n`);
82
+ }
83
+ else {
84
+ const CommandClass = registration.command;
85
+ CommandClass.id = registration.id;
86
+ CommandClass.plugin = plugin;
87
+ CommandClass.pluginName = pjson.name;
88
+ CommandClass.pluginAlias = pjson.name;
89
+ CommandClass.pluginType = 'core';
90
+ const instance = new CommandClass([...globals, ...argv], config);
91
+ await instance._run();
92
+ }
93
+ async function emitHelp(commandId, argv) {
94
+ const commands = commandRegistry.map((registration) => {
95
+ const CommandClass = registration.command;
96
+ const staticArgs = CommandClass.args ?? {};
97
+ return {
98
+ aliases: registration.aliases.map((alias) => alias.replaceAll(' ', ':')),
99
+ args: Object.fromEntries(Object.entries(staticArgs).map(([name, arg]) => [name, typeof arg === 'object' && arg ? { ...arg, name } : arg])),
100
+ description: registration.description,
101
+ examples: registration.command.examples,
102
+ flags: Object.fromEntries(Object.entries(registration.flags).map(([name, flag]) => [name, typeof flag === 'object' && flag ? { ...flag, name } : flag])),
103
+ hasDynamicHelp: false,
104
+ hidden: false,
105
+ hiddenAliases: [],
106
+ id: registration.id.replaceAll(' ', ':'),
107
+ load: async () => registration.command,
108
+ pluginAlias: pjson.name,
109
+ pluginName: pjson.name,
110
+ pluginType: 'core',
111
+ strict: registration.command.strict,
112
+ summary: registration.summary,
113
+ };
114
+ });
115
+ const helpPlugin = { ...plugin, commands, commandIDs: commands.map((command) => command.id) };
116
+ const helpConfig = await Config.load({ root: moduleDir, pjson, plugins: new Map([[pjson.name, helpPlugin]]) });
117
+ const help = new Help(helpConfig, { hideAliasesFromRoot: true });
118
+ await help.showHelp(commandId ? [...commandId.split(' '), ...argv, '--help'] : ['--help']);
119
+ }
120
+ //# sourceMappingURL=entry.js.map
package/dist/io/io.js ADDED
@@ -0,0 +1,322 @@
1
+ import { ux } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ export function isPlainOutput() {
4
+ return Boolean(process.env.NO_COLOR) || Boolean(process.env.CI) || !process.stdout.isTTY;
5
+ }
6
+ export function stylize(value, style) {
7
+ return isPlainOutput() ? value : style(value);
8
+ }
9
+ export async function writeStdout(value) {
10
+ process.stdout.write(value);
11
+ }
12
+ export async function writeStderr(value) {
13
+ process.stderr.write(value);
14
+ }
15
+ export function renderKeyValue(rows) {
16
+ return rows.map(([key, value]) => `${chalk.bold(key)} ${value}`).join('\n');
17
+ }
18
+ export function renderModelList(data) {
19
+ const lines = ['', ` ${'MODEL'.padEnd(40)}`, ` ${'─'.repeat(40)}`];
20
+ for (const model of data.models)
21
+ lines.push(` ${model}`);
22
+ lines.push('', ` ${data.count} models available`, '');
23
+ return lines.join('\n');
24
+ }
25
+ export function renderAuthStatus(data) {
26
+ const rows = [['profile', data.profile], ['auth', data.auth_type]];
27
+ if (data.key)
28
+ rows.push(['key', data.key]);
29
+ rows.push(['endpoint', data.endpoint]);
30
+ return renderKeyValue(rows);
31
+ }
32
+ export function renderAuthProfiles(data) {
33
+ if (data.profiles.length === 0)
34
+ return 'no profiles. run: escli auth login';
35
+ const lines = [`${'PROFILE'.padEnd(16)} ${'KEY'.padEnd(24)} ENDPOINT`];
36
+ for (const profile of data.profiles)
37
+ lines.push(`${profile.active ? '*' : ' '} ${profile.name.padEnd(14)} ${profile.key.padEnd(24)} ${profile.endpoint}`);
38
+ lines.push(`${data.count} profile(s)`);
39
+ return lines.join('\n');
40
+ }
41
+ export function renderAuthLogin(data) {
42
+ const rows = [['profile', data.profile], ['auth', data.auth_type]];
43
+ if (data.key)
44
+ rows.push(['key', data.key]);
45
+ if (data.models)
46
+ rows.push(['models', data.models]);
47
+ if (data.endpoint)
48
+ rows.push(['endpoint', data.endpoint]);
49
+ if (data.gate)
50
+ rows.push(['gate', data.gate]);
51
+ return renderKeyValue(rows);
52
+ }
53
+ export function renderAuthSwitch(data) {
54
+ return `switched → ${data.active_profile}`;
55
+ }
56
+ export function renderAuthLogout(data) {
57
+ if (data.message)
58
+ return data.message;
59
+ return `profile '${data.profile ?? ''}' ${data.deleted ? 'removed' : 'not found'}`;
60
+ }
61
+ export function renderDocsSearch(data) {
62
+ if (data.results.length === 0)
63
+ return ' No results found.\n';
64
+ const cacheLabel = data.cached ? ' (cached)' : '';
65
+ const lines = ['', ` Search results${cacheLabel}:`, ''];
66
+ for (const result of data.results) {
67
+ lines.push(` ${(result.id ?? '').padEnd(40)} ${result.title ?? ''}`);
68
+ lines.push(` state=${result.state ?? ''} stars=${result.stars ?? ''} ${(result.description ?? '').slice(0, 80)}`);
69
+ }
70
+ lines.push('');
71
+ return `${lines.join('\n')}\n`;
72
+ }
73
+ export function renderDocsSearchTsv(data) {
74
+ return data.results.map((result) => `${result.id ?? ''}\t${result.title ?? ''}\t${result.state ?? ''}`).join('\n') + (data.results.length > 0 ? '\n' : '');
75
+ }
76
+ export function renderDocsContent(data) {
77
+ return data.content;
78
+ }
79
+ export function renderFetch(data) {
80
+ const lines = [];
81
+ for (const result of data.results) {
82
+ if (result.title)
83
+ lines.push(`\n # ${result.title}`);
84
+ lines.push(` ${result.url}\n`);
85
+ for (const excerpt of result.excerpts ?? [])
86
+ lines.push(excerpt, '');
87
+ if (result.full_content)
88
+ lines.push(result.full_content);
89
+ }
90
+ return lines.join('\n');
91
+ }
92
+ export function renderSearch(data) {
93
+ const lines = [];
94
+ for (const result of data.results) {
95
+ lines.push(`\n ${result.title}`);
96
+ lines.push(` ${result.url}`);
97
+ for (const excerpt of result.excerpts.slice(0, 2))
98
+ lines.push(` ${excerpt.trim().replace(/\n/gu, ' ').slice(0, 200)}`);
99
+ }
100
+ return lines.join('\n');
101
+ }
102
+ export function renderSocial(data) {
103
+ const lines = [];
104
+ if (data.answer)
105
+ lines.push(`\n ${data.answer}\n`);
106
+ for (const result of data.results) {
107
+ lines.push(` ${socialPlatformTag(result.url)}${result.title}`);
108
+ lines.push(` ${result.url}`);
109
+ if (result.content)
110
+ lines.push(` ${result.content.trim().replace(/\n/gu, ' ').slice(0, 200)}`);
111
+ lines.push('');
112
+ }
113
+ return lines.join('\n');
114
+ }
115
+ function socialPlatformTag(url) {
116
+ if (url.includes('reddit.com'))
117
+ return '[reddit] ';
118
+ if (url.includes('x.com') || url.includes('twitter.com'))
119
+ return '[x] ';
120
+ if (url.includes('tiktok.com'))
121
+ return '[tiktok] ';
122
+ if (url.includes('linkedin.com'))
123
+ return '[linkedin] ';
124
+ if (url.includes('instagram.com'))
125
+ return '[instagram] ';
126
+ if (url.includes('facebook.com'))
127
+ return '[facebook] ';
128
+ if (url.includes('youtube.com'))
129
+ return '[youtube] ';
130
+ return '';
131
+ }
132
+ export function renderResearch(data) {
133
+ if (Array.isArray(data.processors)) {
134
+ const lines = ['', ' Standard processors:', ''];
135
+ for (const processor of data.processors)
136
+ lines.push(` ${processor.name}`);
137
+ lines.push('');
138
+ return lines.join('\n');
139
+ }
140
+ if (data.status)
141
+ return `${data.run_id}: ${data.status}`;
142
+ if (data.path)
143
+ return data.path;
144
+ if (data.output === null)
145
+ return '*No output returned.*';
146
+ const content = data.output?.content;
147
+ if (typeof content === 'string')
148
+ return content;
149
+ if (content !== undefined)
150
+ return JSON.stringify(content, null, 2);
151
+ return '';
152
+ }
153
+ export function renderVersion(data) {
154
+ return `escli v${data.version}\nendpoint ${data.endpoint}`;
155
+ }
156
+ export function renderUsage(data) {
157
+ if (Array.isArray(data.rules) || Array.isArray(data.pricing) || Array.isArray(data.pricing_rules))
158
+ return renderUsagePricing(data);
159
+ if (Array.isArray(data.days) || Array.isArray(data.by_day))
160
+ return renderUsageDaily(data);
161
+ if (Array.isArray(data.services))
162
+ return renderUsageByService(data);
163
+ return renderUsageSummary(data);
164
+ }
165
+ function renderUsageSummary(data) {
166
+ const days = numberValue(data.days) ?? 30;
167
+ const service = stringValue(data.service);
168
+ const totalRequests = data.total_requests ?? data.requests ?? data.total_events ?? 0;
169
+ const totalCost = data.total_cost_usd ?? data.total_cost ?? data.estimated_cost_usd ?? 0;
170
+ const lines = ['', ` Usage · last ${days} day${days === 1 ? '' : 's'}${service ? ` · service: ${service}` : ''}`];
171
+ const start = stringValue(data.start ?? data.range_start);
172
+ const end = stringValue(data.end ?? data.range_end);
173
+ if (start && end)
174
+ lines.push(` · ${formatDateRange(start)} → ${formatDateRange(end)}`);
175
+ lines.push(` ${'─'.repeat(60)}`);
176
+ lines.push(` ${'requests'.padEnd(24)} ${formatInt(totalRequests).padStart(20)}`);
177
+ lines.push(` ${'estimated spend'.padEnd(24)} ${formatMoney(totalCost).padStart(20)}`);
178
+ const services = serviceRows(data.by_service ?? data.services);
179
+ if (services.length > 0) {
180
+ lines.push('', ` ${'SERVICE'.padEnd(16)} ${'REQUESTS'.padStart(12)} ${'SPEND'.padStart(14)}`, ` ${'─'.repeat(46)}`);
181
+ services.sort((a, b) => b.cost - a.cost);
182
+ for (const row of services)
183
+ lines.push(` ${row.name.padEnd(16)} ${formatInt(row.requests).padStart(12)} ${formatMoney(row.cost).padStart(14)}`);
184
+ }
185
+ lines.push('');
186
+ return lines.join('\n');
187
+ }
188
+ function renderUsageByService(data) {
189
+ const days = numberValue(data.days) ?? 30;
190
+ const rows = serviceRows(data.services ?? data.by_service, true);
191
+ const lines = [
192
+ '',
193
+ ` By service · last ${days} day${days === 1 ? '' : 's'}`,
194
+ ` ${'─'.repeat(70)}`,
195
+ ` ${'SERVICE'.padEnd(14)} ${'REQUESTS'.padStart(10)} ${'TOKENS IN'.padStart(12)} ${'TOKENS OUT'.padStart(12)} ${'SPEND'.padStart(14)}`,
196
+ ` ${'─'.repeat(70)}`,
197
+ ];
198
+ for (const row of rows)
199
+ lines.push(` ${row.name.padEnd(14)} ${formatInt(row.requests).padStart(10)} ${formatInt(row.tokensIn).padStart(12)} ${formatInt(row.tokensOut).padStart(12)} ${formatMoney(row.cost).padStart(14)}`);
200
+ lines.push('');
201
+ return lines.join('\n');
202
+ }
203
+ function renderUsageDaily(data) {
204
+ const days = numberValue(data.days) ?? 30;
205
+ const service = stringValue(data.service);
206
+ const rows = arrayValue(data.days ?? data.by_day);
207
+ const lines = [
208
+ '',
209
+ ` Daily usage · last ${days} day${days === 1 ? '' : 's'}${service ? ` · service: ${service}` : ''}`,
210
+ ` ${'─'.repeat(50)}`,
211
+ ` ${'DATE'.padEnd(12)} ${'REQUESTS'.padStart(12)} ${'SPEND'.padStart(14)}`,
212
+ ` ${'─'.repeat(50)}`,
213
+ ];
214
+ let totalRequests = 0;
215
+ let totalCost = 0;
216
+ for (const item of rows) {
217
+ const entry = recordValue(item) ?? {};
218
+ const date = String(entry.date ?? entry.day ?? '?').split('T')[0];
219
+ const requests = numberValue(entry.requests ?? entry.count) ?? 0;
220
+ const cost = numberValue(entry.cost_usd ?? entry.cost) ?? 0;
221
+ totalRequests += requests;
222
+ totalCost += cost;
223
+ lines.push(` ${date.padEnd(12)} ${formatInt(requests).padStart(12)} ${formatMoney(cost).padStart(14)}`);
224
+ }
225
+ if (rows.length > 0) {
226
+ lines.push(` ${'─'.repeat(50)}`);
227
+ lines.push(` ${'total'.padEnd(12)} ${formatInt(totalRequests).padStart(12)} ${formatMoney(totalCost).padStart(14)}`);
228
+ }
229
+ else {
230
+ lines.push(' (no events in range)');
231
+ }
232
+ lines.push('');
233
+ return lines.join('\n');
234
+ }
235
+ function renderUsagePricing(data) {
236
+ const rules = arrayValue(data.rules ?? data.pricing ?? data.pricing_rules)
237
+ .map((item) => recordValue(item) ?? {})
238
+ .filter((rule) => rule.enabled !== 0)
239
+ .sort((a, b) => `${a.service ?? ''}:${a.sku ?? ''}`.localeCompare(`${b.service ?? ''}:${b.sku ?? ''}`));
240
+ const lines = [
241
+ '',
242
+ ' Pricing rules',
243
+ ` ${'─'.repeat(70)}`,
244
+ ` ${'SERVICE'.padEnd(14)} ${'SKU'.padEnd(24)} ${'UNIT'.padEnd(14)} ${'COST/UNIT'.padStart(14)}`,
245
+ ` ${'─'.repeat(70)}`,
246
+ ];
247
+ for (const rule of rules) {
248
+ lines.push(` ${String(rule.service ?? '?').padEnd(14)} ${String(rule.sku ?? '?').padEnd(24)} ${String(rule.unit_label ?? rule.unit ?? '').padEnd(14)} ${formatUnitCost(rule.cost_per_unit).padStart(14)}`);
249
+ }
250
+ if (rules.length === 0)
251
+ lines.push(' (no pricing rules configured)');
252
+ lines.push('');
253
+ return lines.join('\n');
254
+ }
255
+ function serviceRows(value, includeTokens = false) {
256
+ const rows = [];
257
+ if (Array.isArray(value)) {
258
+ for (const item of value) {
259
+ const entry = recordValue(item) ?? {};
260
+ rows.push({
261
+ name: String(entry.service ?? entry.name ?? '?'),
262
+ requests: numberValue(entry.requests ?? entry.count) ?? 0,
263
+ cost: numberValue(entry.cost_usd ?? entry.cost) ?? 0,
264
+ tokensIn: includeTokens ? numberValue(entry.tokens_in) ?? 0 : 0,
265
+ tokensOut: includeTokens ? numberValue(entry.tokens_out) ?? 0 : 0,
266
+ });
267
+ }
268
+ return rows;
269
+ }
270
+ const record = recordValue(value);
271
+ if (!record)
272
+ return rows;
273
+ for (const [name, item] of Object.entries(record)) {
274
+ const entry = recordValue(item) ?? {};
275
+ rows.push({
276
+ name,
277
+ requests: numberValue(entry.requests ?? entry.count) ?? 0,
278
+ cost: numberValue(entry.cost_usd ?? entry.cost) ?? 0,
279
+ tokensIn: includeTokens ? numberValue(entry.tokens_in) ?? 0 : 0,
280
+ tokensOut: includeTokens ? numberValue(entry.tokens_out) ?? 0 : 0,
281
+ });
282
+ }
283
+ return rows;
284
+ }
285
+ function formatMoney(value) {
286
+ const number = numberValue(value);
287
+ if (number === undefined)
288
+ return '—';
289
+ if (number === 0)
290
+ return '$0.00';
291
+ if (Math.abs(number) < 0.01)
292
+ return `$${number.toFixed(4)}`;
293
+ return `$${number.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
294
+ }
295
+ function formatUnitCost(value) {
296
+ const number = numberValue(value);
297
+ if (number === undefined)
298
+ return '—';
299
+ return number < 0.01 ? `$${number.toFixed(6)}` : `$${number.toFixed(4)}`;
300
+ }
301
+ function formatInt(value) {
302
+ const number = numberValue(value);
303
+ return number === undefined ? '—' : Math.trunc(number).toLocaleString('en-US');
304
+ }
305
+ function formatDateRange(value) {
306
+ return value.split('.')[0]?.replace('T', ' ') ?? value;
307
+ }
308
+ function arrayValue(value) {
309
+ return Array.isArray(value) ? value : [];
310
+ }
311
+ function recordValue(value) {
312
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
313
+ }
314
+ function stringValue(value) {
315
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
316
+ }
317
+ function numberValue(value) {
318
+ const number = Number(value);
319
+ return Number.isFinite(number) ? number : undefined;
320
+ }
321
+ export { chalk, ux };
322
+ //# sourceMappingURL=io.js.map
@@ -0,0 +1,2 @@
1
+ export const IS_TEST_BUILD = typeof __ESCLI_TEST__ === 'undefined' ? true : __ESCLI_TEST__;
2
+ //# sourceMappingURL=build-flags.js.map
@@ -0,0 +1,8 @@
1
+ export const commandMetadata = new Map();
2
+ export function registerCommandMetadata(metadata) {
3
+ commandMetadata.set(metadata.id, metadata);
4
+ }
5
+ export function getCommandMetadata(id) {
6
+ return commandMetadata.get(id);
7
+ }
8
+ //# sourceMappingURL=command-metadata.js.map