@tenderprompt/cli 0.1.27 → 0.1.29
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/index.d.ts +5 -3
- package/dist/index.js +249 -330
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -9,9 +9,11 @@ type TenderCliRuntime = {
|
|
|
9
9
|
sleeper?: (milliseconds: number) => Promise<void>;
|
|
10
10
|
now?: () => number;
|
|
11
11
|
stdin?: string;
|
|
12
|
-
commandRunner?: (command: string, args: string[], options:
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
commandRunner?: (command: string, args: string[], options: TenderCommandOptions) => Promise<TenderCommandResult>;
|
|
13
|
+
};
|
|
14
|
+
type TenderCommandOptions = {
|
|
15
|
+
cwd: string;
|
|
16
|
+
env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
|
|
15
17
|
};
|
|
16
18
|
type TenderCommandResult = {
|
|
17
19
|
exitCode: number;
|
package/dist/index.js
CHANGED
|
@@ -95,6 +95,14 @@ class TenderCliUsageError extends Error {
|
|
|
95
95
|
this.name = 'TenderCliUsageError';
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
+
class TenderCliGuidedUsageError extends TenderCliUsageError {
|
|
99
|
+
nextAction;
|
|
100
|
+
constructor(message, nextAction) {
|
|
101
|
+
super(message);
|
|
102
|
+
this.name = 'TenderCliGuidedUsageError';
|
|
103
|
+
this.nextAction = nextAction;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
98
106
|
class TenderCliAnalyticsApiError extends Error {
|
|
99
107
|
reasonCode;
|
|
100
108
|
scope;
|
|
@@ -121,6 +129,18 @@ class TenderCliDbApiError extends Error {
|
|
|
121
129
|
this.nextAction = nextAction;
|
|
122
130
|
}
|
|
123
131
|
}
|
|
132
|
+
class TenderCliConnectorsApiError extends Error {
|
|
133
|
+
reasonCode;
|
|
134
|
+
status;
|
|
135
|
+
payload;
|
|
136
|
+
constructor(message, reasonCode, status, payload = null) {
|
|
137
|
+
super(message);
|
|
138
|
+
this.name = 'TenderCliConnectorsApiError';
|
|
139
|
+
this.reasonCode = reasonCode;
|
|
140
|
+
this.status = status;
|
|
141
|
+
this.payload = payload;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
124
144
|
const DEFAULT_PROFILE_NAME = 'default';
|
|
125
145
|
const DEFAULT_BASE_URL = 'https://app.tenderprompt.com';
|
|
126
146
|
const ARTIFACT_SOURCE_EXPORT_MAX_FILES = 1000;
|
|
@@ -433,9 +453,9 @@ Examples:
|
|
|
433
453
|
printf "$SHOPIFY_CLIENT_SECRET" | tender connectors shopify credentials set conn_123 --shop demo.myshopify.com --client-id abc --client-secret-stdin --profile account --json
|
|
434
454
|
printf "$SHOPIFY_APP_AUTOMATION_TOKEN" | tender connectors shopify app-automation-token set conn_123 --token-stdin --expires-at 2026-08-22 --profile account --json
|
|
435
455
|
tender connectors shopify verify conn_123 --profile account --json
|
|
436
|
-
tender connectors shopify source init conn_123 --dir ./shopify-app --client-id abc --
|
|
437
|
-
tender connectors shopify source validate --dir ./shopify-app --execute --json
|
|
438
|
-
|
|
456
|
+
tender connectors shopify source init conn_123 --dir ./shopify-app --client-id abc --profile account --json
|
|
457
|
+
tender connectors shopify source validate conn_123 --dir ./shopify-app --execute --json
|
|
458
|
+
tender connectors shopify source deploy conn_123 --dir ./shopify-app --confirm deploy --profile account --json
|
|
439
459
|
tender connectors bind SHOPIFY_CONNECTOR conn_123 --instance default --json`;
|
|
440
460
|
}
|
|
441
461
|
function connectorsShopifyCreateHelp() {
|
|
@@ -493,16 +513,19 @@ Examples:
|
|
|
493
513
|
tender connectors shopify verify conn_123 --profile account --json`;
|
|
494
514
|
}
|
|
495
515
|
function connectorsShopifySourceInitHelp() {
|
|
496
|
-
return `Usage: tender connectors shopify source init <connector-id> --dir <path> --client-id <shopify-client-id>
|
|
516
|
+
return `Usage: tender connectors shopify source init <connector-id> --dir <path> --client-id <shopify-client-id> [--source-name <name>] [--scopes <csv>] [--force] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
497
517
|
|
|
498
518
|
Creates or reuses an internal Tender source artifact for the merchant-owned
|
|
499
|
-
Shopify app, checks it out to --dir, writes non-secret Shopify app
|
|
500
|
-
commits it, pushes it to Artifact Git, and records
|
|
501
|
-
|
|
502
|
-
|
|
519
|
+
Shopify app, checks it out to --dir, writes a minimal non-secret Shopify app
|
|
520
|
+
source tree, commits it, pushes it to Artifact Git, and records
|
|
521
|
+
sourceArtifactId on the connector.
|
|
522
|
+
|
|
523
|
+
The CLI bootstraps and deploys source. App blocks, web pixels, customer
|
|
524
|
+
metadata definitions, and app-owned GraphQL resources are source edits guided by
|
|
525
|
+
playbooks, not source init flags.
|
|
503
526
|
|
|
504
527
|
Examples:
|
|
505
|
-
tender connectors shopify source init conn_123 --dir ./shopify-app --client-id abc --
|
|
528
|
+
tender connectors shopify source init conn_123 --dir ./shopify-app --client-id abc --profile account --json`;
|
|
506
529
|
}
|
|
507
530
|
function connectorsShopifySourceCheckoutHelp() {
|
|
508
531
|
return `Usage: tender connectors shopify source checkout <connector-id> --dir <path> [--remote <name>] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
@@ -526,11 +549,13 @@ function connectorsShopifySourceDeployHelp() {
|
|
|
526
549
|
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]
|
|
527
550
|
|
|
528
551
|
Without --confirm deploy, prints the exact Shopify CLI deployment command. With
|
|
529
|
-
--confirm deploy, runs npx --yes @shopify/cli@latest app deploy.
|
|
530
|
-
SHOPIFY_APP_AUTOMATION_TOKEN
|
|
552
|
+
--confirm deploy, runs npx --yes @shopify/cli@latest app deploy. If
|
|
553
|
+
SHOPIFY_APP_AUTOMATION_TOKEN is not set locally, the CLI requests the
|
|
554
|
+
connector's stored App Automation Token and injects it only into the spawned
|
|
555
|
+
Shopify CLI child process.
|
|
531
556
|
|
|
532
557
|
Examples:
|
|
533
|
-
|
|
558
|
+
tender connectors shopify source deploy conn_123 --dir ./shopify-app --version connector-source-smoke --confirm deploy --profile account --json`;
|
|
534
559
|
}
|
|
535
560
|
function connectorsShopifyDeleteHelp() {
|
|
536
561
|
return `Usage: tender connectors shopify delete <connector-id> --confirm delete [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
@@ -1476,6 +1501,51 @@ function tenderCliCapabilities() {
|
|
|
1476
1501
|
persistence: 'session_only',
|
|
1477
1502
|
writesToCheckout: false,
|
|
1478
1503
|
},
|
|
1504
|
+
connectors: {
|
|
1505
|
+
mode: 'tenant_owned_platform_bindings',
|
|
1506
|
+
listCommand: 'tender connectors list --profile account --json',
|
|
1507
|
+
bindCommand: 'tender connectors bind <binding-name> <connector-id> --instance <instance-id> --json',
|
|
1508
|
+
shopify: {
|
|
1509
|
+
playbookId: 'shopify-connector-custom-app',
|
|
1510
|
+
playbookCommand: 'tender playbooks get shopify-connector-custom-app --json',
|
|
1511
|
+
customerMetadataPlaybookCommand: 'tender playbooks get shopify-connector-customer-metadata-widget --json',
|
|
1512
|
+
whatItIs: 'A merchant-owned Shopify custom app exposed to Tender apps through an explicit platform connector binding.',
|
|
1513
|
+
whenToUse: [
|
|
1514
|
+
'Shopify Admin GraphQL reads or writes',
|
|
1515
|
+
'customer metafields or customer-linked metaobjects',
|
|
1516
|
+
'app-owned custom data definitions',
|
|
1517
|
+
'Theme App Extension app blocks',
|
|
1518
|
+
'privileged background Shopify work that browser code cannot safely perform',
|
|
1519
|
+
],
|
|
1520
|
+
whenNotToUse: [
|
|
1521
|
+
'pure frontend widgets',
|
|
1522
|
+
'Tender analytics only',
|
|
1523
|
+
'public Shopify data already present on the page',
|
|
1524
|
+
'client-side event publishing without Admin API writes',
|
|
1525
|
+
],
|
|
1526
|
+
binding: {
|
|
1527
|
+
name: 'SHOPIFY_CONNECTOR',
|
|
1528
|
+
source: 'platform:shopify_connector',
|
|
1529
|
+
interface: 'ShopifyConnector',
|
|
1530
|
+
callShape: 'await this.env.SHOPIFY_CONNECTOR.invoke("shopify.graphql", { query, variables })',
|
|
1531
|
+
},
|
|
1532
|
+
sourceProject: {
|
|
1533
|
+
durableIdentity: 'connector.sourceProject.sourceArtifactId',
|
|
1534
|
+
targetPackId: 'shopify-connector-source',
|
|
1535
|
+
visibility: 'internal_source_artifact_not_customer_facing_app',
|
|
1536
|
+
},
|
|
1537
|
+
sourceModel: {
|
|
1538
|
+
cliOwns: ['source artifact lifecycle', 'checkout', 'Shopify CLI validate/deploy forwarding'],
|
|
1539
|
+
playbooksOwn: ['Theme App Extension app blocks', 'web pixels', 'customer metadata definitions', 'Admin GraphQL resource shape'],
|
|
1540
|
+
},
|
|
1541
|
+
guardrails: [
|
|
1542
|
+
'Use the account profile because connectors are account-level resources.',
|
|
1543
|
+
'Use minimum Shopify scopes for the requested job.',
|
|
1544
|
+
'Never commit client secrets, Admin API tokens, app automation tokens, Tender tokens, or customer data exports.',
|
|
1545
|
+
'Do not create bindings/<id>/ files for Shopify connectors; use the platform connector source in app.json.',
|
|
1546
|
+
],
|
|
1547
|
+
},
|
|
1548
|
+
},
|
|
1479
1549
|
analyticsAuthoring: {
|
|
1480
1550
|
command: ANALYTICS_AUTHORING_COMMAND,
|
|
1481
1551
|
playbookId: ANALYTICS_AUTHORING_PLAYBOOK_ID,
|
|
@@ -1502,6 +1572,8 @@ function tenderCliCapabilities() {
|
|
|
1502
1572
|
'tender app db --help',
|
|
1503
1573
|
'tender app bindings outbound --help',
|
|
1504
1574
|
'tender app outbound-auth --help',
|
|
1575
|
+
'tender connectors --help',
|
|
1576
|
+
'tender connectors shopify source init --help',
|
|
1505
1577
|
],
|
|
1506
1578
|
},
|
|
1507
1579
|
workflows: [
|
|
@@ -1527,6 +1599,24 @@ function tenderCliCapabilities() {
|
|
|
1527
1599
|
'Do not write fetched playbook contents into the checkout or install them as skills.',
|
|
1528
1600
|
],
|
|
1529
1601
|
},
|
|
1602
|
+
{
|
|
1603
|
+
name: 'shopify_connector_discovery',
|
|
1604
|
+
when: 'When a Tender App needs Shopify Admin API access, customer metadata, app blocks, app-owned custom data, or a merchant-owned Shopify custom app.',
|
|
1605
|
+
commands: [
|
|
1606
|
+
'tender capabilities --json',
|
|
1607
|
+
'tender playbooks list --json',
|
|
1608
|
+
'tender playbooks get shopify-connector-custom-app --json',
|
|
1609
|
+
'tender connectors list --profile account --json',
|
|
1610
|
+
'tender connectors bind SHOPIFY_CONNECTOR <connector-id> --instance default --json',
|
|
1611
|
+
],
|
|
1612
|
+
notes: [
|
|
1613
|
+
'Connectors are tenant-owned platform bindings, not generated binding workers.',
|
|
1614
|
+
'The merchant owns the Shopify custom app; Tender stores connector credentials and app source metadata.',
|
|
1615
|
+
'Use connector.sourceProject.sourceArtifactId as the durable Shopify app source identity.',
|
|
1616
|
+
'Use source init for bootstrap only; app blocks, pixels, metadata definitions, and Admin GraphQL operations are source edits guided by playbooks.',
|
|
1617
|
+
'Fetch shopify-connector-customer-metadata-widget when the requested job involves customer metafields, metaobjects, or profile writes.',
|
|
1618
|
+
],
|
|
1619
|
+
},
|
|
1530
1620
|
{
|
|
1531
1621
|
name: 'create_new_app_with_preview',
|
|
1532
1622
|
when: 'When the user wants a new Tender App that is immediately visible.',
|
|
@@ -3500,18 +3590,11 @@ function parseConnectorsShopifySourceInitArgs(args) {
|
|
|
3500
3590
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3501
3591
|
return { command: 'help', topic: 'connectors shopify source init' };
|
|
3502
3592
|
}
|
|
3503
|
-
const connectorId = parseConnectorId(args[0], 'tender connectors shopify source init <connector-id> --dir ./shopify-app --client-id abc --
|
|
3593
|
+
const connectorId = parseConnectorId(args[0], 'tender connectors shopify source init <connector-id> --dir ./shopify-app --client-id abc --json');
|
|
3504
3594
|
const parsed = parseConnectorsCommonArgs(args, 1);
|
|
3505
3595
|
let dir = null;
|
|
3506
3596
|
let clientId = null;
|
|
3507
3597
|
let sourceName = null;
|
|
3508
|
-
let tenderAppUrl = null;
|
|
3509
|
-
let tenderElement = null;
|
|
3510
|
-
let artifactId = null;
|
|
3511
|
-
let publicId = null;
|
|
3512
|
-
let themeExtensionHandle = 'tender-widget';
|
|
3513
|
-
let themeExtensionUid = null;
|
|
3514
|
-
let blockName = 'Tender Prompt Widget';
|
|
3515
3598
|
let scopes = null;
|
|
3516
3599
|
let force = false;
|
|
3517
3600
|
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
@@ -3531,41 +3614,6 @@ function parseConnectorsShopifySourceInitArgs(args) {
|
|
|
3531
3614
|
index += 1;
|
|
3532
3615
|
continue;
|
|
3533
3616
|
}
|
|
3534
|
-
if (arg === '--tender-app-url') {
|
|
3535
|
-
tenderAppUrl = parseRequiredFlagValue(parsed.remaining[index + 1], '--tender-app-url');
|
|
3536
|
-
index += 1;
|
|
3537
|
-
continue;
|
|
3538
|
-
}
|
|
3539
|
-
if (arg === '--tender-element') {
|
|
3540
|
-
tenderElement = parseRequiredFlagValue(parsed.remaining[index + 1], '--tender-element');
|
|
3541
|
-
index += 1;
|
|
3542
|
-
continue;
|
|
3543
|
-
}
|
|
3544
|
-
if (arg === '--artifact-id') {
|
|
3545
|
-
artifactId = parseRequiredFlagValue(parsed.remaining[index + 1], '--artifact-id');
|
|
3546
|
-
index += 1;
|
|
3547
|
-
continue;
|
|
3548
|
-
}
|
|
3549
|
-
if (arg === '--public-id') {
|
|
3550
|
-
publicId = parseRequiredFlagValue(parsed.remaining[index + 1], '--public-id');
|
|
3551
|
-
index += 1;
|
|
3552
|
-
continue;
|
|
3553
|
-
}
|
|
3554
|
-
if (arg === '--theme-extension-handle') {
|
|
3555
|
-
themeExtensionHandle = parseRequiredFlagValue(parsed.remaining[index + 1], '--theme-extension-handle');
|
|
3556
|
-
index += 1;
|
|
3557
|
-
continue;
|
|
3558
|
-
}
|
|
3559
|
-
if (arg === '--theme-extension-uid') {
|
|
3560
|
-
themeExtensionUid = parseRequiredFlagValue(parsed.remaining[index + 1], '--theme-extension-uid');
|
|
3561
|
-
index += 1;
|
|
3562
|
-
continue;
|
|
3563
|
-
}
|
|
3564
|
-
if (arg === '--block-name') {
|
|
3565
|
-
blockName = parseRequiredFlagValue(parsed.remaining[index + 1], '--block-name');
|
|
3566
|
-
index += 1;
|
|
3567
|
-
continue;
|
|
3568
|
-
}
|
|
3569
3617
|
if (arg === '--scopes') {
|
|
3570
3618
|
scopes = parseRequiredFlagValue(parsed.remaining[index + 1], '--scopes');
|
|
3571
3619
|
index += 1;
|
|
@@ -3578,31 +3626,17 @@ function parseConnectorsShopifySourceInitArgs(args) {
|
|
|
3578
3626
|
throw new TenderCliUsageError(`Unknown connectors shopify source init option: ${arg}`);
|
|
3579
3627
|
}
|
|
3580
3628
|
if (!dir) {
|
|
3581
|
-
throw new TenderCliUsageError('--dir is required. Example: tender connectors shopify source init conn_123 --dir ./shopify-app --client-id abc --
|
|
3629
|
+
throw new TenderCliUsageError('--dir is required. Example: tender connectors shopify source init conn_123 --dir ./shopify-app --client-id abc --json');
|
|
3582
3630
|
}
|
|
3583
3631
|
if (!clientId) {
|
|
3584
3632
|
throw new TenderCliUsageError('--client-id is required because Tender stores only the non-secret client ID prefix.');
|
|
3585
3633
|
}
|
|
3586
|
-
if (!tenderAppUrl) {
|
|
3587
|
-
throw new TenderCliUsageError('--tender-app-url is required. Pass the Tender app client.js URL to load in the app block.');
|
|
3588
|
-
}
|
|
3589
|
-
if (!tenderElement) {
|
|
3590
|
-
throw new TenderCliUsageError('--tender-element is required. Pass the Tender custom element tag to mount in Shopify.');
|
|
3591
|
-
}
|
|
3592
3634
|
return {
|
|
3593
3635
|
command: 'connectors shopify source init',
|
|
3594
3636
|
connectorId,
|
|
3595
3637
|
dir,
|
|
3596
3638
|
clientId,
|
|
3597
3639
|
sourceName,
|
|
3598
|
-
template: 'dog-profile',
|
|
3599
|
-
tenderAppUrl,
|
|
3600
|
-
tenderElement,
|
|
3601
|
-
artifactId,
|
|
3602
|
-
publicId,
|
|
3603
|
-
themeExtensionHandle,
|
|
3604
|
-
themeExtensionUid,
|
|
3605
|
-
blockName,
|
|
3606
3640
|
scopes,
|
|
3607
3641
|
force,
|
|
3608
3642
|
...parsed,
|
|
@@ -7502,7 +7536,7 @@ async function requestConnectorsApi(input) {
|
|
|
7502
7536
|
const message = (typeof payload?.message === 'string' && payload.message) ||
|
|
7503
7537
|
(typeof payload?.reasonCode === 'string' && payload.reasonCode) ||
|
|
7504
7538
|
`Connector request failed with HTTP ${response.status}.`;
|
|
7505
|
-
throw new
|
|
7539
|
+
throw new TenderCliConnectorsApiError([message, `Reason: ${reasonCode}.`, 'Example: tender connectors list --profile account --json.'].join('\n'), reasonCode, response.status, payload ?? null);
|
|
7506
7540
|
}
|
|
7507
7541
|
return payload;
|
|
7508
7542
|
}
|
|
@@ -8898,6 +8932,7 @@ function defaultCommandRunner(command, args, options) {
|
|
|
8898
8932
|
return new Promise((resolve, reject) => {
|
|
8899
8933
|
const child = spawn(command, args, {
|
|
8900
8934
|
cwd: options.cwd,
|
|
8935
|
+
...(options.env ? { env: options.env } : {}),
|
|
8901
8936
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
8902
8937
|
});
|
|
8903
8938
|
const stdout = [];
|
|
@@ -11076,20 +11111,6 @@ async function runConnectorsShopifyVerify(command, io, runtime) {
|
|
|
11076
11111
|
function requireShopifySourceString(value, fallback = '') {
|
|
11077
11112
|
return typeof value === 'string' && value.trim() ? value.trim() : fallback;
|
|
11078
11113
|
}
|
|
11079
|
-
function normalizeTenderElementTag(value) {
|
|
11080
|
-
const normalized = value.trim().toLowerCase();
|
|
11081
|
-
if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)+$/.test(normalized)) {
|
|
11082
|
-
throw new TenderCliUsageError('--tender-element must be a valid custom element tag, for example tender-dog-profile.');
|
|
11083
|
-
}
|
|
11084
|
-
return normalized;
|
|
11085
|
-
}
|
|
11086
|
-
function normalizeThemeExtensionHandle(value) {
|
|
11087
|
-
const normalized = value.trim().toLowerCase();
|
|
11088
|
-
if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(normalized)) {
|
|
11089
|
-
throw new TenderCliUsageError('--theme-extension-handle must contain only lowercase letters, numbers, and dashes.');
|
|
11090
|
-
}
|
|
11091
|
-
return normalized;
|
|
11092
|
-
}
|
|
11093
11114
|
function tomlString(value) {
|
|
11094
11115
|
return JSON.stringify(value);
|
|
11095
11116
|
}
|
|
@@ -11099,107 +11120,30 @@ function uniqueSortedScopes(scopes) {
|
|
|
11099
11120
|
function parseScopeCsv(value) {
|
|
11100
11121
|
return uniqueSortedScopes((value ?? '').split(/[,\s]+/));
|
|
11101
11122
|
}
|
|
11102
|
-
function
|
|
11123
|
+
function defaultShopifySourceScopes(connectorScopes, override) {
|
|
11103
11124
|
if (override) {
|
|
11104
11125
|
return parseScopeCsv(override);
|
|
11105
11126
|
}
|
|
11106
11127
|
const fromConnector = Array.isArray(connectorScopes) ? connectorScopes.filter((scope) => typeof scope === 'string') : [];
|
|
11107
|
-
return uniqueSortedScopes(
|
|
11108
|
-
...fromConnector,
|
|
11109
|
-
'read_customers',
|
|
11110
|
-
'write_customers',
|
|
11111
|
-
'read_metaobject_definitions',
|
|
11112
|
-
'write_metaobject_definitions',
|
|
11113
|
-
'read_metaobjects',
|
|
11114
|
-
'write_metaobjects',
|
|
11115
|
-
]);
|
|
11116
|
-
}
|
|
11117
|
-
function tenderWidgetAssetJs() {
|
|
11118
|
-
return `(function () {
|
|
11119
|
-
const loader = (window.TenderPromptBlockLoader ||= { scripts: new Map() });
|
|
11120
|
-
|
|
11121
|
-
function loadScript(src) {
|
|
11122
|
-
if (!src) return Promise.reject(new Error("Missing Tender runtime URL"));
|
|
11123
|
-
|
|
11124
|
-
const existing = loader.scripts.get(src);
|
|
11125
|
-
if (existing) return existing.promise;
|
|
11126
|
-
|
|
11127
|
-
const promise = new Promise((resolve, reject) => {
|
|
11128
|
-
const script = document.createElement("script");
|
|
11129
|
-
script.type = "module";
|
|
11130
|
-
script.src = src;
|
|
11131
|
-
script.onload = resolve;
|
|
11132
|
-
script.onerror = () => reject(new Error(\`Failed to load \${src}\`));
|
|
11133
|
-
document.head.appendChild(script);
|
|
11134
|
-
});
|
|
11135
|
-
|
|
11136
|
-
loader.scripts.set(src, { status: "loading", promise });
|
|
11137
|
-
promise.then(
|
|
11138
|
-
() => loader.scripts.set(src, { status: "loaded", promise }),
|
|
11139
|
-
() => loader.scripts.delete(src),
|
|
11140
|
-
);
|
|
11141
|
-
|
|
11142
|
-
return promise;
|
|
11143
|
-
}
|
|
11144
|
-
|
|
11145
|
-
class TenderPromptWidgetBlock extends HTMLElement {
|
|
11146
|
-
connectedCallback() {
|
|
11147
|
-
this.mount();
|
|
11148
|
-
}
|
|
11149
|
-
|
|
11150
|
-
async mount() {
|
|
11151
|
-
const mount = this.querySelector("[data-tender-mount]");
|
|
11152
|
-
const elementTag = this.dataset.tenderElementTag;
|
|
11153
|
-
if (!mount || !elementTag || mount.dataset.tenderMounted === "true") return;
|
|
11154
|
-
|
|
11155
|
-
await loadScript(this.dataset.tenderRuntimeSrc);
|
|
11156
|
-
|
|
11157
|
-
const widget = document.createElement(elementTag);
|
|
11158
|
-
for (const [key, value] of Object.entries(this.dataset)) {
|
|
11159
|
-
if (value == null || key === "tenderRuntimeSrc" || key === "tenderElementTag") continue;
|
|
11160
|
-
widget.dataset[key] = value;
|
|
11161
|
-
}
|
|
11162
|
-
|
|
11163
|
-
mount.replaceChildren(widget);
|
|
11164
|
-
mount.dataset.tenderMounted = "true";
|
|
11165
|
-
}
|
|
11166
|
-
}
|
|
11167
|
-
|
|
11168
|
-
if (!customElements.get("tender-prompt-widget-block")) {
|
|
11169
|
-
customElements.define("tender-prompt-widget-block", TenderPromptWidgetBlock);
|
|
11170
|
-
}
|
|
11171
|
-
})();
|
|
11172
|
-
`;
|
|
11128
|
+
return uniqueSortedScopes(fromConnector);
|
|
11173
11129
|
}
|
|
11174
11130
|
function buildShopifySourceFiles(input) {
|
|
11175
11131
|
const appName = requireShopifySourceString(input.connector.appName, 'Tender Prompt Apps');
|
|
11176
11132
|
const adminApiVersion = requireShopifySourceString(input.connector.adminApiVersion, '2026-04');
|
|
11177
11133
|
const shopDomain = requireShopifySourceString(input.connector.shopDomain);
|
|
11178
|
-
const
|
|
11179
|
-
const extensionHandle = normalizeThemeExtensionHandle(input.themeExtensionHandle);
|
|
11180
|
-
const scopes = defaultDogProfileScopes(input.connector.scopes, input.scopes);
|
|
11181
|
-
const artifactId = input.artifactId ?? 'artifact_replace_me';
|
|
11182
|
-
const publicId = input.publicId ?? 'tender-widget';
|
|
11183
|
-
const blockName = input.blockName.trim().slice(0, 120) || 'Tender Prompt Widget';
|
|
11134
|
+
const scopes = defaultShopifySourceScopes(input.connector.scopes, input.scopes);
|
|
11184
11135
|
const connectorManifest = {
|
|
11185
11136
|
version: 1,
|
|
11186
11137
|
kind: 'shopify_connector_source',
|
|
11187
11138
|
connectorId: input.connectorId,
|
|
11188
11139
|
instanceId: requireShopifySourceString(input.connector.instanceId, 'default'),
|
|
11189
|
-
template: input.template,
|
|
11190
11140
|
shopDomain: shopDomain || null,
|
|
11191
11141
|
adminApiVersion,
|
|
11192
11142
|
clientId: input.clientId,
|
|
11193
|
-
|
|
11194
|
-
tender: {
|
|
11195
|
-
artifactId,
|
|
11196
|
-
publicId,
|
|
11197
|
-
elementTag: tenderElement,
|
|
11198
|
-
runtimeUrl: input.tenderAppUrl,
|
|
11199
|
-
},
|
|
11143
|
+
scopes,
|
|
11200
11144
|
secrets: {
|
|
11201
11145
|
clientSecret: 'stored in Tender connector credentials, not in this source tree',
|
|
11202
|
-
appAutomationToken: '
|
|
11146
|
+
appAutomationToken: 'stored in Tender connector secret store, not in this source tree',
|
|
11203
11147
|
},
|
|
11204
11148
|
};
|
|
11205
11149
|
const shopifyAppToml = `# Merchant-owned Shopify app source generated by Tender Prompt.
|
|
@@ -11221,157 +11165,31 @@ api_version = ${tomlString(adminApiVersion)}
|
|
|
11221
11165
|
|
|
11222
11166
|
[build]
|
|
11223
11167
|
automatically_update_urls_on_dev = true
|
|
11224
|
-
|
|
11225
|
-
[metaobjects.app.dog_profile]
|
|
11226
|
-
name = "Dog profile"
|
|
11227
|
-
display_name_field = "dog_name"
|
|
11228
|
-
access.admin = "merchant_read_write"
|
|
11229
|
-
|
|
11230
|
-
[metaobjects.app.dog_profile.fields.dog_name]
|
|
11231
|
-
name = "Dog name"
|
|
11232
|
-
type = "single_line_text_field"
|
|
11233
|
-
required = true
|
|
11234
|
-
|
|
11235
|
-
[metaobjects.app.dog_profile.fields.preferences]
|
|
11236
|
-
name = "Preferences"
|
|
11237
|
-
type = "json"
|
|
11238
|
-
|
|
11239
|
-
[customer.metafields.app.dog_profile]
|
|
11240
|
-
name = "Dog profile"
|
|
11241
|
-
type = "metaobject_reference<$app:dog_profile>"
|
|
11242
|
-
access.admin = "merchant_read_write"
|
|
11243
|
-
`;
|
|
11244
|
-
const extensionToml = [
|
|
11245
|
-
`name = ${tomlString(extensionHandle)}`,
|
|
11246
|
-
'type = "theme"',
|
|
11247
|
-
input.themeExtensionUid ? `uid = ${tomlString(input.themeExtensionUid)}` : null,
|
|
11248
|
-
'',
|
|
11249
|
-
]
|
|
11250
|
-
.filter((line) => line !== null)
|
|
11251
|
-
.join('\n');
|
|
11252
|
-
const liquid = `{% doc %}
|
|
11253
|
-
Tender Prompt app block generated from connector ${input.connectorId}.
|
|
11254
|
-
|
|
11255
|
-
The block loads one Tender runtime script and mounts the configured Tender
|
|
11256
|
-
custom element. Keep merchant-editable copy and version pins in block settings.
|
|
11257
|
-
{% enddoc %}
|
|
11258
|
-
|
|
11259
|
-
{% liquid
|
|
11260
|
-
assign artifact_id = block.settings.artifact_id | default: ${tomlString(artifactId)}
|
|
11261
|
-
assign runtime_src = block.settings.runtime_src | default: ${tomlString(input.tenderAppUrl)}
|
|
11262
|
-
assign widget_version = block.settings.widget_version | default: 'latest'
|
|
11263
|
-
%}
|
|
11264
|
-
|
|
11265
|
-
<tender-prompt-widget-block
|
|
11266
|
-
{{ block.shopify_attributes }}
|
|
11267
|
-
class="tender-prompt-widget-block tender-prompt-widget-block--{{ block.settings.tone }}"
|
|
11268
|
-
data-tender-artifact-id="{{ artifact_id | escape }}"
|
|
11269
|
-
data-tender-public-id="{{ block.settings.public_id | escape }}"
|
|
11270
|
-
data-tender-version="{{ widget_version | escape }}"
|
|
11271
|
-
data-tender-runtime-src="{{ runtime_src | escape }}"
|
|
11272
|
-
data-tender-element-tag="{{ block.settings.element_tag | escape }}"
|
|
11273
|
-
data-tender-mode="shopify_app_block"
|
|
11274
|
-
data-surface="shopify_theme_app_extension"
|
|
11275
|
-
data-tender-block-id="{{ block.id | escape }}"
|
|
11276
|
-
data-tender-shop-domain="{{ shop.permanent_domain | escape }}"
|
|
11277
|
-
data-tender-heading="{{ block.settings.heading | escape }}"
|
|
11278
|
-
data-tender-intro="{{ block.settings.intro | escape }}"
|
|
11279
|
-
data-tender-tone="{{ block.settings.tone | escape }}"
|
|
11280
|
-
{% if customer %}
|
|
11281
|
-
data-tender-customer-id="{{ customer.id }}"
|
|
11282
|
-
{% endif %}
|
|
11283
|
-
{% if product %}
|
|
11284
|
-
data-tender-product-id="{{ product.id }}"
|
|
11285
|
-
data-tender-product-handle="{{ product.handle | escape }}"
|
|
11286
|
-
{% endif %}
|
|
11287
|
-
>
|
|
11288
|
-
<div data-tender-mount>
|
|
11289
|
-
<p>{{ block.settings.heading }}</p>
|
|
11290
|
-
</div>
|
|
11291
|
-
</tender-prompt-widget-block>
|
|
11292
|
-
|
|
11293
|
-
{% schema %}
|
|
11294
|
-
{
|
|
11295
|
-
"name": ${JSON.stringify(blockName)},
|
|
11296
|
-
"target": "section",
|
|
11297
|
-
"javascript": "tender-widget.js",
|
|
11298
|
-
"settings": [
|
|
11299
|
-
{
|
|
11300
|
-
"type": "text",
|
|
11301
|
-
"id": "runtime_src",
|
|
11302
|
-
"label": "Tender runtime URL",
|
|
11303
|
-
"default": ${JSON.stringify(input.tenderAppUrl)}
|
|
11304
|
-
},
|
|
11305
|
-
{
|
|
11306
|
-
"type": "text",
|
|
11307
|
-
"id": "element_tag",
|
|
11308
|
-
"label": "Tender custom element",
|
|
11309
|
-
"default": ${JSON.stringify(tenderElement)}
|
|
11310
|
-
},
|
|
11311
|
-
{
|
|
11312
|
-
"type": "text",
|
|
11313
|
-
"id": "artifact_id",
|
|
11314
|
-
"label": "Tender artifact ID",
|
|
11315
|
-
"default": ${JSON.stringify(artifactId)}
|
|
11316
|
-
},
|
|
11317
|
-
{
|
|
11318
|
-
"type": "text",
|
|
11319
|
-
"id": "public_id",
|
|
11320
|
-
"label": "Tender public ID",
|
|
11321
|
-
"default": ${JSON.stringify(publicId)}
|
|
11322
|
-
},
|
|
11323
|
-
{
|
|
11324
|
-
"type": "select",
|
|
11325
|
-
"id": "widget_version",
|
|
11326
|
-
"label": "Widget version",
|
|
11327
|
-
"options": [
|
|
11328
|
-
{ "value": "latest", "label": "Latest published version" }
|
|
11329
|
-
],
|
|
11330
|
-
"default": "latest"
|
|
11331
|
-
},
|
|
11332
|
-
{
|
|
11333
|
-
"type": "inline_richtext",
|
|
11334
|
-
"id": "heading",
|
|
11335
|
-
"label": "Heading",
|
|
11336
|
-
"default": "Tell us about your customer"
|
|
11337
|
-
},
|
|
11338
|
-
{
|
|
11339
|
-
"type": "text",
|
|
11340
|
-
"id": "intro",
|
|
11341
|
-
"label": "Intro",
|
|
11342
|
-
"default": "Answer a few quick questions and keep your profile up to date."
|
|
11343
|
-
},
|
|
11344
|
-
{
|
|
11345
|
-
"type": "select",
|
|
11346
|
-
"id": "tone",
|
|
11347
|
-
"label": "Tone",
|
|
11348
|
-
"options": [
|
|
11349
|
-
{ "value": "default", "label": "Default" },
|
|
11350
|
-
{ "value": "subtle", "label": "Subtle" },
|
|
11351
|
-
{ "value": "contrast", "label": "Contrast" }
|
|
11352
|
-
],
|
|
11353
|
-
"default": "default"
|
|
11354
|
-
}
|
|
11355
|
-
]
|
|
11356
|
-
}
|
|
11357
|
-
{% endschema %}
|
|
11358
11168
|
`;
|
|
11359
11169
|
const readme = `# ${appName}
|
|
11360
11170
|
|
|
11361
|
-
This is the merchant-owned Shopify app source
|
|
11171
|
+
This is the merchant-owned Shopify app source bootstrapped by Tender Prompt for
|
|
11362
11172
|
connector \`${input.connectorId}\`.
|
|
11363
11173
|
|
|
11364
11174
|
## What lives here
|
|
11365
11175
|
|
|
11366
|
-
- \`shopify.app.toml\`: Shopify app configuration
|
|
11367
|
-
- \`
|
|
11368
|
-
|
|
11176
|
+
- \`shopify.app.toml\`: Shopify app configuration and reviewed Admin API scopes.
|
|
11177
|
+
- \`connector.json\`: Non-secret Tender metadata that maps this source tree back
|
|
11178
|
+
to the connector.
|
|
11179
|
+
|
|
11180
|
+
## Add capabilities
|
|
11181
|
+
|
|
11182
|
+
This bootstrap intentionally does not generate app blocks, web pixels, customer
|
|
11183
|
+
metadata definitions, or app-owned GraphQL resources. Add those as source edits
|
|
11184
|
+
using the Shopify connector playbooks so the source tree stays the durable
|
|
11185
|
+
record of what the merchant-owned app actually contains.
|
|
11369
11186
|
|
|
11370
11187
|
## Secrets
|
|
11371
11188
|
|
|
11372
11189
|
Do not commit the Shopify client secret, Admin API token, or App Automation
|
|
11373
11190
|
Token. Runtime credentials stay in the Tender connector secret store. Deployment
|
|
11374
|
-
uses
|
|
11191
|
+
uses the connector's stored App Automation Token and passes it only to the
|
|
11192
|
+
spawned Shopify CLI child process.
|
|
11375
11193
|
|
|
11376
11194
|
## Validate
|
|
11377
11195
|
|
|
@@ -11382,21 +11200,16 @@ npx --yes @shopify/cli@latest app config validate --path . --json
|
|
|
11382
11200
|
## Deploy
|
|
11383
11201
|
|
|
11384
11202
|
\`\`\`sh
|
|
11385
|
-
|
|
11203
|
+
tender connectors shopify source deploy ${input.connectorId} --dir . --confirm deploy --profile account --json
|
|
11386
11204
|
\`\`\`
|
|
11387
11205
|
`;
|
|
11388
11206
|
return {
|
|
11389
|
-
extensionHandle,
|
|
11390
11207
|
manifest: connectorManifest,
|
|
11391
11208
|
files: new Map([
|
|
11392
11209
|
['.gitignore', '.shopify/\nnode_modules/\n.env\n.env.*\n'],
|
|
11393
11210
|
['README.md', readme],
|
|
11394
11211
|
['connector.json', `${JSON.stringify(connectorManifest, null, 2)}\n`],
|
|
11395
11212
|
['shopify.app.toml', shopifyAppToml],
|
|
11396
|
-
[`extensions/${extensionHandle}/shopify.extension.toml`, extensionToml],
|
|
11397
|
-
[`extensions/${extensionHandle}/blocks/tender_widget.liquid`, liquid],
|
|
11398
|
-
[`extensions/${extensionHandle}/assets/tender-widget.js`, tenderWidgetAssetJs()],
|
|
11399
|
-
[`extensions/${extensionHandle}/locales/en.default.json`, `${JSON.stringify({ tender: { block: { name: blockName } } }, null, 2)}\n`],
|
|
11400
11213
|
]),
|
|
11401
11214
|
};
|
|
11402
11215
|
}
|
|
@@ -11469,7 +11282,6 @@ async function patchConnectorSourceProject(input) {
|
|
|
11469
11282
|
version: 1,
|
|
11470
11283
|
kind: 'shopify_app_source',
|
|
11471
11284
|
sourceArtifactId: input.sourceArtifactId,
|
|
11472
|
-
template: input.template,
|
|
11473
11285
|
updatedAt: input.updatedAt,
|
|
11474
11286
|
};
|
|
11475
11287
|
const payload = await requestConnectorsApi({
|
|
@@ -11656,13 +11468,117 @@ function shopifyDeployArgs(input) {
|
|
|
11656
11468
|
function commandLine(command, args) {
|
|
11657
11469
|
return [command, ...args.map(shellQuote)].join(' ');
|
|
11658
11470
|
}
|
|
11471
|
+
function redactSensitiveText(value, secrets = []) {
|
|
11472
|
+
let redacted = value;
|
|
11473
|
+
for (const secret of secrets) {
|
|
11474
|
+
if (secret) {
|
|
11475
|
+
redacted = redacted.split(secret).join('[REDACTED]');
|
|
11476
|
+
}
|
|
11477
|
+
}
|
|
11478
|
+
return redacted
|
|
11479
|
+
.replace(/shpat_[A-Za-z0-9_:-]+/g, 'shpat_[REDACTED]')
|
|
11480
|
+
.replace(/\b(SHOPIFY_APP_AUTOMATION_TOKEN\s*[=:]\s*)([^\s'"]+)/gi, '$1[REDACTED]')
|
|
11481
|
+
.replace(/\b(authorization\s*[:=]\s*(?:Bearer\s+)?)([^\s'"]+)/gi, '$1[REDACTED]')
|
|
11482
|
+
.replace(/\b(x-shopify-access-token\s*[:=]\s*)([^\s'"]+)/gi, '$1[REDACTED]');
|
|
11483
|
+
}
|
|
11659
11484
|
async function runNpxCommand(input) {
|
|
11660
11485
|
const runner = input.runtime.commandRunner ?? defaultCommandRunner;
|
|
11661
|
-
const result = await runner('npx', input.args, {
|
|
11662
|
-
|
|
11663
|
-
|
|
11486
|
+
const result = await runner('npx', input.args, {
|
|
11487
|
+
cwd: input.cwd,
|
|
11488
|
+
...(input.env ? { env: input.env } : {}),
|
|
11489
|
+
});
|
|
11490
|
+
const redactedResult = {
|
|
11491
|
+
...result,
|
|
11492
|
+
stdout: redactSensitiveText(result.stdout, input.redactions),
|
|
11493
|
+
stderr: redactSensitiveText(result.stderr, input.redactions),
|
|
11494
|
+
};
|
|
11495
|
+
if (redactedResult.exitCode !== 0) {
|
|
11496
|
+
throw new Error([
|
|
11497
|
+
`npx ${input.args.join(' ')} failed with exit code ${redactedResult.exitCode}.`,
|
|
11498
|
+
redactedResult.stderr.trim() || redactedResult.stdout.trim(),
|
|
11499
|
+
]
|
|
11500
|
+
.filter(Boolean)
|
|
11501
|
+
.join('\n'));
|
|
11502
|
+
}
|
|
11503
|
+
return redactedResult;
|
|
11504
|
+
}
|
|
11505
|
+
async function fetchShopifyDeployToken(input) {
|
|
11506
|
+
const payload = await requestConnectorsApi({
|
|
11507
|
+
connectorsPath: `${encodeURIComponent(input.connectorId)}/shopify/deploy-token`,
|
|
11508
|
+
method: 'POST',
|
|
11509
|
+
baseUrl: input.credentials.baseUrl,
|
|
11510
|
+
token: input.credentials.token,
|
|
11511
|
+
fetcher: input.fetcher,
|
|
11512
|
+
body: {
|
|
11513
|
+
sourceArtifactId: input.sourceArtifactId,
|
|
11514
|
+
...(input.version ? { version: input.version } : {}),
|
|
11515
|
+
},
|
|
11516
|
+
});
|
|
11517
|
+
if (typeof payload.token !== 'string' || !payload.token.trim()) {
|
|
11518
|
+
throw new TenderCliUsageError('Tender did not return a Shopify deploy token. Re-run connector setup or store an App Automation Token.');
|
|
11519
|
+
}
|
|
11520
|
+
return {
|
|
11521
|
+
token: payload.token.trim(),
|
|
11522
|
+
expiresAt: typeof payload.expiresAt === 'number' ? payload.expiresAt : null,
|
|
11523
|
+
};
|
|
11524
|
+
}
|
|
11525
|
+
function localShopifyDeployToken(runtime) {
|
|
11526
|
+
const env = runtime.env ?? process.env;
|
|
11527
|
+
const token = env.SHOPIFY_APP_AUTOMATION_TOKEN;
|
|
11528
|
+
return token?.trim() ? token.trim() : null;
|
|
11529
|
+
}
|
|
11530
|
+
function childShopifyDeployEnv(runtime, token) {
|
|
11531
|
+
return {
|
|
11532
|
+
...process.env,
|
|
11533
|
+
...(runtime.env ?? {}),
|
|
11534
|
+
SHOPIFY_APP_AUTOMATION_TOKEN: token,
|
|
11535
|
+
};
|
|
11536
|
+
}
|
|
11537
|
+
function connectorsProfileFlag(command) {
|
|
11538
|
+
return command.profileName ? `--profile ${command.profileName}` : '--profile account';
|
|
11539
|
+
}
|
|
11540
|
+
function shopifyDeployTokenRecoveryMessage(command, reasonCode) {
|
|
11541
|
+
const profileFlag = connectorsProfileFlag(command);
|
|
11542
|
+
return [
|
|
11543
|
+
reasonCode === 'shopify_deploy_token_expired'
|
|
11544
|
+
? 'The connector stored Shopify App Automation Token is expired.'
|
|
11545
|
+
: 'The connector does not have a stored Shopify App Automation Token for source deploys.',
|
|
11546
|
+
`Store a fresh token: printf "$SHOPIFY_APP_AUTOMATION_TOKEN" | tender connectors shopify app-automation-token set ${command.connectorId} --token-stdin ${profileFlag} --json`,
|
|
11547
|
+
`Setup URL: tender connectors shopify setup-url ${command.connectorId} ${profileFlag} --json`,
|
|
11548
|
+
'Fallback: export SHOPIFY_APP_AUTOMATION_TOKEN locally before running source deploy.',
|
|
11549
|
+
].join('\n');
|
|
11550
|
+
}
|
|
11551
|
+
async function resolveShopifyDeployTokenEnvironment(input) {
|
|
11552
|
+
const localToken = localShopifyDeployToken(input.runtime);
|
|
11553
|
+
if (localToken) {
|
|
11554
|
+
return {
|
|
11555
|
+
env: childShopifyDeployEnv(input.runtime, localToken),
|
|
11556
|
+
redactions: [localToken],
|
|
11557
|
+
source: 'local-env',
|
|
11558
|
+
};
|
|
11559
|
+
}
|
|
11560
|
+
try {
|
|
11561
|
+
const handoff = await fetchShopifyDeployToken({
|
|
11562
|
+
connectorId: input.command.connectorId,
|
|
11563
|
+
sourceArtifactId: input.sourceArtifactId,
|
|
11564
|
+
version: input.command.version,
|
|
11565
|
+
credentials: input.credentials,
|
|
11566
|
+
fetcher: input.fetcher,
|
|
11567
|
+
});
|
|
11568
|
+
return {
|
|
11569
|
+
env: childShopifyDeployEnv(input.runtime, handoff.token),
|
|
11570
|
+
redactions: [handoff.token],
|
|
11571
|
+
source: 'stored-connector-token',
|
|
11572
|
+
};
|
|
11573
|
+
}
|
|
11574
|
+
catch (error) {
|
|
11575
|
+
if (error instanceof TenderCliConnectorsApiError &&
|
|
11576
|
+
(error.reasonCode === 'shopify_deploy_token_required' || error.reasonCode === 'shopify_deploy_token_expired')) {
|
|
11577
|
+
const recovery = shopifyDeployTokenRecoveryMessage(input.command, error.reasonCode);
|
|
11578
|
+
throw new TenderCliGuidedUsageError(recovery, recovery);
|
|
11579
|
+
}
|
|
11580
|
+
throw error;
|
|
11664
11581
|
}
|
|
11665
|
-
return result;
|
|
11666
11582
|
}
|
|
11667
11583
|
async function runConnectorsShopifySourceInit(command, io, runtime) {
|
|
11668
11584
|
const fetcher = runtime.fetcher ?? fetch;
|
|
@@ -11686,14 +11602,6 @@ async function runConnectorsShopifySourceInit(command, io, runtime) {
|
|
|
11686
11602
|
connectorId: command.connectorId,
|
|
11687
11603
|
connector,
|
|
11688
11604
|
clientId: command.clientId,
|
|
11689
|
-
template: command.template,
|
|
11690
|
-
tenderAppUrl: command.tenderAppUrl,
|
|
11691
|
-
tenderElement: command.tenderElement,
|
|
11692
|
-
artifactId: command.artifactId,
|
|
11693
|
-
publicId: command.publicId,
|
|
11694
|
-
themeExtensionHandle: command.themeExtensionHandle,
|
|
11695
|
-
themeExtensionUid: command.themeExtensionUid,
|
|
11696
|
-
blockName: command.blockName,
|
|
11697
11605
|
scopes: command.scopes,
|
|
11698
11606
|
});
|
|
11699
11607
|
const files = await writeShopifySourceFiles({ dir, files: source.files, force: command.force });
|
|
@@ -11708,7 +11616,6 @@ async function runConnectorsShopifySourceInit(command, io, runtime) {
|
|
|
11708
11616
|
const patched = await patchConnectorSourceProject({
|
|
11709
11617
|
connectorId: command.connectorId,
|
|
11710
11618
|
sourceArtifactId: sourceArtifact.sourceArtifactId,
|
|
11711
|
-
template: command.template,
|
|
11712
11619
|
credentials,
|
|
11713
11620
|
fetcher,
|
|
11714
11621
|
updatedAt: runtime.now ? runtime.now() : Date.now(),
|
|
@@ -11732,7 +11639,7 @@ async function runConnectorsShopifySourceInit(command, io, runtime) {
|
|
|
11732
11639
|
commit: gitCommit,
|
|
11733
11640
|
},
|
|
11734
11641
|
validateCommand: commandLine('npx', shopifyValidateArgs({ dir, clientId: command.clientId })),
|
|
11735
|
-
deployCommand: commandLine('npx', shopifyDeployArgs({ dir, clientId: command.clientId, version: '
|
|
11642
|
+
deployCommand: commandLine('npx', shopifyDeployArgs({ dir, clientId: command.clientId, version: 'connector-source', message: null, sourceControlUrl: null, allowDeletes: false })),
|
|
11736
11643
|
};
|
|
11737
11644
|
printConnectorsPayload(command, io, result, [
|
|
11738
11645
|
`connector_id: ${command.connectorId}`,
|
|
@@ -11741,6 +11648,7 @@ async function runConnectorsShopifySourceInit(command, io, runtime) {
|
|
|
11741
11648
|
`files: ${files.length}`,
|
|
11742
11649
|
`git_pushed: ${gitCommit.pushed ? 'true' : 'false'}`,
|
|
11743
11650
|
`next_validate: ${result.validateCommand}`,
|
|
11651
|
+
'next_source_edits: add app blocks, pixels, or metadata definitions from playbooks',
|
|
11744
11652
|
]);
|
|
11745
11653
|
return 0;
|
|
11746
11654
|
}
|
|
@@ -11801,7 +11709,8 @@ async function runConnectorsShopifySourceValidate(command, io, runtime) {
|
|
|
11801
11709
|
return 0;
|
|
11802
11710
|
}
|
|
11803
11711
|
async function runConnectorsShopifySourceDeploy(command, io, runtime) {
|
|
11804
|
-
const
|
|
11712
|
+
const fetcher = runtime.fetcher ?? fetch;
|
|
11713
|
+
const { credentials, connector } = await requestConnector(command, runtime);
|
|
11805
11714
|
const sourceArtifactId = readConnectorSourceArtifactId(connector);
|
|
11806
11715
|
if (!sourceArtifactId) {
|
|
11807
11716
|
throw new TenderCliUsageError('Connector does not have a source artifact yet. Run tender connectors shopify source init <connector-id> --dir ./shopify-app first.');
|
|
@@ -11819,7 +11728,14 @@ async function runConnectorsShopifySourceDeploy(command, io, runtime) {
|
|
|
11819
11728
|
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']);
|
|
11820
11729
|
return 0;
|
|
11821
11730
|
}
|
|
11822
|
-
const
|
|
11731
|
+
const deployToken = await resolveShopifyDeployTokenEnvironment({
|
|
11732
|
+
command,
|
|
11733
|
+
sourceArtifactId,
|
|
11734
|
+
credentials,
|
|
11735
|
+
runtime,
|
|
11736
|
+
fetcher,
|
|
11737
|
+
});
|
|
11738
|
+
const result = await runNpxCommand({ args, cwd: dir, runtime, env: deployToken.env, redactions: deployToken.redactions });
|
|
11823
11739
|
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']);
|
|
11824
11740
|
return 0;
|
|
11825
11741
|
}
|
|
@@ -12662,7 +12578,7 @@ function cliErrorMessage(error) {
|
|
|
12662
12578
|
return String(error);
|
|
12663
12579
|
}
|
|
12664
12580
|
function cliErrorCode(error) {
|
|
12665
|
-
if (error instanceof TenderCliDbApiError || error instanceof TenderCliAnalyticsApiError) {
|
|
12581
|
+
if (error instanceof TenderCliDbApiError || error instanceof TenderCliAnalyticsApiError || error instanceof TenderCliConnectorsApiError) {
|
|
12666
12582
|
return error.reasonCode;
|
|
12667
12583
|
}
|
|
12668
12584
|
if (error && typeof error === 'object' && 'code' in error) {
|
|
@@ -12737,6 +12653,9 @@ function exampleForError(error, args, command) {
|
|
|
12737
12653
|
return dbPath ? dbCorrectedExample(artifactId, dbPath) : null;
|
|
12738
12654
|
}
|
|
12739
12655
|
function nextActionForError(error) {
|
|
12656
|
+
if (error instanceof TenderCliGuidedUsageError) {
|
|
12657
|
+
return error.nextAction;
|
|
12658
|
+
}
|
|
12740
12659
|
return error instanceof TenderCliDbApiError ? error.nextAction : null;
|
|
12741
12660
|
}
|
|
12742
12661
|
function cliErrorPayload(error, args, command) {
|