@cxtms/cx-schema 1.7.11 → 1.7.13

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
@@ -119,6 +119,7 @@ ${chalk_1.default.bold.yellow('COMMANDS:')}
119
119
  ${chalk_1.default.green('publish')} Publish all modules and workflows to a CX server
120
120
  ${chalk_1.default.green('app')} Manage app manifests (install/upgrade from git, publish to git, list)
121
121
  ${chalk_1.default.green('query')} Run a GraphQL query against the CX server
122
+ ${chalk_1.default.green('gql')} Explore GraphQL schema (types, queries, mutations)
122
123
  ${chalk_1.default.green('schema')} Show JSON schema for a component or task
123
124
  ${chalk_1.default.green('example')} Show example YAML for a component or task
124
125
  ${chalk_1.default.green('list')} List available schemas (modules, workflows, tasks)
@@ -336,6 +337,20 @@ ${chalk_1.default.bold.yellow('QUERY COMMANDS:')}
336
337
  ${chalk_1.default.gray('# Pass variables as JSON')}
337
338
  ${chalk_1.default.cyan(`${PROGRAM_NAME} query my-query.graphql --vars '{"id": 42}'`)}
338
339
 
340
+ ${chalk_1.default.bold.yellow('GRAPHQL SCHEMA EXPLORATION:')}
341
+ ${chalk_1.default.gray('# List all queries, mutations, and types')}
342
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} gql queries`)}
343
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} gql mutations`)}
344
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} gql types`)}
345
+
346
+ ${chalk_1.default.gray('# Filter by name')}
347
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} gql types --filter audit`)}
348
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} gql queries --filter order`)}
349
+
350
+ ${chalk_1.default.gray('# Inspect a specific type')}
351
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} gql type OrderGqlDto`)}
352
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} gql type AuditChangeEntry`)}
353
+
339
354
  ${chalk_1.default.bold.yellow('VALIDATION TYPES:')}
340
355
  ${chalk_1.default.bold('module')} - CargoXplorer UI module definitions (components, routes, entities)
341
356
  ${chalk_1.default.bold('workflow')} - CargoXplorer workflow definitions (activities, tasks, triggers)
@@ -2282,10 +2297,64 @@ async function runWorkflowUndeploy(uuid, orgOverride) {
2282
2297
  });
2283
2298
  console.log(chalk_1.default.green(` ✓ Deleted: ${uuid}\n`));
2284
2299
  }
2285
- async function runWorkflowExecute(workflowIdOrFile, orgOverride, variables) {
2300
+ async function uploadFileToServer(domain, token, orgId, localPath) {
2301
+ const fileName = path.basename(localPath);
2302
+ const ext = path.extname(localPath).toLowerCase();
2303
+ const contentTypeMap = {
2304
+ '.csv': 'text/csv', '.json': 'application/json', '.xml': 'application/xml',
2305
+ '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
2306
+ '.xls': 'application/vnd.ms-excel', '.pdf': 'application/pdf',
2307
+ '.txt': 'text/plain', '.zip': 'application/zip',
2308
+ };
2309
+ const contentType = contentTypeMap[ext] || 'application/octet-stream';
2310
+ // Step 1: Get presigned upload URL
2311
+ const data = await graphqlRequest(domain, token, `
2312
+ query ($organizationId: Int!, $fileName: String!, $contentType: String!) {
2313
+ uploadUrl(organizationId: $organizationId, fileName: $fileName, contentType: $contentType) {
2314
+ presignedUrl
2315
+ fileUrl
2316
+ }
2317
+ }
2318
+ `, { organizationId: orgId, fileName, contentType });
2319
+ const presignedUrl = data?.uploadUrl?.presignedUrl;
2320
+ const fileUrl = data?.uploadUrl?.fileUrl;
2321
+ if (!presignedUrl || !fileUrl) {
2322
+ throw new Error('Failed to get upload URL from server');
2323
+ }
2324
+ // Step 2: PUT file content to presigned URL
2325
+ const fileContent = fs.readFileSync(localPath);
2326
+ const url = new URL(presignedUrl);
2327
+ const httpModule = url.protocol === 'https:' ? https : http;
2328
+ await new Promise((resolve, reject) => {
2329
+ const req = httpModule.request(url, {
2330
+ method: 'PUT',
2331
+ headers: {
2332
+ 'Content-Type': contentType,
2333
+ 'Content-Length': fileContent.length,
2334
+ 'x-ms-blob-type': 'BlockBlob',
2335
+ },
2336
+ }, (res) => {
2337
+ let body = '';
2338
+ res.on('data', (chunk) => body += chunk);
2339
+ res.on('end', () => {
2340
+ if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
2341
+ resolve();
2342
+ }
2343
+ else {
2344
+ reject(new Error(`File upload failed (${res.statusCode}): ${body}`));
2345
+ }
2346
+ });
2347
+ });
2348
+ req.on('error', reject);
2349
+ req.write(fileContent);
2350
+ req.end();
2351
+ });
2352
+ return fileUrl;
2353
+ }
2354
+ async function runWorkflowExecute(workflowIdOrFile, orgOverride, variables, fileArgs) {
2286
2355
  if (!workflowIdOrFile) {
2287
2356
  console.error(chalk_1.default.red('Error: Workflow ID or YAML file required'));
2288
- console.error(chalk_1.default.gray(`Usage: ${PROGRAM_NAME} workflow execute <workflowId|file.yaml> [--org <id>] [--vars '{"key":"value"}']`));
2357
+ console.error(chalk_1.default.gray(`Usage: ${PROGRAM_NAME} workflow execute <workflowId|file.yaml> [--org <id>] [--vars '{"key":"value"}'] [--file varName=path]`));
2289
2358
  process.exit(2);
2290
2359
  }
2291
2360
  const session = resolveSession();
@@ -2318,6 +2387,28 @@ async function runWorkflowExecute(workflowIdOrFile, orgOverride, variables) {
2318
2387
  process.exit(2);
2319
2388
  }
2320
2389
  }
2390
+ // Process --file args: upload files and set URLs as variables
2391
+ if (fileArgs && fileArgs.length > 0) {
2392
+ if (!vars)
2393
+ vars = {};
2394
+ for (const fileArg of fileArgs) {
2395
+ const eqIdx = fileArg.indexOf('=');
2396
+ if (eqIdx < 1) {
2397
+ console.error(chalk_1.default.red(`Error: --file must be in format varName=path (got: ${fileArg})`));
2398
+ process.exit(2);
2399
+ }
2400
+ const varName = fileArg.substring(0, eqIdx);
2401
+ const filePath = fileArg.substring(eqIdx + 1);
2402
+ if (!fs.existsSync(filePath)) {
2403
+ console.error(chalk_1.default.red(`Error: File not found: ${filePath}`));
2404
+ process.exit(2);
2405
+ }
2406
+ console.log(chalk_1.default.gray(` Uploading ${path.basename(filePath)}...`));
2407
+ const fileUrl = await uploadFileToServer(domain, token, orgId, filePath);
2408
+ vars[varName] = fileUrl;
2409
+ console.log(chalk_1.default.gray(` → ${varName} = ${fileUrl}`));
2410
+ }
2411
+ }
2321
2412
  console.log(chalk_1.default.bold.cyan('\n Workflow Execute\n'));
2322
2413
  console.log(chalk_1.default.gray(` Server: ${new URL(domain).hostname}`));
2323
2414
  console.log(chalk_1.default.gray(` Org: ${orgId}`));
@@ -3112,6 +3203,169 @@ async function runQuery(queryArg, variables) {
3112
3203
  console.log(JSON.stringify(data, null, 2));
3113
3204
  }
3114
3205
  // ============================================================================
3206
+ // GQL Schema Exploration Command
3207
+ // ============================================================================
3208
+ async function runGql(sub, filter) {
3209
+ if (!sub) {
3210
+ console.error(chalk_1.default.red('Error: subcommand required'));
3211
+ console.error(chalk_1.default.gray(`Usage: ${PROGRAM_NAME} gql <queries|mutations|types|type> [name] [--filter <text>]`));
3212
+ process.exit(2);
3213
+ }
3214
+ const session = resolveSession();
3215
+ if (sub === 'type') {
3216
+ if (!filter) {
3217
+ console.error(chalk_1.default.red('Error: type name required'));
3218
+ console.error(chalk_1.default.gray(`Usage: ${PROGRAM_NAME} gql type <TypeName>`));
3219
+ process.exit(2);
3220
+ }
3221
+ await runGqlType(session, filter);
3222
+ }
3223
+ else if (sub === 'queries') {
3224
+ await runGqlRootFields(session, 'queryType', filter);
3225
+ }
3226
+ else if (sub === 'mutations') {
3227
+ await runGqlRootFields(session, 'mutationType', filter);
3228
+ }
3229
+ else if (sub === 'types') {
3230
+ await runGqlTypes(session, filter);
3231
+ }
3232
+ else {
3233
+ console.error(chalk_1.default.red(`Unknown gql subcommand: ${sub}`));
3234
+ console.error(chalk_1.default.gray(`Usage: ${PROGRAM_NAME} gql <queries|mutations|types|type> [--filter <text>]`));
3235
+ process.exit(2);
3236
+ }
3237
+ }
3238
+ function formatGqlType(t) {
3239
+ if (!t)
3240
+ return 'unknown';
3241
+ if (t.kind === 'NON_NULL')
3242
+ return `${formatGqlType(t.ofType)}!`;
3243
+ if (t.kind === 'LIST')
3244
+ return `[${formatGqlType(t.ofType)}]`;
3245
+ return t.name || 'unknown';
3246
+ }
3247
+ async function runGqlType(session, typeName) {
3248
+ const query = `{
3249
+ __type(name: "${typeName}") {
3250
+ name kind description
3251
+ fields { name description type { name kind ofType { name kind ofType { name kind ofType { name kind } } } } args { name type { name kind ofType { name kind ofType { name kind } } } defaultValue } }
3252
+ inputFields { name type { name kind ofType { name kind ofType { name kind } } } defaultValue }
3253
+ enumValues { name description }
3254
+ }
3255
+ }`;
3256
+ const data = await graphqlRequest(session.domain, session.access_token, query, {});
3257
+ const type = data.__type;
3258
+ if (!type) {
3259
+ console.error(chalk_1.default.red(`Type "${typeName}" not found`));
3260
+ process.exit(1);
3261
+ }
3262
+ console.log(chalk_1.default.bold.cyan(`${type.name}`) + chalk_1.default.gray(` (${type.kind})`));
3263
+ if (type.description)
3264
+ console.log(chalk_1.default.gray(type.description));
3265
+ console.log('');
3266
+ if (type.fields && type.fields.length > 0) {
3267
+ console.log(chalk_1.default.bold.yellow('Fields:'));
3268
+ for (const f of type.fields) {
3269
+ const typeStr = formatGqlType(f.type);
3270
+ let line = ` ${chalk_1.default.green(f.name)}: ${chalk_1.default.cyan(typeStr)}`;
3271
+ if (f.args && f.args.length > 0) {
3272
+ const argsStr = f.args.map((a) => {
3273
+ const argType = formatGqlType(a.type);
3274
+ return a.defaultValue ? `${a.name}: ${argType} = ${a.defaultValue}` : `${a.name}: ${argType}`;
3275
+ }).join(', ');
3276
+ line += chalk_1.default.gray(` (${argsStr})`);
3277
+ }
3278
+ if (f.description)
3279
+ line += chalk_1.default.gray(` — ${f.description}`);
3280
+ console.log(line);
3281
+ }
3282
+ }
3283
+ if (type.inputFields && type.inputFields.length > 0) {
3284
+ console.log(chalk_1.default.bold.yellow('Input Fields:'));
3285
+ for (const f of type.inputFields) {
3286
+ const typeStr = formatGqlType(f.type);
3287
+ let line = ` ${chalk_1.default.green(f.name)}: ${chalk_1.default.cyan(typeStr)}`;
3288
+ if (f.defaultValue)
3289
+ line += chalk_1.default.gray(` = ${f.defaultValue}`);
3290
+ console.log(line);
3291
+ }
3292
+ }
3293
+ if (type.enumValues && type.enumValues.length > 0) {
3294
+ console.log(chalk_1.default.bold.yellow('Enum Values:'));
3295
+ for (const v of type.enumValues) {
3296
+ let line = ` ${chalk_1.default.green(v.name)}`;
3297
+ if (v.description)
3298
+ line += chalk_1.default.gray(` — ${v.description}`);
3299
+ console.log(line);
3300
+ }
3301
+ }
3302
+ }
3303
+ async function runGqlRootFields(session, rootType, filter) {
3304
+ const query = `{
3305
+ __schema {
3306
+ ${rootType} {
3307
+ fields { name description args { name type { name kind ofType { name kind ofType { name kind } } } defaultValue } type { name kind ofType { name kind ofType { name kind } } } }
3308
+ }
3309
+ }
3310
+ }`;
3311
+ const data = await graphqlRequest(session.domain, session.access_token, query, {});
3312
+ const fields = data.__schema?.[rootType]?.fields || [];
3313
+ const filtered = filter
3314
+ ? fields.filter((f) => f.name.toLowerCase().includes(filter.toLowerCase()))
3315
+ : fields;
3316
+ const label = rootType === 'queryType' ? 'Queries' : 'Mutations';
3317
+ console.log(chalk_1.default.bold.yellow(`${label}${filter ? ` (filter: "${filter}")` : ''}:`));
3318
+ console.log('');
3319
+ for (const f of filtered) {
3320
+ const returnType = formatGqlType(f.type);
3321
+ console.log(` ${chalk_1.default.green(f.name)}: ${chalk_1.default.cyan(returnType)}`);
3322
+ if (f.description)
3323
+ console.log(` ${chalk_1.default.gray(f.description)}`);
3324
+ if (f.args && f.args.length > 0) {
3325
+ for (const a of f.args) {
3326
+ const argType = formatGqlType(a.type);
3327
+ const def = a.defaultValue ? chalk_1.default.gray(` = ${a.defaultValue}`) : '';
3328
+ console.log(` ${chalk_1.default.white(a.name)}: ${chalk_1.default.cyan(argType)}${def}`);
3329
+ }
3330
+ }
3331
+ console.log('');
3332
+ }
3333
+ console.log(chalk_1.default.gray(`${filtered.length} ${label.toLowerCase()} found`));
3334
+ }
3335
+ async function runGqlTypes(session, filter) {
3336
+ const query = `{
3337
+ __schema {
3338
+ types { name kind description }
3339
+ }
3340
+ }`;
3341
+ const data = await graphqlRequest(session.domain, session.access_token, query, {});
3342
+ const types = (data.__schema?.types || [])
3343
+ .filter((t) => !t.name.startsWith('__'))
3344
+ .filter((t) => !filter || t.name.toLowerCase().includes(filter.toLowerCase()));
3345
+ const grouped = {};
3346
+ for (const t of types) {
3347
+ const kind = t.kind || 'OTHER';
3348
+ if (!grouped[kind])
3349
+ grouped[kind] = [];
3350
+ grouped[kind].push(t);
3351
+ }
3352
+ const kindOrder = ['OBJECT', 'INPUT_OBJECT', 'ENUM', 'INTERFACE', 'UNION', 'SCALAR'];
3353
+ for (const kind of kindOrder) {
3354
+ const items = grouped[kind];
3355
+ if (!items || items.length === 0)
3356
+ continue;
3357
+ console.log(chalk_1.default.bold.yellow(`${kind} (${items.length}):`));
3358
+ for (const t of items.sort((a, b) => a.name.localeCompare(b.name))) {
3359
+ let line = ` ${chalk_1.default.green(t.name)}`;
3360
+ if (t.description)
3361
+ line += chalk_1.default.gray(` — ${t.description}`);
3362
+ console.log(line);
3363
+ }
3364
+ console.log('');
3365
+ }
3366
+ console.log(chalk_1.default.gray(`${types.length} types found${filter ? ` matching "${filter}"` : ''}`));
3367
+ }
3368
+ // ============================================================================
3115
3369
  // Extract Command
3116
3370
  // ============================================================================
3117
3371
  function runExtract(sourceFile, componentName, targetFile, copy) {
@@ -3322,7 +3576,7 @@ function parseArgs(args) {
3322
3576
  reportFormat: 'json'
3323
3577
  };
3324
3578
  // Check for commands
3325
- const commands = ['validate', 'schema', 'example', 'list', 'help', 'version', 'report', 'init', 'create', 'extract', 'sync-schemas', 'install-skills', 'update', 'setup-claude', 'login', 'logout', 'pat', 'appmodule', 'orgs', 'workflow', 'publish', 'query', 'app'];
3579
+ const commands = ['validate', 'schema', 'example', 'list', 'help', 'version', 'report', 'init', 'create', 'extract', 'sync-schemas', 'install-skills', 'update', 'setup-claude', 'login', 'logout', 'pat', 'appmodule', 'orgs', 'workflow', 'publish', 'query', 'gql', 'app'];
3326
3580
  if (args.length > 0 && commands.includes(args[0])) {
3327
3581
  command = args[0];
3328
3582
  args = args.slice(1);
@@ -3428,6 +3682,14 @@ function parseArgs(args) {
3428
3682
  else if (arg === '--branch' || arg === '-b') {
3429
3683
  options.branch = args[++i];
3430
3684
  }
3685
+ else if (arg === '--file') {
3686
+ if (!options.file)
3687
+ options.file = [];
3688
+ options.file.push(args[++i]);
3689
+ }
3690
+ else if (arg === '--filter') {
3691
+ options.filter = args[++i];
3692
+ }
3431
3693
  else if (arg === '--force') {
3432
3694
  options.force = true;
3433
3695
  }
@@ -4315,7 +4577,7 @@ async function main() {
4315
4577
  await runWorkflowUndeploy(files[1], options.orgId);
4316
4578
  }
4317
4579
  else if (sub === 'execute') {
4318
- await runWorkflowExecute(files[1], options.orgId, options.vars);
4580
+ await runWorkflowExecute(files[1], options.orgId, options.vars, options.file);
4319
4581
  }
4320
4582
  else if (sub === 'logs') {
4321
4583
  await runWorkflowLogs(files[1], options.orgId, options.from, options.to);
@@ -4354,6 +4616,14 @@ async function main() {
4354
4616
  }
4355
4617
  process.exit(0);
4356
4618
  }
4619
+ // Handle gql command (no schemas needed)
4620
+ if (command === 'gql') {
4621
+ const sub = files[0];
4622
+ // For 'gql type <name>', the type name is in files[1] — use it as filter
4623
+ const filterArg = sub === 'type' ? (files[1] || options.filter) : options.filter;
4624
+ await runGql(sub, filterArg);
4625
+ process.exit(0);
4626
+ }
4357
4627
  // Handle query command (no schemas needed)
4358
4628
  if (command === 'query') {
4359
4629
  await runQuery(files[0], options.vars);