@eide/foir-cli 0.1.41 → 0.1.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,8 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ findConfigFile,
4
+ loadConfigFile
5
+ } from "./chunk-L642MYIL.js";
2
6
 
3
7
  // src/cli.ts
4
8
  import { config } from "dotenv";
5
- import { resolve as resolve7, dirname as dirname6 } from "path";
9
+ import { resolve as resolve6, dirname as dirname6 } from "path";
6
10
  import { fileURLToPath as fileURLToPath2 } from "url";
7
11
  import { createRequire } from "module";
8
12
  import { Command } from "commander";
@@ -95,9 +99,19 @@ function getProjectContextDir() {
95
99
  function getProjectContextPath() {
96
100
  return join(getProjectContextDir(), "project.json");
97
101
  }
98
- async function getProjectContext() {
102
+ function getProfilesDir() {
103
+ return join(getProjectContextDir(), "profiles");
104
+ }
105
+ function getProfilePath(name) {
106
+ return join(getProfilesDir(), `${name}.json`);
107
+ }
108
+ function getDefaultProfilePath() {
109
+ return join(getProjectContextDir(), "default-profile");
110
+ }
111
+ async function getProjectContext(profileName) {
112
+ const filePath = profileName ? getProfilePath(profileName) : getProjectContextPath();
99
113
  try {
100
- const content = await fs.readFile(getProjectContextPath(), "utf-8");
114
+ const content = await fs.readFile(filePath, "utf-8");
101
115
  return JSON.parse(content);
102
116
  } catch (error) {
103
117
  if (error.code === "ENOENT") {
@@ -106,13 +120,135 @@ async function getProjectContext() {
106
120
  throw error;
107
121
  }
108
122
  }
109
- async function writeProjectContext(project) {
110
- const dir = getProjectContextDir();
111
- await ensureDir(dir);
112
- const filePath = getProjectContextPath();
113
- await fs.writeFile(filePath, JSON.stringify(project, null, 2), {
114
- mode: 384
115
- });
123
+ async function writeProjectContext(project, profileName) {
124
+ if (profileName) {
125
+ await ensureDir(getProfilesDir());
126
+ const filePath = getProfilePath(profileName);
127
+ await fs.writeFile(filePath, JSON.stringify(project, null, 2), {
128
+ mode: 384
129
+ });
130
+ } else {
131
+ const dir = getProjectContextDir();
132
+ await ensureDir(dir);
133
+ const filePath = getProjectContextPath();
134
+ await fs.writeFile(filePath, JSON.stringify(project, null, 2), {
135
+ mode: 384
136
+ });
137
+ }
138
+ }
139
+ async function listProfiles() {
140
+ const dir = getProfilesDir();
141
+ let files;
142
+ try {
143
+ files = await fs.readdir(dir);
144
+ } catch (error) {
145
+ if (error.code === "ENOENT") {
146
+ return [];
147
+ }
148
+ throw error;
149
+ }
150
+ const profiles = [];
151
+ for (const file of files) {
152
+ if (!file.endsWith(".json")) continue;
153
+ const name = file.replace(/\.json$/, "");
154
+ try {
155
+ const content = await fs.readFile(join(dir, file), "utf-8");
156
+ profiles.push({ name, project: JSON.parse(content) });
157
+ } catch {
158
+ }
159
+ }
160
+ return profiles;
161
+ }
162
+ async function getDefaultProfile() {
163
+ try {
164
+ const content = await fs.readFile(getDefaultProfilePath(), "utf-8");
165
+ return content.trim() || null;
166
+ } catch (error) {
167
+ if (error.code === "ENOENT") {
168
+ return null;
169
+ }
170
+ throw error;
171
+ }
172
+ }
173
+ async function setDefaultProfile(name) {
174
+ await ensureDir(getProjectContextDir());
175
+ await fs.writeFile(getDefaultProfilePath(), name, { mode: 384 });
176
+ }
177
+ async function deleteProfile(name) {
178
+ try {
179
+ await fs.unlink(getProfilePath(name));
180
+ } catch (error) {
181
+ if (error.code !== "ENOENT") {
182
+ throw error;
183
+ }
184
+ }
185
+ const defaultProfile = await getDefaultProfile();
186
+ if (defaultProfile === name) {
187
+ try {
188
+ await fs.unlink(getDefaultProfilePath());
189
+ } catch (error) {
190
+ if (error.code !== "ENOENT") {
191
+ throw error;
192
+ }
193
+ }
194
+ }
195
+ }
196
+ async function resolveProjectContext(options) {
197
+ if (options?.project) {
198
+ const project2 = await getProjectContext(options.project);
199
+ if (project2) {
200
+ return {
201
+ project: project2,
202
+ source: "--project flag",
203
+ profileName: options.project
204
+ };
205
+ }
206
+ throw new Error(
207
+ `Profile "${options.project}" not found. Run \`foir profiles list\` to see available profiles.`
208
+ );
209
+ }
210
+ const envProject = process.env.FOIR_PROJECT;
211
+ if (envProject) {
212
+ const project2 = await getProjectContext(envProject);
213
+ if (project2) {
214
+ return {
215
+ project: project2,
216
+ source: "FOIR_PROJECT env",
217
+ profileName: envProject
218
+ };
219
+ }
220
+ }
221
+ try {
222
+ const { loadConfigProject } = await import("./loader-7VE4OF73.js");
223
+ const configProfile = await loadConfigProject();
224
+ if (configProfile) {
225
+ const project2 = await getProjectContext(configProfile);
226
+ if (project2) {
227
+ return {
228
+ project: project2,
229
+ source: "foir.config.ts",
230
+ profileName: configProfile
231
+ };
232
+ }
233
+ }
234
+ } catch {
235
+ }
236
+ const defaultProfile = await getDefaultProfile();
237
+ if (defaultProfile) {
238
+ const project2 = await getProjectContext(defaultProfile);
239
+ if (project2) {
240
+ return {
241
+ project: project2,
242
+ source: "default profile",
243
+ profileName: defaultProfile
244
+ };
245
+ }
246
+ }
247
+ const project = await getProjectContext();
248
+ if (project) {
249
+ return { project, source: "project.json" };
250
+ }
251
+ return null;
116
252
  }
117
253
 
118
254
  // src/lib/config.ts
@@ -226,13 +362,13 @@ function withErrorHandler(optsFn, fn) {
226
362
  // src/commands/login.ts
227
363
  async function findAvailablePort(start, end) {
228
364
  for (let port = start; port <= end; port++) {
229
- const available = await new Promise((resolve8) => {
365
+ const available = await new Promise((resolve7) => {
230
366
  const server = http.createServer();
231
367
  server.listen(port, () => {
232
368
  server.close();
233
- resolve8(true);
369
+ resolve7(true);
234
370
  });
235
- server.on("error", () => resolve8(false));
371
+ server.on("error", () => resolve7(false));
236
372
  });
237
373
  if (available) return port;
238
374
  }
@@ -270,7 +406,7 @@ async function loginAction(globalOpts) {
270
406
  const state = crypto.randomBytes(16).toString("hex");
271
407
  const port = await findAvailablePort(9876, 9900);
272
408
  const redirectUri = `http://localhost:${port}/callback`;
273
- const authCode = await new Promise((resolve8, reject) => {
409
+ const authCode = await new Promise((resolve7, reject) => {
274
410
  const server = http.createServer((req, res) => {
275
411
  const url = new URL(req.url, `http://localhost:${port}`);
276
412
  if (url.pathname === "/callback") {
@@ -303,7 +439,7 @@ async function loginAction(globalOpts) {
303
439
  `<html><head><meta http-equiv="refresh" content="2;url=${mainUrl}"></head><body style="font-family:system-ui;text-align:center;padding:50px"><h1>Authentication successful!</h1><p>You can close this window.</p></body></html>`
304
440
  );
305
441
  server.close();
306
- resolve8(code);
442
+ resolve7(code);
307
443
  }
308
444
  });
309
445
  server.listen(port);
@@ -469,7 +605,7 @@ async function provisionApiKey(apiUrl, accessToken, projectId, tenantId) {
469
605
  return { apiKey: created.plainKey, apiKeyId: created.apiKey.id };
470
606
  }
471
607
  function registerSelectProjectCommand(program2, globalOpts) {
472
- program2.command("select-project").description("Choose which project to work with").option("--project-id <id>", "Project ID to select directly").action(
608
+ 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(
473
609
  withErrorHandler(globalOpts, async (cmdOpts) => {
474
610
  const opts = globalOpts();
475
611
  const apiUrl = getApiUrl(opts);
@@ -538,17 +674,26 @@ function registerSelectProjectCommand(program2, globalOpts) {
538
674
  selectedProject.id,
539
675
  selectedProject.tenantId
540
676
  );
541
- await writeProjectContext({
542
- id: selectedProject.id,
543
- name: selectedProject.name,
544
- tenantId: selectedProject.tenantId,
545
- apiKey,
546
- apiKeyId
547
- });
677
+ await writeProjectContext(
678
+ {
679
+ id: selectedProject.id,
680
+ name: selectedProject.name,
681
+ tenantId: selectedProject.tenantId,
682
+ apiKey,
683
+ apiKeyId
684
+ },
685
+ cmdOpts.saveAs
686
+ );
548
687
  console.log(`
549
688
  \u2713 Selected project: ${selectedProject.name}`);
550
689
  console.log("\u2713 API key provisioned for CLI access");
551
- console.log(" (stored in .foir/project.json for this repository)");
690
+ if (cmdOpts.saveAs) {
691
+ console.log(
692
+ ` Saved as profile "${cmdOpts.saveAs}". Use --project ${cmdOpts.saveAs} or set as default with \`foir profiles default ${cmdOpts.saveAs}\``
693
+ );
694
+ } else {
695
+ console.log(" (stored in .foir/project.json for this repository)");
696
+ }
552
697
  })
553
698
  );
554
699
  }
@@ -653,14 +798,18 @@ function registerWhoamiCommand(program2, globalOpts) {
653
798
  throw new Error("Not authenticated");
654
799
  }
655
800
  const expired = isTokenExpired(credentials);
656
- const projectContext = await getProjectContext();
801
+ const resolved = await resolveProjectContext(opts);
657
802
  if (opts.json || opts.jsonl) {
658
803
  formatOutput(
659
804
  {
660
805
  authenticated: true,
661
806
  tokenValid: !expired,
662
807
  user: credentials.user,
663
- selectedProject: projectContext ?? null
808
+ selectedProject: resolved ? {
809
+ ...resolved.project,
810
+ profile: resolved.profileName ?? null,
811
+ source: resolved.source
812
+ } : null
664
813
  },
665
814
  opts
666
815
  );
@@ -673,13 +822,18 @@ function registerWhoamiCommand(program2, globalOpts) {
673
822
  );
674
823
  console.log(`User ID: ${credentials.user.id}`);
675
824
  console.log(`Token: ${expired ? "\u26A0 Expired" : "\u2713 Valid"}`);
676
- if (projectContext) {
825
+ if (resolved) {
677
826
  console.log("");
678
827
  console.log("Selected Project (this repo)");
679
828
  console.log("\u2500".repeat(40));
680
- console.log(`Name: ${projectContext.name}`);
681
- console.log(`ID: ${projectContext.id}`);
682
- console.log(`Tenant ID: ${projectContext.tenantId}`);
829
+ console.log(`Name: ${resolved.project.name}`);
830
+ console.log(`ID: ${resolved.project.id}`);
831
+ console.log(`Tenant ID: ${resolved.project.tenantId}`);
832
+ if (resolved.profileName) {
833
+ console.log(
834
+ `Profile: ${resolved.profileName} (from ${resolved.source})`
835
+ );
836
+ }
683
837
  } else {
684
838
  console.log("");
685
839
  console.log("No project selected for this repository.");
@@ -715,10 +869,10 @@ async function createClient(options) {
715
869
  throw new Error("Session expired. Run `foir login` to re-authenticate.");
716
870
  }
717
871
  headers["Authorization"] = `Bearer ${credentials.accessToken}`;
718
- const project = await getProjectContext();
719
- if (project) {
720
- headers["x-tenant-id"] = project.tenantId;
721
- headers["x-project-id"] = project.id;
872
+ const resolved = await resolveProjectContext(options);
873
+ if (resolved) {
874
+ headers["x-tenant-id"] = resolved.project.tenantId;
875
+ headers["x-project-id"] = resolved.project.id;
722
876
  }
723
877
  return new GraphQLClient(endpoint, { headers });
724
878
  }
@@ -738,10 +892,10 @@ async function getRestAuth(options) {
738
892
  throw new Error("Session expired. Run `foir login` to re-authenticate.");
739
893
  }
740
894
  headers["Authorization"] = `Bearer ${credentials.accessToken}`;
741
- const project = await getProjectContext();
742
- if (project) {
743
- headers["x-tenant-id"] = project.tenantId;
744
- headers["x-project-id"] = project.id;
895
+ const resolved = await resolveProjectContext(options);
896
+ if (resolved) {
897
+ headers["x-tenant-id"] = resolved.project.tenantId;
898
+ headers["x-project-id"] = resolved.project.id;
745
899
  }
746
900
  return { apiUrl, headers };
747
901
  }
@@ -784,21 +938,10 @@ function registerMediaCommands(program2, globalOpts) {
784
938
  }
785
939
 
786
940
  // src/commands/pull.ts
787
- import { resolve as resolve2 } from "path";
941
+ import { resolve } from "path";
788
942
  import chalk4 from "chalk";
789
943
 
790
944
  // src/config/pull-config.ts
791
- import { resolve } from "path";
792
- import { pathToFileURL } from "url";
793
- import { existsSync } from "fs";
794
- var CONFIG_FILE_NAMES = [
795
- "foir.config.ts",
796
- "foir.config.js",
797
- "foir.config.mjs",
798
- ".foirrc.ts",
799
- ".foirrc.js",
800
- ".foirrc.mjs"
801
- ];
802
945
  var DEFAULT_TYPES_DIR = "./src/generated/types";
803
946
  var DEFAULT_DOCS_DIR = "./src/generated/documents";
804
947
  var ALL_DOMAINS = {
@@ -813,25 +956,6 @@ var ALL_DOMAINS = {
813
956
  embeddings: true,
814
957
  analytics: true
815
958
  };
816
- function findConfigFile(explicitPath) {
817
- if (explicitPath) {
818
- const abs = resolve(explicitPath);
819
- if (existsSync(abs)) return abs;
820
- throw new Error(`Config file not found: ${explicitPath}`);
821
- }
822
- const cwd = process.cwd();
823
- for (const name of CONFIG_FILE_NAMES) {
824
- const candidate = resolve(cwd, name);
825
- if (existsSync(candidate)) return candidate;
826
- }
827
- return null;
828
- }
829
- async function loadConfigFile(filePath) {
830
- const url = pathToFileURL(filePath).href;
831
- const mod = await import(url);
832
- const config2 = mod.default ?? mod;
833
- return config2;
834
- }
835
959
  async function loadPullConfig(flags) {
836
960
  let fileConfig = {};
837
961
  const configPath = findConfigFile(flags.config);
@@ -882,361 +1006,9 @@ async function loadPullConfig(flags) {
882
1006
  }
883
1007
 
884
1008
  // src/graphql/generated.ts
885
- var GetCustomerProfileSchemaDocument = {
886
- kind: "Document",
887
- definitions: [
888
- {
889
- kind: "OperationDefinition",
890
- operation: "query",
891
- name: { kind: "Name", value: "GetCustomerProfileSchema" },
892
- selectionSet: {
893
- kind: "SelectionSet",
894
- selections: [
895
- {
896
- kind: "Field",
897
- name: { kind: "Name", value: "customerProfileSchema" },
898
- selectionSet: {
899
- kind: "SelectionSet",
900
- selections: [
901
- { kind: "Field", name: { kind: "Name", value: "id" } },
902
- {
903
- kind: "Field",
904
- name: { kind: "Name", value: "fields" },
905
- selectionSet: {
906
- kind: "SelectionSet",
907
- selections: [
908
- { kind: "Field", name: { kind: "Name", value: "key" } },
909
- { kind: "Field", name: { kind: "Name", value: "type" } },
910
- { kind: "Field", name: { kind: "Name", value: "label" } },
911
- {
912
- kind: "Field",
913
- name: { kind: "Name", value: "required" }
914
- },
915
- {
916
- kind: "Field",
917
- name: { kind: "Name", value: "helpText" }
918
- },
919
- {
920
- kind: "Field",
921
- name: { kind: "Name", value: "defaultValue" }
922
- },
923
- {
924
- kind: "Field",
925
- name: { kind: "Name", value: "config" }
926
- },
927
- {
928
- kind: "Field",
929
- name: { kind: "Name", value: "validation" },
930
- selectionSet: {
931
- kind: "SelectionSet",
932
- selections: [
933
- {
934
- kind: "Field",
935
- name: { kind: "Name", value: "rule" }
936
- },
937
- {
938
- kind: "Field",
939
- name: { kind: "Name", value: "value" }
940
- },
941
- {
942
- kind: "Field",
943
- name: { kind: "Name", value: "message" }
944
- }
945
- ]
946
- }
947
- }
948
- ]
949
- }
950
- },
951
- {
952
- kind: "Field",
953
- name: { kind: "Name", value: "publicFields" }
954
- },
955
- { kind: "Field", name: { kind: "Name", value: "version" } },
956
- { kind: "Field", name: { kind: "Name", value: "createdAt" } },
957
- { kind: "Field", name: { kind: "Name", value: "updatedAt" } }
958
- ]
959
- }
960
- }
961
- ]
962
- }
963
- }
964
- ]
965
- };
966
- var ModelsForCodegenDocument = {
967
- kind: "Document",
968
- definitions: [
969
- {
970
- kind: "OperationDefinition",
971
- operation: "query",
972
- name: { kind: "Name", value: "ModelsForCodegen" },
973
- variableDefinitions: [
974
- {
975
- kind: "VariableDefinition",
976
- variable: {
977
- kind: "Variable",
978
- name: { kind: "Name", value: "search" }
979
- },
980
- type: { kind: "NamedType", name: { kind: "Name", value: "String" } }
981
- },
982
- {
983
- kind: "VariableDefinition",
984
- variable: {
985
- kind: "Variable",
986
- name: { kind: "Name", value: "limit" }
987
- },
988
- type: { kind: "NamedType", name: { kind: "Name", value: "Int" } }
989
- },
990
- {
991
- kind: "VariableDefinition",
992
- variable: {
993
- kind: "Variable",
994
- name: { kind: "Name", value: "offset" }
995
- },
996
- type: { kind: "NamedType", name: { kind: "Name", value: "Int" } }
997
- }
998
- ],
999
- selectionSet: {
1000
- kind: "SelectionSet",
1001
- selections: [
1002
- {
1003
- kind: "Field",
1004
- name: { kind: "Name", value: "models" },
1005
- arguments: [
1006
- {
1007
- kind: "Argument",
1008
- name: { kind: "Name", value: "search" },
1009
- value: {
1010
- kind: "Variable",
1011
- name: { kind: "Name", value: "search" }
1012
- }
1013
- },
1014
- {
1015
- kind: "Argument",
1016
- name: { kind: "Name", value: "limit" },
1017
- value: {
1018
- kind: "Variable",
1019
- name: { kind: "Name", value: "limit" }
1020
- }
1021
- },
1022
- {
1023
- kind: "Argument",
1024
- name: { kind: "Name", value: "offset" },
1025
- value: {
1026
- kind: "Variable",
1027
- name: { kind: "Name", value: "offset" }
1028
- }
1029
- }
1030
- ],
1031
- selectionSet: {
1032
- kind: "SelectionSet",
1033
- selections: [
1034
- {
1035
- kind: "Field",
1036
- name: { kind: "Name", value: "items" },
1037
- selectionSet: {
1038
- kind: "SelectionSet",
1039
- selections: [
1040
- { kind: "Field", name: { kind: "Name", value: "id" } },
1041
- { kind: "Field", name: { kind: "Name", value: "key" } },
1042
- { kind: "Field", name: { kind: "Name", value: "name" } },
1043
- {
1044
- kind: "Field",
1045
- name: { kind: "Name", value: "pluralName" }
1046
- },
1047
- {
1048
- kind: "Field",
1049
- name: { kind: "Name", value: "description" }
1050
- },
1051
- {
1052
- kind: "Field",
1053
- name: { kind: "Name", value: "category" }
1054
- },
1055
- {
1056
- kind: "Field",
1057
- name: { kind: "Name", value: "fields" }
1058
- },
1059
- {
1060
- kind: "Field",
1061
- name: { kind: "Name", value: "config" }
1062
- },
1063
- { kind: "Field", name: { kind: "Name", value: "hooks" } },
1064
- {
1065
- kind: "Field",
1066
- name: { kind: "Name", value: "createdAt" }
1067
- },
1068
- {
1069
- kind: "Field",
1070
- name: { kind: "Name", value: "updatedAt" }
1071
- }
1072
- ]
1073
- }
1074
- },
1075
- { kind: "Field", name: { kind: "Name", value: "total" } }
1076
- ]
1077
- }
1078
- }
1079
- ]
1080
- }
1081
- }
1082
- ]
1083
- };
1084
- var GlobalSearchDocument = {
1085
- kind: "Document",
1086
- definitions: [
1087
- {
1088
- kind: "OperationDefinition",
1089
- operation: "query",
1090
- name: { kind: "Name", value: "GlobalSearch" },
1091
- variableDefinitions: [
1092
- {
1093
- kind: "VariableDefinition",
1094
- variable: {
1095
- kind: "Variable",
1096
- name: { kind: "Name", value: "query" }
1097
- },
1098
- type: {
1099
- kind: "NonNullType",
1100
- type: {
1101
- kind: "NamedType",
1102
- name: { kind: "Name", value: "String" }
1103
- }
1104
- }
1105
- },
1106
- {
1107
- kind: "VariableDefinition",
1108
- variable: {
1109
- kind: "Variable",
1110
- name: { kind: "Name", value: "limit" }
1111
- },
1112
- type: { kind: "NamedType", name: { kind: "Name", value: "Int" } }
1113
- },
1114
- {
1115
- kind: "VariableDefinition",
1116
- variable: {
1117
- kind: "Variable",
1118
- name: { kind: "Name", value: "modelKeys" }
1119
- },
1120
- type: {
1121
- kind: "ListType",
1122
- type: {
1123
- kind: "NonNullType",
1124
- type: {
1125
- kind: "NamedType",
1126
- name: { kind: "Name", value: "String" }
1127
- }
1128
- }
1129
- }
1130
- },
1131
- {
1132
- kind: "VariableDefinition",
1133
- variable: {
1134
- kind: "Variable",
1135
- name: { kind: "Name", value: "includeMedia" }
1136
- },
1137
- type: { kind: "NamedType", name: { kind: "Name", value: "Boolean" } }
1138
- }
1139
- ],
1140
- selectionSet: {
1141
- kind: "SelectionSet",
1142
- selections: [
1143
- {
1144
- kind: "Field",
1145
- name: { kind: "Name", value: "globalSearch" },
1146
- arguments: [
1147
- {
1148
- kind: "Argument",
1149
- name: { kind: "Name", value: "query" },
1150
- value: {
1151
- kind: "Variable",
1152
- name: { kind: "Name", value: "query" }
1153
- }
1154
- },
1155
- {
1156
- kind: "Argument",
1157
- name: { kind: "Name", value: "limit" },
1158
- value: {
1159
- kind: "Variable",
1160
- name: { kind: "Name", value: "limit" }
1161
- }
1162
- },
1163
- {
1164
- kind: "Argument",
1165
- name: { kind: "Name", value: "modelKeys" },
1166
- value: {
1167
- kind: "Variable",
1168
- name: { kind: "Name", value: "modelKeys" }
1169
- }
1170
- },
1171
- {
1172
- kind: "Argument",
1173
- name: { kind: "Name", value: "includeMedia" },
1174
- value: {
1175
- kind: "Variable",
1176
- name: { kind: "Name", value: "includeMedia" }
1177
- }
1178
- }
1179
- ],
1180
- selectionSet: {
1181
- kind: "SelectionSet",
1182
- selections: [
1183
- {
1184
- kind: "Field",
1185
- name: { kind: "Name", value: "records" },
1186
- selectionSet: {
1187
- kind: "SelectionSet",
1188
- selections: [
1189
- { kind: "Field", name: { kind: "Name", value: "id" } },
1190
- {
1191
- kind: "Field",
1192
- name: { kind: "Name", value: "modelKey" }
1193
- },
1194
- { kind: "Field", name: { kind: "Name", value: "title" } },
1195
- {
1196
- kind: "Field",
1197
- name: { kind: "Name", value: "naturalKey" }
1198
- },
1199
- {
1200
- kind: "Field",
1201
- name: { kind: "Name", value: "subtitle" }
1202
- },
1203
- {
1204
- kind: "Field",
1205
- name: { kind: "Name", value: "updatedAt" }
1206
- }
1207
- ]
1208
- }
1209
- },
1210
- {
1211
- kind: "Field",
1212
- name: { kind: "Name", value: "media" },
1213
- selectionSet: {
1214
- kind: "SelectionSet",
1215
- selections: [
1216
- { kind: "Field", name: { kind: "Name", value: "id" } },
1217
- {
1218
- kind: "Field",
1219
- name: { kind: "Name", value: "fileName" }
1220
- },
1221
- {
1222
- kind: "Field",
1223
- name: { kind: "Name", value: "altText" }
1224
- },
1225
- {
1226
- kind: "Field",
1227
- name: { kind: "Name", value: "fileUrl" }
1228
- }
1229
- ]
1230
- }
1231
- }
1232
- ]
1233
- }
1234
- }
1235
- ]
1236
- }
1237
- }
1238
- ]
1239
- };
1009
+ var GetCustomerProfileSchemaDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "GetCustomerProfileSchema" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "customerProfileSchema" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "fields" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "key" } }, { "kind": "Field", "name": { "kind": "Name", "value": "type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "label" } }, { "kind": "Field", "name": { "kind": "Name", "value": "required" } }, { "kind": "Field", "name": { "kind": "Name", "value": "helpText" } }, { "kind": "Field", "name": { "kind": "Name", "value": "defaultValue" } }, { "kind": "Field", "name": { "kind": "Name", "value": "config" } }, { "kind": "Field", "name": { "kind": "Name", "value": "validation" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "rule" } }, { "kind": "Field", "name": { "kind": "Name", "value": "value" } }, { "kind": "Field", "name": { "kind": "Name", "value": "message" } }] } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "publicFields" } }, { "kind": "Field", "name": { "kind": "Name", "value": "version" } }, { "kind": "Field", "name": { "kind": "Name", "value": "createdAt" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updatedAt" } }] } }] } }] };
1010
+ var ModelsForCodegenDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "ModelsForCodegen" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "search" } }, "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": "offset" } }, "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "models" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "search" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "search" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "limit" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "limit" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "offset" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "offset" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "items" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "key" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "pluralName" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "category" } }, { "kind": "Field", "name": { "kind": "Name", "value": "fields" } }, { "kind": "Field", "name": { "kind": "Name", "value": "config" } }, { "kind": "Field", "name": { "kind": "Name", "value": "hooks" } }, { "kind": "Field", "name": { "kind": "Name", "value": "createdAt" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updatedAt" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "total" } }] } }] } }] };
1011
+ 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" } }] } }] } }] } }] };
1240
1012
 
1241
1013
  // src/codegen/fetch-models.ts
1242
1014
  function normalizeConfig(raw) {
@@ -4797,47 +4569,47 @@ function registerPullCommand(program2, globalOpts) {
4797
4569
  )
4798
4570
  );
4799
4571
  const cwd = process.cwd();
4800
- const typesDir = resolve2(cwd, config2.output.types);
4801
- const docsDir = resolve2(cwd, config2.output.documents);
4802
- const opsDir = resolve2(cwd, config2.output.operations);
4803
- const hooksDir = config2.output.hooks ? resolve2(cwd, config2.output.hooks) : null;
4804
- const loadersDir = config2.output.loaders ? resolve2(cwd, config2.output.loaders) : null;
4572
+ const typesDir = resolve(cwd, config2.output.types);
4573
+ const docsDir = resolve(cwd, config2.output.documents);
4574
+ const opsDir = resolve(cwd, config2.output.operations);
4575
+ const hooksDir = config2.output.hooks ? resolve(cwd, config2.output.hooks) : null;
4576
+ const loadersDir = config2.output.loaders ? resolve(cwd, config2.output.loaders) : null;
4805
4577
  const files = [];
4806
4578
  const hasCustomerProfile = !!(cpSchema && cpSchema.fields.length > 0);
4807
4579
  const publicModels = models.filter(
4808
4580
  (m) => m.config.publicApi && m.config.records
4809
4581
  );
4810
4582
  files.push({
4811
- path: resolve2(typesDir, "field-types.ts"),
4583
+ path: resolve(typesDir, "field-types.ts"),
4812
4584
  content: generateFieldTypesFile()
4813
4585
  });
4814
4586
  files.push({
4815
- path: resolve2(typesDir, "config.ts"),
4587
+ path: resolve(typesDir, "config.ts"),
4816
4588
  content: generateConfigFile()
4817
4589
  });
4818
4590
  for (const model of models) {
4819
4591
  files.push({
4820
- path: resolve2(typesDir, "models", `${model.key}.ts`),
4592
+ path: resolve(typesDir, "models", `${model.key}.ts`),
4821
4593
  content: generateModelTypes(model, models)
4822
4594
  });
4823
4595
  }
4824
4596
  files.push({
4825
- path: resolve2(typesDir, "models", "index.ts"),
4597
+ path: resolve(typesDir, "models", "index.ts"),
4826
4598
  content: generateModelIndex(models)
4827
4599
  });
4828
4600
  if (hasCustomerProfile) {
4829
4601
  files.push({
4830
- path: resolve2(typesDir, "customer-profile.ts"),
4602
+ path: resolve(typesDir, "customer-profile.ts"),
4831
4603
  content: generateCustomerProfileTypes(cpSchema)
4832
4604
  });
4833
4605
  }
4834
4606
  files.push({
4835
- path: resolve2(typesDir, "index.ts"),
4607
+ path: resolve(typesDir, "index.ts"),
4836
4608
  content: generateMainIndex(hasCustomerProfile)
4837
4609
  });
4838
4610
  for (const model of publicModels) {
4839
4611
  files.push({
4840
- path: resolve2(docsDir, `${model.key}.graphql`),
4612
+ path: resolve(docsDir, `${model.key}.graphql`),
4841
4613
  content: generateModelDocuments(model)
4842
4614
  });
4843
4615
  }
@@ -4846,46 +4618,46 @@ function registerPullCommand(program2, globalOpts) {
4846
4618
  );
4847
4619
  if (hasSharingModels) {
4848
4620
  files.push({
4849
- path: resolve2(docsDir, "_shared.graphql"),
4621
+ path: resolve(docsDir, "_shared.graphql"),
4850
4622
  content: generateSharedFragments()
4851
4623
  });
4852
4624
  }
4853
4625
  files.push({
4854
- path: resolve2(docsDir, "customer-profile.graphql"),
4626
+ path: resolve(docsDir, "customer-profile.graphql"),
4855
4627
  content: generateCustomerProfileDocuments()
4856
4628
  });
4857
4629
  const staticDocs = generateStaticDocuments(config2.domains);
4858
4630
  for (const doc of staticDocs) {
4859
4631
  files.push({
4860
- path: resolve2(docsDir, doc.filename),
4632
+ path: resolve(docsDir, doc.filename),
4861
4633
  content: doc.content
4862
4634
  });
4863
4635
  }
4864
4636
  if (publicSchema) {
4865
4637
  files.push({
4866
- path: resolve2(docsDir, "public-schema.graphql"),
4638
+ path: resolve(docsDir, "public-schema.graphql"),
4867
4639
  content: publicSchema
4868
4640
  });
4869
4641
  }
4870
4642
  const typesRelPath = computeTypesRelPath(opsDir, typesDir);
4871
4643
  files.push({
4872
- path: resolve2(opsDir, "_common.ts"),
4644
+ path: resolve(opsDir, "_common.ts"),
4873
4645
  content: generateTypedOperationsCommon(typesRelPath)
4874
4646
  });
4875
4647
  for (const model of publicModels) {
4876
4648
  files.push({
4877
- path: resolve2(opsDir, `${model.key}.ts`),
4649
+ path: resolve(opsDir, `${model.key}.ts`),
4878
4650
  content: generateTypedOperations(model, typesRelPath)
4879
4651
  });
4880
4652
  }
4881
4653
  if (hasCustomerProfile) {
4882
4654
  files.push({
4883
- path: resolve2(opsDir, "customer-profile.ts"),
4655
+ path: resolve(opsDir, "customer-profile.ts"),
4884
4656
  content: generateCustomerProfileOperations(typesRelPath)
4885
4657
  });
4886
4658
  }
4887
4659
  files.push({
4888
- path: resolve2(opsDir, "index.ts"),
4660
+ path: resolve(opsDir, "index.ts"),
4889
4661
  content: generateTypedOperationsIndex(
4890
4662
  publicModels,
4891
4663
  hasCustomerProfile
@@ -4894,18 +4666,18 @@ function registerPullCommand(program2, globalOpts) {
4894
4666
  if (hooksDir) {
4895
4667
  for (const model of publicModels) {
4896
4668
  files.push({
4897
- path: resolve2(hooksDir, `${model.key}.ts`),
4669
+ path: resolve(hooksDir, `${model.key}.ts`),
4898
4670
  content: generateReactHooks(model)
4899
4671
  });
4900
4672
  }
4901
4673
  if (hasCustomerProfile) {
4902
4674
  files.push({
4903
- path: resolve2(hooksDir, "customer-profile.ts"),
4675
+ path: resolve(hooksDir, "customer-profile.ts"),
4904
4676
  content: generateCustomerProfileHooks()
4905
4677
  });
4906
4678
  }
4907
4679
  files.push({
4908
- path: resolve2(hooksDir, "index.ts"),
4680
+ path: resolve(hooksDir, "index.ts"),
4909
4681
  content: generateReactHooksIndex(
4910
4682
  publicModels,
4911
4683
  hasCustomerProfile
@@ -4915,18 +4687,18 @@ function registerPullCommand(program2, globalOpts) {
4915
4687
  if (loadersDir) {
4916
4688
  for (const model of publicModels) {
4917
4689
  files.push({
4918
- path: resolve2(loadersDir, `${model.key}.ts`),
4690
+ path: resolve(loadersDir, `${model.key}.ts`),
4919
4691
  content: generateRemixLoaders(model)
4920
4692
  });
4921
4693
  }
4922
4694
  if (hasCustomerProfile) {
4923
4695
  files.push({
4924
- path: resolve2(loadersDir, "customer-profile.ts"),
4696
+ path: resolve(loadersDir, "customer-profile.ts"),
4925
4697
  content: generateCustomerProfileLoaders()
4926
4698
  });
4927
4699
  }
4928
4700
  files.push({
4929
- path: resolve2(loadersDir, "index.ts"),
4701
+ path: resolve(loadersDir, "index.ts"),
4930
4702
  content: generateRemixLoadersIndex(
4931
4703
  publicModels,
4932
4704
  hasCustomerProfile
@@ -4934,25 +4706,25 @@ function registerPullCommand(program2, globalOpts) {
4934
4706
  });
4935
4707
  }
4936
4708
  if (config2.output.swift) {
4937
- const swiftDir = resolve2(cwd, config2.output.swift);
4709
+ const swiftDir = resolve(cwd, config2.output.swift);
4938
4710
  files.push({
4939
- path: resolve2(swiftDir, "FieldTypes.swift"),
4711
+ path: resolve(swiftDir, "FieldTypes.swift"),
4940
4712
  content: generateSwiftFieldTypesFile()
4941
4713
  });
4942
4714
  files.push({
4943
- path: resolve2(swiftDir, "ModelKeys.swift"),
4715
+ path: resolve(swiftDir, "ModelKeys.swift"),
4944
4716
  content: generateSwiftModelKeys(models)
4945
4717
  });
4946
4718
  for (const model of models) {
4947
4719
  const swiftTypeName = toPascalCase(model.key);
4948
4720
  files.push({
4949
- path: resolve2(swiftDir, `${swiftTypeName}.swift`),
4721
+ path: resolve(swiftDir, `${swiftTypeName}.swift`),
4950
4722
  content: generateSwiftModelFile(model)
4951
4723
  });
4952
4724
  }
4953
4725
  if (hasCustomerProfile) {
4954
4726
  files.push({
4955
- path: resolve2(swiftDir, "CustomerProfile.swift"),
4727
+ path: resolve(swiftDir, "CustomerProfile.swift"),
4956
4728
  content: generateSwiftCustomerProfileFile(cpSchema)
4957
4729
  });
4958
4730
  }
@@ -4994,7 +4766,7 @@ Generated ${files.length} file(s)`) + chalk4.dim(
4994
4766
  }
4995
4767
  if (config2.output.swift) {
4996
4768
  console.log(
4997
- chalk4.dim(` Swift: ${resolve2(cwd, config2.output.swift)}`)
4769
+ chalk4.dim(` Swift: ${resolve(cwd, config2.output.swift)}`)
4998
4770
  );
4999
4771
  }
5000
4772
  }
@@ -5653,9 +5425,9 @@ Media (${data.globalSearch.media.length}):`);
5653
5425
  }
5654
5426
 
5655
5427
  // src/commands/init.ts
5656
- import { existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
5428
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
5657
5429
  import { writeFile as writeFile2 } from "fs/promises";
5658
- import { resolve as resolve4, join as join4 } from "path";
5430
+ import { resolve as resolve3, join as join4 } from "path";
5659
5431
  import chalk6 from "chalk";
5660
5432
  import inquirer3 from "inquirer";
5661
5433
  var FIELD_DEFAULTS = {
@@ -5746,8 +5518,8 @@ function registerInitCommands(program2, globalOpts) {
5746
5518
  async (key, opts) => {
5747
5519
  const globalFlags = globalOpts();
5748
5520
  const template = generateModelTemplate(key);
5749
- const outDir = resolve4(opts.output);
5750
- if (!existsSync4(outDir)) {
5521
+ const outDir = resolve3(opts.output);
5522
+ if (!existsSync3(outDir)) {
5751
5523
  mkdirSync2(outDir, { recursive: true });
5752
5524
  }
5753
5525
  const ext = opts.ts ? "ts" : "json";
@@ -5824,8 +5596,8 @@ Edit the file, then run:
5824
5596
  console.log("No models selected.");
5825
5597
  return;
5826
5598
  }
5827
- const outDir = resolve4(opts.output);
5828
- if (!existsSync4(outDir)) {
5599
+ const outDir = resolve3(opts.output);
5600
+ if (!existsSync3(outDir)) {
5829
5601
  mkdirSync2(outDir, { recursive: true });
5830
5602
  }
5831
5603
  const createdFiles = [];
@@ -5856,11 +5628,248 @@ Edit the files, then run:
5856
5628
  );
5857
5629
  }
5858
5630
 
5631
+ // src/commands/profiles.ts
5632
+ import chalk7 from "chalk";
5633
+
5634
+ // src/lib/input.ts
5635
+ import inquirer4 from "inquirer";
5636
+
5637
+ // src/lib/config-loader.ts
5638
+ import { readFile } from "fs/promises";
5639
+ import { pathToFileURL } from "url";
5640
+ import { resolve as resolve4 } from "path";
5641
+ async function loadConfig(filePath) {
5642
+ const absPath = resolve4(filePath);
5643
+ if (filePath.endsWith(".ts")) {
5644
+ const configModule = await import(pathToFileURL(absPath).href);
5645
+ return configModule.default;
5646
+ }
5647
+ if (filePath.endsWith(".js") || filePath.endsWith(".mjs")) {
5648
+ const configModule = await import(pathToFileURL(absPath).href);
5649
+ return configModule.default;
5650
+ }
5651
+ if (filePath.endsWith(".json")) {
5652
+ const content = await readFile(absPath, "utf-8");
5653
+ return JSON.parse(content);
5654
+ }
5655
+ throw new Error(
5656
+ `Unsupported file extension for "${filePath}". Supported: .ts, .js, .mjs, .json`
5657
+ );
5658
+ }
5659
+
5660
+ // src/lib/input.ts
5661
+ async function parseInputData(opts) {
5662
+ if (opts.data) {
5663
+ return JSON.parse(opts.data);
5664
+ }
5665
+ if (opts.file) {
5666
+ return await loadConfig(opts.file);
5667
+ }
5668
+ if (!process.stdin.isTTY) {
5669
+ const chunks = [];
5670
+ for await (const chunk of process.stdin) {
5671
+ chunks.push(chunk);
5672
+ }
5673
+ const stdinContent = Buffer.concat(chunks).toString("utf-8").trim();
5674
+ if (stdinContent) {
5675
+ return JSON.parse(stdinContent);
5676
+ }
5677
+ }
5678
+ throw new Error(
5679
+ "No input data provided. Use --data, --file, or pipe via stdin."
5680
+ );
5681
+ }
5682
+ function isUUID(value) {
5683
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
5684
+ value
5685
+ ) || /^c[a-z0-9]{24,}$/.test(value);
5686
+ }
5687
+ async function confirmAction(message, opts) {
5688
+ if (opts?.confirm) return true;
5689
+ const { confirmed } = await inquirer4.prompt([
5690
+ {
5691
+ type: "confirm",
5692
+ name: "confirmed",
5693
+ message,
5694
+ default: false
5695
+ }
5696
+ ]);
5697
+ return confirmed;
5698
+ }
5699
+
5700
+ // src/commands/profiles.ts
5701
+ function registerProfilesCommand(program2, globalOpts) {
5702
+ const profiles = program2.command("profiles").description("Manage named project profiles");
5703
+ profiles.command("list").description("List all saved project profiles").action(
5704
+ withErrorHandler(globalOpts, async () => {
5705
+ const opts = globalOpts();
5706
+ const allProfiles = await listProfiles();
5707
+ const defaultName = await getDefaultProfile();
5708
+ if (allProfiles.length === 0) {
5709
+ if (opts.json || opts.jsonl) {
5710
+ formatOutput([], opts);
5711
+ } else {
5712
+ console.log("No profiles saved.");
5713
+ console.log(
5714
+ "Use `foir select-project --save-as <name>` to create one."
5715
+ );
5716
+ }
5717
+ return;
5718
+ }
5719
+ if (opts.json || opts.jsonl) {
5720
+ formatOutput(
5721
+ allProfiles.map((p) => ({
5722
+ name: p.name,
5723
+ default: p.name === defaultName,
5724
+ projectName: p.project.name,
5725
+ projectId: p.project.id,
5726
+ tenantId: p.project.tenantId
5727
+ })),
5728
+ opts
5729
+ );
5730
+ return;
5731
+ }
5732
+ const rows = allProfiles.map((p) => ({
5733
+ name: p.name === defaultName ? `${p.name} (default)` : p.name,
5734
+ project: p.project.name,
5735
+ projectId: p.project.id,
5736
+ tenantId: p.project.tenantId
5737
+ }));
5738
+ formatList(rows, opts, {
5739
+ columns: [
5740
+ { key: "name", header: "Profile", width: 20 },
5741
+ { key: "project", header: "Project", width: 30 },
5742
+ { key: "projectId", header: "Project ID", width: 28 },
5743
+ { key: "tenantId", header: "Tenant ID", width: 28 }
5744
+ ]
5745
+ });
5746
+ })
5747
+ );
5748
+ profiles.command("default [name]").description("Show or set the default profile").action(
5749
+ withErrorHandler(globalOpts, async (name) => {
5750
+ const opts = globalOpts();
5751
+ if (name) {
5752
+ const project = await getProjectContext(name);
5753
+ if (!project) {
5754
+ throw new Error(
5755
+ `Profile "${name}" not found. Run \`foir profiles list\` to see available profiles.`
5756
+ );
5757
+ }
5758
+ await setDefaultProfile(name);
5759
+ if (opts.json || opts.jsonl) {
5760
+ formatOutput({ default: name }, opts);
5761
+ } else {
5762
+ console.log(`Default profile set to "${name}".`);
5763
+ }
5764
+ } else {
5765
+ const current = await getDefaultProfile();
5766
+ if (opts.json || opts.jsonl) {
5767
+ formatOutput({ default: current }, opts);
5768
+ } else if (current) {
5769
+ console.log(`Default profile: ${current}`);
5770
+ } else {
5771
+ console.log("No default profile set.");
5772
+ console.log(
5773
+ "Use `foir profiles default <name>` to set one."
5774
+ );
5775
+ }
5776
+ }
5777
+ })
5778
+ );
5779
+ profiles.command("show [name]").description("Show details of a profile (or active profile if no name)").action(
5780
+ withErrorHandler(globalOpts, async (name) => {
5781
+ const opts = globalOpts();
5782
+ if (name) {
5783
+ const project = await getProjectContext(name);
5784
+ if (!project) {
5785
+ throw new Error(
5786
+ `Profile "${name}" not found. Run \`foir profiles list\` to see available profiles.`
5787
+ );
5788
+ }
5789
+ const defaultName = await getDefaultProfile();
5790
+ if (opts.json || opts.jsonl) {
5791
+ formatOutput(
5792
+ {
5793
+ profileName: name,
5794
+ default: name === defaultName,
5795
+ projectName: project.name,
5796
+ projectId: project.id,
5797
+ tenantId: project.tenantId
5798
+ },
5799
+ opts
5800
+ );
5801
+ } else {
5802
+ console.log(`Profile: ${name}${name === defaultName ? " (default)" : ""}`);
5803
+ console.log("\u2500".repeat(40));
5804
+ console.log(`Name: ${project.name}`);
5805
+ console.log(`ID: ${project.id}`);
5806
+ console.log(`Tenant ID: ${project.tenantId}`);
5807
+ }
5808
+ } else {
5809
+ const resolved = await resolveProjectContext(opts);
5810
+ if (!resolved) {
5811
+ console.log("No active project context.");
5812
+ console.log(
5813
+ "Run `foir select-project` to choose a project."
5814
+ );
5815
+ return;
5816
+ }
5817
+ if (opts.json || opts.jsonl) {
5818
+ formatOutput(
5819
+ {
5820
+ profileName: resolved.profileName ?? null,
5821
+ source: resolved.source,
5822
+ ...resolved.project
5823
+ },
5824
+ opts
5825
+ );
5826
+ } else {
5827
+ console.log(
5828
+ `Active Project${resolved.profileName ? ` (profile: ${resolved.profileName})` : ""}`
5829
+ );
5830
+ console.log("\u2500".repeat(40));
5831
+ console.log(`Name: ${resolved.project.name}`);
5832
+ console.log(`ID: ${resolved.project.id}`);
5833
+ console.log(`Tenant ID: ${resolved.project.tenantId}`);
5834
+ console.log(`Source: ${resolved.source}`);
5835
+ }
5836
+ }
5837
+ })
5838
+ );
5839
+ profiles.command("delete <name>").description("Delete a named profile").option("--confirm", "Skip confirmation prompt").action(
5840
+ withErrorHandler(globalOpts, async (name, cmdOpts) => {
5841
+ const opts = globalOpts();
5842
+ const project = await getProjectContext(name);
5843
+ if (!project) {
5844
+ throw new Error(
5845
+ `Profile "${name}" not found. Run \`foir profiles list\` to see available profiles.`
5846
+ );
5847
+ }
5848
+ const confirmed = await confirmAction(
5849
+ `Delete profile "${name}" (${project.name})?`,
5850
+ { confirm: !!cmdOpts.confirm }
5851
+ );
5852
+ if (!confirmed) {
5853
+ console.log("Aborted.");
5854
+ return;
5855
+ }
5856
+ await deleteProfile(name);
5857
+ if (opts.json || opts.jsonl) {
5858
+ formatOutput({ deleted: name }, opts);
5859
+ } else {
5860
+ console.log(
5861
+ chalk7.green(`Deleted profile "${name}".`)
5862
+ );
5863
+ }
5864
+ })
5865
+ );
5866
+ }
5867
+
5859
5868
  // src/commands/register-commands.ts
5860
5869
  import { readFileSync, readdirSync } from "fs";
5861
- import { resolve as resolve6, dirname as dirname5 } from "path";
5870
+ import { resolve as resolve5, dirname as dirname5 } from "path";
5862
5871
  import { fileURLToPath } from "url";
5863
- import chalk7 from "chalk";
5872
+ import chalk8 from "chalk";
5864
5873
 
5865
5874
  // ../command-registry/src/command-map.ts
5866
5875
  var COMMANDS = [
@@ -7457,81 +7466,15 @@ function createSchemaEngine(sdl) {
7457
7466
  };
7458
7467
  }
7459
7468
 
7460
- // src/lib/input.ts
7461
- import inquirer4 from "inquirer";
7462
-
7463
- // src/lib/config-loader.ts
7464
- import { readFile } from "fs/promises";
7465
- import { pathToFileURL as pathToFileURL2 } from "url";
7466
- import { resolve as resolve5 } from "path";
7467
- async function loadConfig(filePath) {
7468
- const absPath = resolve5(filePath);
7469
- if (filePath.endsWith(".ts")) {
7470
- const configModule = await import(pathToFileURL2(absPath).href);
7471
- return configModule.default;
7472
- }
7473
- if (filePath.endsWith(".js") || filePath.endsWith(".mjs")) {
7474
- const configModule = await import(pathToFileURL2(absPath).href);
7475
- return configModule.default;
7476
- }
7477
- if (filePath.endsWith(".json")) {
7478
- const content = await readFile(absPath, "utf-8");
7479
- return JSON.parse(content);
7480
- }
7481
- throw new Error(
7482
- `Unsupported file extension for "${filePath}". Supported: .ts, .js, .mjs, .json`
7483
- );
7484
- }
7485
-
7486
- // src/lib/input.ts
7487
- async function parseInputData(opts) {
7488
- if (opts.data) {
7489
- return JSON.parse(opts.data);
7490
- }
7491
- if (opts.file) {
7492
- return await loadConfig(opts.file);
7493
- }
7494
- if (!process.stdin.isTTY) {
7495
- const chunks = [];
7496
- for await (const chunk of process.stdin) {
7497
- chunks.push(chunk);
7498
- }
7499
- const stdinContent = Buffer.concat(chunks).toString("utf-8").trim();
7500
- if (stdinContent) {
7501
- return JSON.parse(stdinContent);
7502
- }
7503
- }
7504
- throw new Error(
7505
- "No input data provided. Use --data, --file, or pipe via stdin."
7506
- );
7507
- }
7508
- function isUUID(value) {
7509
- return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
7510
- value
7511
- ) || /^c[a-z0-9]{24,}$/.test(value);
7512
- }
7513
- async function confirmAction(message, opts) {
7514
- if (opts?.confirm) return true;
7515
- const { confirmed } = await inquirer4.prompt([
7516
- {
7517
- type: "confirm",
7518
- name: "confirmed",
7519
- message,
7520
- default: false
7521
- }
7522
- ]);
7523
- return confirmed;
7524
- }
7525
-
7526
7469
  // src/commands/register-commands.ts
7527
7470
  var __filename = fileURLToPath(import.meta.url);
7528
7471
  var __dirname = dirname5(__filename);
7529
7472
  function loadSchemaSDL() {
7530
- const bundledPath = resolve6(__dirname, "schema.graphql");
7473
+ const bundledPath = resolve5(__dirname, "schema.graphql");
7531
7474
  try {
7532
7475
  return readFileSync(bundledPath, "utf-8");
7533
7476
  } catch {
7534
- const monorepoPath = resolve6(
7477
+ const monorepoPath = resolve5(
7535
7478
  __dirname,
7536
7479
  "../../../graphql-core/schema.graphql"
7537
7480
  );
@@ -7687,11 +7630,11 @@ function registerDynamicCommands(program2, globalOpts) {
7687
7630
  );
7688
7631
  Object.assign(variables, coerced);
7689
7632
  if (flags.dir && entry.acceptsInput) {
7690
- const dirPath = resolve6(String(flags.dir));
7633
+ const dirPath = resolve5(String(flags.dir));
7691
7634
  const files = readdirSync(dirPath).filter((f) => /\.(json|ts|js|mjs)$/.test(f)).sort();
7692
7635
  if (files.length === 0) {
7693
7636
  console.error(
7694
- chalk7.yellow(`\u26A0 No .json/.ts/.js files found in ${dirPath}`)
7637
+ chalk8.yellow(`\u26A0 No .json/.ts/.js files found in ${dirPath}`)
7695
7638
  );
7696
7639
  return;
7697
7640
  }
@@ -7699,7 +7642,7 @@ function registerDynamicCommands(program2, globalOpts) {
7699
7642
  let updated = 0;
7700
7643
  let failed = 0;
7701
7644
  for (const file of files) {
7702
- const filePath = resolve6(dirPath, file);
7645
+ const filePath = resolve5(dirPath, file);
7703
7646
  const fileData = await parseInputData({ file: filePath });
7704
7647
  const argName = entry.inputArgName ?? "input";
7705
7648
  const fileVars = { ...variables, [argName]: fileData };
@@ -7736,19 +7679,19 @@ function registerDynamicCommands(program2, globalOpts) {
7736
7679
  } catch (updateErr) {
7737
7680
  failed++;
7738
7681
  const msg2 = updateErr instanceof Error ? updateErr.message : String(updateErr);
7739
- console.error(chalk7.red(`\u2717 ${label}:`), msg2);
7682
+ console.error(chalk8.red(`\u2717 ${label}:`), msg2);
7740
7683
  continue;
7741
7684
  }
7742
7685
  }
7743
7686
  failed++;
7744
7687
  const msg = err instanceof Error ? err.message : String(err);
7745
- console.error(chalk7.red(`\u2717 ${label}:`), msg);
7688
+ console.error(chalk8.red(`\u2717 ${label}:`), msg);
7746
7689
  }
7747
7690
  }
7748
7691
  if (!(opts.json || opts.jsonl || opts.quiet)) {
7749
7692
  console.log("");
7750
7693
  console.log(
7751
- chalk7.bold(
7694
+ chalk8.bold(
7752
7695
  `Done: ${created} created${updated ? `, ${updated} updated` : ""}${failed ? `, ${failed} failed` : ""}`
7753
7696
  )
7754
7697
  );
@@ -7767,13 +7710,13 @@ function registerDynamicCommands(program2, globalOpts) {
7767
7710
  );
7768
7711
  const fieldNames = new Set(inputFields.map((f) => f.name));
7769
7712
  if (fieldNames.has("projectId") && !inputData.projectId || fieldNames.has("tenantId") && !inputData.tenantId) {
7770
- const project = await getProjectContext();
7771
- if (project) {
7713
+ const resolved = await resolveProjectContext(opts);
7714
+ if (resolved) {
7772
7715
  if (fieldNames.has("projectId") && !inputData.projectId) {
7773
- inputData.projectId = project.id;
7716
+ inputData.projectId = resolved.project.id;
7774
7717
  }
7775
7718
  if (fieldNames.has("tenantId") && !inputData.tenantId) {
7776
- inputData.tenantId = project.tenantId;
7719
+ inputData.tenantId = resolved.project.tenantId;
7777
7720
  }
7778
7721
  }
7779
7722
  }
@@ -7924,7 +7867,7 @@ function registerDynamicCommands(program2, globalOpts) {
7924
7867
  }
7925
7868
  } else if (!(opts.json || opts.jsonl || opts.quiet)) {
7926
7869
  console.error(
7927
- chalk7.yellow(
7870
+ chalk8.yellow(
7928
7871
  "\u26A0 Could not auto-publish: no version found in response"
7929
7872
  )
7930
7873
  );
@@ -7948,24 +7891,26 @@ function autoColumns(items) {
7948
7891
  // src/cli.ts
7949
7892
  var __filename2 = fileURLToPath2(import.meta.url);
7950
7893
  var __dirname2 = dirname6(__filename2);
7951
- config({ path: resolve7(__dirname2, "../.env.local") });
7894
+ config({ path: resolve6(__dirname2, "../.env.local") });
7952
7895
  var require2 = createRequire(import.meta.url);
7953
7896
  var { version } = require2("../package.json");
7954
7897
  var program = new Command();
7955
- program.name("foir").description("CLI for Foir platform").version(version).option("--api-url <url>", "API endpoint URL").option("--json", "Output as JSON").option("--jsonl", "Output as JSON Lines").option("--quiet", "Minimal output (IDs only)");
7898
+ program.name("foir").description("CLI for Foir platform").version(version).option("--api-url <url>", "API endpoint URL").option("--json", "Output as JSON").option("--jsonl", "Output as JSON Lines").option("--quiet", "Minimal output (IDs only)").option("--project <name>", "Use a named project profile");
7956
7899
  function getGlobalOpts() {
7957
7900
  const opts = program.opts();
7958
7901
  return {
7959
7902
  apiUrl: opts.apiUrl,
7960
7903
  json: !!opts.json,
7961
7904
  jsonl: !!opts.jsonl,
7962
- quiet: !!opts.quiet
7905
+ quiet: !!opts.quiet,
7906
+ project: opts.project
7963
7907
  };
7964
7908
  }
7965
7909
  registerLoginCommand(program, getGlobalOpts);
7966
7910
  registerLogoutCommand(program, getGlobalOpts);
7967
7911
  registerSelectProjectCommand(program, getGlobalOpts);
7968
7912
  registerWhoamiCommand(program, getGlobalOpts);
7913
+ registerProfilesCommand(program, getGlobalOpts);
7969
7914
  registerMediaCommands(program, getGlobalOpts);
7970
7915
  registerSearchCommands(program, getGlobalOpts);
7971
7916
  registerPullCommand(program, getGlobalOpts);