@eide/foir-cli 0.1.34 → 0.1.35

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/dist/cli.js CHANGED
@@ -9,37 +9,18 @@ import { createRequire } from 'module';
9
9
  import { Command } from 'commander';
10
10
  const require = createRequire(import.meta.url);
11
11
  const { version } = require('../package.json');
12
- // Auth commands
12
+ // Auth commands (special — not GraphQL-driven)
13
13
  import { registerLoginCommand } from './commands/login.js';
14
14
  import { registerLogoutCommand } from './commands/logout.js';
15
15
  import { registerSelectProjectCommand } from './commands/select-project.js';
16
16
  import { registerWhoamiCommand } from './commands/whoami.js';
17
- // Resource commands
18
- import { registerRecordsCommands } from './commands/records.js';
19
- import { registerModelsCommands } from './commands/models.js';
20
- import { registerSearchCommands } from './commands/search.js';
21
- import { registerCustomersCommands } from './commands/customers.js';
22
- import { registerCustomerProfilesCommands } from './commands/customer-profiles.js';
23
- import { registerSegmentsCommands } from './commands/segments.js';
24
- import { registerExperimentsCommands } from './commands/experiments.js';
25
- import { registerSettingsCommands } from './commands/settings.js';
26
- import { registerApiKeysCommands } from './commands/api-keys.js';
27
- import { registerAuthProvidersCommands } from './commands/auth-providers.js';
28
- import { registerExtensionsCommands } from './commands/extensions.js';
29
- import { registerOperationsCommands } from './commands/operations.js';
30
- import { registerHooksCommands } from './commands/hooks.js';
31
- import { registerSchedulesCommands } from './commands/schedules.js';
17
+ // Special commands (REST, codegen, scaffolding, multi-type search)
32
18
  import { registerMediaCommands } from './commands/media.js';
33
- import { registerContextCommands } from './commands/context.js';
34
- import { registerNotificationsCommands } from './commands/notifications.js';
35
- import { registerLocalesCommands } from './commands/locales.js';
36
- import { registerFilesCommands } from './commands/files.js';
37
- import { registerNotesCommands } from './commands/notes.js';
38
- import { registerVariantCatalogCommands } from './commands/variant-catalog.js';
39
- import { registerAuthConfigCommands } from './commands/auth-config.js';
40
- import { registerEmbeddingsCommands } from './commands/embeddings.js';
41
19
  import { registerPullCommand } from './commands/pull.js';
42
20
  import { registerCreateExtensionCommand } from './commands/create-extension.js';
21
+ import { registerSearchCommands } from './commands/search.js';
22
+ // Dynamic commands from command registry (replaces ~25 imperative command files)
23
+ import { registerDynamicCommands } from './commands/register-commands.js';
43
24
  const program = new Command();
44
25
  program
45
26
  .name('foir')
@@ -58,36 +39,18 @@ function getGlobalOpts() {
58
39
  quiet: !!opts.quiet,
59
40
  };
60
41
  }
61
- // Register all command groups
42
+ // Auth commands
62
43
  registerLoginCommand(program, getGlobalOpts);
63
44
  registerLogoutCommand(program, getGlobalOpts);
64
45
  registerSelectProjectCommand(program, getGlobalOpts);
65
46
  registerWhoamiCommand(program, getGlobalOpts);
66
- registerRecordsCommands(program, getGlobalOpts);
67
- registerModelsCommands(program, getGlobalOpts);
68
- registerSearchCommands(program, getGlobalOpts);
69
- registerCustomersCommands(program, getGlobalOpts);
70
- registerCustomerProfilesCommands(program, getGlobalOpts);
71
- registerSegmentsCommands(program, getGlobalOpts);
72
- registerExperimentsCommands(program, getGlobalOpts);
73
- registerSettingsCommands(program, getGlobalOpts);
74
- registerApiKeysCommands(program, getGlobalOpts);
75
- registerAuthProvidersCommands(program, getGlobalOpts);
76
- registerExtensionsCommands(program, getGlobalOpts);
77
- registerOperationsCommands(program, getGlobalOpts);
78
- registerHooksCommands(program, getGlobalOpts);
79
- registerSchedulesCommands(program, getGlobalOpts);
47
+ // Special commands
80
48
  registerMediaCommands(program, getGlobalOpts);
81
- registerContextCommands(program, getGlobalOpts);
82
- registerNotificationsCommands(program, getGlobalOpts);
83
- registerLocalesCommands(program, getGlobalOpts);
84
- registerFilesCommands(program, getGlobalOpts);
85
- registerNotesCommands(program, getGlobalOpts);
86
- registerVariantCatalogCommands(program, getGlobalOpts);
87
- registerAuthConfigCommands(program, getGlobalOpts);
88
- registerEmbeddingsCommands(program, getGlobalOpts);
49
+ registerSearchCommands(program, getGlobalOpts);
89
50
  // Codegen
90
51
  registerPullCommand(program, getGlobalOpts);
91
52
  // Scaffolding
92
53
  registerCreateExtensionCommand(program, getGlobalOpts);
54
+ // All GraphQL-driven commands (models, records, locales, etc.)
55
+ registerDynamicCommands(program, getGlobalOpts);
93
56
  program.parse();
@@ -147,7 +147,8 @@ function generateDataInterface(model, fields, interfaceName, allModels) {
147
147
  }
148
148
  }
149
149
  // Only parameterize if all targets resolved — otherwise keep default generic
150
- if (resolvedPreviewTypes.length === refTypes.length && resolvedPreviewTypes.length > 0) {
150
+ if (resolvedPreviewTypes.length === refTypes.length &&
151
+ resolvedPreviewTypes.length > 0) {
151
152
  fieldType = `ReferenceValue<${resolvedPreviewTypes.join(' | ')}>`;
152
153
  }
153
154
  }
@@ -0,0 +1,4 @@
1
+ import { type Command } from 'commander';
2
+ import type { GlobalOptions } from '../lib/config.js';
3
+ export declare function registerPlaygroundCommand(program: Command, getGlobalOpts: () => GlobalOptions): void;
4
+ //# sourceMappingURL=playground.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../src/commands/playground.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAMzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAgCtD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,MAAM,aAAa,QA8QnC"}
@@ -0,0 +1,270 @@
1
+ import chalk from 'chalk';
2
+ import * as readline from 'readline';
3
+ import { homedir } from 'os';
4
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
5
+ import { join } from 'path';
6
+ import { createClient } from '../lib/client.js';
7
+ import { getProjectContext } from '../auth/credentials.js';
8
+ import { ListModelsDocument } from '../graphql/generated.js';
9
+ const HISTORY_FILE = join(homedir(), '.foir', 'repl-history');
10
+ const MAX_HISTORY = 500;
11
+ function loadHistory() {
12
+ try {
13
+ if (existsSync(HISTORY_FILE)) {
14
+ return readFileSync(HISTORY_FILE, 'utf-8')
15
+ .split('\n')
16
+ .filter(Boolean)
17
+ .slice(-MAX_HISTORY);
18
+ }
19
+ }
20
+ catch {
21
+ // ignore
22
+ }
23
+ return [];
24
+ }
25
+ function saveHistory(lines) {
26
+ try {
27
+ const dir = join(homedir(), '.foir');
28
+ if (!existsSync(dir))
29
+ mkdirSync(dir, { recursive: true });
30
+ writeFileSync(HISTORY_FILE, lines.slice(-MAX_HISTORY).join('\n') + '\n');
31
+ }
32
+ catch {
33
+ // ignore
34
+ }
35
+ }
36
+ export function registerPlaygroundCommand(program, getGlobalOpts) {
37
+ program
38
+ .command('playground')
39
+ .alias('repl')
40
+ .description('Interactive REPL with tab completion')
41
+ .action(async () => {
42
+ const opts = getGlobalOpts();
43
+ // Validate auth and create client
44
+ let client;
45
+ try {
46
+ client = await createClient(opts);
47
+ }
48
+ catch (err) {
49
+ throw new Error('Authentication required. Run `foir login` first.');
50
+ }
51
+ // Pre-fetch model keys for tab completion (don't block startup)
52
+ let modelKeys = [];
53
+ client
54
+ .request(ListModelsDocument, { limit: 200 })
55
+ .then((data) => {
56
+ modelKeys = data.models.items.map((m) => m.key);
57
+ })
58
+ .catch(() => {
59
+ /* ignore — completion just won't have model keys */
60
+ });
61
+ // Get project context for prompt
62
+ let projectName = 'no project';
63
+ try {
64
+ const ctx = await getProjectContext();
65
+ if (ctx?.name)
66
+ projectName = ctx.name;
67
+ }
68
+ catch {
69
+ // no project context
70
+ }
71
+ // Collect command names for completion
72
+ const commandNames = program.commands.map((c) => c.name());
73
+ const commandMap = new Map();
74
+ for (const cmd of program.commands) {
75
+ commandMap.set(cmd.name(), cmd);
76
+ for (const alias of cmd.aliases()) {
77
+ commandMap.set(alias, cmd);
78
+ }
79
+ }
80
+ // Build completer
81
+ const metaCommands = [
82
+ '.help',
83
+ '.clear',
84
+ '.exit',
85
+ '.commands',
86
+ '.context',
87
+ '.models',
88
+ ];
89
+ function completer(line) {
90
+ const trimmed = line.trim();
91
+ const parts = trimmed.split(/\s+/);
92
+ // Meta commands
93
+ if (parts.length === 1 && trimmed.startsWith('.')) {
94
+ const hits = metaCommands.filter((c) => c.startsWith(trimmed));
95
+ return [hits.length ? hits : metaCommands, trimmed];
96
+ }
97
+ // First word: command names
98
+ if (parts.length <= 1) {
99
+ const hits = commandNames.filter((c) => c.startsWith(trimmed));
100
+ return [hits.length ? hits : commandNames, trimmed];
101
+ }
102
+ // Second word: subcommands
103
+ const firstPart = parts[0] ?? '';
104
+ const cmd = commandMap.get(firstPart);
105
+ if (cmd && parts.length === 2) {
106
+ const subNames = cmd.commands.map((c) => c.name());
107
+ const partial = parts[1] ?? '';
108
+ const hits = subNames.filter((s) => s.startsWith(partial));
109
+ return [hits.length ? hits : subNames, partial];
110
+ }
111
+ // Level 3: model keys for records/search commands
112
+ const cmdName = cmd?.name();
113
+ if ((cmdName === 'records' || cmdName === 'search') &&
114
+ parts.length === 3 &&
115
+ modelKeys.length > 0) {
116
+ const partial = parts[2] ?? '';
117
+ const hits = modelKeys.filter((k) => k.startsWith(partial));
118
+ return [hits.length ? hits : modelKeys, partial];
119
+ }
120
+ // Flags
121
+ if (cmd) {
122
+ const lastPart = parts[parts.length - 1] ?? '';
123
+ if (lastPart.startsWith('-')) {
124
+ const flags = cmd.options.map((o) => o.long).filter((f) => Boolean(f));
125
+ const hits = flags.filter((f) => f.startsWith(lastPart));
126
+ return [hits.length ? hits : [], lastPart];
127
+ }
128
+ }
129
+ return [[], trimmed];
130
+ }
131
+ // Load history
132
+ const history = loadHistory();
133
+ const rl = readline.createInterface({
134
+ input: process.stdin,
135
+ output: process.stdout,
136
+ prompt: chalk.cyan(`foir (${projectName})> `),
137
+ completer,
138
+ history,
139
+ historySize: MAX_HISTORY,
140
+ });
141
+ console.log(chalk.bold('\nFoir Interactive Playground'));
142
+ console.log(chalk.dim('Type commands without the "foir" prefix. Use Tab for completion.'));
143
+ console.log(chalk.dim('Type .help for available meta-commands.\n'));
144
+ rl.prompt();
145
+ // Prevent Commander from calling process.exit()
146
+ program.exitOverride();
147
+ rl.on('line', async (line) => {
148
+ const input = line.trim();
149
+ if (!input) {
150
+ rl.prompt();
151
+ return;
152
+ }
153
+ // Meta commands
154
+ if (input === '.help') {
155
+ console.log(chalk.bold('\nMeta-commands:'));
156
+ console.log(' .help Show this help');
157
+ console.log(' .clear Clear terminal');
158
+ console.log(' .exit Exit playground');
159
+ console.log(' .commands List available commands');
160
+ console.log(' .context Show current auth/project context');
161
+ console.log(' .models Refresh and list model keys');
162
+ console.log(chalk.dim('\nRun any foir command without the "foir" prefix.'));
163
+ console.log(chalk.dim('Example: records list page\n'));
164
+ rl.prompt();
165
+ return;
166
+ }
167
+ if (input === '.clear') {
168
+ console.clear();
169
+ rl.prompt();
170
+ return;
171
+ }
172
+ if (input === '.exit' || input === 'exit' || input === 'quit') {
173
+ saveHistory(rl.history || []);
174
+ console.log(chalk.dim('Goodbye!'));
175
+ rl.close();
176
+ return;
177
+ }
178
+ if (input === '.commands') {
179
+ console.log(chalk.bold('\nAvailable commands:'));
180
+ for (const cmd of program.commands) {
181
+ if (cmd.name() === 'playground')
182
+ continue;
183
+ const desc = cmd.description() || '';
184
+ console.log(` ${chalk.green(cmd.name().padEnd(24))} ${chalk.dim(desc)}`);
185
+ }
186
+ console.log('');
187
+ rl.prompt();
188
+ return;
189
+ }
190
+ if (input === '.context') {
191
+ try {
192
+ const ctx = await getProjectContext();
193
+ console.log(chalk.bold('\nCurrent context:'));
194
+ if (ctx) {
195
+ console.log(` Project: ${chalk.green(ctx.name || 'unnamed')}`);
196
+ console.log(` ID: ${chalk.dim(ctx.id || 'n/a')}`);
197
+ console.log(` Tenant: ${chalk.dim(ctx.tenantId || 'n/a')}`);
198
+ }
199
+ else {
200
+ console.log(chalk.yellow(' No project selected. Run: select-project'));
201
+ }
202
+ }
203
+ catch {
204
+ console.log(chalk.yellow(' No project context available.'));
205
+ }
206
+ console.log('');
207
+ rl.prompt();
208
+ return;
209
+ }
210
+ if (input === '.models') {
211
+ try {
212
+ const data = await client.request(ListModelsDocument, {
213
+ limit: 200,
214
+ });
215
+ modelKeys = data.models.items.map((m) => m.key);
216
+ console.log(chalk.bold('\nModel keys:'));
217
+ for (const key of modelKeys) {
218
+ console.log(` ${chalk.green(key)}`);
219
+ }
220
+ if (modelKeys.length === 0) {
221
+ console.log(chalk.yellow(' No models found.'));
222
+ }
223
+ }
224
+ catch {
225
+ console.log(chalk.yellow(' Failed to fetch models.'));
226
+ }
227
+ console.log('');
228
+ rl.prompt();
229
+ return;
230
+ }
231
+ // Regular command - parse and execute via Commander
232
+ const args = input.match(/(?:[^\s"']+|"[^"]*"|'[^']*')/g) || [];
233
+ // Strip quotes from arguments
234
+ const cleanArgs = args.map((a) => a.replace(/^["']|["']$/g, ''));
235
+ try {
236
+ await program.parseAsync(['node', 'foir', ...cleanArgs], {
237
+ from: 'user',
238
+ });
239
+ }
240
+ catch (err) {
241
+ // CommanderError is thrown by exitOverride instead of process.exit
242
+ if (err.code === 'commander.helpDisplayed' ||
243
+ err.code === 'commander.version') {
244
+ // Help/version output already printed
245
+ }
246
+ else if (err.code === 'commander.unknownCommand') {
247
+ console.error(chalk.red(`Unknown command: ${cleanArgs[0]}`));
248
+ console.log(chalk.dim('Type .commands to see available commands.'));
249
+ }
250
+ else if (err.exitCode !== undefined) {
251
+ // Commander error with exit code - already printed message
252
+ }
253
+ else {
254
+ // Application error
255
+ console.error(chalk.red(err.message || String(err)));
256
+ }
257
+ }
258
+ console.log(''); // spacing
259
+ rl.prompt();
260
+ });
261
+ rl.on('close', () => {
262
+ saveHistory(rl.history || []);
263
+ });
264
+ // Handle SIGINT gracefully
265
+ rl.on('SIGINT', () => {
266
+ console.log(chalk.dim('\n(Use .exit or Ctrl+D to quit)'));
267
+ rl.prompt();
268
+ });
269
+ });
270
+ }
@@ -0,0 +1,7 @@
1
+ import type { Command } from 'commander';
2
+ import type { GlobalOptions } from '../lib/config.js';
3
+ /**
4
+ * Register all declarative commands from the command registry.
5
+ */
6
+ export declare function registerDynamicCommands(program: Command, globalOpts: () => GlobalOptions): void;
7
+ //# sourceMappingURL=register-commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-commands.d.ts","sourceRoot":"","sources":["../../src/commands/register-commands.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AA0GtD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,aAAa,GAC9B,IAAI,CA8LN"}
@@ -0,0 +1,259 @@
1
+ import { readFileSync } from 'fs';
2
+ import { resolve, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { COMMANDS, createSchemaEngine, } from '@eide/command-registry';
5
+ import { withErrorHandler } from '../lib/errors.js';
6
+ import { createClient } from '../lib/client.js';
7
+ import { formatOutput, formatList, timeAgo, success, } from '../lib/output.js';
8
+ import { parseInputData, confirmAction, isUUID } from '../lib/input.js';
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+ function loadSchemaSDL() {
12
+ // Try monorepo path first (development)
13
+ const monorepoPath = resolve(__dirname, '../../../../graphql-core/schema.graphql');
14
+ try {
15
+ return readFileSync(monorepoPath, 'utf-8');
16
+ }
17
+ catch {
18
+ // Fall back to relative path from dist (might differ)
19
+ const altPath = resolve(__dirname, '../../../graphql-core/schema.graphql');
20
+ try {
21
+ return readFileSync(altPath, 'utf-8');
22
+ }
23
+ catch {
24
+ throw new Error('Could not find schema.graphql. Ensure you are running from within the EIDE monorepo.');
25
+ }
26
+ }
27
+ }
28
+ /**
29
+ * Extract the useful data from a GraphQL result, unwrapping list wrappers.
30
+ */
31
+ function extractResult(result, operationName) {
32
+ const raw = result[operationName];
33
+ if (!raw || typeof raw !== 'object')
34
+ return { data: raw };
35
+ const obj = raw;
36
+ // Check for list wrappers: { items: [...], total } or { entityName: [...], total }
37
+ if ('total' in obj) {
38
+ for (const [key, val] of Object.entries(obj)) {
39
+ if (key !== 'total' && key !== '__typename' && Array.isArray(val)) {
40
+ return { data: val, total: obj.total };
41
+ }
42
+ }
43
+ }
44
+ return { data: raw };
45
+ }
46
+ /**
47
+ * Convert registry ColumnDef (with string format) to CLI ColumnDef (with format function).
48
+ */
49
+ function toCliColumns(columns) {
50
+ if (!columns)
51
+ return undefined;
52
+ return columns.map((col) => {
53
+ const cliCol = {
54
+ key: col.key,
55
+ header: col.header,
56
+ width: col.width,
57
+ };
58
+ switch (col.format) {
59
+ case 'timeAgo':
60
+ cliCol.format = (v) => timeAgo(v);
61
+ break;
62
+ case 'boolean':
63
+ cliCol.format = (v) => (v ? 'yes' : '');
64
+ break;
65
+ case 'truncate':
66
+ cliCol.format = (v) => {
67
+ const str = String(v ?? '');
68
+ return str.length > 40 ? str.slice(0, 39) + '\u2026' : str;
69
+ };
70
+ break;
71
+ case 'join':
72
+ cliCol.format = (v) => Array.isArray(v) ? v.join(', ') : String(v ?? '');
73
+ break;
74
+ case 'bytes':
75
+ cliCol.format = (v) => {
76
+ const num = Number(v);
77
+ if (num < 1024)
78
+ return `${num} B`;
79
+ if (num < 1024 * 1024)
80
+ return `${(num / 1024).toFixed(1)} KB`;
81
+ return `${(num / (1024 * 1024)).toFixed(1)} MB`;
82
+ };
83
+ break;
84
+ }
85
+ return cliCol;
86
+ });
87
+ }
88
+ /**
89
+ * Register all declarative commands from the command registry.
90
+ */
91
+ export function registerDynamicCommands(program, globalOpts) {
92
+ let engine;
93
+ try {
94
+ const sdl = loadSchemaSDL();
95
+ engine = createSchemaEngine(sdl);
96
+ }
97
+ catch (err) {
98
+ // If schema can't be loaded, skip dynamic command registration
99
+ // (special commands still work)
100
+ console.error(`Warning: Could not load schema for dynamic commands: ${err instanceof Error ? err.message : String(err)}`);
101
+ return;
102
+ }
103
+ // Group commands by group name
104
+ const groups = new Map();
105
+ for (const cmd of COMMANDS) {
106
+ if (!groups.has(cmd.group))
107
+ groups.set(cmd.group, []);
108
+ groups.get(cmd.group).push(cmd);
109
+ }
110
+ for (const [groupName, entries] of groups) {
111
+ const group = program.command(groupName).description(`Manage ${groupName}`);
112
+ for (const entry of entries) {
113
+ let cmd = group.command(entry.name).description(entry.description);
114
+ // Add positional args
115
+ for (const pos of entry.positionalArgs ?? []) {
116
+ cmd = cmd.argument(`<${pos.name}>`, pos.description ?? pos.name);
117
+ }
118
+ // Add flags from schema args (excluding positional args and input args)
119
+ const schemaArgs = engine.getOperationArgs(entry.operation, entry.operationType);
120
+ for (const arg of schemaArgs) {
121
+ // Skip if already a positional arg
122
+ if (entry.positionalArgs?.some((p) => p.graphqlArg === arg.name))
123
+ continue;
124
+ // Skip if it's the input arg
125
+ if (entry.acceptsInput && arg.name === (entry.inputArgName ?? 'input'))
126
+ continue;
127
+ const reqStr = arg.required ? ' (required)' : '';
128
+ cmd = cmd.option(`--${arg.name} <value>`, `${arg.name}${reqStr}`);
129
+ }
130
+ // Add --data/--file for input commands
131
+ if (entry.acceptsInput) {
132
+ cmd = cmd.option('-d, --data <json>', 'Data as JSON');
133
+ cmd = cmd.option('-f, --file <path>', 'Read data from file');
134
+ }
135
+ // Add --confirm for destructive commands
136
+ if (entry.requiresConfirmation) {
137
+ cmd = cmd.option('--confirm', 'Skip confirmation prompt');
138
+ }
139
+ // Action handler
140
+ cmd.action(withErrorHandler(globalOpts, async (...actionArgs) => {
141
+ const opts = globalOpts();
142
+ const client = await createClient(opts);
143
+ // Build variables from positional args + flags
144
+ const variables = {};
145
+ // Map positional args
146
+ const positionals = entry.positionalArgs ?? [];
147
+ for (let i = 0; i < positionals.length; i++) {
148
+ const posDef = positionals[i];
149
+ const value = actionArgs[i];
150
+ if (value) {
151
+ variables[posDef.graphqlArg] = value;
152
+ }
153
+ }
154
+ // The flags/options object is the last argument from Commander
155
+ const flags = (actionArgs[positionals.length] ?? {});
156
+ // Map named flags to variables (coerce types)
157
+ const rawFlags = {};
158
+ for (const [key, val] of Object.entries(flags)) {
159
+ if (key === 'data' || key === 'file' || key === 'confirm')
160
+ continue;
161
+ rawFlags[key] = String(val);
162
+ }
163
+ const coerced = engine.coerceArgs(entry.operation, entry.operationType, rawFlags);
164
+ Object.assign(variables, coerced);
165
+ // Handle input data (--data, --file, stdin)
166
+ if (entry.acceptsInput && (flags.data || flags.file)) {
167
+ const inputData = await parseInputData({
168
+ data: flags.data,
169
+ file: flags.file,
170
+ });
171
+ const argName = entry.inputArgName ?? 'input';
172
+ variables[argName] = inputData;
173
+ }
174
+ // Handle alternate get (by key/code instead of UUID)
175
+ if (entry.alternateGet &&
176
+ positionals.length > 0 &&
177
+ variables[positionals[0].graphqlArg]) {
178
+ const firstArgValue = String(variables[positionals[0].graphqlArg]);
179
+ if (!isUUID(firstArgValue)) {
180
+ // Use alternate operation
181
+ const altEntry = {
182
+ ...entry,
183
+ operation: entry.alternateGet.operation,
184
+ positionalArgs: [
185
+ {
186
+ name: positionals[0].name,
187
+ graphqlArg: entry.alternateGet.argName,
188
+ },
189
+ ],
190
+ };
191
+ // Remap the variable
192
+ delete variables[positionals[0].graphqlArg];
193
+ variables[entry.alternateGet.argName] = firstArgValue;
194
+ const queryStr = engine.buildQuery(altEntry, variables);
195
+ const result = (await client.request(queryStr, variables));
196
+ const { data } = extractResult(result, altEntry.operation);
197
+ formatOutput(data, opts);
198
+ return;
199
+ }
200
+ }
201
+ // Confirmation prompt for destructive actions
202
+ if (entry.requiresConfirmation) {
203
+ const confirmed = await confirmAction(`${entry.description}?`, {
204
+ confirm: !!flags.confirm,
205
+ });
206
+ if (!confirmed) {
207
+ console.log('Aborted.');
208
+ return;
209
+ }
210
+ }
211
+ // Build and execute query
212
+ const queryStr = engine.buildQuery(entry, variables);
213
+ const result = (await client.request(queryStr, variables));
214
+ const { data, total } = extractResult(result, entry.operation);
215
+ // Format output
216
+ if (entry.scalarResult) {
217
+ if (!(opts.json || opts.jsonl || opts.quiet)) {
218
+ if (entry.successMessage) {
219
+ success(entry.successMessage);
220
+ }
221
+ }
222
+ else {
223
+ formatOutput(data, opts);
224
+ }
225
+ }
226
+ else if (Array.isArray(data)) {
227
+ const cliColumns = toCliColumns(entry.columns);
228
+ formatList(data, opts, {
229
+ columns: cliColumns ?? autoColumns(data),
230
+ total,
231
+ });
232
+ }
233
+ else {
234
+ formatOutput(data, opts);
235
+ if (entry.successMessage &&
236
+ !(opts.json || opts.jsonl || opts.quiet)) {
237
+ success(entry.successMessage);
238
+ }
239
+ }
240
+ }));
241
+ }
242
+ }
243
+ }
244
+ /**
245
+ * Auto-generate column definitions from the first item in an array.
246
+ */
247
+ function autoColumns(items) {
248
+ if (items.length === 0)
249
+ return [];
250
+ const first = items[0];
251
+ return Object.keys(first)
252
+ .filter((k) => k !== '__typename')
253
+ .slice(0, 6)
254
+ .map((key) => ({
255
+ key,
256
+ header: key,
257
+ width: 20,
258
+ }));
259
+ }
@@ -38,8 +38,8 @@ async function fetchSessionContext(apiUrl, accessToken) {
38
38
  return data.sessionContext;
39
39
  }
40
40
  async function fetchApiKeys(apiUrl, accessToken, projectId, tenantId) {
41
- const data = await gqlRequest(apiUrl, accessToken, `query { listApiKeys(includeInactive: false, limit: 100) { apiKeys { id name isActive } } }`, undefined, { 'x-tenant-id': tenantId, 'x-project-id': projectId });
42
- return data.listApiKeys?.apiKeys ?? [];
41
+ const data = await gqlRequest(apiUrl, accessToken, `query { listApiKeys(includeInactive: false, limit: 100) { items { id name isActive } } }`, undefined, { 'x-tenant-id': tenantId, 'x-project-id': projectId });
42
+ return data.listApiKeys?.items ?? [];
43
43
  }
44
44
  async function createApiKey(apiUrl, accessToken, projectId, tenantId) {
45
45
  const data = await gqlRequest(apiUrl, accessToken, `mutation($input: CreateApiKeyInput!) { createApiKey(input: $input) { apiKey { id name isActive } plainKey } }`, {