@eide/foir-cli 0.1.45 → 0.1.46

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { config } from "dotenv";
5
- import { resolve as resolve5, dirname as dirname5 } from "path";
5
+ import { resolve as resolve6, dirname as dirname5 } from "path";
6
6
  import { fileURLToPath as fileURLToPath2 } from "url";
7
7
  import { createRequire } from "module";
8
8
  import { Command } from "commander";
@@ -343,13 +343,13 @@ function withErrorHandler(optsFn, fn) {
343
343
  // src/commands/login.ts
344
344
  async function findAvailablePort(start, end) {
345
345
  for (let port = start; port <= end; port++) {
346
- const available = await new Promise((resolve6) => {
346
+ const available = await new Promise((resolve7) => {
347
347
  const server = http.createServer();
348
348
  server.listen(port, () => {
349
349
  server.close();
350
- resolve6(true);
350
+ resolve7(true);
351
351
  });
352
- server.on("error", () => resolve6(false));
352
+ server.on("error", () => resolve7(false));
353
353
  });
354
354
  if (available) return port;
355
355
  }
@@ -387,7 +387,7 @@ async function loginAction(globalOpts) {
387
387
  const state = crypto.randomBytes(16).toString("hex");
388
388
  const port = await findAvailablePort(9876, 9900);
389
389
  const redirectUri = `http://localhost:${port}/callback`;
390
- const authCode = await new Promise((resolve6, reject) => {
390
+ const authCode = await new Promise((resolve7, reject) => {
391
391
  const server = http.createServer((req, res) => {
392
392
  const url = new URL(req.url, `http://localhost:${port}`);
393
393
  if (url.pathname === "/callback") {
@@ -420,7 +420,7 @@ async function loginAction(globalOpts) {
420
420
  `<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>`
421
421
  );
422
422
  server.close();
423
- resolve6(code);
423
+ resolve7(code);
424
424
  }
425
425
  });
426
426
  server.listen(port);
@@ -497,7 +497,7 @@ var CLI_API_KEY_SCOPES = [
497
497
  "records:publish",
498
498
  "files:read",
499
499
  "files:write",
500
- "extensions:read",
500
+ "configs:read",
501
501
  "operations:read",
502
502
  "operations:execute"
503
503
  ];
@@ -921,7 +921,7 @@ function registerMediaCommands(program2, globalOpts) {
921
921
  );
922
922
  }
923
923
 
924
- // src/commands/create-extension.ts
924
+ // src/commands/create-config.ts
925
925
  import chalk4 from "chalk";
926
926
  import inquirer2 from "inquirer";
927
927
 
@@ -977,14 +977,14 @@ function getManagerInfo(manager) {
977
977
  }
978
978
 
979
979
  // src/scaffold/scaffold.ts
980
- async function scaffold(projectName, extensionType, apiUrl) {
980
+ async function scaffold(projectName, configType, apiUrl) {
981
981
  const projectDir = path2.resolve(process.cwd(), projectName);
982
982
  if (fs4.existsSync(projectDir)) {
983
983
  throw new Error(
984
984
  `Directory "${projectName}" already exists. Choose a different name or remove the existing directory.`
985
985
  );
986
986
  }
987
- const files = getFiles(projectName, extensionType, apiUrl);
987
+ const files = getFiles(projectName, configType, apiUrl);
988
988
  for (const [filePath, content] of Object.entries(files)) {
989
989
  const fullPath = path2.join(projectDir, filePath);
990
990
  const dir = path2.dirname(fullPath);
@@ -1009,11 +1009,12 @@ async function scaffold(projectName, extensionType, apiUrl) {
1009
1009
  console.log(` ${pm.name === "npm" ? "npm run" : pm.name} dev`);
1010
1010
  console.log();
1011
1011
  }
1012
- function getFiles(projectName, extensionType, apiUrl) {
1012
+ function getFiles(projectName, configType, apiUrl) {
1013
1013
  return {
1014
1014
  // Root
1015
1015
  "package.json": getRootPackageJson(projectName),
1016
- "extension.manifest.json": getManifest(projectName, extensionType),
1016
+ // Config manifest
1017
+ "foir.config.ts": getFoirConfig(projectName, configType),
1017
1018
  // UI (Vite SPA)
1018
1019
  "ui/package.json": getUiPackageJson(projectName),
1019
1020
  "ui/tsconfig.json": getUiTsconfig(),
@@ -1022,7 +1023,7 @@ function getFiles(projectName, extensionType, apiUrl) {
1022
1023
  "ui/.env.example": getUiEnvExample(apiUrl),
1023
1024
  "ui/.gitignore": getUiGitignore(),
1024
1025
  "ui/src/main.tsx": getUiMain(),
1025
- "ui/src/App.tsx": getUiApp(extensionType),
1026
+ "ui/src/App.tsx": getUiApp(configType),
1026
1027
  "ui/src/index.css": getUiCss(),
1027
1028
  "ui/src/vite-env.d.ts": '/// <reference types="vite/client" />\n',
1028
1029
  // API (Hono)
@@ -1032,8 +1033,7 @@ function getFiles(projectName, extensionType, apiUrl) {
1032
1033
  "api/.gitignore": "node_modules\ndist\n.env\n.env.local\n",
1033
1034
  "api/src/index.ts": getApiIndex(),
1034
1035
  "api/src/routes/webhooks.ts": getApiWebhooks(),
1035
- "api/src/routes/health.ts": getApiHealth(),
1036
- "api/src/lib/platform.ts": getApiPlatform()
1036
+ "api/src/routes/health.ts": getApiHealth()
1037
1037
  };
1038
1038
  }
1039
1039
  function getRootPackageJson(name) {
@@ -1046,20 +1046,31 @@ function getRootPackageJson(name) {
1046
1046
  build: "pnpm --filter ./ui build && pnpm --filter ./api build"
1047
1047
  },
1048
1048
  devDependencies: {
1049
- concurrently: "^9.0.0"
1049
+ concurrently: "^9.0.0",
1050
+ "@eide/foir-cli": "^0.1.0"
1050
1051
  }
1051
1052
  };
1052
1053
  return JSON.stringify(pkg, null, 2) + "\n";
1053
1054
  }
1054
- function getManifest(name, extensionType) {
1055
- const manifest = {
1056
- name: name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
1057
- version: "0.1.0",
1058
- type: extensionType,
1059
- description: `${extensionType} extension`,
1060
- entityTypes: []
1061
- };
1062
- return JSON.stringify(manifest, null, 2) + "\n";
1055
+ function getFoirConfig(name, configType) {
1056
+ const displayName = name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
1057
+ return `import { defineConfig } from '@eide/foir-cli/configs';
1058
+
1059
+ export default defineConfig({
1060
+ key: '${name}',
1061
+ name: '${displayName}',
1062
+ configType: '${configType}',
1063
+
1064
+ // Uncomment and configure as needed:
1065
+ // models: [],
1066
+ // operations: [],
1067
+ // schedules: [],
1068
+ // hooks: [],
1069
+ // segments: [],
1070
+ // authProviders: [],
1071
+ // placements: [],
1072
+ });
1073
+ `;
1063
1074
  }
1064
1075
  function getUiPackageJson(name) {
1065
1076
  const pkg = {
@@ -1073,7 +1084,7 @@ function getUiPackageJson(name) {
1073
1084
  preview: "vite preview"
1074
1085
  },
1075
1086
  dependencies: {
1076
- "@eide/extension-sdk": "^0.1.0",
1087
+ "@eide/foir-editor-sdk": "^0.1.0",
1077
1088
  react: "^19.0.0",
1078
1089
  "react-dom": "^19.0.0"
1079
1090
  },
@@ -1170,13 +1181,13 @@ dist
1170
1181
  function getUiMain() {
1171
1182
  return `import { StrictMode } from 'react';
1172
1183
  import { createRoot } from 'react-dom/client';
1173
- import { ExtensionProvider, useExtension } from '@eide/extension-sdk';
1184
+ import { EditorProvider, useEditor } from '@eide/foir-editor-sdk';
1174
1185
  import { useEffect } from 'react';
1175
1186
  import { App } from './App';
1176
1187
  import './index.css';
1177
1188
 
1178
1189
  function ThemeSync({ children }: { children: React.ReactNode }) {
1179
- const { theme } = useExtension();
1190
+ const { theme } = useEditor();
1180
1191
 
1181
1192
  useEffect(() => {
1182
1193
  const root = document.documentElement;
@@ -1189,17 +1200,17 @@ function ThemeSync({ children }: { children: React.ReactNode }) {
1189
1200
 
1190
1201
  createRoot(document.getElementById('root')!).render(
1191
1202
  <StrictMode>
1192
- <ExtensionProvider>
1203
+ <EditorProvider>
1193
1204
  <ThemeSync>
1194
1205
  <App />
1195
1206
  </ThemeSync>
1196
- </ExtensionProvider>
1207
+ </EditorProvider>
1197
1208
  </StrictMode>
1198
1209
  );
1199
1210
  `;
1200
1211
  }
1201
- function getUiApp(extensionType) {
1202
- switch (extensionType) {
1212
+ function getUiApp(configType) {
1213
+ switch (configType) {
1203
1214
  case "custom-editor":
1204
1215
  return getCustomEditorApp();
1205
1216
  case "widget":
@@ -1209,10 +1220,10 @@ function getUiApp(extensionType) {
1209
1220
  }
1210
1221
  }
1211
1222
  function getCustomEditorApp() {
1212
- return `import { useExtension, useAutoResize } from '@eide/extension-sdk';
1223
+ return `import { useEditor, useAutoResize } from '@eide/foir-editor-sdk';
1213
1224
 
1214
1225
  export function App() {
1215
- const { isReady, init, updateField, setDirty } = useExtension();
1226
+ const { isReady, init, updateField, setDirty } = useEditor();
1216
1227
  const containerRef = useAutoResize({ minHeight: 600 });
1217
1228
 
1218
1229
  if (!isReady) return null;
@@ -1220,7 +1231,7 @@ export function App() {
1220
1231
  return (
1221
1232
  <div ref={containerRef} className="p-6 space-y-4">
1222
1233
  <h1 className="text-lg font-semibold">
1223
- Editing: {init?.entityModelKey}
1234
+ Editing: {init?.modelKey}
1224
1235
  </h1>
1225
1236
  <p className="text-sm text-gray-500">
1226
1237
  Record: {init?.recordId}
@@ -1232,10 +1243,10 @@ export function App() {
1232
1243
  `;
1233
1244
  }
1234
1245
  function getWidgetApp() {
1235
- return `import { useExtension, useAutoResize } from '@eide/extension-sdk';
1246
+ return `import { useEditor, useAutoResize } from '@eide/foir-editor-sdk';
1236
1247
 
1237
1248
  export function App() {
1238
- const { isReady, init, client } = useExtension();
1249
+ const { isReady, init, client } = useEditor();
1239
1250
  const containerRef = useAutoResize({ minHeight: 300 });
1240
1251
 
1241
1252
  if (!isReady) return null;
@@ -1244,7 +1255,7 @@ export function App() {
1244
1255
  <div ref={containerRef} className="p-6 space-y-4">
1245
1256
  <h2 className="text-lg font-semibold">Dashboard Widget</h2>
1246
1257
  <p className="text-sm text-gray-500">
1247
- Connected to: {init?.entityModelKey}
1258
+ Connected to: {init?.modelKey}
1248
1259
  </p>
1249
1260
  {/* Add your widget content here */}
1250
1261
  </div>
@@ -1253,19 +1264,19 @@ export function App() {
1253
1264
  `;
1254
1265
  }
1255
1266
  function getWorkflowApp() {
1256
- return `import { useExtension, useAutoResize } from '@eide/extension-sdk';
1267
+ return `import { useEditor, useAutoResize } from '@eide/foir-editor-sdk';
1257
1268
 
1258
1269
  export function App() {
1259
- const { isReady, init, client, requestSave } = useExtension();
1270
+ const { isReady, init, client, requestSave } = useEditor();
1260
1271
  const containerRef = useAutoResize({ minHeight: 400 });
1261
1272
 
1262
1273
  if (!isReady) return null;
1263
1274
 
1264
1275
  return (
1265
1276
  <div ref={containerRef} className="p-6 space-y-4">
1266
- <h1 className="text-lg font-semibold">Workflow Extension</h1>
1277
+ <h1 className="text-lg font-semibold">Workflow Config</h1>
1267
1278
  <p className="text-sm text-gray-500">
1268
- Processing: {init?.entityModelKey} / {init?.recordId}
1279
+ Processing: {init?.modelKey} / {init?.recordId}
1269
1280
  </p>
1270
1281
  {/* Add your workflow steps here */}
1271
1282
  </div>
@@ -1305,7 +1316,7 @@ function getApiPackageJson(name) {
1305
1316
  start: "node dist/index.js"
1306
1317
  },
1307
1318
  dependencies: {
1308
- "@eide/extension-sdk": "^0.1.0",
1319
+ "@eide/foir-editor-sdk": "^0.1.0",
1309
1320
  hono: "^4.0.0",
1310
1321
  "@hono/node-server": "^1.0.0"
1311
1322
  },
@@ -1341,8 +1352,8 @@ function getApiEnvExample(apiUrl) {
1341
1352
  PLATFORM_BASE_URL=${baseUrl}
1342
1353
  PLATFORM_API_KEY=sk_your_api_key_here
1343
1354
 
1344
- # Extension
1345
- EXTENSION_KEY=${"{your-extension-key}"}
1355
+ # Config
1356
+ CONFIG_KEY=${"{your-config-key}"}
1346
1357
  WEBHOOK_SECRET=your_webhook_secret_here
1347
1358
 
1348
1359
  # Server
@@ -1363,25 +1374,25 @@ app.route('/', health);
1363
1374
 
1364
1375
  const port = parseInt(process.env.PORT || '3002', 10);
1365
1376
 
1366
- console.log(\`Extension API running on http://localhost:\${port}\`);
1377
+ console.log(\`Config API running on http://localhost:\${port}\`);
1367
1378
 
1368
1379
  serve({ fetch: app.fetch, port });
1369
1380
  `;
1370
1381
  }
1371
1382
  function getApiWebhooks() {
1372
1383
  return `import { Hono } from 'hono';
1373
- import { verifyWebhookSignature } from '@eide/extension-sdk/server';
1384
+ import { verifyWebhookSignature } from '@eide/foir-editor-sdk/server';
1374
1385
 
1375
1386
  const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || '';
1376
1387
 
1377
1388
  export const webhooks = new Hono();
1378
1389
 
1379
1390
  /**
1380
- * Receive entity lifecycle events from the platform.
1391
+ * Receive record lifecycle events from the platform.
1381
1392
  */
1382
- webhooks.post('/entity-changed', async (c) => {
1393
+ webhooks.post('/record-changed', async (c) => {
1383
1394
  const body = await c.req.text();
1384
- const signature = c.req.header('x-eide-signature') ?? '';
1395
+ const signature = c.req.header('x-foir-signature') ?? '';
1385
1396
 
1386
1397
  const valid = await verifyWebhookSignature(body, signature, WEBHOOK_SECRET);
1387
1398
  if (!valid) {
@@ -1389,7 +1400,7 @@ webhooks.post('/entity-changed', async (c) => {
1389
1400
  }
1390
1401
 
1391
1402
  const payload = JSON.parse(body);
1392
- console.log('[Webhook] Entity changed:', payload.event, payload.entityId);
1403
+ console.log('[Webhook] Record changed:', payload.event, payload.recordId);
1393
1404
 
1394
1405
  // TODO: Handle the event (sync, transform, notify, etc.)
1395
1406
 
@@ -1407,36 +1418,14 @@ health.get('/health', (c) => {
1407
1418
  });
1408
1419
  `;
1409
1420
  }
1410
- function getApiPlatform() {
1411
- return `import { createExtensionClient } from '@eide/extension-sdk/server';
1412
1421
 
1413
- /**
1414
- * Pre-configured platform client for this extension.
1415
- *
1416
- * Uses env vars for configuration:
1417
- * - PLATFORM_BASE_URL: Platform API base URL
1418
- * - PLATFORM_API_KEY: Project-scoped API key (sk_*)
1419
- * - EXTENSION_KEY: This extension's extension key
1420
- */
1421
- export const platform = createExtensionClient({
1422
- baseUrl: process.env.PLATFORM_BASE_URL || 'http://localhost:4000',
1423
- apiKey: process.env.PLATFORM_API_KEY || '',
1424
- extensionKey: process.env.EXTENSION_KEY || '',
1425
- });
1426
- `;
1422
+ // src/commands/create-config.ts
1423
+ var CONFIG_TYPES = ["custom-editor", "workflow", "widget"];
1424
+ function isValidConfigType(value) {
1425
+ return CONFIG_TYPES.includes(value);
1427
1426
  }
1428
-
1429
- // src/commands/create-extension.ts
1430
- var EXTENSION_TYPES = [
1431
- "custom-editor",
1432
- "workflow",
1433
- "widget"
1434
- ];
1435
- function isValidExtensionType(value) {
1436
- return EXTENSION_TYPES.includes(value);
1437
- }
1438
- function registerCreateExtensionCommand(program2, globalOpts) {
1439
- program2.command("create-extension [name]").description("Scaffold a new Foir extension").option("--type <type>", "Extension type: custom-editor, workflow, widget").option(
1427
+ 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(
1440
1429
  "--api-url <url>",
1441
1430
  "Platform API URL",
1442
1431
  "http://localhost:4000/graphql"
@@ -1445,43 +1434,43 @@ function registerCreateExtensionCommand(program2, globalOpts) {
1445
1434
  globalOpts,
1446
1435
  async (name, cmdOpts) => {
1447
1436
  console.log();
1448
- console.log(chalk4.bold(" Create Foir Extension"));
1449
- console.log(chalk4.gray(" ---------------------"));
1437
+ console.log(chalk4.bold(" Create Foir Config"));
1438
+ console.log(chalk4.gray(" ------------------"));
1450
1439
  console.log();
1451
- let extensionName = name;
1452
- if (!extensionName) {
1440
+ let configName = name;
1441
+ if (!configName) {
1453
1442
  const { inputName } = await inquirer2.prompt([
1454
1443
  {
1455
1444
  type: "input",
1456
1445
  name: "inputName",
1457
- message: "Extension name:",
1458
- default: "my-extension"
1446
+ message: "Config name:",
1447
+ default: "my-config"
1459
1448
  }
1460
1449
  ]);
1461
- extensionName = inputName;
1450
+ configName = inputName;
1462
1451
  }
1463
- let extensionType;
1464
- if (cmdOpts?.type && isValidExtensionType(cmdOpts.type)) {
1465
- extensionType = cmdOpts.type;
1452
+ let configType;
1453
+ if (cmdOpts?.type && isValidConfigType(cmdOpts.type)) {
1454
+ configType = cmdOpts.type;
1466
1455
  } else {
1467
1456
  const { selectedType } = await inquirer2.prompt([
1468
1457
  {
1469
1458
  type: "list",
1470
1459
  name: "selectedType",
1471
- message: "Extension type:",
1472
- choices: EXTENSION_TYPES,
1460
+ message: "Config type:",
1461
+ choices: CONFIG_TYPES,
1473
1462
  default: "custom-editor"
1474
1463
  }
1475
1464
  ]);
1476
- extensionType = selectedType;
1465
+ configType = selectedType;
1477
1466
  }
1478
1467
  const apiUrl = cmdOpts?.apiUrl ?? "http://localhost:4000/graphql";
1479
1468
  console.log();
1480
1469
  console.log(
1481
- ` Scaffolding ${chalk4.cyan(`"${extensionName}"`)} (${extensionType})...`
1470
+ ` Scaffolding ${chalk4.cyan(`"${configName}"`)} (${configType})...`
1482
1471
  );
1483
1472
  console.log();
1484
- await scaffold(extensionName, extensionType, apiUrl);
1473
+ await scaffold(configName, configType, apiUrl);
1485
1474
  }
1486
1475
  )
1487
1476
  );
@@ -1916,11 +1905,10 @@ Edit the files, then run:
1916
1905
  );
1917
1906
  }
1918
1907
 
1919
- // src/commands/profiles.ts
1908
+ // src/commands/push.ts
1920
1909
  import chalk6 from "chalk";
1921
-
1922
- // src/lib/input.ts
1923
- import inquirer4 from "inquirer";
1910
+ import { existsSync as existsSync4 } from "fs";
1911
+ import { resolve as resolve4 } from "path";
1924
1912
 
1925
1913
  // src/lib/config-loader.ts
1926
1914
  import { readFile } from "fs/promises";
@@ -1945,7 +1933,194 @@ async function loadConfig(filePath) {
1945
1933
  );
1946
1934
  }
1947
1935
 
1936
+ // src/commands/push.ts
1937
+ var CONFIG_FILE_NAMES = [
1938
+ "foir.config.ts",
1939
+ "foir.config.js",
1940
+ "foir.config.mjs",
1941
+ "foir.config.json"
1942
+ ];
1943
+ var APPLY_CONFIG_MUTATION = (
1944
+ /* GraphQL */
1945
+ `
1946
+ mutation ApplyConfig($input: ApplyConfigInput!) {
1947
+ applyConfig(input: $input) {
1948
+ configId
1949
+ configKey
1950
+ credentials {
1951
+ platformApiKey
1952
+ platformEditorKey
1953
+ webhookSecret
1954
+ }
1955
+ modelsCreated
1956
+ modelsUpdated
1957
+ operationsCreated
1958
+ operationsUpdated
1959
+ segmentsCreated
1960
+ segmentsUpdated
1961
+ schedulesCreated
1962
+ schedulesUpdated
1963
+ hooksCreated
1964
+ hooksUpdated
1965
+ isUpdate
1966
+ }
1967
+ }
1968
+ `
1969
+ );
1970
+ function discoverConfigFile() {
1971
+ for (const name of CONFIG_FILE_NAMES) {
1972
+ const path3 = resolve4(process.cwd(), name);
1973
+ if (existsSync4(path3)) return path3;
1974
+ }
1975
+ return null;
1976
+ }
1977
+ function registerPushCommand(program2, globalOpts) {
1978
+ program2.command("push").description("Push foir.config.ts to the platform").option("--config <path>", "Path to config file (default: auto-discover)").option("--force", "Force reinstall (delete and recreate)", false).action(
1979
+ withErrorHandler(
1980
+ globalOpts,
1981
+ async (opts) => {
1982
+ const configPath = opts.config ? resolve4(opts.config) : discoverConfigFile();
1983
+ if (!configPath) {
1984
+ throw new Error(
1985
+ "No config file found. Create a foir.config.ts or use --config <path>."
1986
+ );
1987
+ }
1988
+ if (!existsSync4(configPath)) {
1989
+ throw new Error(`Config file not found: ${configPath}`);
1990
+ }
1991
+ console.log(chalk6.dim(`Loading ${configPath}...`));
1992
+ const config2 = await loadConfig(configPath);
1993
+ if (!config2?.key || !config2?.name) {
1994
+ throw new Error(
1995
+ 'Config must have at least "key" and "name" fields.'
1996
+ );
1997
+ }
1998
+ if (opts.force) {
1999
+ config2.force = true;
2000
+ }
2001
+ const client = await createClient(globalOpts());
2002
+ console.log(
2003
+ chalk6.dim(`Pushing config "${config2.key}" to platform...`)
2004
+ );
2005
+ const data = await client.request(
2006
+ APPLY_CONFIG_MUTATION,
2007
+ { input: config2 }
2008
+ );
2009
+ const result = data.applyConfig;
2010
+ console.log();
2011
+ if (result.isUpdate) {
2012
+ console.log(chalk6.green("Config updated successfully."));
2013
+ } else {
2014
+ console.log(chalk6.green("Config applied successfully."));
2015
+ }
2016
+ console.log();
2017
+ console.log(` Config ID: ${chalk6.cyan(result.configId)}`);
2018
+ console.log(` Config Key: ${chalk6.cyan(result.configKey)}`);
2019
+ console.log();
2020
+ const stats = [
2021
+ ["Models", result.modelsCreated, result.modelsUpdated],
2022
+ ["Operations", result.operationsCreated, result.operationsUpdated],
2023
+ ["Segments", result.segmentsCreated, result.segmentsUpdated],
2024
+ ["Schedules", result.schedulesCreated, result.schedulesUpdated],
2025
+ ["Hooks", result.hooksCreated, result.hooksUpdated]
2026
+ ].filter(([, c, u]) => c > 0 || u > 0);
2027
+ if (stats.length > 0) {
2028
+ for (const [label, created, updated] of stats) {
2029
+ const parts = [];
2030
+ if (created > 0)
2031
+ parts.push(chalk6.green(`${created} created`));
2032
+ if (updated > 0)
2033
+ parts.push(chalk6.yellow(`${updated} updated`));
2034
+ console.log(` ${label}: ${parts.join(", ")}`);
2035
+ }
2036
+ console.log();
2037
+ }
2038
+ if (result.credentials) {
2039
+ console.log(chalk6.bold.yellow("Credentials (save these now):"));
2040
+ console.log();
2041
+ console.log(
2042
+ ` PLATFORM_API_KEY: ${chalk6.cyan(result.credentials.platformApiKey)}`
2043
+ );
2044
+ console.log(
2045
+ ` PLATFORM_EDITOR_KEY: ${chalk6.cyan(result.credentials.platformEditorKey)}`
2046
+ );
2047
+ console.log(
2048
+ ` WEBHOOK_SECRET: ${chalk6.cyan(result.credentials.webhookSecret)}`
2049
+ );
2050
+ console.log();
2051
+ console.log(
2052
+ chalk6.dim(
2053
+ "These credentials are only shown once. Store them securely."
2054
+ )
2055
+ );
2056
+ }
2057
+ }
2058
+ )
2059
+ );
2060
+ }
2061
+
2062
+ // src/commands/remove.ts
2063
+ import chalk7 from "chalk";
2064
+ import inquirer4 from "inquirer";
2065
+ var GET_CONFIG_QUERY = (
2066
+ /* GraphQL */
2067
+ `
2068
+ query GetConfigByKey($key: String!) {
2069
+ configByKey(key: $key) {
2070
+ id
2071
+ key
2072
+ name
2073
+ configType
2074
+ }
2075
+ }
2076
+ `
2077
+ );
2078
+ var UNREGISTER_MUTATION = (
2079
+ /* GraphQL */
2080
+ `
2081
+ mutation UnregisterConfig($id: ID!) {
2082
+ unregisterConfig(id: $id)
2083
+ }
2084
+ `
2085
+ );
2086
+ function registerRemoveCommand(program2, globalOpts) {
2087
+ program2.command("remove <key>").description("Remove a config and all its provisioned resources").option("--force", "Skip confirmation prompt", false).action(
2088
+ withErrorHandler(
2089
+ globalOpts,
2090
+ async (key, opts) => {
2091
+ const client = await createClient(globalOpts());
2092
+ const { configByKey: config2 } = await client.request(GET_CONFIG_QUERY, { key });
2093
+ if (!config2) {
2094
+ throw new Error(`Config not found: ${key}`);
2095
+ }
2096
+ if (!opts.force) {
2097
+ const { confirmed } = await inquirer4.prompt([
2098
+ {
2099
+ type: "confirm",
2100
+ name: "confirmed",
2101
+ message: `Remove config "${config2.name}" (${config2.key})? This will delete all its models, operations, hooks, and schedules.`,
2102
+ default: false
2103
+ }
2104
+ ]);
2105
+ if (!confirmed) {
2106
+ console.log(chalk7.dim("Cancelled."));
2107
+ return;
2108
+ }
2109
+ }
2110
+ await client.request(UNREGISTER_MUTATION, { id: config2.id });
2111
+ console.log(
2112
+ chalk7.green(`Removed config "${config2.name}" (${config2.key}).`)
2113
+ );
2114
+ }
2115
+ )
2116
+ );
2117
+ }
2118
+
2119
+ // src/commands/profiles.ts
2120
+ import chalk8 from "chalk";
2121
+
1948
2122
  // src/lib/input.ts
2123
+ import inquirer5 from "inquirer";
1949
2124
  async function parseInputData(opts) {
1950
2125
  if (opts.data) {
1951
2126
  return JSON.parse(opts.data);
@@ -1974,7 +2149,7 @@ function isUUID(value) {
1974
2149
  }
1975
2150
  async function confirmAction(message, opts) {
1976
2151
  if (opts?.confirm) return true;
1977
- const { confirmed } = await inquirer4.prompt([
2152
+ const { confirmed } = await inquirer5.prompt([
1978
2153
  {
1979
2154
  type: "confirm",
1980
2155
  name: "confirmed",
@@ -2145,7 +2320,7 @@ function registerProfilesCommand(program2, globalOpts) {
2145
2320
  if (opts.json || opts.jsonl) {
2146
2321
  formatOutput({ deleted: name }, opts);
2147
2322
  } else {
2148
- console.log(chalk6.green(`Deleted profile "${name}".`));
2323
+ console.log(chalk8.green(`Deleted profile "${name}".`));
2149
2324
  }
2150
2325
  }
2151
2326
  )
@@ -2154,9 +2329,9 @@ function registerProfilesCommand(program2, globalOpts) {
2154
2329
 
2155
2330
  // src/commands/register-commands.ts
2156
2331
  import { readFileSync, readdirSync } from "fs";
2157
- import { resolve as resolve4, dirname as dirname4 } from "path";
2332
+ import { resolve as resolve5, dirname as dirname4 } from "path";
2158
2333
  import { fileURLToPath } from "url";
2159
- import chalk7 from "chalk";
2334
+ import chalk9 from "chalk";
2160
2335
 
2161
2336
  // ../command-registry/src/command-map.ts
2162
2337
  var COMMANDS = [
@@ -2782,65 +2957,65 @@ var COMMANDS = [
2782
2957
  // EXTENSIONS
2783
2958
  // =========================================================================
2784
2959
  {
2785
- group: "extensions",
2960
+ group: "configs",
2786
2961
  name: "list",
2787
- description: "List extensions",
2788
- operation: "extensions",
2962
+ description: "List configs",
2963
+ operation: "configs",
2789
2964
  operationType: "query",
2790
2965
  columns: [
2791
2966
  { key: "id", header: "ID", width: 28 },
2792
2967
  { key: "key", header: "Key", width: 20 },
2793
2968
  { key: "name", header: "Name", width: 20 },
2794
- { key: "extensionType", header: "Type", width: 12 },
2969
+ { key: "configType", header: "Type", width: 12 },
2795
2970
  { key: "enabled", header: "Enabled", width: 8, format: "boolean" },
2796
2971
  { key: "syncStatus", header: "Sync", width: 10 }
2797
2972
  ]
2798
2973
  },
2799
2974
  {
2800
- group: "extensions",
2975
+ group: "configs",
2801
2976
  name: "get",
2802
- description: "Get an extension",
2803
- operation: "extension",
2977
+ description: "Get a config",
2978
+ operation: "config",
2804
2979
  operationType: "query",
2805
2980
  positionalArgs: [{ name: "id", graphqlArg: "id" }],
2806
- alternateGet: { operation: "extensionByKey", argName: "key" }
2981
+ alternateGet: { operation: "configByKey", argName: "key" }
2807
2982
  },
2808
2983
  {
2809
- group: "extensions",
2984
+ group: "configs",
2810
2985
  name: "register",
2811
- description: "Register an extension",
2812
- operation: "registerExtension",
2986
+ description: "Register a config",
2987
+ operation: "registerConfig",
2813
2988
  operationType: "mutation",
2814
2989
  acceptsInput: true,
2815
- successMessage: "Registered extension"
2990
+ successMessage: "Registered config"
2816
2991
  },
2817
2992
  {
2818
- group: "extensions",
2819
- name: "install",
2820
- description: "Install an extension",
2821
- operation: "installExtension",
2993
+ group: "configs",
2994
+ name: "apply",
2995
+ description: "Apply a config",
2996
+ operation: "applyConfig",
2822
2997
  operationType: "mutation",
2823
2998
  acceptsInput: true,
2824
- successMessage: "Installed extension"
2999
+ successMessage: "Applied config"
2825
3000
  },
2826
3001
  {
2827
- group: "extensions",
3002
+ group: "configs",
2828
3003
  name: "uninstall",
2829
- description: "Unregister an extension",
2830
- operation: "unregisterExtension",
3004
+ description: "Unregister a config",
3005
+ operation: "unregisterConfig",
2831
3006
  operationType: "mutation",
2832
3007
  positionalArgs: [{ name: "id", graphqlArg: "id" }],
2833
3008
  requiresConfirmation: true,
2834
3009
  scalarResult: true,
2835
- successMessage: "Unregistered extension"
3010
+ successMessage: "Unregistered config"
2836
3011
  },
2837
3012
  {
2838
- group: "extensions",
3013
+ group: "configs",
2839
3014
  name: "sync",
2840
- description: "Trigger extension sync",
2841
- operation: "triggerExtensionSync",
3015
+ description: "Trigger config sync",
3016
+ operation: "triggerConfigSync",
2842
3017
  operationType: "mutation",
2843
- positionalArgs: [{ name: "extensionId", graphqlArg: "extensionId" }],
3018
+ positionalArgs: [{ name: "configId", graphqlArg: "configId" }],
2844
3019
  successMessage: "Triggered sync"
2845
3020
  },
2846
3021
  // =========================================================================
@@ -3757,11 +3932,11 @@ function createSchemaEngine(sdl) {
3757
3932
  var __filename = fileURLToPath(import.meta.url);
3758
3933
  var __dirname = dirname4(__filename);
3759
3934
  function loadSchemaSDL() {
3760
- const bundledPath = resolve4(__dirname, "schema.graphql");
3935
+ const bundledPath = resolve5(__dirname, "schema.graphql");
3761
3936
  try {
3762
3937
  return readFileSync(bundledPath, "utf-8");
3763
3938
  } catch {
3764
- const monorepoPath = resolve4(
3939
+ const monorepoPath = resolve5(
3765
3940
  __dirname,
3766
3941
  "../../../graphql-core/schema.graphql"
3767
3942
  );
@@ -3917,11 +4092,11 @@ function registerDynamicCommands(program2, globalOpts) {
3917
4092
  );
3918
4093
  Object.assign(variables, coerced);
3919
4094
  if (flags.dir && entry.acceptsInput) {
3920
- const dirPath = resolve4(String(flags.dir));
4095
+ const dirPath = resolve5(String(flags.dir));
3921
4096
  const files = readdirSync(dirPath).filter((f) => /\.(json|ts|js|mjs)$/.test(f)).sort();
3922
4097
  if (files.length === 0) {
3923
4098
  console.error(
3924
- chalk7.yellow(`\u26A0 No .json/.ts/.js files found in ${dirPath}`)
4099
+ chalk9.yellow(`\u26A0 No .json/.ts/.js files found in ${dirPath}`)
3925
4100
  );
3926
4101
  return;
3927
4102
  }
@@ -3929,7 +4104,7 @@ function registerDynamicCommands(program2, globalOpts) {
3929
4104
  let updated = 0;
3930
4105
  let failed = 0;
3931
4106
  for (const file of files) {
3932
- const filePath = resolve4(dirPath, file);
4107
+ const filePath = resolve5(dirPath, file);
3933
4108
  const fileData = await parseInputData({ file: filePath });
3934
4109
  const argName = entry.inputArgName ?? "input";
3935
4110
  const fileVars = { ...variables, [argName]: fileData };
@@ -3966,19 +4141,19 @@ function registerDynamicCommands(program2, globalOpts) {
3966
4141
  } catch (updateErr) {
3967
4142
  failed++;
3968
4143
  const msg2 = updateErr instanceof Error ? updateErr.message : String(updateErr);
3969
- console.error(chalk7.red(`\u2717 ${label}:`), msg2);
4144
+ console.error(chalk9.red(`\u2717 ${label}:`), msg2);
3970
4145
  continue;
3971
4146
  }
3972
4147
  }
3973
4148
  failed++;
3974
4149
  const msg = err instanceof Error ? err.message : String(err);
3975
- console.error(chalk7.red(`\u2717 ${label}:`), msg);
4150
+ console.error(chalk9.red(`\u2717 ${label}:`), msg);
3976
4151
  }
3977
4152
  }
3978
4153
  if (!(opts.json || opts.jsonl || opts.quiet)) {
3979
4154
  console.log("");
3980
4155
  console.log(
3981
- chalk7.bold(
4156
+ chalk9.bold(
3982
4157
  `Done: ${created} created${updated ? `, ${updated} updated` : ""}${failed ? `, ${failed} failed` : ""}`
3983
4158
  )
3984
4159
  );
@@ -4154,7 +4329,7 @@ function registerDynamicCommands(program2, globalOpts) {
4154
4329
  }
4155
4330
  } else if (!(opts.json || opts.jsonl || opts.quiet)) {
4156
4331
  console.error(
4157
- chalk7.yellow(
4332
+ chalk9.yellow(
4158
4333
  "\u26A0 Could not auto-publish: no version found in response"
4159
4334
  )
4160
4335
  );
@@ -4178,7 +4353,7 @@ function autoColumns(items) {
4178
4353
  // src/cli.ts
4179
4354
  var __filename2 = fileURLToPath2(import.meta.url);
4180
4355
  var __dirname2 = dirname5(__filename2);
4181
- config({ path: resolve5(__dirname2, "../.env.local") });
4356
+ config({ path: resolve6(__dirname2, "../.env.local") });
4182
4357
  var require2 = createRequire(import.meta.url);
4183
4358
  var { version } = require2("../package.json");
4184
4359
  var program = new Command();
@@ -4200,7 +4375,9 @@ registerWhoamiCommand(program, getGlobalOpts);
4200
4375
  registerProfilesCommand(program, getGlobalOpts);
4201
4376
  registerMediaCommands(program, getGlobalOpts);
4202
4377
  registerSearchCommands(program, getGlobalOpts);
4203
- registerCreateExtensionCommand(program, getGlobalOpts);
4378
+ registerPushCommand(program, getGlobalOpts);
4379
+ registerRemoveCommand(program, getGlobalOpts);
4380
+ registerCreateConfigCommand(program, getGlobalOpts);
4204
4381
  registerInitCommands(program, getGlobalOpts);
4205
4382
  registerDynamicCommands(program, getGlobalOpts);
4206
4383
  program.parse();