@eide/foir-cli 0.1.47 → 0.2.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.
- package/dist/cli.js +1003 -861
- package/dist/lib/config-helpers.d.ts +86 -208
- package/package.json +6 -13
- package/dist/schema.graphql +0 -6212
package/dist/cli.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { config } from "dotenv";
|
|
5
|
-
import { resolve as resolve6, dirname as
|
|
6
|
-
import { fileURLToPath
|
|
5
|
+
import { resolve as resolve6, dirname as dirname4 } from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
7
|
import { createRequire } from "module";
|
|
8
8
|
import { Command } from "commander";
|
|
9
9
|
|
|
@@ -237,23 +237,15 @@ var DEFAULT_API_URL = "https://api.foir.dev";
|
|
|
237
237
|
function getApiUrl(options) {
|
|
238
238
|
return process.env.FOIR_API_URL ?? options?.apiUrl ?? DEFAULT_API_URL;
|
|
239
239
|
}
|
|
240
|
-
function getGraphQLEndpoint(apiUrl) {
|
|
241
|
-
const base = apiUrl.replace(/\/$/, "").replace(/\/graphql$/, "");
|
|
242
|
-
return `${base}/graphql`;
|
|
243
|
-
}
|
|
244
240
|
|
|
245
241
|
// src/lib/errors.ts
|
|
246
242
|
import chalk from "chalk";
|
|
243
|
+
import { ConnectError, Code } from "@connectrpc/connect";
|
|
247
244
|
function extractErrorMessage(error) {
|
|
248
245
|
if (!error || typeof error !== "object")
|
|
249
246
|
return String(error ?? "Unknown error");
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (gqlErrors && gqlErrors.length > 0) {
|
|
253
|
-
return gqlErrors[0].message;
|
|
254
|
-
}
|
|
255
|
-
if (err.message && err.message !== "undefined") {
|
|
256
|
-
return err.message;
|
|
247
|
+
if (error instanceof ConnectError) {
|
|
248
|
+
return error.message;
|
|
257
249
|
}
|
|
258
250
|
if (error instanceof Error && error.message) {
|
|
259
251
|
return error.message;
|
|
@@ -266,44 +258,25 @@ function withErrorHandler(optsFn, fn) {
|
|
|
266
258
|
await fn(...args);
|
|
267
259
|
} catch (error) {
|
|
268
260
|
const opts = optsFn();
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const first = gqlErrors[0];
|
|
273
|
-
const code = first.extensions?.code;
|
|
274
|
-
const validationErrors = first.extensions?.validationErrors;
|
|
261
|
+
if (error instanceof ConnectError) {
|
|
262
|
+
const code = error.code;
|
|
263
|
+
const message2 = error.message;
|
|
275
264
|
if (opts?.json || opts?.jsonl) {
|
|
276
265
|
console.error(
|
|
277
266
|
JSON.stringify({
|
|
278
267
|
error: {
|
|
279
|
-
message:
|
|
280
|
-
code: code ?? "
|
|
281
|
-
...validationErrors ? { validationErrors } : {},
|
|
282
|
-
...gqlErrors.length > 1 ? {
|
|
283
|
-
additionalErrors: gqlErrors.slice(1).map((e) => e.message)
|
|
284
|
-
} : {}
|
|
268
|
+
message: message2,
|
|
269
|
+
code: Code[code] ?? "UNKNOWN"
|
|
285
270
|
}
|
|
286
271
|
})
|
|
287
272
|
);
|
|
288
273
|
} else {
|
|
289
|
-
console.error(chalk.red("Error:"),
|
|
290
|
-
|
|
291
|
-
console.error(chalk.red(" \u2022"), extra.message);
|
|
292
|
-
}
|
|
293
|
-
if (validationErrors && Object.keys(validationErrors).length > 0) {
|
|
294
|
-
console.error("");
|
|
295
|
-
console.error(chalk.yellow("Field errors:"));
|
|
296
|
-
for (const [field, messages] of Object.entries(validationErrors)) {
|
|
297
|
-
for (const msg of messages) {
|
|
298
|
-
console.error(chalk.gray(` \u2022 ${field}:`), msg);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
if (code === "UNAUTHENTICATED") {
|
|
274
|
+
console.error(chalk.red("Error:"), message2);
|
|
275
|
+
if (code === Code.Unauthenticated) {
|
|
303
276
|
console.error(
|
|
304
277
|
chalk.gray("\nHint: Run `foir login` to authenticate.")
|
|
305
278
|
);
|
|
306
|
-
} else if (code ===
|
|
279
|
+
} else if (code === Code.PermissionDenied) {
|
|
307
280
|
console.error(
|
|
308
281
|
chalk.gray(
|
|
309
282
|
"\nHint: You may not have permission. Check your API key scopes."
|
|
@@ -313,22 +286,6 @@ function withErrorHandler(optsFn, fn) {
|
|
|
313
286
|
}
|
|
314
287
|
process.exit(1);
|
|
315
288
|
}
|
|
316
|
-
const status = gqlErr?.response?.status;
|
|
317
|
-
if (status === 401 || status === 403) {
|
|
318
|
-
if (opts?.json || opts?.jsonl) {
|
|
319
|
-
console.error(
|
|
320
|
-
JSON.stringify({
|
|
321
|
-
error: { message: "Authentication failed", code: "AUTH_ERROR" }
|
|
322
|
-
})
|
|
323
|
-
);
|
|
324
|
-
} else {
|
|
325
|
-
console.error(chalk.red("Error:"), "Authentication failed.");
|
|
326
|
-
console.error(
|
|
327
|
-
chalk.gray("Hint: Run `foir login` or set FOIR_API_KEY.")
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
process.exit(1);
|
|
331
|
-
}
|
|
332
289
|
const message = extractErrorMessage(error);
|
|
333
290
|
if (opts?.json || opts?.jsonl) {
|
|
334
291
|
console.error(JSON.stringify({ error: { message } }));
|
|
@@ -489,102 +446,153 @@ function registerLogoutCommand(program2, globalOpts) {
|
|
|
489
446
|
|
|
490
447
|
// src/commands/select-project.ts
|
|
491
448
|
import inquirer from "inquirer";
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
449
|
+
|
|
450
|
+
// src/lib/client.ts
|
|
451
|
+
import { createClient } from "@connectrpc/connect";
|
|
452
|
+
import { createConnectTransport } from "@connectrpc/connect-node";
|
|
453
|
+
import {
|
|
454
|
+
IdentityService,
|
|
455
|
+
ModelsService,
|
|
456
|
+
RecordsService,
|
|
457
|
+
ConfigsService,
|
|
458
|
+
SegmentsService,
|
|
459
|
+
ExperimentsService,
|
|
460
|
+
SettingsService,
|
|
461
|
+
StorageService
|
|
462
|
+
} from "@foir/connect-clients/services";
|
|
463
|
+
import { createIdentityMethods } from "@foir/connect-clients/identity";
|
|
464
|
+
import { createModelsMethods } from "@foir/connect-clients/models";
|
|
465
|
+
import { createRecordsMethods } from "@foir/connect-clients/records";
|
|
466
|
+
import { createConfigsMethods } from "@foir/connect-clients/configs";
|
|
467
|
+
import { createSegmentsMethods } from "@foir/connect-clients/segments";
|
|
468
|
+
import { createExperimentsMethods } from "@foir/connect-clients/experiments";
|
|
469
|
+
import { createSettingsMethods } from "@foir/connect-clients/settings";
|
|
470
|
+
import { createStorageMethods } from "@foir/connect-clients/storage";
|
|
471
|
+
async function createPlatformClient(options) {
|
|
472
|
+
const apiUrl = getApiUrl(options);
|
|
473
|
+
const headers = {};
|
|
474
|
+
const envApiKey = process.env.FOIR_API_KEY;
|
|
475
|
+
if (envApiKey) {
|
|
476
|
+
headers["x-api-key"] = envApiKey;
|
|
477
|
+
} else {
|
|
478
|
+
const credentials = await getCredentials();
|
|
479
|
+
if (!credentials) {
|
|
480
|
+
throw new Error(
|
|
481
|
+
"Not authenticated. Run `foir login` or set FOIR_API_KEY."
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
if (isTokenExpired(credentials)) {
|
|
485
|
+
throw new Error("Session expired. Run `foir login` to re-authenticate.");
|
|
486
|
+
}
|
|
487
|
+
headers["Authorization"] = `Bearer ${credentials.accessToken}`;
|
|
516
488
|
}
|
|
517
|
-
const
|
|
518
|
-
if (
|
|
519
|
-
|
|
489
|
+
const resolved = await resolveProjectContext(options);
|
|
490
|
+
if (resolved) {
|
|
491
|
+
headers["x-tenant-id"] = resolved.project.tenantId;
|
|
492
|
+
headers["x-project-id"] = resolved.project.id;
|
|
520
493
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
accessToken,
|
|
545
|
-
`mutation($input: CreateApiKeyInput!) { createApiKey(input: $input) { apiKey { id name isActive } plainKey } }`,
|
|
546
|
-
{
|
|
547
|
-
input: {
|
|
548
|
-
name: CLI_API_KEY_NAME,
|
|
549
|
-
projectId,
|
|
550
|
-
keyType: "SECRET",
|
|
551
|
-
scopes: CLI_API_KEY_SCOPES
|
|
552
|
-
}
|
|
553
|
-
},
|
|
554
|
-
{ "x-tenant-id": tenantId, "x-project-id": projectId }
|
|
555
|
-
);
|
|
556
|
-
return data.createApiKey;
|
|
494
|
+
const authInterceptor = (next) => async (req) => {
|
|
495
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
496
|
+
req.header.set(key, value);
|
|
497
|
+
}
|
|
498
|
+
return next(req);
|
|
499
|
+
};
|
|
500
|
+
const transport = createConnectTransport({
|
|
501
|
+
baseUrl: apiUrl.replace(/\/$/, ""),
|
|
502
|
+
httpVersion: "1.1",
|
|
503
|
+
interceptors: [authInterceptor]
|
|
504
|
+
});
|
|
505
|
+
return {
|
|
506
|
+
identity: createIdentityMethods(createClient(IdentityService, transport)),
|
|
507
|
+
models: createModelsMethods(createClient(ModelsService, transport)),
|
|
508
|
+
records: createRecordsMethods(createClient(RecordsService, transport)),
|
|
509
|
+
configs: createConfigsMethods(createClient(ConfigsService, transport)),
|
|
510
|
+
segments: createSegmentsMethods(createClient(SegmentsService, transport)),
|
|
511
|
+
experiments: createExperimentsMethods(
|
|
512
|
+
createClient(ExperimentsService, transport)
|
|
513
|
+
),
|
|
514
|
+
settings: createSettingsMethods(createClient(SettingsService, transport)),
|
|
515
|
+
storage: createStorageMethods(createClient(StorageService, transport))
|
|
516
|
+
};
|
|
557
517
|
}
|
|
558
|
-
|
|
559
|
-
const
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
518
|
+
function createPlatformClientWithHeaders(apiUrl, headers) {
|
|
519
|
+
const authInterceptor = (next) => async (req) => {
|
|
520
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
521
|
+
req.header.set(key, value);
|
|
522
|
+
}
|
|
523
|
+
return next(req);
|
|
524
|
+
};
|
|
525
|
+
const transport = createConnectTransport({
|
|
526
|
+
baseUrl: apiUrl.replace(/\/$/, ""),
|
|
527
|
+
httpVersion: "1.1",
|
|
528
|
+
interceptors: [authInterceptor]
|
|
529
|
+
});
|
|
530
|
+
return {
|
|
531
|
+
identity: createIdentityMethods(createClient(IdentityService, transport)),
|
|
532
|
+
models: createModelsMethods(createClient(ModelsService, transport)),
|
|
533
|
+
records: createRecordsMethods(createClient(RecordsService, transport)),
|
|
534
|
+
configs: createConfigsMethods(createClient(ConfigsService, transport)),
|
|
535
|
+
segments: createSegmentsMethods(createClient(SegmentsService, transport)),
|
|
536
|
+
experiments: createExperimentsMethods(
|
|
537
|
+
createClient(ExperimentsService, transport)
|
|
538
|
+
),
|
|
539
|
+
settings: createSettingsMethods(createClient(SettingsService, transport)),
|
|
540
|
+
storage: createStorageMethods(createClient(StorageService, transport))
|
|
541
|
+
};
|
|
567
542
|
}
|
|
568
|
-
async function
|
|
569
|
-
const
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
543
|
+
async function getStorageAuth(options) {
|
|
544
|
+
const apiUrl = getApiUrl(options);
|
|
545
|
+
const baseHeaders = {
|
|
546
|
+
"Content-Type": "application/json"
|
|
547
|
+
};
|
|
548
|
+
const envApiKey = process.env.FOIR_API_KEY;
|
|
549
|
+
if (envApiKey) {
|
|
550
|
+
baseHeaders["x-api-key"] = envApiKey;
|
|
551
|
+
} else {
|
|
552
|
+
const credentials = await getCredentials();
|
|
553
|
+
if (!credentials) {
|
|
554
|
+
throw new Error(
|
|
555
|
+
"Not authenticated. Run `foir login` or set FOIR_API_KEY."
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
if (isTokenExpired(credentials)) {
|
|
559
|
+
throw new Error("Session expired. Run `foir login` to re-authenticate.");
|
|
560
|
+
}
|
|
561
|
+
baseHeaders["Authorization"] = `Bearer ${credentials.accessToken}`;
|
|
583
562
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
563
|
+
const resolved = await resolveProjectContext(options);
|
|
564
|
+
if (resolved) {
|
|
565
|
+
baseHeaders["x-tenant-id"] = resolved.project.tenantId;
|
|
566
|
+
baseHeaders["x-project-id"] = resolved.project.id;
|
|
567
|
+
}
|
|
568
|
+
let cachedToken = null;
|
|
569
|
+
const getToken = async () => {
|
|
570
|
+
if (cachedToken && Date.now() < cachedToken.expiresAt - 3e4) {
|
|
571
|
+
return cachedToken.token;
|
|
572
|
+
}
|
|
573
|
+
const tokenUrl = `${apiUrl.replace(/\/$/, "")}/api/auth/token`;
|
|
574
|
+
const res = await fetch(tokenUrl, {
|
|
575
|
+
method: "POST",
|
|
576
|
+
headers: baseHeaders,
|
|
577
|
+
body: JSON.stringify({ purpose: "storage" })
|
|
578
|
+
});
|
|
579
|
+
if (!res.ok) {
|
|
580
|
+
throw new Error(
|
|
581
|
+
`Failed to get storage token (${res.status}): ${await res.text()}`
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
const data = await res.json();
|
|
585
|
+
cachedToken = {
|
|
586
|
+
token: data.token,
|
|
587
|
+
expiresAt: new Date(data.expiresAt).getTime()
|
|
588
|
+
};
|
|
589
|
+
return cachedToken.token;
|
|
590
|
+
};
|
|
591
|
+
return { getToken };
|
|
587
592
|
}
|
|
593
|
+
|
|
594
|
+
// src/commands/select-project.ts
|
|
595
|
+
var CLI_API_KEY_NAME = "Foir CLI";
|
|
588
596
|
function registerSelectProjectCommand(program2, globalOpts) {
|
|
589
597
|
program2.command("select-project").description("Choose which project to work with").option("--project-id <id>", "Project ID to select directly").option("--save-as <name>", "Save as a named profile").action(
|
|
590
598
|
withErrorHandler(
|
|
@@ -598,11 +606,24 @@ function registerSelectProjectCommand(program2, globalOpts) {
|
|
|
598
606
|
throw new Error("Not authenticated");
|
|
599
607
|
}
|
|
600
608
|
console.log("Fetching your projects...\n");
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
609
|
+
const client = createPlatformClientWithHeaders(apiUrl, {
|
|
610
|
+
Authorization: `Bearer ${credentials.accessToken}`
|
|
611
|
+
});
|
|
612
|
+
const sessionContext = await client.identity.getSessionContext();
|
|
613
|
+
if (!sessionContext) {
|
|
614
|
+
throw new Error("Could not fetch session context");
|
|
615
|
+
}
|
|
616
|
+
const tenants = (sessionContext.availableTenants ?? []).map(
|
|
617
|
+
(t) => ({
|
|
618
|
+
id: t.id,
|
|
619
|
+
name: t.name
|
|
620
|
+
})
|
|
604
621
|
);
|
|
605
|
-
const
|
|
622
|
+
const projects = (sessionContext.availableProjects ?? []).map((p) => ({
|
|
623
|
+
id: p.id,
|
|
624
|
+
name: p.name,
|
|
625
|
+
tenantId: p.tenantId
|
|
626
|
+
}));
|
|
606
627
|
if (projects.length === 0) {
|
|
607
628
|
console.log("No projects found. Create one in the platform first.");
|
|
608
629
|
throw new Error("No projects available");
|
|
@@ -651,12 +672,12 @@ function registerSelectProjectCommand(program2, globalOpts) {
|
|
|
651
672
|
selectedProject = projects.find((p) => p.id === projectId);
|
|
652
673
|
}
|
|
653
674
|
console.log("\nProvisioning API key for CLI access...");
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
selectedProject.id
|
|
658
|
-
|
|
659
|
-
);
|
|
675
|
+
const projectClient = createPlatformClientWithHeaders(apiUrl, {
|
|
676
|
+
Authorization: `Bearer ${credentials.accessToken}`,
|
|
677
|
+
"x-tenant-id": selectedProject.tenantId,
|
|
678
|
+
"x-project-id": selectedProject.id
|
|
679
|
+
});
|
|
680
|
+
const { apiKey, apiKeyId } = await provisionApiKey(projectClient);
|
|
660
681
|
await writeProjectContext(
|
|
661
682
|
{
|
|
662
683
|
id: selectedProject.id,
|
|
@@ -681,6 +702,26 @@ function registerSelectProjectCommand(program2, globalOpts) {
|
|
|
681
702
|
)
|
|
682
703
|
);
|
|
683
704
|
}
|
|
705
|
+
async function provisionApiKey(client) {
|
|
706
|
+
const { items: apiKeys } = await client.identity.listApiKeys({ limit: 100 });
|
|
707
|
+
const existing = apiKeys.find(
|
|
708
|
+
(k) => k.name === CLI_API_KEY_NAME && k.isActive
|
|
709
|
+
);
|
|
710
|
+
if (existing) {
|
|
711
|
+
console.log(" Rotating existing CLI API key...");
|
|
712
|
+
const result2 = await client.identity.rotateApiKey(existing.id);
|
|
713
|
+
if (!result2.apiKey?.rawKey) {
|
|
714
|
+
throw new Error("Failed to rotate API key \u2014 no raw key returned");
|
|
715
|
+
}
|
|
716
|
+
return { apiKey: result2.apiKey.rawKey, apiKeyId: result2.apiKey.id };
|
|
717
|
+
}
|
|
718
|
+
console.log(" Creating CLI API key...");
|
|
719
|
+
const result = await client.identity.createApiKey({ name: CLI_API_KEY_NAME });
|
|
720
|
+
if (!result.apiKey?.rawKey) {
|
|
721
|
+
throw new Error("Failed to create API key \u2014 no raw key returned");
|
|
722
|
+
}
|
|
723
|
+
return { apiKey: result.apiKey.rawKey, apiKeyId: result.apiKey.id };
|
|
724
|
+
}
|
|
684
725
|
|
|
685
726
|
// src/lib/output.ts
|
|
686
727
|
import chalk2 from "chalk";
|
|
@@ -734,11 +775,11 @@ function formatList(items, options, config2) {
|
|
|
734
775
|
${items.length} of ${config2.total} shown`));
|
|
735
776
|
}
|
|
736
777
|
}
|
|
737
|
-
function pad(
|
|
738
|
-
if (
|
|
739
|
-
return
|
|
778
|
+
function pad(str2, width) {
|
|
779
|
+
if (str2.length > width) {
|
|
780
|
+
return str2.slice(0, width - 1) + "\u2026";
|
|
740
781
|
}
|
|
741
|
-
return
|
|
782
|
+
return str2.padEnd(width);
|
|
742
783
|
}
|
|
743
784
|
function timeAgo(dateStr) {
|
|
744
785
|
if (!dateStr) return "\u2014";
|
|
@@ -831,99 +872,303 @@ function registerWhoamiCommand(program2, globalOpts) {
|
|
|
831
872
|
import { promises as fs2 } from "fs";
|
|
832
873
|
import { basename } from "path";
|
|
833
874
|
import chalk3 from "chalk";
|
|
875
|
+
import { createStorageClient } from "@foir/connect-clients/storage";
|
|
834
876
|
|
|
835
|
-
// src/lib/
|
|
836
|
-
import
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
const
|
|
844
|
-
if (
|
|
845
|
-
|
|
846
|
-
return
|
|
847
|
-
}
|
|
848
|
-
const credentials = await getCredentials();
|
|
849
|
-
if (!credentials) {
|
|
850
|
-
throw new Error("Not authenticated. Run `foir login` or set FOIR_API_KEY.");
|
|
877
|
+
// src/lib/input.ts
|
|
878
|
+
import inquirer2 from "inquirer";
|
|
879
|
+
|
|
880
|
+
// src/lib/config-loader.ts
|
|
881
|
+
import { readFile } from "fs/promises";
|
|
882
|
+
import { pathToFileURL } from "url";
|
|
883
|
+
import { resolve } from "path";
|
|
884
|
+
async function loadConfig(filePath) {
|
|
885
|
+
const absPath = resolve(filePath);
|
|
886
|
+
if (filePath.endsWith(".ts")) {
|
|
887
|
+
const configModule = await import(pathToFileURL(absPath).href);
|
|
888
|
+
return configModule.default;
|
|
851
889
|
}
|
|
852
|
-
if (
|
|
853
|
-
|
|
890
|
+
if (filePath.endsWith(".js") || filePath.endsWith(".mjs")) {
|
|
891
|
+
const configModule = await import(pathToFileURL(absPath).href);
|
|
892
|
+
return configModule.default;
|
|
854
893
|
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
headers["x-tenant-id"] = resolved.project.tenantId;
|
|
859
|
-
headers["x-project-id"] = resolved.project.id;
|
|
894
|
+
if (filePath.endsWith(".json")) {
|
|
895
|
+
const content = await readFile(absPath, "utf-8");
|
|
896
|
+
return JSON.parse(content);
|
|
860
897
|
}
|
|
861
|
-
|
|
898
|
+
throw new Error(
|
|
899
|
+
`Unsupported file extension for "${filePath}". Supported: .ts, .js, .mjs, .json`
|
|
900
|
+
);
|
|
862
901
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
headers["x-api-key"] = envApiKey;
|
|
869
|
-
return { apiUrl, headers };
|
|
870
|
-
}
|
|
871
|
-
const credentials = await getCredentials();
|
|
872
|
-
if (!credentials) {
|
|
873
|
-
throw new Error("Not authenticated. Run `foir login` or set FOIR_API_KEY.");
|
|
902
|
+
|
|
903
|
+
// src/lib/input.ts
|
|
904
|
+
async function parseInputData(opts) {
|
|
905
|
+
if (opts.data) {
|
|
906
|
+
return JSON.parse(opts.data);
|
|
874
907
|
}
|
|
875
|
-
if (
|
|
876
|
-
|
|
908
|
+
if (opts.file) {
|
|
909
|
+
return await loadConfig(opts.file);
|
|
877
910
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
911
|
+
if (!process.stdin.isTTY) {
|
|
912
|
+
const chunks = [];
|
|
913
|
+
for await (const chunk of process.stdin) {
|
|
914
|
+
chunks.push(chunk);
|
|
915
|
+
}
|
|
916
|
+
const stdinContent = Buffer.concat(chunks).toString("utf-8").trim();
|
|
917
|
+
if (stdinContent) {
|
|
918
|
+
return JSON.parse(stdinContent);
|
|
919
|
+
}
|
|
883
920
|
}
|
|
884
|
-
|
|
921
|
+
throw new Error(
|
|
922
|
+
"No input data provided. Use --data, --file, or pipe via stdin."
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
function isUUID(value) {
|
|
926
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
|
927
|
+
value
|
|
928
|
+
) || /^c[a-z0-9]{24,}$/.test(value);
|
|
929
|
+
}
|
|
930
|
+
async function confirmAction(message, opts) {
|
|
931
|
+
if (opts?.confirm) return true;
|
|
932
|
+
const { confirmed } = await inquirer2.prompt([
|
|
933
|
+
{
|
|
934
|
+
type: "confirm",
|
|
935
|
+
name: "confirmed",
|
|
936
|
+
message,
|
|
937
|
+
default: false
|
|
938
|
+
}
|
|
939
|
+
]);
|
|
940
|
+
return confirmed;
|
|
885
941
|
}
|
|
886
942
|
|
|
887
943
|
// src/commands/media.ts
|
|
944
|
+
var MIME_TYPES = {
|
|
945
|
+
".jpg": "image/jpeg",
|
|
946
|
+
".jpeg": "image/jpeg",
|
|
947
|
+
".png": "image/png",
|
|
948
|
+
".gif": "image/gif",
|
|
949
|
+
".webp": "image/webp",
|
|
950
|
+
".avif": "image/avif",
|
|
951
|
+
".svg": "image/svg+xml",
|
|
952
|
+
".mp4": "video/mp4",
|
|
953
|
+
".webm": "video/webm",
|
|
954
|
+
".mov": "video/quicktime",
|
|
955
|
+
".mp3": "audio/mpeg",
|
|
956
|
+
".wav": "audio/wav",
|
|
957
|
+
".pdf": "application/pdf",
|
|
958
|
+
".json": "application/json",
|
|
959
|
+
".csv": "text/csv",
|
|
960
|
+
".txt": "text/plain"
|
|
961
|
+
};
|
|
962
|
+
function guessMimeType(filename) {
|
|
963
|
+
const ext = filename.slice(filename.lastIndexOf(".")).toLowerCase();
|
|
964
|
+
return MIME_TYPES[ext] ?? "application/octet-stream";
|
|
965
|
+
}
|
|
966
|
+
function getStorageUrl() {
|
|
967
|
+
return process.env.FOIR_STORAGE_URL ?? "https://storage.foir.dev";
|
|
968
|
+
}
|
|
969
|
+
function createClient2(getToken) {
|
|
970
|
+
return createStorageClient({
|
|
971
|
+
baseUrl: getStorageUrl(),
|
|
972
|
+
getAuthToken: getToken
|
|
973
|
+
});
|
|
974
|
+
}
|
|
888
975
|
function registerMediaCommands(program2, globalOpts) {
|
|
889
976
|
const media = program2.command("media").description("Media file operations");
|
|
890
|
-
media.command("upload <filepath>").description("Upload a file").action(
|
|
891
|
-
withErrorHandler(
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
977
|
+
media.command("upload <filepath>").description("Upload a file").option("--folder <folder>", "Target folder").action(
|
|
978
|
+
withErrorHandler(
|
|
979
|
+
globalOpts,
|
|
980
|
+
async (filepath, flags) => {
|
|
981
|
+
const opts = globalOpts();
|
|
982
|
+
const { getToken } = await getStorageAuth(opts);
|
|
983
|
+
const storage = createClient2(getToken);
|
|
984
|
+
const fileBuffer = await fs2.readFile(filepath);
|
|
985
|
+
const filename = basename(filepath);
|
|
986
|
+
const mimeType = guessMimeType(filename);
|
|
987
|
+
const upload = await storage.createFileUpload({
|
|
988
|
+
filename,
|
|
989
|
+
mimeType,
|
|
990
|
+
size: fileBuffer.byteLength,
|
|
991
|
+
folder: flags.folder
|
|
992
|
+
});
|
|
993
|
+
const uploadResp = await fetch(upload.uploadUrl, {
|
|
994
|
+
method: "PUT",
|
|
995
|
+
headers: { "Content-Type": mimeType },
|
|
996
|
+
body: fileBuffer
|
|
997
|
+
});
|
|
998
|
+
if (!uploadResp.ok) {
|
|
999
|
+
throw new Error(
|
|
1000
|
+
`Upload to storage failed (${uploadResp.status}): ${await uploadResp.text()}`
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
const file = await storage.confirmFileUpload(upload.uploadId);
|
|
1004
|
+
if (opts.json || opts.jsonl) {
|
|
1005
|
+
formatOutput(file, opts);
|
|
1006
|
+
} else {
|
|
1007
|
+
success(`Uploaded ${filename}`);
|
|
1008
|
+
if (file?.url) {
|
|
1009
|
+
console.log(chalk3.bold(` URL: ${file.url}`));
|
|
1010
|
+
}
|
|
1011
|
+
if (file?.storageKey) {
|
|
1012
|
+
console.log(chalk3.gray(` Key: ${file.storageKey}`));
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
907
1015
|
}
|
|
908
|
-
|
|
1016
|
+
)
|
|
1017
|
+
);
|
|
1018
|
+
media.command("list").description("List files").option("--folder <folder>", "Filter by folder").option("--mime-type <type>", "Filter by MIME type").option("--search <query>", "Search files").option("--include-deleted", "Include soft-deleted files").option("--limit <n>", "Max results", "50").option("--offset <n>", "Offset", "0").action(
|
|
1019
|
+
withErrorHandler(
|
|
1020
|
+
globalOpts,
|
|
1021
|
+
async (flags) => {
|
|
1022
|
+
const opts = globalOpts();
|
|
1023
|
+
const { getToken } = await getStorageAuth(opts);
|
|
1024
|
+
const storage = createClient2(getToken);
|
|
1025
|
+
const result = await storage.listFiles({
|
|
1026
|
+
folder: flags.folder,
|
|
1027
|
+
mimeType: flags["mime-type"] ?? flags.mimeType,
|
|
1028
|
+
search: flags.search,
|
|
1029
|
+
includeDeleted: !!flags.includeDeleted,
|
|
1030
|
+
limit: Number(flags.limit) || 50,
|
|
1031
|
+
offset: Number(flags.offset) || 0
|
|
1032
|
+
});
|
|
1033
|
+
formatList(result.items, opts, {
|
|
1034
|
+
columns: [
|
|
1035
|
+
{ key: "id", header: "ID", width: 28 },
|
|
1036
|
+
{ key: "filename", header: "Filename", width: 24 },
|
|
1037
|
+
{ key: "mimeType", header: "Type", width: 16 },
|
|
1038
|
+
{
|
|
1039
|
+
key: "size",
|
|
1040
|
+
header: "Size",
|
|
1041
|
+
width: 10,
|
|
1042
|
+
format: (v) => {
|
|
1043
|
+
const num2 = Number(v);
|
|
1044
|
+
if (num2 < 1024) return `${num2} B`;
|
|
1045
|
+
if (num2 < 1024 * 1024) return `${(num2 / 1024).toFixed(1)} KB`;
|
|
1046
|
+
return `${(num2 / (1024 * 1024)).toFixed(1)} MB`;
|
|
1047
|
+
}
|
|
1048
|
+
},
|
|
1049
|
+
{ key: "folder", header: "Folder", width: 16 },
|
|
1050
|
+
{
|
|
1051
|
+
key: "createdAt",
|
|
1052
|
+
header: "Created",
|
|
1053
|
+
width: 12,
|
|
1054
|
+
format: (v) => timeAgo(v)
|
|
1055
|
+
}
|
|
1056
|
+
],
|
|
1057
|
+
total: result.total
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
)
|
|
1061
|
+
);
|
|
1062
|
+
media.command("get <id>").description("Get file details").action(
|
|
1063
|
+
withErrorHandler(globalOpts, async (id) => {
|
|
1064
|
+
const opts = globalOpts();
|
|
1065
|
+
const { getToken } = await getStorageAuth(opts);
|
|
1066
|
+
const storage = createClient2(getToken);
|
|
1067
|
+
const file = await storage.getFile(id);
|
|
1068
|
+
formatOutput(file, opts);
|
|
1069
|
+
})
|
|
1070
|
+
);
|
|
1071
|
+
media.command("usage").description("Get storage usage").action(
|
|
1072
|
+
withErrorHandler(globalOpts, async () => {
|
|
1073
|
+
const opts = globalOpts();
|
|
1074
|
+
const { getToken } = await getStorageAuth(opts);
|
|
1075
|
+
const storage = createClient2(getToken);
|
|
1076
|
+
const usage = await storage.getStorageUsage();
|
|
909
1077
|
if (opts.json || opts.jsonl) {
|
|
910
|
-
formatOutput(
|
|
1078
|
+
formatOutput(usage, opts);
|
|
911
1079
|
} else {
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1080
|
+
const mb = (Number(usage?.totalBytes ?? 0) / (1024 * 1024)).toFixed(
|
|
1081
|
+
1
|
|
1082
|
+
);
|
|
1083
|
+
console.log(chalk3.bold(`Files: ${usage?.totalFiles ?? 0}`));
|
|
1084
|
+
console.log(chalk3.bold(`Storage: ${mb} MB`));
|
|
1085
|
+
}
|
|
1086
|
+
})
|
|
1087
|
+
);
|
|
1088
|
+
media.command("update <id>").description("Update a file").option("--filename <name>", "New filename").option("--folder <folder>", "Move to folder").option("--tags <tags>", "Comma-separated tags").action(
|
|
1089
|
+
withErrorHandler(
|
|
1090
|
+
globalOpts,
|
|
1091
|
+
async (id, flags) => {
|
|
1092
|
+
const opts = globalOpts();
|
|
1093
|
+
const { getToken } = await getStorageAuth(opts);
|
|
1094
|
+
const storage = createClient2(getToken);
|
|
1095
|
+
const file = await storage.updateFile({
|
|
1096
|
+
id,
|
|
1097
|
+
filename: flags.filename,
|
|
1098
|
+
folder: flags.folder,
|
|
1099
|
+
tags: flags.tags?.split(",").map((t) => t.trim())
|
|
1100
|
+
});
|
|
1101
|
+
if (opts.json || opts.jsonl) {
|
|
1102
|
+
formatOutput(file, opts);
|
|
1103
|
+
} else {
|
|
1104
|
+
success("Updated file");
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
)
|
|
1108
|
+
);
|
|
1109
|
+
media.command("update-metadata <id>").description("Update file metadata (alt text, caption, description)").option("--alt-text <text>", "Alt text").option("--caption <text>", "Caption").option("--description <text>", "Description").action(
|
|
1110
|
+
withErrorHandler(
|
|
1111
|
+
globalOpts,
|
|
1112
|
+
async (id, flags) => {
|
|
1113
|
+
const opts = globalOpts();
|
|
1114
|
+
const { getToken } = await getStorageAuth(opts);
|
|
1115
|
+
const storage = createClient2(getToken);
|
|
1116
|
+
const file = await storage.updateFileMetadata({
|
|
1117
|
+
id,
|
|
1118
|
+
altText: flags.altText ?? flags["alt-text"],
|
|
1119
|
+
caption: flags.caption,
|
|
1120
|
+
description: flags.description
|
|
1121
|
+
});
|
|
1122
|
+
if (opts.json || opts.jsonl) {
|
|
1123
|
+
formatOutput(file, opts);
|
|
1124
|
+
} else {
|
|
1125
|
+
success("Updated file metadata");
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
)
|
|
1129
|
+
);
|
|
1130
|
+
media.command("delete <id>").description("Delete a file").option("--confirm", "Skip confirmation prompt").option("--permanent", "Permanently delete (cannot be restored)").action(
|
|
1131
|
+
withErrorHandler(
|
|
1132
|
+
globalOpts,
|
|
1133
|
+
async (id, flags) => {
|
|
1134
|
+
const opts = globalOpts();
|
|
1135
|
+
const confirmed = await confirmAction("Delete this file?", {
|
|
1136
|
+
confirm: !!flags.confirm
|
|
1137
|
+
});
|
|
1138
|
+
if (!confirmed) {
|
|
1139
|
+
console.log("Aborted.");
|
|
1140
|
+
return;
|
|
915
1141
|
}
|
|
916
|
-
|
|
917
|
-
|
|
1142
|
+
const { getToken } = await getStorageAuth(opts);
|
|
1143
|
+
const storage = createClient2(getToken);
|
|
1144
|
+
if (flags.permanent) {
|
|
1145
|
+
await storage.permanentlyDeleteFile(id);
|
|
1146
|
+
success("Permanently deleted file");
|
|
1147
|
+
} else {
|
|
1148
|
+
await storage.deleteFile(id);
|
|
1149
|
+
success("Deleted file");
|
|
918
1150
|
}
|
|
919
1151
|
}
|
|
1152
|
+
)
|
|
1153
|
+
);
|
|
1154
|
+
media.command("restore <id>").description("Restore a deleted file").action(
|
|
1155
|
+
withErrorHandler(globalOpts, async (id) => {
|
|
1156
|
+
const opts = globalOpts();
|
|
1157
|
+
const { getToken } = await getStorageAuth(opts);
|
|
1158
|
+
const storage = createClient2(getToken);
|
|
1159
|
+
const file = await storage.restoreFile(id);
|
|
1160
|
+
if (opts.json || opts.jsonl) {
|
|
1161
|
+
formatOutput(file, opts);
|
|
1162
|
+
} else {
|
|
1163
|
+
success("Restored file");
|
|
1164
|
+
}
|
|
920
1165
|
})
|
|
921
1166
|
);
|
|
922
1167
|
}
|
|
923
1168
|
|
|
924
1169
|
// src/commands/create-config.ts
|
|
925
1170
|
import chalk4 from "chalk";
|
|
926
|
-
import
|
|
1171
|
+
import inquirer3 from "inquirer";
|
|
927
1172
|
|
|
928
1173
|
// src/scaffold/scaffold.ts
|
|
929
1174
|
import * as fs4 from "fs";
|
|
@@ -1347,7 +1592,7 @@ function getApiTsconfig() {
|
|
|
1347
1592
|
return JSON.stringify(config2, null, 2) + "\n";
|
|
1348
1593
|
}
|
|
1349
1594
|
function getApiEnvExample(apiUrl) {
|
|
1350
|
-
const baseUrl = apiUrl.replace(
|
|
1595
|
+
const baseUrl = apiUrl.replace(/\/$/, "");
|
|
1351
1596
|
return `# Platform API
|
|
1352
1597
|
PLATFORM_BASE_URL=${baseUrl}
|
|
1353
1598
|
PLATFORM_API_KEY=sk_your_api_key_here
|
|
@@ -1425,11 +1670,7 @@ function isValidConfigType(value) {
|
|
|
1425
1670
|
return CONFIG_TYPES.includes(value);
|
|
1426
1671
|
}
|
|
1427
1672
|
function registerCreateConfigCommand(program2, globalOpts) {
|
|
1428
|
-
program2.command("create-config [name]").description("Scaffold a new Foir config").option("--type <type>", "Config type: custom-editor, workflow, widget").option(
|
|
1429
|
-
"--api-url <url>",
|
|
1430
|
-
"Platform API URL",
|
|
1431
|
-
"http://localhost:4000/graphql"
|
|
1432
|
-
).action(
|
|
1673
|
+
program2.command("create-config [name]").description("Scaffold a new Foir config").option("--type <type>", "Config type: custom-editor, workflow, widget").option("--api-url <url>", "Platform API URL", "http://localhost:4011").action(
|
|
1433
1674
|
withErrorHandler(
|
|
1434
1675
|
globalOpts,
|
|
1435
1676
|
async (name, cmdOpts) => {
|
|
@@ -1439,7 +1680,7 @@ function registerCreateConfigCommand(program2, globalOpts) {
|
|
|
1439
1680
|
console.log();
|
|
1440
1681
|
let configName = name;
|
|
1441
1682
|
if (!configName) {
|
|
1442
|
-
const { inputName } = await
|
|
1683
|
+
const { inputName } = await inquirer3.prompt([
|
|
1443
1684
|
{
|
|
1444
1685
|
type: "input",
|
|
1445
1686
|
name: "inputName",
|
|
@@ -1453,7 +1694,7 @@ function registerCreateConfigCommand(program2, globalOpts) {
|
|
|
1453
1694
|
if (cmdOpts?.type && isValidConfigType(cmdOpts.type)) {
|
|
1454
1695
|
configType = cmdOpts.type;
|
|
1455
1696
|
} else {
|
|
1456
|
-
const { selectedType } = await
|
|
1697
|
+
const { selectedType } = await inquirer3.prompt([
|
|
1457
1698
|
{
|
|
1458
1699
|
type: "list",
|
|
1459
1700
|
name: "selectedType",
|
|
@@ -1464,7 +1705,7 @@ function registerCreateConfigCommand(program2, globalOpts) {
|
|
|
1464
1705
|
]);
|
|
1465
1706
|
configType = selectedType;
|
|
1466
1707
|
}
|
|
1467
|
-
const apiUrl = cmdOpts?.apiUrl ?? "http://localhost:
|
|
1708
|
+
const apiUrl = cmdOpts?.apiUrl ?? "http://localhost:4011";
|
|
1468
1709
|
console.log();
|
|
1469
1710
|
console.log(
|
|
1470
1711
|
` Scaffolding ${chalk4.cyan(`"${configName}"`)} (${configType})...`
|
|
@@ -1476,69 +1717,53 @@ function registerCreateConfigCommand(program2, globalOpts) {
|
|
|
1476
1717
|
);
|
|
1477
1718
|
}
|
|
1478
1719
|
|
|
1479
|
-
// src/graphql/generated.ts
|
|
1480
|
-
var GlobalSearchDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "GlobalSearch" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "query" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "String" } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "limit" } }, "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "modelKeys" } }, "type": { "kind": "ListType", "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "String" } } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "includeMedia" } }, "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Boolean" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "globalSearch" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "query" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "query" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "limit" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "limit" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "modelKeys" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "modelKeys" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "includeMedia" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "includeMedia" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "records" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "modelKey" } }, { "kind": "Field", "name": { "kind": "Name", "value": "title" } }, { "kind": "Field", "name": { "kind": "Name", "value": "naturalKey" } }, { "kind": "Field", "name": { "kind": "Name", "value": "subtitle" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updatedAt" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "media" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "fileName" } }, { "kind": "Field", "name": { "kind": "Name", "value": "altText" } }, { "kind": "Field", "name": { "kind": "Name", "value": "fileUrl" } }] } }] } }] } }] };
|
|
1481
|
-
|
|
1482
1720
|
// src/commands/search.ts
|
|
1483
1721
|
function registerSearchCommands(program2, globalOpts) {
|
|
1484
|
-
program2.command("search <query>").description("Search across all records
|
|
1722
|
+
program2.command("search <query>").description("Search across all records").option(
|
|
1485
1723
|
"--models <keys>",
|
|
1486
1724
|
"Filter to specific model keys (comma-separated)"
|
|
1487
|
-
).option("--limit <n>", "Max results", "20").
|
|
1725
|
+
).option("--limit <n>", "Max results", "20").action(
|
|
1488
1726
|
withErrorHandler(
|
|
1489
1727
|
globalOpts,
|
|
1490
1728
|
async (query, cmdOpts) => {
|
|
1491
1729
|
const opts = globalOpts();
|
|
1492
|
-
const client = await
|
|
1730
|
+
const client = await createPlatformClient(opts);
|
|
1493
1731
|
const modelKeys = typeof cmdOpts.models === "string" ? cmdOpts.models.split(",").map((k) => k.trim()) : void 0;
|
|
1494
|
-
const
|
|
1732
|
+
const result = await client.records.globalSearch({
|
|
1495
1733
|
query,
|
|
1496
1734
|
limit: parseInt(String(cmdOpts.limit ?? "20"), 10),
|
|
1497
|
-
modelKeys
|
|
1498
|
-
includeMedia: cmdOpts.media !== false
|
|
1735
|
+
modelKeys
|
|
1499
1736
|
});
|
|
1500
1737
|
if (opts.json || opts.jsonl) {
|
|
1501
|
-
formatOutput(
|
|
1738
|
+
formatOutput(result, opts);
|
|
1502
1739
|
return;
|
|
1503
1740
|
}
|
|
1504
|
-
if (
|
|
1741
|
+
if (result.items.length > 0) {
|
|
1505
1742
|
console.log(`
|
|
1506
|
-
Records (${
|
|
1743
|
+
Records (${result.items.length}):`);
|
|
1507
1744
|
formatList(
|
|
1508
|
-
|
|
1745
|
+
result.items.map((item) => ({
|
|
1746
|
+
id: item.id,
|
|
1747
|
+
modelKey: item.modelKey,
|
|
1748
|
+
naturalKey: item.naturalKey ?? "",
|
|
1749
|
+
score: item.score
|
|
1750
|
+
})),
|
|
1509
1751
|
opts,
|
|
1510
1752
|
{
|
|
1511
1753
|
columns: [
|
|
1512
1754
|
{ key: "id", header: "ID", width: 28 },
|
|
1513
1755
|
{ key: "modelKey", header: "Model", width: 18 },
|
|
1514
|
-
{ key: "title", header: "Title", width: 28 },
|
|
1515
1756
|
{ key: "naturalKey", header: "Key", width: 20 },
|
|
1516
1757
|
{
|
|
1517
|
-
key: "
|
|
1518
|
-
header: "
|
|
1519
|
-
width:
|
|
1520
|
-
format: (v) =>
|
|
1758
|
+
key: "score",
|
|
1759
|
+
header: "Score",
|
|
1760
|
+
width: 8,
|
|
1761
|
+
format: (v) => Number(v).toFixed(2)
|
|
1521
1762
|
}
|
|
1522
1763
|
]
|
|
1523
1764
|
}
|
|
1524
1765
|
);
|
|
1525
|
-
}
|
|
1526
|
-
if (data.globalSearch.media.length > 0) {
|
|
1527
|
-
console.log(`
|
|
1528
|
-
Media (${data.globalSearch.media.length}):`);
|
|
1529
|
-
formatList(
|
|
1530
|
-
data.globalSearch.media,
|
|
1531
|
-
opts,
|
|
1532
|
-
{
|
|
1533
|
-
columns: [
|
|
1534
|
-
{ key: "id", header: "ID", width: 28 },
|
|
1535
|
-
{ key: "fileName", header: "File", width: 30 },
|
|
1536
|
-
{ key: "altText", header: "Alt", width: 24 }
|
|
1537
|
-
]
|
|
1538
|
-
}
|
|
1539
|
-
);
|
|
1540
|
-
}
|
|
1541
|
-
if (data.globalSearch.records.length === 0 && data.globalSearch.media.length === 0) {
|
|
1766
|
+
} else {
|
|
1542
1767
|
console.log("No results found.");
|
|
1543
1768
|
}
|
|
1544
1769
|
}
|
|
@@ -1549,9 +1774,9 @@ Media (${data.globalSearch.media.length}):`);
|
|
|
1549
1774
|
// src/commands/init.ts
|
|
1550
1775
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
1551
1776
|
import { writeFile } from "fs/promises";
|
|
1552
|
-
import { resolve as
|
|
1777
|
+
import { resolve as resolve3, join as join4 } from "path";
|
|
1553
1778
|
import chalk5 from "chalk";
|
|
1554
|
-
import
|
|
1779
|
+
import inquirer4 from "inquirer";
|
|
1555
1780
|
var FIELD_DEFAULTS = {
|
|
1556
1781
|
text: "",
|
|
1557
1782
|
richtext: "",
|
|
@@ -1613,9 +1838,9 @@ function generateRecordSeed(model) {
|
|
|
1613
1838
|
)) {
|
|
1614
1839
|
continue;
|
|
1615
1840
|
}
|
|
1616
|
-
if (field.type === "list" && field.
|
|
1841
|
+
if (field.type === "list" && field.itemType) {
|
|
1617
1842
|
data[field.key] = [
|
|
1618
|
-
defaultValueForField({ key: field.key, type: field.
|
|
1843
|
+
defaultValueForField({ key: field.key, type: field.itemType })
|
|
1619
1844
|
];
|
|
1620
1845
|
} else {
|
|
1621
1846
|
data[field.key] = defaultValueForField(field);
|
|
@@ -1640,7 +1865,7 @@ function registerInitCommands(program2, globalOpts) {
|
|
|
1640
1865
|
async (key, opts) => {
|
|
1641
1866
|
const globalFlags = globalOpts();
|
|
1642
1867
|
const template = generateModelTemplate(key);
|
|
1643
|
-
const outDir =
|
|
1868
|
+
const outDir = resolve3(opts.output);
|
|
1644
1869
|
if (!existsSync3(outDir)) {
|
|
1645
1870
|
mkdirSync2(outDir, { recursive: true });
|
|
1646
1871
|
}
|
|
@@ -1671,10 +1896,19 @@ Edit the file, then run:
|
|
|
1671
1896
|
globalOpts,
|
|
1672
1897
|
async (opts) => {
|
|
1673
1898
|
const globalFlags = globalOpts();
|
|
1674
|
-
const client = await
|
|
1675
|
-
const
|
|
1676
|
-
const
|
|
1677
|
-
|
|
1899
|
+
const client = await createPlatformClient(globalFlags);
|
|
1900
|
+
const result = await client.models.listModels({ limit: 100 });
|
|
1901
|
+
const models = result.items.map((m) => ({
|
|
1902
|
+
key: m.key,
|
|
1903
|
+
name: m.name,
|
|
1904
|
+
fields: (m.fields ?? []).map((f) => ({
|
|
1905
|
+
key: f.key,
|
|
1906
|
+
type: f.type,
|
|
1907
|
+
label: f.label,
|
|
1908
|
+
required: f.required,
|
|
1909
|
+
itemType: f.itemType
|
|
1910
|
+
}))
|
|
1911
|
+
}));
|
|
1678
1912
|
if (models.length === 0) {
|
|
1679
1913
|
console.log(
|
|
1680
1914
|
chalk5.yellow(
|
|
@@ -1699,7 +1933,7 @@ Edit the file, then run:
|
|
|
1699
1933
|
selectedModels.push(found);
|
|
1700
1934
|
}
|
|
1701
1935
|
} else {
|
|
1702
|
-
const { selected } = await
|
|
1936
|
+
const { selected } = await inquirer4.prompt([
|
|
1703
1937
|
{
|
|
1704
1938
|
type: "checkbox",
|
|
1705
1939
|
name: "selected",
|
|
@@ -1718,7 +1952,7 @@ Edit the file, then run:
|
|
|
1718
1952
|
console.log("No models selected.");
|
|
1719
1953
|
return;
|
|
1720
1954
|
}
|
|
1721
|
-
const outDir =
|
|
1955
|
+
const outDir = resolve3(opts.output);
|
|
1722
1956
|
if (!existsSync3(outDir)) {
|
|
1723
1957
|
mkdirSync2(outDir, { recursive: true });
|
|
1724
1958
|
}
|
|
@@ -1754,64 +1988,12 @@ Edit the files, then run:
|
|
|
1754
1988
|
import chalk6 from "chalk";
|
|
1755
1989
|
import { existsSync as existsSync4 } from "fs";
|
|
1756
1990
|
import { resolve as resolve4 } from "path";
|
|
1757
|
-
|
|
1758
|
-
// src/lib/config-loader.ts
|
|
1759
|
-
import { readFile } from "fs/promises";
|
|
1760
|
-
import { pathToFileURL } from "url";
|
|
1761
|
-
import { resolve as resolve3 } from "path";
|
|
1762
|
-
async function loadConfig(filePath) {
|
|
1763
|
-
const absPath = resolve3(filePath);
|
|
1764
|
-
if (filePath.endsWith(".ts")) {
|
|
1765
|
-
const configModule = await import(pathToFileURL(absPath).href);
|
|
1766
|
-
return configModule.default;
|
|
1767
|
-
}
|
|
1768
|
-
if (filePath.endsWith(".js") || filePath.endsWith(".mjs")) {
|
|
1769
|
-
const configModule = await import(pathToFileURL(absPath).href);
|
|
1770
|
-
return configModule.default;
|
|
1771
|
-
}
|
|
1772
|
-
if (filePath.endsWith(".json")) {
|
|
1773
|
-
const content = await readFile(absPath, "utf-8");
|
|
1774
|
-
return JSON.parse(content);
|
|
1775
|
-
}
|
|
1776
|
-
throw new Error(
|
|
1777
|
-
`Unsupported file extension for "${filePath}". Supported: .ts, .js, .mjs, .json`
|
|
1778
|
-
);
|
|
1779
|
-
}
|
|
1780
|
-
|
|
1781
|
-
// src/commands/push.ts
|
|
1782
1991
|
var CONFIG_FILE_NAMES = [
|
|
1783
1992
|
"foir.config.ts",
|
|
1784
1993
|
"foir.config.js",
|
|
1785
1994
|
"foir.config.mjs",
|
|
1786
1995
|
"foir.config.json"
|
|
1787
1996
|
];
|
|
1788
|
-
var APPLY_CONFIG_MUTATION = (
|
|
1789
|
-
/* GraphQL */
|
|
1790
|
-
`
|
|
1791
|
-
mutation ApplyConfig($input: ApplyConfigInput!) {
|
|
1792
|
-
applyConfig(input: $input) {
|
|
1793
|
-
configId
|
|
1794
|
-
configKey
|
|
1795
|
-
credentials {
|
|
1796
|
-
platformApiKey
|
|
1797
|
-
platformEditorKey
|
|
1798
|
-
webhookSecret
|
|
1799
|
-
}
|
|
1800
|
-
modelsCreated
|
|
1801
|
-
modelsUpdated
|
|
1802
|
-
operationsCreated
|
|
1803
|
-
operationsUpdated
|
|
1804
|
-
segmentsCreated
|
|
1805
|
-
segmentsUpdated
|
|
1806
|
-
schedulesCreated
|
|
1807
|
-
schedulesUpdated
|
|
1808
|
-
hooksCreated
|
|
1809
|
-
hooksUpdated
|
|
1810
|
-
isUpdate
|
|
1811
|
-
}
|
|
1812
|
-
}
|
|
1813
|
-
`
|
|
1814
|
-
);
|
|
1815
1997
|
function discoverConfigFile() {
|
|
1816
1998
|
for (const name of CONFIG_FILE_NAMES) {
|
|
1817
1999
|
const path3 = resolve4(process.cwd(), name);
|
|
@@ -1836,69 +2018,32 @@ function registerPushCommand(program2, globalOpts) {
|
|
|
1836
2018
|
console.log(chalk6.dim(`Loading ${configPath}...`));
|
|
1837
2019
|
const config2 = await loadConfig(configPath);
|
|
1838
2020
|
if (!config2?.key || !config2?.name) {
|
|
1839
|
-
throw new Error(
|
|
1840
|
-
'Config must have at least "key" and "name" fields.'
|
|
1841
|
-
);
|
|
1842
|
-
}
|
|
1843
|
-
if (opts.force) {
|
|
1844
|
-
config2.force = true;
|
|
1845
|
-
}
|
|
1846
|
-
const client = await createClient(globalOpts());
|
|
1847
|
-
console.log(
|
|
1848
|
-
chalk6.dim(`Pushing config "${config2.key}" to platform...`)
|
|
1849
|
-
);
|
|
1850
|
-
const data = await client.request(
|
|
1851
|
-
APPLY_CONFIG_MUTATION,
|
|
1852
|
-
{ input: config2 }
|
|
1853
|
-
);
|
|
1854
|
-
const result = data.applyConfig;
|
|
1855
|
-
console.log();
|
|
1856
|
-
if (result.isUpdate) {
|
|
1857
|
-
console.log(chalk6.green("Config updated successfully."));
|
|
1858
|
-
} else {
|
|
1859
|
-
console.log(chalk6.green("Config applied successfully."));
|
|
1860
|
-
}
|
|
1861
|
-
console.log();
|
|
1862
|
-
console.log(` Config ID: ${chalk6.cyan(result.configId)}`);
|
|
1863
|
-
console.log(` Config Key: ${chalk6.cyan(result.configKey)}`);
|
|
1864
|
-
console.log();
|
|
1865
|
-
const stats = [
|
|
1866
|
-
["Models", result.modelsCreated, result.modelsUpdated],
|
|
1867
|
-
["Operations", result.operationsCreated, result.operationsUpdated],
|
|
1868
|
-
["Segments", result.segmentsCreated, result.segmentsUpdated],
|
|
1869
|
-
["Schedules", result.schedulesCreated, result.schedulesUpdated],
|
|
1870
|
-
["Hooks", result.hooksCreated, result.hooksUpdated]
|
|
1871
|
-
].filter(([, c, u]) => c > 0 || u > 0);
|
|
1872
|
-
if (stats.length > 0) {
|
|
1873
|
-
for (const [label, created, updated] of stats) {
|
|
1874
|
-
const parts = [];
|
|
1875
|
-
if (created > 0)
|
|
1876
|
-
parts.push(chalk6.green(`${created} created`));
|
|
1877
|
-
if (updated > 0)
|
|
1878
|
-
parts.push(chalk6.yellow(`${updated} updated`));
|
|
1879
|
-
console.log(` ${label}: ${parts.join(", ")}`);
|
|
1880
|
-
}
|
|
1881
|
-
console.log();
|
|
1882
|
-
}
|
|
1883
|
-
if (result.credentials) {
|
|
1884
|
-
console.log(chalk6.bold.yellow("Credentials (save these now):"));
|
|
1885
|
-
console.log();
|
|
1886
|
-
console.log(
|
|
1887
|
-
` PLATFORM_API_KEY: ${chalk6.cyan(result.credentials.platformApiKey)}`
|
|
1888
|
-
);
|
|
1889
|
-
console.log(
|
|
1890
|
-
` PLATFORM_EDITOR_KEY: ${chalk6.cyan(result.credentials.platformEditorKey)}`
|
|
1891
|
-
);
|
|
1892
|
-
console.log(
|
|
1893
|
-
` WEBHOOK_SECRET: ${chalk6.cyan(result.credentials.webhookSecret)}`
|
|
2021
|
+
throw new Error(
|
|
2022
|
+
'Config must have at least "key" and "name" fields.'
|
|
1894
2023
|
);
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
2024
|
+
}
|
|
2025
|
+
if (opts.force) {
|
|
2026
|
+
config2.force = true;
|
|
2027
|
+
}
|
|
2028
|
+
const client = await createPlatformClient(globalOpts());
|
|
2029
|
+
console.log(
|
|
2030
|
+
chalk6.dim(`Pushing config "${config2.key}" to platform...`)
|
|
2031
|
+
);
|
|
2032
|
+
const result = await client.configs.applyConfig(
|
|
2033
|
+
config2.key,
|
|
2034
|
+
config2
|
|
2035
|
+
);
|
|
2036
|
+
if (!result) {
|
|
2037
|
+
throw new Error(
|
|
2038
|
+
"Failed to apply config \u2014 no response from server."
|
|
1900
2039
|
);
|
|
1901
2040
|
}
|
|
2041
|
+
console.log();
|
|
2042
|
+
console.log(chalk6.green("Config applied successfully."));
|
|
2043
|
+
console.log();
|
|
2044
|
+
console.log(` Config ID: ${chalk6.cyan(result.id)}`);
|
|
2045
|
+
console.log(` Config Key: ${chalk6.cyan(result.key)}`);
|
|
2046
|
+
console.log();
|
|
1902
2047
|
}
|
|
1903
2048
|
)
|
|
1904
2049
|
);
|
|
@@ -1906,40 +2051,19 @@ function registerPushCommand(program2, globalOpts) {
|
|
|
1906
2051
|
|
|
1907
2052
|
// src/commands/remove.ts
|
|
1908
2053
|
import chalk7 from "chalk";
|
|
1909
|
-
import
|
|
1910
|
-
var GET_CONFIG_QUERY = (
|
|
1911
|
-
/* GraphQL */
|
|
1912
|
-
`
|
|
1913
|
-
query GetConfigByKey($key: String!) {
|
|
1914
|
-
configByKey(key: $key) {
|
|
1915
|
-
id
|
|
1916
|
-
key
|
|
1917
|
-
name
|
|
1918
|
-
configType
|
|
1919
|
-
}
|
|
1920
|
-
}
|
|
1921
|
-
`
|
|
1922
|
-
);
|
|
1923
|
-
var UNREGISTER_MUTATION = (
|
|
1924
|
-
/* GraphQL */
|
|
1925
|
-
`
|
|
1926
|
-
mutation UnregisterConfig($id: ID!) {
|
|
1927
|
-
unregisterConfig(id: $id)
|
|
1928
|
-
}
|
|
1929
|
-
`
|
|
1930
|
-
);
|
|
2054
|
+
import inquirer5 from "inquirer";
|
|
1931
2055
|
function registerRemoveCommand(program2, globalOpts) {
|
|
1932
2056
|
program2.command("remove <key>").description("Remove a config and all its provisioned resources").option("--force", "Skip confirmation prompt", false).action(
|
|
1933
2057
|
withErrorHandler(
|
|
1934
2058
|
globalOpts,
|
|
1935
2059
|
async (key, opts) => {
|
|
1936
|
-
const client = await
|
|
1937
|
-
const
|
|
2060
|
+
const client = await createPlatformClient(globalOpts());
|
|
2061
|
+
const config2 = await client.configs.getConfigByKey(key);
|
|
1938
2062
|
if (!config2) {
|
|
1939
2063
|
throw new Error(`Config not found: ${key}`);
|
|
1940
2064
|
}
|
|
1941
2065
|
if (!opts.force) {
|
|
1942
|
-
const { confirmed } = await
|
|
2066
|
+
const { confirmed } = await inquirer5.prompt([
|
|
1943
2067
|
{
|
|
1944
2068
|
type: "confirm",
|
|
1945
2069
|
name: "confirmed",
|
|
@@ -1952,7 +2076,7 @@ function registerRemoveCommand(program2, globalOpts) {
|
|
|
1952
2076
|
return;
|
|
1953
2077
|
}
|
|
1954
2078
|
}
|
|
1955
|
-
await client.
|
|
2079
|
+
await client.configs.deleteConfig(config2.id);
|
|
1956
2080
|
console.log(
|
|
1957
2081
|
chalk7.green(`Removed config "${config2.name}" (${config2.key}).`)
|
|
1958
2082
|
);
|
|
@@ -1963,49 +2087,6 @@ function registerRemoveCommand(program2, globalOpts) {
|
|
|
1963
2087
|
|
|
1964
2088
|
// src/commands/profiles.ts
|
|
1965
2089
|
import chalk8 from "chalk";
|
|
1966
|
-
|
|
1967
|
-
// src/lib/input.ts
|
|
1968
|
-
import inquirer5 from "inquirer";
|
|
1969
|
-
async function parseInputData(opts) {
|
|
1970
|
-
if (opts.data) {
|
|
1971
|
-
return JSON.parse(opts.data);
|
|
1972
|
-
}
|
|
1973
|
-
if (opts.file) {
|
|
1974
|
-
return await loadConfig(opts.file);
|
|
1975
|
-
}
|
|
1976
|
-
if (!process.stdin.isTTY) {
|
|
1977
|
-
const chunks = [];
|
|
1978
|
-
for await (const chunk of process.stdin) {
|
|
1979
|
-
chunks.push(chunk);
|
|
1980
|
-
}
|
|
1981
|
-
const stdinContent = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1982
|
-
if (stdinContent) {
|
|
1983
|
-
return JSON.parse(stdinContent);
|
|
1984
|
-
}
|
|
1985
|
-
}
|
|
1986
|
-
throw new Error(
|
|
1987
|
-
"No input data provided. Use --data, --file, or pipe via stdin."
|
|
1988
|
-
);
|
|
1989
|
-
}
|
|
1990
|
-
function isUUID(value) {
|
|
1991
|
-
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
|
1992
|
-
value
|
|
1993
|
-
) || /^c[a-z0-9]{24,}$/.test(value);
|
|
1994
|
-
}
|
|
1995
|
-
async function confirmAction(message, opts) {
|
|
1996
|
-
if (opts?.confirm) return true;
|
|
1997
|
-
const { confirmed } = await inquirer5.prompt([
|
|
1998
|
-
{
|
|
1999
|
-
type: "confirm",
|
|
2000
|
-
name: "confirmed",
|
|
2001
|
-
message,
|
|
2002
|
-
default: false
|
|
2003
|
-
}
|
|
2004
|
-
]);
|
|
2005
|
-
return confirmed;
|
|
2006
|
-
}
|
|
2007
|
-
|
|
2008
|
-
// src/commands/profiles.ts
|
|
2009
2090
|
function registerProfilesCommand(program2, globalOpts) {
|
|
2010
2091
|
const profiles = program2.command("profiles").description("Manage named project profiles");
|
|
2011
2092
|
profiles.command("list").description("List all saved project profiles").action(
|
|
@@ -2173,9 +2254,8 @@ function registerProfilesCommand(program2, globalOpts) {
|
|
|
2173
2254
|
}
|
|
2174
2255
|
|
|
2175
2256
|
// src/commands/register-commands.ts
|
|
2176
|
-
import {
|
|
2177
|
-
import { resolve as resolve5
|
|
2178
|
-
import { fileURLToPath } from "url";
|
|
2257
|
+
import { readdirSync } from "fs";
|
|
2258
|
+
import { resolve as resolve5 } from "path";
|
|
2179
2259
|
import chalk9 from "chalk";
|
|
2180
2260
|
|
|
2181
2261
|
// ../command-registry/src/command-map.ts
|
|
@@ -2953,68 +3033,6 @@ var COMMANDS = [
|
|
|
2953
3033
|
successMessage: "Updated customer profile"
|
|
2954
3034
|
},
|
|
2955
3035
|
// =========================================================================
|
|
2956
|
-
// FILES
|
|
2957
|
-
// =========================================================================
|
|
2958
|
-
{
|
|
2959
|
-
group: "files",
|
|
2960
|
-
name: "list",
|
|
2961
|
-
description: "List files",
|
|
2962
|
-
operation: "files",
|
|
2963
|
-
operationType: "query",
|
|
2964
|
-
columns: [
|
|
2965
|
-
{ key: "id", header: "ID", width: 28 },
|
|
2966
|
-
{ key: "filename", header: "Filename", width: 24 },
|
|
2967
|
-
{ key: "mimeType", header: "Type", width: 16 },
|
|
2968
|
-
{ key: "size", header: "Size", width: 10, format: "bytes" },
|
|
2969
|
-
{ key: "folder", header: "Folder", width: 16 },
|
|
2970
|
-
{ key: "createdAt", header: "Created", width: 12, format: "timeAgo" }
|
|
2971
|
-
]
|
|
2972
|
-
},
|
|
2973
|
-
{
|
|
2974
|
-
group: "files",
|
|
2975
|
-
name: "get",
|
|
2976
|
-
description: "Get a file",
|
|
2977
|
-
operation: "file",
|
|
2978
|
-
operationType: "query",
|
|
2979
|
-
positionalArgs: [{ name: "id", graphqlArg: "id" }]
|
|
2980
|
-
},
|
|
2981
|
-
{
|
|
2982
|
-
group: "files",
|
|
2983
|
-
name: "usage",
|
|
2984
|
-
description: "Get file storage usage",
|
|
2985
|
-
operation: "fileStorageUsage",
|
|
2986
|
-
operationType: "query"
|
|
2987
|
-
},
|
|
2988
|
-
{
|
|
2989
|
-
group: "files",
|
|
2990
|
-
name: "update",
|
|
2991
|
-
description: "Update a file",
|
|
2992
|
-
operation: "updateFile",
|
|
2993
|
-
operationType: "mutation",
|
|
2994
|
-
positionalArgs: [{ name: "id", graphqlArg: "id" }],
|
|
2995
|
-
successMessage: "Updated file"
|
|
2996
|
-
},
|
|
2997
|
-
{
|
|
2998
|
-
group: "files",
|
|
2999
|
-
name: "update-metadata",
|
|
3000
|
-
description: "Update file metadata",
|
|
3001
|
-
operation: "updateFileMetadata",
|
|
3002
|
-
operationType: "mutation",
|
|
3003
|
-
positionalArgs: [{ name: "id", graphqlArg: "id" }],
|
|
3004
|
-
successMessage: "Updated file metadata"
|
|
3005
|
-
},
|
|
3006
|
-
{
|
|
3007
|
-
group: "files",
|
|
3008
|
-
name: "delete",
|
|
3009
|
-
description: "Delete a file",
|
|
3010
|
-
operation: "deleteFile",
|
|
3011
|
-
operationType: "mutation",
|
|
3012
|
-
positionalArgs: [{ name: "id", graphqlArg: "id" }],
|
|
3013
|
-
requiresConfirmation: true,
|
|
3014
|
-
scalarResult: true,
|
|
3015
|
-
successMessage: "Deleted file"
|
|
3016
|
-
},
|
|
3017
|
-
// =========================================================================
|
|
3018
3036
|
// OPERATIONS
|
|
3019
3037
|
// =========================================================================
|
|
3020
3038
|
{
|
|
@@ -3582,230 +3600,397 @@ var COMMANDS = [
|
|
|
3582
3600
|
}
|
|
3583
3601
|
];
|
|
3584
3602
|
|
|
3585
|
-
//
|
|
3586
|
-
import {
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
}
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
}
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3603
|
+
// src/commands/register-commands.ts
|
|
3604
|
+
import { RecordType } from "@foir/connect-clients/records";
|
|
3605
|
+
function buildDispatchTable() {
|
|
3606
|
+
return {
|
|
3607
|
+
// ── Models ──────────────────────────────────────────────────
|
|
3608
|
+
models: {
|
|
3609
|
+
models: async (_v, c) => wrapList(
|
|
3610
|
+
await c.models.listModels({
|
|
3611
|
+
limit: num(_v.limit, 50),
|
|
3612
|
+
search: str(_v.search),
|
|
3613
|
+
category: str(_v.category),
|
|
3614
|
+
offset: num(_v.offset, 0)
|
|
3615
|
+
})
|
|
3616
|
+
),
|
|
3617
|
+
modelByKey: async (v, c) => await c.models.getModelByKey(str(v.key)),
|
|
3618
|
+
model: async (v, c) => await c.models.getModel(str(v.id)),
|
|
3619
|
+
createModel: async (v, c) => {
|
|
3620
|
+
const input = v.input;
|
|
3621
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
3622
|
+
return await c.models.createModel({
|
|
3623
|
+
key: str(input.key),
|
|
3624
|
+
name: str(input.name),
|
|
3625
|
+
fields: input.fields ?? [],
|
|
3626
|
+
config: input.config,
|
|
3627
|
+
configId: str(input.configId)
|
|
3628
|
+
});
|
|
3629
|
+
},
|
|
3630
|
+
updateModel: async (v, c) => {
|
|
3631
|
+
const input = v.input;
|
|
3632
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
3633
|
+
return await c.models.updateModel({
|
|
3634
|
+
id: str(input.id ?? v.id),
|
|
3635
|
+
name: str(input.name),
|
|
3636
|
+
fields: input.fields,
|
|
3637
|
+
config: input.config,
|
|
3638
|
+
changeDescription: str(input.changeDescription)
|
|
3639
|
+
});
|
|
3640
|
+
},
|
|
3641
|
+
deleteModel: async (v, c) => await c.models.deleteModel(str(v.id)),
|
|
3642
|
+
modelVersions: async (v, c) => wrapList(
|
|
3643
|
+
await c.models.listModelVersions(str(v.modelId), {
|
|
3644
|
+
limit: num(v.limit, 50)
|
|
3645
|
+
})
|
|
3646
|
+
)
|
|
3647
|
+
},
|
|
3648
|
+
// ── Records ─────────────────────────────────────────────────
|
|
3649
|
+
records: {
|
|
3650
|
+
records: async (v, c) => wrapList(
|
|
3651
|
+
await c.records.listRecords({
|
|
3652
|
+
modelKey: str(v.modelKey),
|
|
3653
|
+
limit: num(v.limit, 50),
|
|
3654
|
+
offset: num(v.offset, 0),
|
|
3655
|
+
search: str(v.search)
|
|
3656
|
+
})
|
|
3657
|
+
),
|
|
3658
|
+
record: async (v, c) => await c.records.getRecord(str(v.id)),
|
|
3659
|
+
recordByKey: async (v, c) => await c.records.getRecordByKey(str(v.modelKey), str(v.naturalKey)),
|
|
3660
|
+
createRecord: async (v, c) => {
|
|
3661
|
+
const input = v.input;
|
|
3662
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
3663
|
+
return await c.records.createRecord({
|
|
3664
|
+
modelKey: str(input.modelKey),
|
|
3665
|
+
naturalKey: str(input.naturalKey),
|
|
3666
|
+
data: input.data,
|
|
3667
|
+
customerId: str(input.customerId),
|
|
3668
|
+
changeDescription: str(input.changeDescription)
|
|
3669
|
+
});
|
|
3670
|
+
},
|
|
3671
|
+
updateRecord: async (v, c) => {
|
|
3672
|
+
const input = v.input;
|
|
3673
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
3674
|
+
return await c.records.updateRecord({
|
|
3675
|
+
id: str(input.id ?? v.id),
|
|
3676
|
+
data: input.data ?? input,
|
|
3677
|
+
naturalKey: str(input.naturalKey)
|
|
3678
|
+
});
|
|
3679
|
+
},
|
|
3680
|
+
deleteRecord: async (v, c) => await c.records.deleteRecord(str(v.id)),
|
|
3681
|
+
publishVersion: async (v, c) => await c.records.publishVersion(str(v.versionId)),
|
|
3682
|
+
unpublishRecord: async (v, c) => await c.records.unpublishRecord(str(v.id)),
|
|
3683
|
+
duplicateRecord: async (v, c) => {
|
|
3684
|
+
const input = v.input;
|
|
3685
|
+
return await c.records.duplicateRecord(
|
|
3686
|
+
str(input?.id ?? v.id),
|
|
3687
|
+
str(input?.newNaturalKey)
|
|
3688
|
+
);
|
|
3689
|
+
},
|
|
3690
|
+
recordVersions: async (v, c) => wrapList(
|
|
3691
|
+
await c.records.listRecordVersions(str(v.parentId), {
|
|
3692
|
+
limit: num(v.limit, 50)
|
|
3693
|
+
})
|
|
3694
|
+
),
|
|
3695
|
+
recordVariants: async (v, c) => wrapList(
|
|
3696
|
+
await c.records.listRecordVariants(str(v.recordId), {
|
|
3697
|
+
limit: num(v.limit, 50)
|
|
3698
|
+
})
|
|
3699
|
+
),
|
|
3700
|
+
createVersion: async (v, c) => {
|
|
3701
|
+
const input = v.input;
|
|
3702
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
3703
|
+
return await c.records.createVersion(
|
|
3704
|
+
str(input.parentId),
|
|
3705
|
+
input.data,
|
|
3706
|
+
str(input.changeDescription)
|
|
3707
|
+
);
|
|
3708
|
+
},
|
|
3709
|
+
createVariant: async (v, c) => {
|
|
3710
|
+
const input = v.input;
|
|
3711
|
+
if (!input) throw new Error("Input required (--data or --file)");
|
|
3712
|
+
return await c.records.createVariant(
|
|
3713
|
+
str(input.recordId),
|
|
3714
|
+
str(input.variantKey),
|
|
3715
|
+
input.data
|
|
3716
|
+
);
|
|
3683
3717
|
}
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3718
|
+
},
|
|
3719
|
+
// ── Locales ─────────────────────────────────────────────────
|
|
3720
|
+
locales: {
|
|
3721
|
+
locales: async (v, c) => {
|
|
3722
|
+
const resp = await c.settings.listLocales({ limit: num(v.limit, 50) });
|
|
3723
|
+
return { items: resp.locales ?? [], total: resp.total ?? 0 };
|
|
3724
|
+
},
|
|
3725
|
+
locale: async (v, c) => await c.settings.getLocale(str(v.id)),
|
|
3726
|
+
createLocale: async (v, c) => {
|
|
3727
|
+
const input = v.input;
|
|
3728
|
+
if (!input) throw new Error("Input required");
|
|
3729
|
+
return await c.settings.createLocale({
|
|
3730
|
+
locale: str(input.locale),
|
|
3731
|
+
displayName: str(input.displayName),
|
|
3732
|
+
nativeName: str(input.nativeName),
|
|
3733
|
+
isDefault: input.isDefault,
|
|
3734
|
+
isRtl: input.isRtl,
|
|
3735
|
+
fallbackLocale: str(input.fallbackLocale)
|
|
3736
|
+
});
|
|
3737
|
+
},
|
|
3738
|
+
updateLocale: async (v, c) => {
|
|
3739
|
+
const input = v.input;
|
|
3740
|
+
if (!input) throw new Error("Input required");
|
|
3741
|
+
return await c.settings.updateLocale({
|
|
3742
|
+
id: str(input.id ?? v.id),
|
|
3743
|
+
displayName: str(input.displayName),
|
|
3744
|
+
nativeName: str(input.nativeName),
|
|
3745
|
+
isDefault: input.isDefault,
|
|
3746
|
+
isActive: input.isActive,
|
|
3747
|
+
isRtl: input.isRtl,
|
|
3748
|
+
fallbackLocale: str(input.fallbackLocale)
|
|
3749
|
+
});
|
|
3750
|
+
},
|
|
3751
|
+
deleteLocale: async (v, c) => await c.settings.deleteLocale(str(v.id))
|
|
3752
|
+
},
|
|
3753
|
+
// ── Segments ────────────────────────────────────────────────
|
|
3754
|
+
segments: {
|
|
3755
|
+
segments: async (v, c) => {
|
|
3756
|
+
const resp = await c.segments.listSegments({ limit: num(v.limit, 50) });
|
|
3757
|
+
return { items: resp.segments ?? [], total: resp.total ?? 0 };
|
|
3758
|
+
},
|
|
3759
|
+
segment: async (v, c) => await c.segments.getSegment(str(v.id)),
|
|
3760
|
+
segmentByKey: async (v, c) => await c.segments.getSegmentByKey(str(v.key)),
|
|
3761
|
+
createSegment: async (v, c) => {
|
|
3762
|
+
const input = v.input;
|
|
3763
|
+
if (!input) throw new Error("Input required");
|
|
3764
|
+
return await c.segments.createSegment({
|
|
3765
|
+
key: str(input.key),
|
|
3766
|
+
name: str(input.name),
|
|
3767
|
+
description: str(input.description),
|
|
3768
|
+
rules: input.rules,
|
|
3769
|
+
evaluationMode: str(input.evaluationMode),
|
|
3770
|
+
isActive: input.isActive
|
|
3771
|
+
});
|
|
3772
|
+
},
|
|
3773
|
+
updateSegment: async (v, c) => {
|
|
3774
|
+
const input = v.input;
|
|
3775
|
+
if (!input) throw new Error("Input required");
|
|
3776
|
+
return await c.segments.updateSegment({
|
|
3777
|
+
id: str(input.id ?? v.id),
|
|
3778
|
+
name: str(input.name),
|
|
3779
|
+
description: str(input.description),
|
|
3780
|
+
rules: input.rules,
|
|
3781
|
+
evaluationMode: str(input.evaluationMode),
|
|
3782
|
+
isActive: input.isActive
|
|
3783
|
+
});
|
|
3784
|
+
},
|
|
3785
|
+
deleteSegment: async (v, c) => await c.segments.deleteSegment(str(v.id)),
|
|
3786
|
+
previewSegmentRules: async (v, c) => {
|
|
3787
|
+
const input = v.rules;
|
|
3788
|
+
return await c.segments.previewSegmentRules(input);
|
|
3789
|
+
},
|
|
3790
|
+
testSegmentEvaluation: async (v, c) => await c.segments.testSegmentEvaluation(
|
|
3791
|
+
str(v.segmentId),
|
|
3792
|
+
str(v.customerId)
|
|
3793
|
+
)
|
|
3794
|
+
},
|
|
3795
|
+
// ── Experiments ─────────────────────────────────────────────
|
|
3796
|
+
experiments: {
|
|
3797
|
+
experiments: async (v, c) => {
|
|
3798
|
+
const resp = await c.experiments.listExperiments({
|
|
3799
|
+
limit: num(v.limit, 50)
|
|
3800
|
+
});
|
|
3801
|
+
return { items: resp.experiments ?? [], total: resp.total ?? 0 };
|
|
3802
|
+
},
|
|
3803
|
+
experiment: async (v, c) => await c.experiments.getExperiment(str(v.id)),
|
|
3804
|
+
experimentByKey: async (v, c) => await c.experiments.getExperimentByKey(str(v.key)),
|
|
3805
|
+
createExperiment: async (v, c) => {
|
|
3806
|
+
const input = v.input;
|
|
3807
|
+
if (!input) throw new Error("Input required");
|
|
3808
|
+
return await c.experiments.createExperiment({
|
|
3809
|
+
key: str(input.key),
|
|
3810
|
+
name: str(input.name),
|
|
3811
|
+
description: str(input.description),
|
|
3812
|
+
targeting: input.targeting,
|
|
3813
|
+
controlPercent: input.controlPercent,
|
|
3814
|
+
variants: input.variants
|
|
3815
|
+
});
|
|
3816
|
+
},
|
|
3817
|
+
updateExperiment: async (v, c) => {
|
|
3818
|
+
const input = v.input;
|
|
3819
|
+
if (!input) throw new Error("Input required");
|
|
3820
|
+
return await c.experiments.updateExperiment({
|
|
3821
|
+
id: str(input.id ?? v.id),
|
|
3822
|
+
name: str(input.name),
|
|
3823
|
+
description: str(input.description),
|
|
3824
|
+
targeting: input.targeting,
|
|
3825
|
+
controlPercent: input.controlPercent,
|
|
3826
|
+
variants: input.variants
|
|
3827
|
+
});
|
|
3828
|
+
},
|
|
3829
|
+
deleteExperiment: async (v, c) => await c.experiments.deleteExperiment(str(v.id)),
|
|
3830
|
+
startExperiment: async (v, c) => await c.experiments.startExperiment(str(v.experimentId)),
|
|
3831
|
+
pauseExperiment: async (v, c) => await c.experiments.pauseExperiment(str(v.experimentId)),
|
|
3832
|
+
resumeExperiment: async (v, c) => await c.experiments.resumeExperiment(str(v.experimentId)),
|
|
3833
|
+
endExperiment: async (v, c) => await c.experiments.endExperiment(str(v.experimentId)),
|
|
3834
|
+
experimentStats: async (v, c) => await c.experiments.getExperimentStats(str(v.experimentId))
|
|
3835
|
+
},
|
|
3836
|
+
// ── Settings ────────────────────────────────────────────────
|
|
3837
|
+
settings: {
|
|
3838
|
+
allSettings: async (v, c) => await c.settings.getSettings({
|
|
3839
|
+
category: str(v.category),
|
|
3840
|
+
key: str(v.key)
|
|
3841
|
+
}),
|
|
3842
|
+
setting: async (v, c) => {
|
|
3843
|
+
const settings = await c.settings.getSettings({ key: str(v.key) });
|
|
3844
|
+
return settings[0] ?? null;
|
|
3845
|
+
},
|
|
3846
|
+
setSetting: async (v, c) => {
|
|
3847
|
+
const input = v.input;
|
|
3848
|
+
if (!input) throw new Error("Input required");
|
|
3849
|
+
return await c.settings.updateSetting({
|
|
3850
|
+
key: str(input.key),
|
|
3851
|
+
value: input.value ?? input
|
|
3852
|
+
});
|
|
3704
3853
|
}
|
|
3705
|
-
}
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3854
|
+
},
|
|
3855
|
+
// ── API Keys ────────────────────────────────────────────────
|
|
3856
|
+
"api-keys": {
|
|
3857
|
+
listApiKeys: async (v, c) => wrapList(await c.identity.listApiKeys({ limit: num(v.limit, 50) })),
|
|
3858
|
+
createApiKey: async (v, c) => {
|
|
3859
|
+
const input = v.input;
|
|
3860
|
+
if (!input) throw new Error("Input required");
|
|
3861
|
+
return await c.identity.createApiKey({
|
|
3862
|
+
name: str(input.name),
|
|
3863
|
+
keyType: input.keyType,
|
|
3864
|
+
rateLimitPerHour: input.rateLimitPerHour
|
|
3865
|
+
});
|
|
3866
|
+
},
|
|
3867
|
+
rotateApiKey: async (v, c) => await c.identity.rotateApiKey(str(v.id)),
|
|
3868
|
+
revokeApiKey: async (v, c) => await c.identity.revokeApiKey(str(v.id))
|
|
3869
|
+
},
|
|
3870
|
+
// ── Auth Providers ──────────────────────────────────────────
|
|
3871
|
+
"auth-providers": {
|
|
3872
|
+
customerAuthProviders: async (v, c) => {
|
|
3873
|
+
const resp = await c.identity.listAuthProviders({
|
|
3874
|
+
limit: num(v.limit, 50)
|
|
3875
|
+
});
|
|
3876
|
+
return { items: resp.items, total: resp.total };
|
|
3877
|
+
},
|
|
3878
|
+
customerAuthProvider: async (v, c) => await c.identity.getAuthProvider(str(v.id)),
|
|
3879
|
+
createCustomerAuthProvider: async (v, c) => {
|
|
3880
|
+
const input = v.input;
|
|
3881
|
+
if (!input) throw new Error("Input required");
|
|
3882
|
+
return await c.identity.createAuthProvider({
|
|
3883
|
+
key: str(input.key),
|
|
3884
|
+
name: str(input.name),
|
|
3885
|
+
type: str(input.type),
|
|
3886
|
+
config: input.config,
|
|
3887
|
+
enabled: input.enabled,
|
|
3888
|
+
isDefault: input.isDefault,
|
|
3889
|
+
priority: input.priority
|
|
3890
|
+
});
|
|
3891
|
+
},
|
|
3892
|
+
updateCustomerAuthProvider: async (v, c) => {
|
|
3893
|
+
const input = v.input;
|
|
3894
|
+
if (!input) throw new Error("Input required");
|
|
3895
|
+
return await c.identity.updateAuthProvider({
|
|
3896
|
+
id: str(input.id ?? v.id),
|
|
3897
|
+
name: str(input.name),
|
|
3898
|
+
config: input.config,
|
|
3899
|
+
enabled: input.enabled,
|
|
3900
|
+
isDefault: input.isDefault,
|
|
3901
|
+
priority: input.priority
|
|
3902
|
+
});
|
|
3903
|
+
},
|
|
3904
|
+
deleteCustomerAuthProvider: async (v, c) => await c.identity.deleteAuthProvider(str(v.id))
|
|
3905
|
+
},
|
|
3906
|
+
// ── Configs ─────────────────────────────────────────────────
|
|
3907
|
+
configs: {
|
|
3908
|
+
configs: async (v, c) => {
|
|
3909
|
+
const resp = await c.configs.listConfigs({ limit: num(v.limit, 50) });
|
|
3910
|
+
return { items: resp.configs ?? [], total: resp.total ?? 0 };
|
|
3911
|
+
},
|
|
3912
|
+
config: async (v, c) => await c.configs.getConfig(str(v.id)),
|
|
3913
|
+
configByKey: async (v, c) => await c.configs.getConfigByKey(str(v.key)),
|
|
3914
|
+
registerConfig: async (v, c) => {
|
|
3915
|
+
const input = v.input;
|
|
3916
|
+
if (!input) throw new Error("Input required");
|
|
3917
|
+
return await c.configs.createConfig({
|
|
3918
|
+
key: str(input.key),
|
|
3919
|
+
configType: str(input.configType),
|
|
3920
|
+
name: str(input.name),
|
|
3921
|
+
description: str(input.description),
|
|
3922
|
+
config: input.config,
|
|
3923
|
+
enabled: input.enabled,
|
|
3924
|
+
direction: str(input.direction)
|
|
3925
|
+
});
|
|
3926
|
+
},
|
|
3927
|
+
applyConfig: async (v, c) => {
|
|
3928
|
+
const input = v.input;
|
|
3929
|
+
if (!input) throw new Error("Input required");
|
|
3930
|
+
return await c.configs.applyConfig(
|
|
3931
|
+
str(input.key ?? input.configKey),
|
|
3932
|
+
input
|
|
3933
|
+
);
|
|
3934
|
+
},
|
|
3935
|
+
unregisterConfig: async (v, c) => await c.configs.deleteConfig(str(v.id)),
|
|
3936
|
+
triggerConfigSync: async (v, _c) => {
|
|
3937
|
+
console.log(
|
|
3938
|
+
chalk9.yellow(
|
|
3939
|
+
`Config sync trigger for ${str(v.configId)} is not yet available via ConnectRPC.`
|
|
3940
|
+
)
|
|
3941
|
+
);
|
|
3942
|
+
return { success: false };
|
|
3725
3943
|
}
|
|
3726
|
-
}
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
const
|
|
3737
|
-
|
|
3738
|
-
}
|
|
3739
|
-
|
|
3740
|
-
|
|
3944
|
+
},
|
|
3945
|
+
// ── Customers ───────────────────────────────────────────────
|
|
3946
|
+
customers: {
|
|
3947
|
+
customers: async (v, c) => wrapList(
|
|
3948
|
+
await c.identity.listCustomers({
|
|
3949
|
+
limit: num(v.limit, 50),
|
|
3950
|
+
search: str(v.search)
|
|
3951
|
+
})
|
|
3952
|
+
),
|
|
3953
|
+
customer: async (v, c) => {
|
|
3954
|
+
const result = await c.identity.getCustomer(str(v.id));
|
|
3955
|
+
return result.customer;
|
|
3956
|
+
},
|
|
3957
|
+
createCustomer: async (v, c) => {
|
|
3958
|
+
const input = v.input;
|
|
3959
|
+
if (!input) throw new Error("Input required");
|
|
3960
|
+
const result = await c.identity.createCustomer({
|
|
3961
|
+
email: str(input.email),
|
|
3962
|
+
password: str(input.password)
|
|
3963
|
+
});
|
|
3964
|
+
return result.customer;
|
|
3965
|
+
},
|
|
3966
|
+
updateCustomer: async (v, c) => {
|
|
3967
|
+
const input = v.input;
|
|
3968
|
+
if (!input) throw new Error("Input required");
|
|
3969
|
+
const result = await c.identity.updateCustomer({
|
|
3970
|
+
id: str(input.id ?? v.id),
|
|
3971
|
+
email: str(input.email),
|
|
3972
|
+
status: input.status
|
|
3973
|
+
});
|
|
3974
|
+
return result.customer;
|
|
3975
|
+
},
|
|
3976
|
+
deleteCustomer: async (v, c) => await c.identity.deleteCustomer(str(v.id)),
|
|
3977
|
+
suspendCustomer: async (v, c) => {
|
|
3978
|
+
const result = await c.identity.suspendCustomer(str(v.id));
|
|
3979
|
+
return result.customer;
|
|
3741
3980
|
}
|
|
3742
3981
|
}
|
|
3743
|
-
const varPart = varDecls ? `(${varDecls})` : "";
|
|
3744
|
-
const argPart = fieldArgs ? `(${fieldArgs})` : "";
|
|
3745
|
-
return `${opType} ${opName}${varPart} { ${entry.operation}${argPart} ${selectionSet} }`;
|
|
3746
|
-
}
|
|
3747
|
-
function getInputFields(operationName, operationType, inputArgName) {
|
|
3748
|
-
const field = getField(operationName, operationType);
|
|
3749
|
-
if (!field) return [];
|
|
3750
|
-
const argName = inputArgName ?? "input";
|
|
3751
|
-
const arg = field.args.find((a) => a.name === argName);
|
|
3752
|
-
if (!arg) return [];
|
|
3753
|
-
const namedType = unwrapType(arg.type);
|
|
3754
|
-
if (!isInputObjectType(namedType)) return [];
|
|
3755
|
-
const fields = namedType.getFields();
|
|
3756
|
-
return Object.entries(fields).map(([name, f]) => ({
|
|
3757
|
-
name,
|
|
3758
|
-
type: typeToString(f.type),
|
|
3759
|
-
required: isNonNullType(f.type)
|
|
3760
|
-
}));
|
|
3761
|
-
}
|
|
3762
|
-
function getCompletions(partial, commandNames) {
|
|
3763
|
-
return commandNames.filter(
|
|
3764
|
-
(name) => name.toLowerCase().startsWith(partial.toLowerCase())
|
|
3765
|
-
);
|
|
3766
|
-
}
|
|
3767
|
-
return {
|
|
3768
|
-
buildQuery,
|
|
3769
|
-
getOperationArgs,
|
|
3770
|
-
coerceArgs,
|
|
3771
|
-
getInputFields,
|
|
3772
|
-
getCompletions
|
|
3773
3982
|
};
|
|
3774
3983
|
}
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
3778
|
-
var __dirname = dirname4(__filename);
|
|
3779
|
-
function loadSchemaSDL() {
|
|
3780
|
-
const bundledPath = resolve5(__dirname, "schema.graphql");
|
|
3781
|
-
try {
|
|
3782
|
-
return readFileSync(bundledPath, "utf-8");
|
|
3783
|
-
} catch {
|
|
3784
|
-
const monorepoPath = resolve5(
|
|
3785
|
-
__dirname,
|
|
3786
|
-
"../../../graphql-core/schema.graphql"
|
|
3787
|
-
);
|
|
3788
|
-
try {
|
|
3789
|
-
return readFileSync(monorepoPath, "utf-8");
|
|
3790
|
-
} catch {
|
|
3791
|
-
throw new Error(
|
|
3792
|
-
"Could not find schema.graphql. Try reinstalling @eide/foir-cli."
|
|
3793
|
-
);
|
|
3794
|
-
}
|
|
3795
|
-
}
|
|
3984
|
+
function str(v) {
|
|
3985
|
+
return v != null ? String(v) : void 0;
|
|
3796
3986
|
}
|
|
3797
|
-
function
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
return { data: val, total: obj.total };
|
|
3805
|
-
}
|
|
3806
|
-
}
|
|
3807
|
-
}
|
|
3808
|
-
return { data: raw };
|
|
3987
|
+
function num(v, fallback) {
|
|
3988
|
+
if (v == null) return fallback;
|
|
3989
|
+
const n = Number(v);
|
|
3990
|
+
return Number.isNaN(n) ? fallback : n;
|
|
3991
|
+
}
|
|
3992
|
+
function wrapList(result) {
|
|
3993
|
+
return result;
|
|
3809
3994
|
}
|
|
3810
3995
|
function toCliColumns(columns) {
|
|
3811
3996
|
if (!columns) return void 0;
|
|
@@ -3824,8 +4009,8 @@ function toCliColumns(columns) {
|
|
|
3824
4009
|
break;
|
|
3825
4010
|
case "truncate":
|
|
3826
4011
|
cliCol.format = (v) => {
|
|
3827
|
-
const
|
|
3828
|
-
return
|
|
4012
|
+
const s = String(v ?? "");
|
|
4013
|
+
return s.length > 40 ? s.slice(0, 39) + "\u2026" : s;
|
|
3829
4014
|
};
|
|
3830
4015
|
break;
|
|
3831
4016
|
case "join":
|
|
@@ -3833,27 +4018,37 @@ function toCliColumns(columns) {
|
|
|
3833
4018
|
break;
|
|
3834
4019
|
case "bytes":
|
|
3835
4020
|
cliCol.format = (v) => {
|
|
3836
|
-
const
|
|
3837
|
-
if (
|
|
3838
|
-
if (
|
|
3839
|
-
return `${(
|
|
4021
|
+
const n = Number(v);
|
|
4022
|
+
if (n < 1024) return `${n} B`;
|
|
4023
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
4024
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
3840
4025
|
};
|
|
3841
4026
|
break;
|
|
3842
4027
|
}
|
|
3843
4028
|
return cliCol;
|
|
3844
4029
|
});
|
|
3845
4030
|
}
|
|
3846
|
-
function
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
4031
|
+
function autoColumns(items) {
|
|
4032
|
+
if (items.length === 0) return [];
|
|
4033
|
+
const first = items[0];
|
|
4034
|
+
return Object.keys(first).filter((k) => k !== "__typename" && k !== "$typeName" && k !== "$unknown").slice(0, 6).map((key) => ({
|
|
4035
|
+
key,
|
|
4036
|
+
header: key,
|
|
4037
|
+
width: 20
|
|
4038
|
+
}));
|
|
4039
|
+
}
|
|
4040
|
+
var DISPATCH = buildDispatchTable();
|
|
4041
|
+
async function executeCommand(entry, variables, client) {
|
|
4042
|
+
const groupHandlers = DISPATCH[entry.group];
|
|
4043
|
+
const handler = groupHandlers?.[entry.operation];
|
|
4044
|
+
if (!handler) {
|
|
4045
|
+
throw new Error(
|
|
4046
|
+
`Command not yet migrated to ConnectRPC: ${entry.group}.${entry.operation}`
|
|
3854
4047
|
);
|
|
3855
|
-
return;
|
|
3856
4048
|
}
|
|
4049
|
+
return handler(variables, client);
|
|
4050
|
+
}
|
|
4051
|
+
function registerDynamicCommands(program2, globalOpts) {
|
|
3857
4052
|
const groups = /* @__PURE__ */ new Map();
|
|
3858
4053
|
for (const cmd of COMMANDS) {
|
|
3859
4054
|
if (!groups.has(cmd.group)) groups.set(cmd.group, []);
|
|
@@ -3862,42 +4057,15 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
3862
4057
|
for (const [groupName, entries] of groups) {
|
|
3863
4058
|
const group = program2.command(groupName).description(`Manage ${groupName}`);
|
|
3864
4059
|
for (const entry of entries) {
|
|
4060
|
+
if (!DISPATCH[entry.group]?.[entry.operation]) continue;
|
|
3865
4061
|
let cmd = group.command(entry.name).description(entry.description);
|
|
3866
4062
|
for (const pos of entry.positionalArgs ?? []) {
|
|
3867
4063
|
cmd = cmd.argument(`<${pos.name}>`, pos.description ?? pos.name);
|
|
3868
4064
|
}
|
|
3869
|
-
|
|
3870
|
-
entry.operation,
|
|
3871
|
-
entry.operationType
|
|
3872
|
-
);
|
|
3873
|
-
for (const arg of schemaArgs) {
|
|
3874
|
-
if (entry.positionalArgs?.some((p) => p.graphqlArg === arg.name))
|
|
3875
|
-
continue;
|
|
3876
|
-
if (entry.acceptsInput && arg.name === (entry.inputArgName ?? "input"))
|
|
3877
|
-
continue;
|
|
3878
|
-
const reqStr = arg.required ? " (required)" : "";
|
|
3879
|
-
cmd = cmd.option(`--${arg.name} <value>`, `${arg.name}${reqStr}`);
|
|
3880
|
-
}
|
|
4065
|
+
cmd = cmd.option("--limit <n>", "Max results");
|
|
3881
4066
|
if (entry.acceptsInput) {
|
|
3882
4067
|
cmd = cmd.option("-d, --data <json>", "Data as JSON");
|
|
3883
4068
|
cmd = cmd.option("-f, --file <path>", "Read data from file");
|
|
3884
|
-
const inputFields = engine.getInputFields(
|
|
3885
|
-
entry.operation,
|
|
3886
|
-
entry.operationType,
|
|
3887
|
-
entry.inputArgName
|
|
3888
|
-
);
|
|
3889
|
-
if (inputFields.length > 0) {
|
|
3890
|
-
const required = inputFields.filter((f) => f.required);
|
|
3891
|
-
const optional = inputFields.filter((f) => !f.required);
|
|
3892
|
-
let fieldHelp = "\nInput fields:";
|
|
3893
|
-
if (required.length > 0) {
|
|
3894
|
-
fieldHelp += "\n Required: " + required.map((f) => `${f.name} (${f.type})`).join(", ");
|
|
3895
|
-
}
|
|
3896
|
-
if (optional.length > 0) {
|
|
3897
|
-
fieldHelp += "\n Optional: " + optional.map((f) => `${f.name} (${f.type})`).join(", ");
|
|
3898
|
-
}
|
|
3899
|
-
cmd = cmd.addHelpText("after", fieldHelp);
|
|
3900
|
-
}
|
|
3901
4069
|
}
|
|
3902
4070
|
for (const cf of entry.customFlags ?? []) {
|
|
3903
4071
|
cmd = cmd.option(cf.flag, cf.description);
|
|
@@ -3908,7 +4076,7 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
3908
4076
|
cmd.action(
|
|
3909
4077
|
withErrorHandler(globalOpts, async (...actionArgs) => {
|
|
3910
4078
|
const opts = globalOpts();
|
|
3911
|
-
const client = await
|
|
4079
|
+
const client = await createPlatformClient(opts);
|
|
3912
4080
|
const variables = {};
|
|
3913
4081
|
const positionals = entry.positionalArgs ?? [];
|
|
3914
4082
|
for (let i = 0; i < positionals.length; i++) {
|
|
@@ -3921,27 +4089,27 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
3921
4089
|
const flags = actionArgs[positionals.length] ?? {};
|
|
3922
4090
|
const customFlagNames = new Set(
|
|
3923
4091
|
(entry.customFlags ?? []).map(
|
|
3924
|
-
(cf) => cf.flag.replace(/ <.*>$/, "").replace(/^--/, "").replace(/-([a-z])/g, (_,
|
|
4092
|
+
(cf) => cf.flag.replace(/ <.*>$/, "").replace(/^--/, "").replace(/-([a-z])/g, (_, ch) => ch.toUpperCase())
|
|
3925
4093
|
)
|
|
3926
4094
|
);
|
|
3927
|
-
const rawFlags = {};
|
|
3928
4095
|
for (const [key, val] of Object.entries(flags)) {
|
|
3929
|
-
if (key === "data" || key === "file" || key === "confirm")
|
|
3930
|
-
|
|
3931
|
-
|
|
4096
|
+
if (key === "data" || key === "file" || key === "confirm" || key === "limit")
|
|
4097
|
+
continue;
|
|
4098
|
+
if (customFlagNames.has(key)) {
|
|
4099
|
+
variables[key] = val;
|
|
4100
|
+
continue;
|
|
4101
|
+
}
|
|
4102
|
+
variables[key] = val;
|
|
4103
|
+
}
|
|
4104
|
+
if (flags.limit) {
|
|
4105
|
+
variables.limit = flags.limit;
|
|
3932
4106
|
}
|
|
3933
|
-
const coerced = engine.coerceArgs(
|
|
3934
|
-
entry.operation,
|
|
3935
|
-
entry.operationType,
|
|
3936
|
-
rawFlags
|
|
3937
|
-
);
|
|
3938
|
-
Object.assign(variables, coerced);
|
|
3939
4107
|
if (flags.dir && entry.acceptsInput) {
|
|
3940
4108
|
const dirPath = resolve5(String(flags.dir));
|
|
3941
4109
|
const files = readdirSync(dirPath).filter((f) => /\.(json|ts|js|mjs)$/.test(f)).sort();
|
|
3942
4110
|
if (files.length === 0) {
|
|
3943
4111
|
console.error(
|
|
3944
|
-
chalk9.yellow(
|
|
4112
|
+
chalk9.yellow(`No .json/.ts/.js files found in ${dirPath}`)
|
|
3945
4113
|
);
|
|
3946
4114
|
return;
|
|
3947
4115
|
}
|
|
@@ -3955,8 +4123,7 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
3955
4123
|
const fileVars = { ...variables, [argName]: fileData };
|
|
3956
4124
|
const label = fileData.key ?? fileData.name ?? file;
|
|
3957
4125
|
try {
|
|
3958
|
-
|
|
3959
|
-
await client.request(q, fileVars);
|
|
4126
|
+
await executeCommand(entry, fileVars, client);
|
|
3960
4127
|
created++;
|
|
3961
4128
|
if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
3962
4129
|
success(`Created ${label}`);
|
|
@@ -3975,8 +4142,7 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
3975
4142
|
[updateEntry.positionalArgs[0].graphqlArg]: fileData.key
|
|
3976
4143
|
} : {}
|
|
3977
4144
|
};
|
|
3978
|
-
|
|
3979
|
-
await client.request(uq, updateVars);
|
|
4145
|
+
await executeCommand(updateEntry, updateVars, client);
|
|
3980
4146
|
updated++;
|
|
3981
4147
|
if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
3982
4148
|
success(`Updated ${label}`);
|
|
@@ -4010,19 +4176,13 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
4010
4176
|
data: flags.data,
|
|
4011
4177
|
file: flags.file
|
|
4012
4178
|
});
|
|
4013
|
-
|
|
4014
|
-
entry.operation,
|
|
4015
|
-
entry.operationType,
|
|
4016
|
-
entry.inputArgName
|
|
4017
|
-
);
|
|
4018
|
-
const fieldNames = new Set(inputFields.map((f) => f.name));
|
|
4019
|
-
if (fieldNames.has("projectId") && !inputData.projectId || fieldNames.has("tenantId") && !inputData.tenantId) {
|
|
4179
|
+
if (!inputData.projectId || !inputData.tenantId) {
|
|
4020
4180
|
const resolved = await resolveProjectContext(opts);
|
|
4021
4181
|
if (resolved) {
|
|
4022
|
-
if (
|
|
4182
|
+
if (!inputData.projectId) {
|
|
4023
4183
|
inputData.projectId = resolved.project.id;
|
|
4024
4184
|
}
|
|
4025
|
-
if (
|
|
4185
|
+
if (!inputData.tenantId) {
|
|
4026
4186
|
inputData.tenantId = resolved.project.tenantId;
|
|
4027
4187
|
}
|
|
4028
4188
|
}
|
|
@@ -4053,12 +4213,7 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
4053
4213
|
if (flags.modelKey) {
|
|
4054
4214
|
variables.modelKey = String(flags.modelKey);
|
|
4055
4215
|
}
|
|
4056
|
-
const
|
|
4057
|
-
const result2 = await client.request(
|
|
4058
|
-
queryStr2,
|
|
4059
|
-
variables
|
|
4060
|
-
);
|
|
4061
|
-
const { data: data2 } = extractResult(result2, altEntry.operation);
|
|
4216
|
+
const data2 = await executeCommand(altEntry, variables, client);
|
|
4062
4217
|
formatOutput(data2, opts);
|
|
4063
4218
|
return;
|
|
4064
4219
|
}
|
|
@@ -4075,13 +4230,10 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
4075
4230
|
if (entry.group === "records" && entry.name === "publish" && variables.versionId) {
|
|
4076
4231
|
const versionIdValue = String(variables.versionId);
|
|
4077
4232
|
if (flags.modelKey) {
|
|
4078
|
-
const
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
naturalKey: versionIdValue
|
|
4083
|
-
});
|
|
4084
|
-
const record = lookupResult.recordByKey;
|
|
4233
|
+
const record = await client.records.getRecordByKey(
|
|
4234
|
+
String(flags.modelKey),
|
|
4235
|
+
versionIdValue
|
|
4236
|
+
);
|
|
4085
4237
|
if (!record?.currentVersionId) {
|
|
4086
4238
|
throw new Error(
|
|
4087
4239
|
`No current version found for record "${versionIdValue}"`
|
|
@@ -4094,23 +4246,18 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
4094
4246
|
);
|
|
4095
4247
|
} else {
|
|
4096
4248
|
try {
|
|
4097
|
-
const
|
|
4098
|
-
|
|
4099
|
-
id: versionIdValue
|
|
4100
|
-
});
|
|
4101
|
-
const record = lookupResult.record;
|
|
4102
|
-
if (record?.recordType === "record" && record?.currentVersionId) {
|
|
4249
|
+
const record = await client.records.getRecord(versionIdValue);
|
|
4250
|
+
if (record?.recordType === RecordType.RECORD && record?.currentVersionId) {
|
|
4103
4251
|
variables.versionId = record.currentVersionId;
|
|
4104
4252
|
}
|
|
4105
4253
|
} catch {
|
|
4106
4254
|
}
|
|
4107
4255
|
}
|
|
4108
4256
|
}
|
|
4109
|
-
|
|
4110
|
-
let result;
|
|
4257
|
+
let data;
|
|
4111
4258
|
let usedUpdate = false;
|
|
4112
4259
|
try {
|
|
4113
|
-
|
|
4260
|
+
data = await executeCommand(entry, variables, client);
|
|
4114
4261
|
} catch (createErr) {
|
|
4115
4262
|
if (flags.upsert && entry.name === "create") {
|
|
4116
4263
|
const updateEntry = COMMANDS.find(
|
|
@@ -4126,8 +4273,7 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
4126
4273
|
[updateEntry.positionalArgs[0].graphqlArg]: inputData.key
|
|
4127
4274
|
} : {}
|
|
4128
4275
|
};
|
|
4129
|
-
|
|
4130
|
-
result = await client.request(uq, updateVars);
|
|
4276
|
+
data = await executeCommand(updateEntry, updateVars, client);
|
|
4131
4277
|
usedUpdate = true;
|
|
4132
4278
|
} else {
|
|
4133
4279
|
throw createErr;
|
|
@@ -4139,8 +4285,14 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
4139
4285
|
const activeEntry = usedUpdate ? COMMANDS.find(
|
|
4140
4286
|
(c) => c.group === entry.group && c.name === "update"
|
|
4141
4287
|
) ?? entry : entry;
|
|
4142
|
-
|
|
4143
|
-
|
|
4288
|
+
let displayData = data;
|
|
4289
|
+
let total;
|
|
4290
|
+
if (data && typeof data === "object" && "items" in data) {
|
|
4291
|
+
const listResult = data;
|
|
4292
|
+
displayData = listResult.items;
|
|
4293
|
+
total = listResult.total;
|
|
4294
|
+
}
|
|
4295
|
+
const responseData = displayData && typeof displayData === "object" && !Array.isArray(displayData) ? displayData : void 0;
|
|
4144
4296
|
const displayEntry = usedUpdate ? activeEntry : entry;
|
|
4145
4297
|
if (displayEntry.scalarResult) {
|
|
4146
4298
|
if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
@@ -4148,16 +4300,16 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
4148
4300
|
success(displayEntry.successMessage, responseData);
|
|
4149
4301
|
}
|
|
4150
4302
|
} else {
|
|
4151
|
-
formatOutput(
|
|
4303
|
+
formatOutput(displayData, opts);
|
|
4152
4304
|
}
|
|
4153
|
-
} else if (Array.isArray(
|
|
4305
|
+
} else if (Array.isArray(displayData)) {
|
|
4154
4306
|
const cliColumns = toCliColumns(displayEntry.columns);
|
|
4155
|
-
formatList(
|
|
4156
|
-
columns: cliColumns ?? autoColumns(
|
|
4307
|
+
formatList(displayData, opts, {
|
|
4308
|
+
columns: cliColumns ?? autoColumns(displayData),
|
|
4157
4309
|
total
|
|
4158
4310
|
});
|
|
4159
4311
|
} else {
|
|
4160
|
-
formatOutput(
|
|
4312
|
+
formatOutput(displayData, opts);
|
|
4161
4313
|
if (displayEntry.successMessage && !(opts.json || opts.jsonl || opts.quiet)) {
|
|
4162
4314
|
success(displayEntry.successMessage, responseData);
|
|
4163
4315
|
}
|
|
@@ -4167,15 +4319,14 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
4167
4319
|
const record = responseData.record;
|
|
4168
4320
|
const versionId = version2?.id ?? record?.currentVersionId;
|
|
4169
4321
|
if (versionId) {
|
|
4170
|
-
|
|
4171
|
-
await client.request(publishQuery, { versionId });
|
|
4322
|
+
await client.records.publishVersion(versionId);
|
|
4172
4323
|
if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
4173
4324
|
success("Published version {id}", { id: versionId });
|
|
4174
4325
|
}
|
|
4175
4326
|
} else if (!(opts.json || opts.jsonl || opts.quiet)) {
|
|
4176
4327
|
console.error(
|
|
4177
4328
|
chalk9.yellow(
|
|
4178
|
-
"
|
|
4329
|
+
"Could not auto-publish: no version found in response"
|
|
4179
4330
|
)
|
|
4180
4331
|
);
|
|
4181
4332
|
}
|
|
@@ -4185,20 +4336,11 @@ function registerDynamicCommands(program2, globalOpts) {
|
|
|
4185
4336
|
}
|
|
4186
4337
|
}
|
|
4187
4338
|
}
|
|
4188
|
-
function autoColumns(items) {
|
|
4189
|
-
if (items.length === 0) return [];
|
|
4190
|
-
const first = items[0];
|
|
4191
|
-
return Object.keys(first).filter((k) => k !== "__typename").slice(0, 6).map((key) => ({
|
|
4192
|
-
key,
|
|
4193
|
-
header: key,
|
|
4194
|
-
width: 20
|
|
4195
|
-
}));
|
|
4196
|
-
}
|
|
4197
4339
|
|
|
4198
4340
|
// src/cli.ts
|
|
4199
|
-
var
|
|
4200
|
-
var
|
|
4201
|
-
config({ path: resolve6(
|
|
4341
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
4342
|
+
var __dirname = dirname4(__filename);
|
|
4343
|
+
config({ path: resolve6(__dirname, "../.env.local") });
|
|
4202
4344
|
var require2 = createRequire(import.meta.url);
|
|
4203
4345
|
var { version } = require2("../package.json");
|
|
4204
4346
|
var program = new Command();
|