@cxtms/cx-schema 1.7.6 → 1.7.8

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.
@@ -61,7 +61,7 @@ npx cxtms orgs use <orgId>
61
61
  npx cxtms orgs use
62
62
  ```
63
63
 
64
- The active org is cached in the session file and used by all server commands. Override with `--org <id>`.
64
+ The active org is cached in the session file and used by all server commands. **Always pass `--org <id>` on deploy/undeploy/publish/execute/logs commands** to avoid the interactive org picker blocking automation.
65
65
 
66
66
  ## Session Resolution
67
67
 
@@ -85,3 +85,39 @@ npx cxtms publish --org 42
85
85
  ```
86
86
 
87
87
  Validates all YAML files first, then pushes modules and workflows to the server. Skips files with validation errors and reports results.
88
+
89
+ ## App Manifest Management
90
+
91
+ Server-side app manifest operations — install from git, publish changes to git, and list installed apps.
92
+
93
+ ```bash
94
+ # Install/refresh app from its git repository into the CX server
95
+ npx cxtms app install
96
+
97
+ # Force reinstall even if same version is already installed
98
+ npx cxtms app install --force
99
+
100
+ # Install from a specific branch
101
+ npx cxtms app install --branch develop
102
+
103
+ # Install but skip modules that have unpublished local changes
104
+ npx cxtms app install --skip-changed
105
+
106
+ # Publish server changes to git (creates a PR)
107
+ npx cxtms app publish
108
+
109
+ # Publish with a custom commit message
110
+ npx cxtms app publish --message "Add new shipping module"
111
+
112
+ # Force publish all modules and workflows (not just changed ones)
113
+ npx cxtms app publish --force
114
+
115
+ # List installed app manifests on the server
116
+ npx cxtms app list
117
+ ```
118
+
119
+ **`app install`** reads `repository` and `branch` from `app.yaml`, downloads the repo on the server side, and installs/updates all modules and workflows. Use `--force` to reinstall even if the version hasn't changed. Use `--skip-changed` to preserve modules with unpublished changes.
120
+
121
+ **`app publish`** takes the current server state and publishes it to git by creating a PR. The server increments the version, creates a publish branch, commits all module/workflow YAML files, and opens a pull request to the target branch.
122
+
123
+ **`app list`** shows all installed app manifests with their version, status flags (disabled, unpublished changes, update available), and repository info.
@@ -18,9 +18,9 @@ You are a CargoXplorer module YAML builder. You generate schema-valid YAML for C
18
18
  - **List schemas**: `npx cxtms list`
19
19
  - **Extract**: `npx cxtms extract <source> <component> --to <target>` — move components between modules
20
20
  - **Feature folder**: `npx cxtms create module <name> --template <template> --feature <feature-name>`
21
- - **Deploy to server**: `npx cxtms appmodule deploy <file.yaml>` — creates or updates module on the CX server
22
- - **Undeploy from server**: `npx cxtms appmodule undeploy <appModuleId>` — removes a module by UUID
23
- - **Publish all**: `npx cxtms publish [--feature <name>]` — push all modules and workflows to the server
21
+ - **Deploy to server**: `npx cxtms appmodule deploy <file.yaml> --org <id>` — creates or updates module on the CX server
22
+ - **Undeploy from server**: `npx cxtms appmodule undeploy <appModuleId> --org <id>` — removes a module by UUID
23
+ - **Publish all**: `npx cxtms publish [--feature <name>] --org <id>` — push all modules and workflows to the server
24
24
 
25
25
  ## Generation Workflow
26
26
 
@@ -16,12 +16,12 @@ You are a CargoXplorer workflow YAML builder. You generate schema-valid YAML for
16
16
  - **Examples**: `npx cxtms example <task>` — show example YAML for a task
17
17
  - **List schemas**: `npx cxtms list --type workflow` — shows all available task schemas in the Tasks section
18
18
  - **Feature folder**: `npx cxtms create workflow <name> --template <template> --feature <feature-name>`
19
- - **Deploy to server**: `npx cxtms workflow deploy <file.yaml>` — creates or updates workflow on the CX server
20
- - **Undeploy from server**: `npx cxtms workflow undeploy <workflowId>` — removes a workflow by UUID
21
- - **Execute**: `npx cxtms workflow execute <workflowId|file.yaml> [--vars '<json>']` — trigger a workflow execution
22
- - **List logs**: `npx cxtms workflow logs <workflowId|file.yaml> [--from YYYY-MM-DD] [--to YYYY-MM-DD]` — list executions with log availability
23
- - **Download log**: `npx cxtms workflow log <executionId> [--json] [--console] [--output <file>]` — download execution log
24
- - **Publish all**: `npx cxtms publish [--feature <name>]` — push all modules and workflows to the server
19
+ - **Deploy to server**: `npx cxtms workflow deploy <file.yaml> --org <id>` — creates or updates workflow on the CX server
20
+ - **Undeploy from server**: `npx cxtms workflow undeploy <workflowId> --org <id>` — removes a workflow by UUID
21
+ - **Execute**: `npx cxtms workflow execute <workflowId|file.yaml> --org <id> [--vars '<json>']` — trigger a workflow execution
22
+ - **List logs**: `npx cxtms workflow logs <workflowId|file.yaml> --org <id> [--from YYYY-MM-DD] [--to YYYY-MM-DD]` — list executions with log availability
23
+ - **Download log**: `npx cxtms workflow log <executionId> --org <id> [--json] [--console] [--output <file>]` — download execution log
24
+ - **Publish all**: `npx cxtms publish [--feature <name>] --org <id>` — push all modules and workflows to the server
25
25
 
26
26
  ## Generation Workflow
27
27
 
package/dist/cli.js CHANGED
@@ -117,6 +117,7 @@ ${chalk_1.default.bold.yellow('COMMANDS:')}
117
117
  ${chalk_1.default.green('appmodule')} Manage app modules on a CX server (deploy, undeploy)
118
118
  ${chalk_1.default.green('workflow')} Manage workflows on a CX server (deploy, undeploy, execute, logs, log)
119
119
  ${chalk_1.default.green('publish')} Publish all modules and workflows to a CX server
120
+ ${chalk_1.default.green('app')} Manage app manifests (install/upgrade from git, publish to git, list)
120
121
  ${chalk_1.default.green('query')} Run a GraphQL query against the CX server
121
122
  ${chalk_1.default.green('schema')} Show JSON schema for a component or task
122
123
  ${chalk_1.default.green('example')} Show example YAML for a component or task
@@ -147,6 +148,10 @@ ${chalk_1.default.bold.yellow('OPTIONS:')}
147
148
  ${chalk_1.default.green('--output <file>')} Save workflow log to file (or -o)
148
149
  ${chalk_1.default.green('--console')} Print workflow log to stdout
149
150
  ${chalk_1.default.green('--json')} Download JSON log instead of text
151
+ ${chalk_1.default.green('-m, --message <msg>')} Commit message for app publish
152
+ ${chalk_1.default.green('-b, --branch <branch>')} Branch override for app install/publish
153
+ ${chalk_1.default.green('--force')} Force install (even if same version) or publish all
154
+ ${chalk_1.default.green('--skip-changed')} Skip modules with unpublished changes during install
150
155
 
151
156
  ${chalk_1.default.bold.yellow('VALIDATION EXAMPLES:')}
152
157
  ${chalk_1.default.gray('# Validate a module YAML file')}
@@ -288,6 +293,35 @@ ${chalk_1.default.bold.yellow('PUBLISH COMMANDS:')}
288
293
  ${chalk_1.default.gray('# Publish with explicit org ID')}
289
294
  ${chalk_1.default.cyan(`${PROGRAM_NAME} publish --org 42`)}
290
295
 
296
+ ${chalk_1.default.bold.yellow('APP COMMANDS:')}
297
+ ${chalk_1.default.gray('# Install/refresh app from git repository into the CX server')}
298
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} app install`)}
299
+
300
+ ${chalk_1.default.gray('# Force reinstall even if same version')}
301
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} app install --force`)}
302
+
303
+ ${chalk_1.default.gray('# Install from a specific branch')}
304
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} app install --branch develop`)}
305
+
306
+ ${chalk_1.default.gray('# Install but skip modules that have local changes')}
307
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} app install --skip-changed`)}
308
+
309
+ ${chalk_1.default.gray('# Upgrade app from git (alias for install)')}
310
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} app upgrade`)}
311
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} app upgrade --force`)}
312
+
313
+ ${chalk_1.default.gray('# Publish server changes to git (creates a PR)')}
314
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} app publish`)}
315
+
316
+ ${chalk_1.default.gray('# Publish with a custom commit message')}
317
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} app publish --message "Add new shipping module"`)}
318
+
319
+ ${chalk_1.default.gray('# Force publish all modules and workflows')}
320
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} app publish --force`)}
321
+
322
+ ${chalk_1.default.gray('# List installed app manifests on the server')}
323
+ ${chalk_1.default.cyan(`${PROGRAM_NAME} app list`)}
324
+
291
325
  ${chalk_1.default.bold.yellow('QUERY COMMANDS:')}
292
326
  ${chalk_1.default.gray('# Run an inline GraphQL query')}
293
327
  ${chalk_1.default.cyan(`${PROGRAM_NAME} query '{ organizations(take: 5) { items { organizationId companyName } } }'`)}
@@ -840,6 +874,23 @@ function applyFieldsToForm(form, fields) {
840
874
  }
841
875
  }
842
876
  }
877
+ function applyFieldsToConfiguration(layout, fields) {
878
+ // Configuration fields are stored under customValues, so prefix all field names
879
+ const configFields = fields.map(f => ({
880
+ component: 'field',
881
+ name: `customValues.${f.name}`,
882
+ props: {
883
+ type: f.type,
884
+ label: { 'en-US': f.label || fieldNameToLabel(f.name) },
885
+ ...(f.required ? { required: true } : {})
886
+ }
887
+ }));
888
+ if (!layout.children)
889
+ layout.children = [];
890
+ layout.children.push(...configFields);
891
+ // Update defaultValue in configurations if present
892
+ // (handled separately since configurations is a top-level key)
893
+ }
843
894
  function findDataGridComponents(obj) {
844
895
  const grids = [];
845
896
  if (!obj || typeof obj !== 'object')
@@ -967,9 +1018,16 @@ function applyCreateOptions(content, optionsArg) {
967
1018
  if (!doc)
968
1019
  throw new Error('Failed to parse template YAML for --options processing');
969
1020
  let applied = false;
1021
+ const isConfiguration = Array.isArray(doc.configurations);
970
1022
  if (doc.components && Array.isArray(doc.components)) {
971
1023
  for (const comp of doc.components) {
972
- // Apply to form components (configuration template)
1024
+ // Apply to configuration templates (fields go directly into layout children)
1025
+ if (isConfiguration && comp.layout) {
1026
+ applyFieldsToConfiguration(comp.layout, fields);
1027
+ applied = true;
1028
+ continue;
1029
+ }
1030
+ // Apply to form components
973
1031
  const forms = findFormComponents(comp);
974
1032
  for (const form of forms) {
975
1033
  applyFieldsToForm(form, fields);
@@ -997,6 +1055,18 @@ function applyCreateOptions(content, optionsArg) {
997
1055
  applyFieldsToEntities(doc, fields, opts.entityName);
998
1056
  applied = true;
999
1057
  }
1058
+ // Apply defaults to configuration defaultValue
1059
+ if (isConfiguration && doc.configurations) {
1060
+ for (const config of doc.configurations) {
1061
+ if (!config.defaultValue)
1062
+ config.defaultValue = {};
1063
+ for (const f of fields) {
1064
+ if (f.default !== undefined) {
1065
+ config.defaultValue[f.name] = f.default;
1066
+ }
1067
+ }
1068
+ }
1069
+ }
1000
1070
  if (!applied) {
1001
1071
  console.warn(chalk_1.default.yellow('Warning: --options provided but no form or dataGrid component found in template'));
1002
1072
  return content;
@@ -2763,6 +2833,192 @@ async function runPublish(featureDir, orgOverride) {
2763
2833
  }
2764
2834
  }
2765
2835
  // ============================================================================
2836
+ // App Manifest Commands (install from git, publish to git, list)
2837
+ // ============================================================================
2838
+ function readAppYaml() {
2839
+ const appYamlPath = path.join(process.cwd(), 'app.yaml');
2840
+ if (!fs.existsSync(appYamlPath)) {
2841
+ console.error(chalk_1.default.red('Error: app.yaml not found in current directory'));
2842
+ process.exit(2);
2843
+ }
2844
+ return yaml_1.default.parse(fs.readFileSync(appYamlPath, 'utf-8'));
2845
+ }
2846
+ async function runAppInstall(orgOverride, branch, force, skipChanged) {
2847
+ const session = resolveSession();
2848
+ const domain = session.domain;
2849
+ const token = session.access_token;
2850
+ const orgId = await resolveOrgId(domain, token, orgOverride);
2851
+ const appYaml = readAppYaml();
2852
+ const repository = appYaml.repository;
2853
+ if (!repository) {
2854
+ console.error(chalk_1.default.red('Error: app.yaml must have a `repository` field'));
2855
+ process.exit(2);
2856
+ }
2857
+ const repositoryBranch = branch || appYaml.branch || 'main';
2858
+ console.log(chalk_1.default.bold.cyan('\n App Install\n'));
2859
+ console.log(chalk_1.default.gray(` Server: ${new URL(domain).hostname}`));
2860
+ console.log(chalk_1.default.gray(` Org: ${orgId}`));
2861
+ console.log(chalk_1.default.gray(` Repository: ${repository}`));
2862
+ console.log(chalk_1.default.gray(` Branch: ${repositoryBranch}`));
2863
+ if (force)
2864
+ console.log(chalk_1.default.gray(` Force: yes`));
2865
+ if (skipChanged)
2866
+ console.log(chalk_1.default.gray(` Skip changed: yes`));
2867
+ console.log('');
2868
+ try {
2869
+ const data = await graphqlRequest(domain, token, `
2870
+ mutation ($input: InstallAppManifestInput!) {
2871
+ installAppManifest(input: $input) {
2872
+ appManifest {
2873
+ appManifestId
2874
+ name
2875
+ currentVersion
2876
+ isEnabled
2877
+ hasUnpublishedChanges
2878
+ isUpdateAvailable
2879
+ }
2880
+ }
2881
+ }
2882
+ `, {
2883
+ input: {
2884
+ organizationId: orgId,
2885
+ values: {
2886
+ repository,
2887
+ repositoryBranch,
2888
+ force: force || false,
2889
+ skipModulesWithChanges: skipChanged || false,
2890
+ }
2891
+ }
2892
+ });
2893
+ const manifest = data?.installAppManifest?.appManifest;
2894
+ if (manifest) {
2895
+ console.log(chalk_1.default.green(` ✓ Installed ${manifest.name} v${manifest.currentVersion}`));
2896
+ if (manifest.hasUnpublishedChanges) {
2897
+ console.log(chalk_1.default.yellow(` Has unpublished changes`));
2898
+ }
2899
+ }
2900
+ else {
2901
+ console.log(chalk_1.default.green(' ✓ Install completed'));
2902
+ }
2903
+ }
2904
+ catch (e) {
2905
+ console.error(chalk_1.default.red(` ✗ Install failed: ${e.message}`));
2906
+ process.exit(1);
2907
+ }
2908
+ console.log('');
2909
+ }
2910
+ async function runAppPublish(orgOverride, message, branch, force) {
2911
+ const session = resolveSession();
2912
+ const domain = session.domain;
2913
+ const token = session.access_token;
2914
+ const orgId = await resolveOrgId(domain, token, orgOverride);
2915
+ const appYaml = readAppYaml();
2916
+ const appManifestId = appYaml.id;
2917
+ if (!appManifestId) {
2918
+ console.error(chalk_1.default.red('Error: app.yaml must have an `id` field'));
2919
+ process.exit(2);
2920
+ }
2921
+ console.log(chalk_1.default.bold.cyan('\n App Publish\n'));
2922
+ console.log(chalk_1.default.gray(` Server: ${new URL(domain).hostname}`));
2923
+ console.log(chalk_1.default.gray(` Org: ${orgId}`));
2924
+ console.log(chalk_1.default.gray(` App: ${appYaml.name || appManifestId}`));
2925
+ if (message)
2926
+ console.log(chalk_1.default.gray(` Message: ${message}`));
2927
+ if (branch)
2928
+ console.log(chalk_1.default.gray(` Branch: ${branch}`));
2929
+ if (force)
2930
+ console.log(chalk_1.default.gray(` Force: yes`));
2931
+ console.log('');
2932
+ try {
2933
+ const data = await graphqlRequest(domain, token, `
2934
+ mutation ($input: PublishAppManifestInput!) {
2935
+ publishAppManifest(input: $input) {
2936
+ appManifest {
2937
+ appManifestId
2938
+ name
2939
+ currentVersion
2940
+ hasUnpublishedChanges
2941
+ }
2942
+ }
2943
+ }
2944
+ `, {
2945
+ input: {
2946
+ organizationId: orgId,
2947
+ appManifestId,
2948
+ values: {
2949
+ message: message || undefined,
2950
+ branch: branch || undefined,
2951
+ force: force || false,
2952
+ }
2953
+ }
2954
+ });
2955
+ const manifest = data?.publishAppManifest?.appManifest;
2956
+ if (manifest) {
2957
+ console.log(chalk_1.default.green(` ✓ Published ${manifest.name} v${manifest.currentVersion}`));
2958
+ }
2959
+ else {
2960
+ console.log(chalk_1.default.green(' ✓ Publish completed'));
2961
+ }
2962
+ }
2963
+ catch (e) {
2964
+ console.error(chalk_1.default.red(` ✗ Publish failed: ${e.message}`));
2965
+ process.exit(1);
2966
+ }
2967
+ console.log('');
2968
+ }
2969
+ async function runAppList(orgOverride) {
2970
+ const session = resolveSession();
2971
+ const domain = session.domain;
2972
+ const token = session.access_token;
2973
+ const orgId = await resolveOrgId(domain, token, orgOverride);
2974
+ console.log(chalk_1.default.bold.cyan('\n App Manifests\n'));
2975
+ console.log(chalk_1.default.gray(` Server: ${new URL(domain).hostname}`));
2976
+ console.log(chalk_1.default.gray(` Org: ${orgId}\n`));
2977
+ try {
2978
+ const data = await graphqlRequest(domain, token, `
2979
+ query ($organizationId: Int!) {
2980
+ appManifests(organizationId: $organizationId) {
2981
+ items {
2982
+ appManifestId
2983
+ name
2984
+ currentVersion
2985
+ isEnabled
2986
+ hasUnpublishedChanges
2987
+ isUpdateAvailable
2988
+ repository
2989
+ repositoryBranch
2990
+ }
2991
+ }
2992
+ }
2993
+ `, { organizationId: orgId });
2994
+ const items = data?.appManifests?.items || [];
2995
+ if (items.length === 0) {
2996
+ console.log(chalk_1.default.gray(' No app manifests installed\n'));
2997
+ return;
2998
+ }
2999
+ for (const app of items) {
3000
+ const flags = [];
3001
+ if (!app.isEnabled)
3002
+ flags.push(chalk_1.default.red('disabled'));
3003
+ if (app.hasUnpublishedChanges)
3004
+ flags.push(chalk_1.default.yellow('unpublished'));
3005
+ if (app.isUpdateAvailable)
3006
+ flags.push(chalk_1.default.cyan('update available'));
3007
+ const flagStr = flags.length > 0 ? ` [${flags.join(', ')}]` : '';
3008
+ console.log(` ${chalk_1.default.bold(app.name)} ${chalk_1.default.gray(`v${app.currentVersion}`)}${flagStr}`);
3009
+ console.log(chalk_1.default.gray(` ID: ${app.appManifestId}`));
3010
+ if (app.repository) {
3011
+ console.log(chalk_1.default.gray(` Repo: ${app.repository} (${app.repositoryBranch || 'main'})`));
3012
+ }
3013
+ }
3014
+ console.log('');
3015
+ }
3016
+ catch (e) {
3017
+ console.error(chalk_1.default.red(` ✗ Failed to list apps: ${e.message}`));
3018
+ process.exit(1);
3019
+ }
3020
+ }
3021
+ // ============================================================================
2766
3022
  // Query Command
2767
3023
  // ============================================================================
2768
3024
  async function runQuery(queryArg, variables) {
@@ -3008,7 +3264,7 @@ function parseArgs(args) {
3008
3264
  reportFormat: 'json'
3009
3265
  };
3010
3266
  // Check for commands
3011
- 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'];
3267
+ 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'];
3012
3268
  if (args.length > 0 && commands.includes(args[0])) {
3013
3269
  command = args[0];
3014
3270
  args = args.slice(1);
@@ -3108,6 +3364,18 @@ function parseArgs(args) {
3108
3364
  else if (arg === '--console') {
3109
3365
  options.console = true;
3110
3366
  }
3367
+ else if (arg === '--message' || arg === '-m') {
3368
+ options.message = args[++i];
3369
+ }
3370
+ else if (arg === '--branch' || arg === '-b') {
3371
+ options.branch = args[++i];
3372
+ }
3373
+ else if (arg === '--force') {
3374
+ options.force = true;
3375
+ }
3376
+ else if (arg === '--skip-changed') {
3377
+ options.skipChanged = true;
3378
+ }
3111
3379
  else if (!arg.startsWith('-')) {
3112
3380
  files.push(arg);
3113
3381
  }
@@ -4009,6 +4277,25 @@ async function main() {
4009
4277
  await runPublish(files[0] || options.feature, options.orgId);
4010
4278
  process.exit(0);
4011
4279
  }
4280
+ // Handle app command (no schemas needed)
4281
+ if (command === 'app') {
4282
+ const sub = files[0];
4283
+ if (sub === 'install' || sub === 'upgrade') {
4284
+ await runAppInstall(options.orgId, options.branch, options.force, options.skipChanged);
4285
+ }
4286
+ else if (sub === 'publish') {
4287
+ await runAppPublish(options.orgId, options.message, options.branch, options.force);
4288
+ }
4289
+ else if (sub === 'list' || !sub) {
4290
+ await runAppList(options.orgId);
4291
+ }
4292
+ else {
4293
+ console.error(chalk_1.default.red(`Unknown app subcommand: ${sub}`));
4294
+ console.error(chalk_1.default.gray(`Usage: ${PROGRAM_NAME} app <install|upgrade|publish|list>`));
4295
+ process.exit(2);
4296
+ }
4297
+ process.exit(0);
4298
+ }
4012
4299
  // Handle query command (no schemas needed)
4013
4300
  if (command === 'query') {
4014
4301
  await runQuery(files[0], options.vars);