@tenderprompt/cli 0.1.34 → 0.1.35

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.
Files changed (2) hide show
  1. package/dist/index.js +95 -25
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -32,6 +32,8 @@ function readCliPackageVersion() {
32
32
  }
33
33
  const CLI_PACKAGE_VERSION = readCliPackageVersion();
34
34
  const GIT_COMMIT_SHA_PATTERN = /^[0-9a-f]{40}$/i;
35
+ const SHOPIFY_CLI_PACKAGE = '@shopify/cli@latest';
36
+ const SHOPIFY_CLI_BIN = 'shopify';
35
37
  const ANALYTICS_AUTHORING_PLAYBOOK_ID = 'analytics-event-authoring';
36
38
  const ANALYTICS_AUTHORING_COMMAND = 'tender app analytics authoring --json';
37
39
  const ANALYTICS_AUTHORING_PLAYBOOK_COMMAND = `tender playbooks get ${ANALYTICS_AUTHORING_PLAYBOOK_ID} --json`;
@@ -145,6 +147,23 @@ class TenderCliConnectorsApiError extends Error {
145
147
  this.example = example;
146
148
  }
147
149
  }
150
+ class TenderCliChildProcessError extends Error {
151
+ command;
152
+ args;
153
+ exitCode;
154
+ stdout;
155
+ stderr;
156
+ constructor(input) {
157
+ const detail = input.stderr.trim() || input.stdout.trim();
158
+ super([`${input.command} ${input.args.join(' ')} failed with exit code ${input.exitCode}.`, detail].filter(Boolean).join('\n'));
159
+ this.name = 'TenderCliChildProcessError';
160
+ this.command = input.command;
161
+ this.args = input.args;
162
+ this.exitCode = input.exitCode;
163
+ this.stdout = input.stdout;
164
+ this.stderr = input.stderr;
165
+ }
166
+ }
148
167
  const DEFAULT_PROFILE_NAME = 'default';
149
168
  const DEFAULT_BASE_URL = 'https://app.tenderprompt.com';
150
169
  const ARTIFACT_SOURCE_EXPORT_MAX_FILES = 1000;
@@ -592,7 +611,7 @@ function connectorsShopifySourceValidateHelp() {
592
611
  return `Usage: tender connectors shopify source validate <connector-id> [--dir <path>] [--client-id <shopify-client-id>] [--execute] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
593
612
 
594
613
  Without --execute, prints the exact Shopify CLI validation command. With
595
- --execute, runs npx --yes @shopify/cli@latest app config validate.
614
+ the --execute flag, runs npm exec --yes --package @shopify/cli@latest -- shopify app config validate with npm lifecycle environment variables scrubbed.
596
615
 
597
616
  Examples:
598
617
  tender connectors shopify source validate conn_123 --dir ./shopify-app --execute --json`;
@@ -601,7 +620,7 @@ function connectorsShopifySourceDeployHelp() {
601
620
  return `Usage: tender connectors shopify source deploy <connector-id> [--dir <path>] [--client-id <shopify-client-id>] [--version <tag>] [--message <text>] [--source-control-url <url>] [--allow-deletes] [--confirm deploy] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
602
621
 
603
622
  Without --confirm deploy, prints the exact Shopify CLI deployment command. With
604
- --confirm deploy, runs npx --yes @shopify/cli@latest app deploy. If
623
+ the --confirm deploy flag, runs npm exec --yes --package @shopify/cli@latest -- shopify app deploy with npm lifecycle environment variables scrubbed. If
605
624
  SHOPIFY_APP_AUTOMATION_TOKEN is not set locally, the CLI requests the
606
625
  connector's stored App Automation Token and injects it only into the spawned
607
626
  Shopify CLI child process.
@@ -12220,7 +12239,7 @@ spawned Shopify CLI child process.
12220
12239
  ## Validate
12221
12240
 
12222
12241
  \`\`\`sh
12223
- npx --yes @shopify/cli@latest app config validate --path . --json
12242
+ npm exec --yes --package @shopify/cli@latest -- shopify app config validate --path . --json
12224
12243
  \`\`\`
12225
12244
 
12226
12245
  ## Deploy
@@ -12482,8 +12501,6 @@ async function commitAndPushSourceFiles(input) {
12482
12501
  }
12483
12502
  function shopifyValidateArgs(input) {
12484
12503
  return [
12485
- '--yes',
12486
- '@shopify/cli@latest',
12487
12504
  'app',
12488
12505
  'config',
12489
12506
  'validate',
@@ -12495,8 +12512,6 @@ function shopifyValidateArgs(input) {
12495
12512
  }
12496
12513
  function shopifyDeployArgs(input) {
12497
12514
  return [
12498
- '--yes',
12499
- '@shopify/cli@latest',
12500
12515
  'app',
12501
12516
  'deploy',
12502
12517
  '--path',
@@ -12508,9 +12523,50 @@ function shopifyDeployArgs(input) {
12508
12523
  ...(input.sourceControlUrl ? ['--source-control-url', input.sourceControlUrl] : []),
12509
12524
  ];
12510
12525
  }
12526
+ function shopifyCliCommand(args) {
12527
+ return {
12528
+ command: 'npm',
12529
+ args: ['exec', '--yes', '--package', SHOPIFY_CLI_PACKAGE, '--', SHOPIFY_CLI_BIN, ...args],
12530
+ };
12531
+ }
12532
+ function shopifyCliCommandLine(args) {
12533
+ const invocation = shopifyCliCommand(args);
12534
+ return commandLine(invocation.command, invocation.args);
12535
+ }
12511
12536
  function commandLine(command, args) {
12512
12537
  return [command, ...args.map(shellQuote)].join(' ');
12513
12538
  }
12539
+ function shouldDropNestedNpmEnv(key) {
12540
+ const normalized = key.toLowerCase();
12541
+ return (normalized === 'init_cwd' ||
12542
+ normalized === 'npm_command' ||
12543
+ normalized === 'npm_config_argv' ||
12544
+ normalized === 'npm_config_call' ||
12545
+ normalized === 'npm_config_package' ||
12546
+ normalized === 'npm_execpath' ||
12547
+ normalized === 'npm_node_execpath' ||
12548
+ normalized.startsWith('npm_lifecycle_') ||
12549
+ normalized.startsWith('npm_package_'));
12550
+ }
12551
+ function sanitizeNestedNpmEnvironment(env) {
12552
+ const sanitized = {};
12553
+ for (const [key, value] of Object.entries(env)) {
12554
+ if (shouldDropNestedNpmEnv(key)) {
12555
+ continue;
12556
+ }
12557
+ sanitized[key] = value;
12558
+ }
12559
+ return sanitized;
12560
+ }
12561
+ function shopifyCliChildEnv(runtime, overrides = {}) {
12562
+ return {
12563
+ ...sanitizeNestedNpmEnvironment({
12564
+ ...process.env,
12565
+ ...(runtime.env ?? {}),
12566
+ }),
12567
+ ...overrides,
12568
+ };
12569
+ }
12514
12570
  function redactSensitiveText(value, secrets = []) {
12515
12571
  let redacted = value;
12516
12572
  for (const secret of secrets) {
@@ -12524,11 +12580,12 @@ function redactSensitiveText(value, secrets = []) {
12524
12580
  .replace(/\b(authorization\s*[:=]\s*(?:Bearer\s+)?)([^\s'"]+)/gi, '$1[REDACTED]')
12525
12581
  .replace(/\b(x-shopify-access-token\s*[:=]\s*)([^\s'"]+)/gi, '$1[REDACTED]');
12526
12582
  }
12527
- async function runNpxCommand(input) {
12583
+ async function runShopifyCliCommand(input) {
12528
12584
  const runner = input.runtime.commandRunner ?? defaultCommandRunner;
12529
- const result = await runner('npx', input.args, {
12585
+ const invocation = shopifyCliCommand(input.args);
12586
+ const result = await runner(invocation.command, invocation.args, {
12530
12587
  cwd: input.cwd,
12531
- ...(input.env ? { env: input.env } : {}),
12588
+ env: shopifyCliChildEnv(input.runtime, input.env),
12532
12589
  });
12533
12590
  const redactedResult = {
12534
12591
  ...result,
@@ -12536,12 +12593,13 @@ async function runNpxCommand(input) {
12536
12593
  stderr: redactSensitiveText(result.stderr, input.redactions),
12537
12594
  };
12538
12595
  if (redactedResult.exitCode !== 0) {
12539
- throw new Error([
12540
- `npx ${input.args.join(' ')} failed with exit code ${redactedResult.exitCode}.`,
12541
- redactedResult.stderr.trim() || redactedResult.stdout.trim(),
12542
- ]
12543
- .filter(Boolean)
12544
- .join('\n'));
12596
+ throw new TenderCliChildProcessError({
12597
+ command: invocation.command,
12598
+ args: invocation.args,
12599
+ exitCode: redactedResult.exitCode,
12600
+ stdout: redactedResult.stdout,
12601
+ stderr: redactedResult.stderr,
12602
+ });
12545
12603
  }
12546
12604
  return redactedResult;
12547
12605
  }
@@ -12570,10 +12628,8 @@ function localShopifyDeployToken(runtime) {
12570
12628
  const token = env.SHOPIFY_APP_AUTOMATION_TOKEN;
12571
12629
  return token?.trim() ? token.trim() : null;
12572
12630
  }
12573
- function childShopifyDeployEnv(runtime, token) {
12631
+ function childShopifyDeployEnv(_runtime, token) {
12574
12632
  return {
12575
- ...process.env,
12576
- ...(runtime.env ?? {}),
12577
12633
  SHOPIFY_APP_AUTOMATION_TOKEN: token,
12578
12634
  };
12579
12635
  }
@@ -12682,8 +12738,8 @@ async function runConnectorsShopifySourceInit(command, io, runtime) {
12682
12738
  sourceCheckout: checkout.sourceCheckout,
12683
12739
  commit: gitCommit,
12684
12740
  },
12685
- validateCommand: commandLine('npx', shopifyValidateArgs({ dir, clientId: command.clientId })),
12686
- deployCommand: commandLine('npx', shopifyDeployArgs({ dir, clientId: command.clientId, version: 'connector-source', message: null, sourceControlUrl: null, allowDeletes: false })),
12741
+ validateCommand: shopifyCliCommandLine(shopifyValidateArgs({ dir, clientId: command.clientId })),
12742
+ deployCommand: shopifyCliCommandLine(shopifyDeployArgs({ dir, clientId: command.clientId, version: 'connector-source', message: null, sourceControlUrl: null, allowDeletes: false })),
12687
12743
  };
12688
12744
  printConnectorsPayload(command, io, result, [
12689
12745
  `connector_id: ${command.connectorId}`,
@@ -12745,10 +12801,10 @@ async function runConnectorsShopifySourceValidate(command, io, runtime) {
12745
12801
  }
12746
12802
  }
12747
12803
  if (!command.execute) {
12748
- printConnectorsPayload(command, io, { ok: true, dryRun: true, command: command.command, connectorId: command.connectorId, sourceArtifactId, dir, shopifyCommand: commandLine('npx', args) }, [`connector_id: ${command.connectorId}`, `source_artifact_id: ${sourceArtifactId}`, `source_dir: ${dir}`, `shopify_command: ${commandLine('npx', args)}`]);
12804
+ printConnectorsPayload(command, io, { ok: true, dryRun: true, command: command.command, connectorId: command.connectorId, sourceArtifactId, dir, shopifyCommand: shopifyCliCommandLine(args) }, [`connector_id: ${command.connectorId}`, `source_artifact_id: ${sourceArtifactId}`, `source_dir: ${dir}`, `shopify_command: ${shopifyCliCommandLine(args)}`]);
12749
12805
  return 0;
12750
12806
  }
12751
- const result = await runNpxCommand({ args, cwd: dir, runtime });
12807
+ const result = await runShopifyCliCommand({ args, cwd: dir, runtime });
12752
12808
  printConnectorsPayload(command, io, { ok: true, command: command.command, connectorId: command.connectorId, sourceArtifactId, dir, exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr }, [`connector_id: ${command.connectorId}`, `source_artifact_id: ${sourceArtifactId}`, `source_dir: ${dir}`, 'validation: passed']);
12753
12809
  return 0;
12754
12810
  }
@@ -12769,7 +12825,7 @@ async function runConnectorsShopifySourceDeploy(command, io, runtime) {
12769
12825
  allowDeletes: command.allowDeletes,
12770
12826
  });
12771
12827
  if (command.confirm !== 'deploy') {
12772
- printConnectorsPayload(command, io, { ok: true, dryRun: true, command: command.command, connectorId: command.connectorId, sourceArtifactId, dir, shopifyCommand: commandLine('npx', args), confirmRequired: 'deploy' }, [`connector_id: ${command.connectorId}`, `source_artifact_id: ${sourceArtifactId}`, `source_dir: ${dir}`, `shopify_command: ${commandLine('npx', args)}`, 'confirm_required: --confirm deploy']);
12828
+ printConnectorsPayload(command, io, { ok: true, dryRun: true, command: command.command, connectorId: command.connectorId, sourceArtifactId, dir, shopifyCommand: shopifyCliCommandLine(args), confirmRequired: 'deploy' }, [`connector_id: ${command.connectorId}`, `source_artifact_id: ${sourceArtifactId}`, `source_dir: ${dir}`, `shopify_command: ${shopifyCliCommandLine(args)}`, 'confirm_required: --confirm deploy']);
12773
12829
  return 0;
12774
12830
  }
12775
12831
  const deployToken = await resolveShopifyDeployTokenEnvironment({
@@ -12779,7 +12835,7 @@ async function runConnectorsShopifySourceDeploy(command, io, runtime) {
12779
12835
  runtime,
12780
12836
  fetcher,
12781
12837
  });
12782
- const result = await runNpxCommand({ args, cwd: dir, runtime, env: deployToken.env, redactions: deployToken.redactions });
12838
+ const result = await runShopifyCliCommand({ args, cwd: dir, runtime, env: deployToken.env, redactions: deployToken.redactions });
12783
12839
  printConnectorsPayload(command, io, { ok: true, command: command.command, connectorId: command.connectorId, sourceArtifactId, dir, exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr }, [`connector_id: ${command.connectorId}`, `source_artifact_id: ${sourceArtifactId}`, `source_dir: ${dir}`, 'deploy: completed']);
12784
12840
  return 0;
12785
12841
  }
@@ -13880,6 +13936,9 @@ function cliErrorMessage(error) {
13880
13936
  return String(error);
13881
13937
  }
13882
13938
  function cliErrorCode(error) {
13939
+ if (error instanceof TenderCliChildProcessError) {
13940
+ return 'child_process_failed';
13941
+ }
13883
13942
  if (error instanceof TenderCliDbApiError || error instanceof TenderCliAnalyticsApiError || error instanceof TenderCliConnectorsApiError) {
13884
13943
  return error.reasonCode;
13885
13944
  }
@@ -13967,6 +14026,16 @@ function cliErrorPayload(error, args, command) {
13967
14026
  const scope = scopeForError(error, args, command);
13968
14027
  const example = exampleForError(error, args, command);
13969
14028
  const nextAction = nextActionForError(error);
14029
+ const childProcess = error instanceof TenderCliChildProcessError
14030
+ ? {
14031
+ command: error.command,
14032
+ args: error.args,
14033
+ commandLine: commandLine(error.command, error.args),
14034
+ exitCode: error.exitCode,
14035
+ stdout: error.stdout,
14036
+ stderr: error.stderr,
14037
+ }
14038
+ : null;
13970
14039
  return {
13971
14040
  ok: false,
13972
14041
  command: commandNameForError(args, command),
@@ -13975,6 +14044,7 @@ function cliErrorPayload(error, args, command) {
13975
14044
  ...(scope ? { scope } : {}),
13976
14045
  ...(nextAction ? { nextAction } : {}),
13977
14046
  ...(example ? { example } : {}),
14047
+ ...(childProcess ? { childProcess } : {}),
13978
14048
  };
13979
14049
  }
13980
14050
  export async function runTenderCli(args, io = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenderprompt/cli",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "tender": "bin/tender.js"