@tenderprompt/cli 0.1.26 → 0.1.28
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.js +2966 -916
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -146,6 +146,16 @@ Commands:
|
|
|
146
146
|
tender playbooks get <id> Fetch one remote playbook body on demand
|
|
147
147
|
tender playbooks file <id> --path <path>
|
|
148
148
|
Fetch one playbook reference file on demand
|
|
149
|
+
tender connectors list List account-level connector instances
|
|
150
|
+
tender connectors shopify create
|
|
151
|
+
Create a merchant-owned Shopify connector
|
|
152
|
+
tender connectors shopify credentials set
|
|
153
|
+
Store Shopify custom app credentials from stdin or env
|
|
154
|
+
tender connectors shopify app-automation-token set
|
|
155
|
+
Store Shopify CLI deployment token from stdin or env
|
|
156
|
+
tender connectors shopify verify
|
|
157
|
+
Exchange credentials for an Admin API access token
|
|
158
|
+
tender connectors bind Print an app.json binding for a connector instance
|
|
149
159
|
tender app list List Tender App records visible to the current token
|
|
150
160
|
tender app create --name <name>
|
|
151
161
|
Create a new Tender App in the current account
|
|
@@ -217,6 +227,12 @@ Examples:
|
|
|
217
227
|
tender playbooks list --json
|
|
218
228
|
tender playbooks get recharge-bundle-builder --json
|
|
219
229
|
tender playbooks file recharge-bundle-builder --path references/recharge-payloads.md --json
|
|
230
|
+
tender connectors list --profile account --json
|
|
231
|
+
tender connectors shopify create --instance default --admin-api-version 2026-04 --shop demo.myshopify.com --profile account --json
|
|
232
|
+
printf "$SHOPIFY_CLIENT_SECRET" | tender connectors shopify credentials set conn_123 --shop demo.myshopify.com --client-id abc --client-secret-stdin --profile account --json
|
|
233
|
+
printf "$SHOPIFY_APP_AUTOMATION_TOKEN" | tender connectors shopify app-automation-token set conn_123 --token-stdin --expires-at 2026-08-22 --profile account --json
|
|
234
|
+
tender connectors shopify verify conn_123 --profile account --json
|
|
235
|
+
tender connectors bind SHOPIFY_CONNECTOR conn_123 --instance default --json
|
|
220
236
|
tender app list --json
|
|
221
237
|
tender app create --name "Exit Intent Widget" --json
|
|
222
238
|
tender app create --name "Exit Intent Widget" --init --dir ./widget --preview --json
|
|
@@ -380,6 +396,159 @@ This command does not write playbooks to disk.
|
|
|
380
396
|
Examples:
|
|
381
397
|
tender playbooks file recharge-bundle-builder --path references/recharge-payloads.md --json`;
|
|
382
398
|
}
|
|
399
|
+
function connectorsHelp() {
|
|
400
|
+
return `Usage: tender connectors <command> [options]
|
|
401
|
+
|
|
402
|
+
Commands:
|
|
403
|
+
tender connectors list
|
|
404
|
+
List account-level connector instances
|
|
405
|
+
tender connectors shopify create
|
|
406
|
+
Create a Shopify connector and setup URL
|
|
407
|
+
tender connectors shopify setup-url <connector-id>
|
|
408
|
+
Create a fresh credential setup URL
|
|
409
|
+
tender connectors shopify credentials set <connector-id>
|
|
410
|
+
Store custom app credentials from stdin or env
|
|
411
|
+
tender connectors shopify app-automation-token set <connector-id>
|
|
412
|
+
Store Shopify CLI deployment token from stdin or env
|
|
413
|
+
tender connectors shopify app-automation-token delete <connector-id>
|
|
414
|
+
Remove the stored Shopify CLI deployment token
|
|
415
|
+
tender connectors shopify verify <connector-id>
|
|
416
|
+
Verify credentials with Shopify
|
|
417
|
+
tender connectors shopify source init <connector-id>
|
|
418
|
+
Create/ensure internal Artifact Git Shopify app source
|
|
419
|
+
tender connectors shopify source checkout <connector-id>
|
|
420
|
+
Checkout the connector source artifact
|
|
421
|
+
tender connectors shopify source validate <connector-id>
|
|
422
|
+
Validate connector source with Shopify CLI
|
|
423
|
+
tender connectors shopify source deploy <connector-id>
|
|
424
|
+
Deploy connector source with Shopify CLI
|
|
425
|
+
tender connectors shopify delete <connector-id>
|
|
426
|
+
Disable and remove stored credentials
|
|
427
|
+
tender connectors bind <binding-name> <connector-id>
|
|
428
|
+
Print an app.json binding for the connector instance
|
|
429
|
+
|
|
430
|
+
Examples:
|
|
431
|
+
tender connectors list --profile account --json
|
|
432
|
+
tender connectors shopify create --instance default --admin-api-version 2026-04 --shop demo.myshopify.com --profile account --json
|
|
433
|
+
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
|
+
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
|
+
tender connectors shopify verify conn_123 --profile account --json
|
|
436
|
+
tender connectors shopify source init conn_123 --dir ./shopify-app --client-id abc --tender-app-url https://example.tenderprompt.app/client.js --tender-element tender-widget --profile account --json
|
|
437
|
+
tender connectors shopify source validate conn_123 --dir ./shopify-app --execute --json
|
|
438
|
+
SHOPIFY_APP_AUTOMATION_TOKEN=shpat_... tender connectors shopify source deploy conn_123 --dir ./shopify-app --confirm deploy --json
|
|
439
|
+
tender connectors bind SHOPIFY_CONNECTOR conn_123 --instance default --json`;
|
|
440
|
+
}
|
|
441
|
+
function connectorsShopifyCreateHelp() {
|
|
442
|
+
return `Usage: tender connectors shopify create [--instance <id>] [--label <text>] [--app-name <text>] [--admin-api-version <version>] [--shop <myshopify-domain>] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
443
|
+
|
|
444
|
+
Creates a merchant-owned Shopify connector record and returns a setup URL for
|
|
445
|
+
secure credential capture.
|
|
446
|
+
|
|
447
|
+
Examples:
|
|
448
|
+
tender connectors shopify create --instance default --admin-api-version 2026-04 --shop demo.myshopify.com --profile account --json`;
|
|
449
|
+
}
|
|
450
|
+
function connectorsShopifySetupUrlHelp() {
|
|
451
|
+
return `Usage: tender connectors shopify setup-url <connector-id> [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
452
|
+
|
|
453
|
+
Creates a fresh one-time credential setup URL for an existing connector.
|
|
454
|
+
|
|
455
|
+
Examples:
|
|
456
|
+
tender connectors shopify setup-url conn_123 --profile account --json`;
|
|
457
|
+
}
|
|
458
|
+
function connectorsShopifyCredentialsSetHelp() {
|
|
459
|
+
return `Usage: tender connectors shopify credentials set <connector-id> --shop <myshopify-domain> --client-id <id> (--client-secret-stdin | --client-secret-env <name>) [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
460
|
+
|
|
461
|
+
Stores credentials without printing the client secret. Prefer stdin or an env
|
|
462
|
+
var so shell history does not capture the secret.
|
|
463
|
+
|
|
464
|
+
Examples:
|
|
465
|
+
printf "$SHOPIFY_CLIENT_SECRET" | tender connectors shopify credentials set conn_123 --shop demo.myshopify.com --client-id abc --client-secret-stdin --profile account --json
|
|
466
|
+
SHOPIFY_CLIENT_SECRET=shpss_123 tender connectors shopify credentials set conn_123 --shop demo.myshopify.com --client-id abc --client-secret-env SHOPIFY_CLIENT_SECRET --profile account --json`;
|
|
467
|
+
}
|
|
468
|
+
function connectorsShopifyAppAutomationTokenSetHelp() {
|
|
469
|
+
return `Usage: tender connectors shopify app-automation-token set <connector-id> (--token-stdin | --token-env <name>) [--expires-at <date-or-iso>] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
470
|
+
|
|
471
|
+
Stores a Shopify App Automation Token for non-interactive Shopify CLI deploys.
|
|
472
|
+
This token is separate from the connector's Admin API runtime credentials.
|
|
473
|
+
|
|
474
|
+
Examples:
|
|
475
|
+
printf "$SHOPIFY_APP_AUTOMATION_TOKEN" | tender connectors shopify app-automation-token set conn_123 --token-stdin --expires-at 2026-08-22 --profile account --json
|
|
476
|
+
tender connectors shopify app-automation-token set conn_123 --token-env SHOPIFY_APP_AUTOMATION_TOKEN --expires-at 2026-08-22 --profile account --json`;
|
|
477
|
+
}
|
|
478
|
+
function connectorsShopifyAppAutomationTokenDeleteHelp() {
|
|
479
|
+
return `Usage: tender connectors shopify app-automation-token delete <connector-id> --confirm delete [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
480
|
+
|
|
481
|
+
Removes the stored Shopify App Automation Token metadata and secret.
|
|
482
|
+
|
|
483
|
+
Examples:
|
|
484
|
+
tender connectors shopify app-automation-token delete conn_123 --confirm delete --profile account --json`;
|
|
485
|
+
}
|
|
486
|
+
function connectorsShopifyVerifyHelp() {
|
|
487
|
+
return `Usage: tender connectors shopify verify <connector-id> [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
488
|
+
|
|
489
|
+
Exchanges the stored client ID and secret through Shopify's client credentials
|
|
490
|
+
grant and stores the returned Admin API access token.
|
|
491
|
+
|
|
492
|
+
Examples:
|
|
493
|
+
tender connectors shopify verify conn_123 --profile account --json`;
|
|
494
|
+
}
|
|
495
|
+
function connectorsShopifySourceInitHelp() {
|
|
496
|
+
return `Usage: tender connectors shopify source init <connector-id> --dir <path> --client-id <shopify-client-id> --tender-app-url <client.js-url> --tender-element <custom-element> [--source-name <name>] [--artifact-id <id>] [--public-id <id>] [--theme-extension-handle <handle>] [--theme-extension-uid <uid>] [--block-name <name>] [--scopes <csv>] [--force] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
497
|
+
|
|
498
|
+
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 source,
|
|
500
|
+
commits it, pushes it to Artifact Git, and records sourceArtifactId on the
|
|
501
|
+
connector. The generated source includes shopify.app.toml, dog-profile app-owned
|
|
502
|
+
custom data definitions, and a Theme App Extension app-block template.
|
|
503
|
+
|
|
504
|
+
Examples:
|
|
505
|
+
tender connectors shopify source init conn_123 --dir ./shopify-app --client-id abc --tender-app-url https://example.tenderprompt.app/client.js --tender-element tender-widget --profile account --json`;
|
|
506
|
+
}
|
|
507
|
+
function connectorsShopifySourceCheckoutHelp() {
|
|
508
|
+
return `Usage: tender connectors shopify source checkout <connector-id> --dir <path> [--remote <name>] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
509
|
+
|
|
510
|
+
Checks out the connector's internal Shopify source artifact and configures the
|
|
511
|
+
Tender Artifact Git remote.
|
|
512
|
+
|
|
513
|
+
Examples:
|
|
514
|
+
tender connectors shopify source checkout conn_123 --dir ./shopify-app --profile account --json`;
|
|
515
|
+
}
|
|
516
|
+
function connectorsShopifySourceValidateHelp() {
|
|
517
|
+
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]
|
|
518
|
+
|
|
519
|
+
Without --execute, prints the exact Shopify CLI validation command. With
|
|
520
|
+
--execute, runs npx --yes @shopify/cli@latest app config validate.
|
|
521
|
+
|
|
522
|
+
Examples:
|
|
523
|
+
tender connectors shopify source validate conn_123 --dir ./shopify-app --execute --json`;
|
|
524
|
+
}
|
|
525
|
+
function connectorsShopifySourceDeployHelp() {
|
|
526
|
+
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
|
+
|
|
528
|
+
Without --confirm deploy, prints the exact Shopify CLI deployment command. With
|
|
529
|
+
--confirm deploy, runs npx --yes @shopify/cli@latest app deploy. Set
|
|
530
|
+
SHOPIFY_APP_AUTOMATION_TOKEN in the environment for non-interactive deploys.
|
|
531
|
+
|
|
532
|
+
Examples:
|
|
533
|
+
SHOPIFY_APP_AUTOMATION_TOKEN=shpat_... tender connectors shopify source deploy conn_123 --dir ./shopify-app --version dog-profile-smoke --confirm deploy --json`;
|
|
534
|
+
}
|
|
535
|
+
function connectorsShopifyDeleteHelp() {
|
|
536
|
+
return `Usage: tender connectors shopify delete <connector-id> --confirm delete [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
537
|
+
|
|
538
|
+
Disables a connector and removes its stored credentials.
|
|
539
|
+
|
|
540
|
+
Examples:
|
|
541
|
+
tender connectors shopify delete conn_123 --confirm delete --profile account --json`;
|
|
542
|
+
}
|
|
543
|
+
function connectorsBindHelp() {
|
|
544
|
+
return `Usage: tender connectors bind <binding-name> <connector-id> --instance <id> [--label <text>] [--json]
|
|
545
|
+
|
|
546
|
+
Prints the app.json binding snippet a Tender App can opt into. The app receives
|
|
547
|
+
the binding at runtime as env.<binding-name>.
|
|
548
|
+
|
|
549
|
+
Examples:
|
|
550
|
+
tender connectors bind SHOPIFY_CONNECTOR conn_123 --instance default --json`;
|
|
551
|
+
}
|
|
383
552
|
function appHelp() {
|
|
384
553
|
return `Usage: tender app <command> [options]
|
|
385
554
|
|
|
@@ -1307,6 +1476,47 @@ function tenderCliCapabilities() {
|
|
|
1307
1476
|
persistence: 'session_only',
|
|
1308
1477
|
writesToCheckout: false,
|
|
1309
1478
|
},
|
|
1479
|
+
connectors: {
|
|
1480
|
+
mode: 'tenant_owned_platform_bindings',
|
|
1481
|
+
listCommand: 'tender connectors list --profile account --json',
|
|
1482
|
+
bindCommand: 'tender connectors bind <binding-name> <connector-id> --instance <instance-id> --json',
|
|
1483
|
+
shopify: {
|
|
1484
|
+
playbookId: 'shopify-connector-custom-app',
|
|
1485
|
+
playbookCommand: 'tender playbooks get shopify-connector-custom-app --json',
|
|
1486
|
+
customerMetadataPlaybookCommand: 'tender playbooks get shopify-connector-customer-metadata-widget --json',
|
|
1487
|
+
whatItIs: 'A merchant-owned Shopify custom app exposed to Tender apps through an explicit platform connector binding.',
|
|
1488
|
+
whenToUse: [
|
|
1489
|
+
'Shopify Admin GraphQL reads or writes',
|
|
1490
|
+
'customer metafields or customer-linked metaobjects',
|
|
1491
|
+
'app-owned custom data definitions',
|
|
1492
|
+
'Theme App Extension app blocks',
|
|
1493
|
+
'privileged background Shopify work that browser code cannot safely perform',
|
|
1494
|
+
],
|
|
1495
|
+
whenNotToUse: [
|
|
1496
|
+
'pure frontend widgets',
|
|
1497
|
+
'Tender analytics only',
|
|
1498
|
+
'public Shopify data already present on the page',
|
|
1499
|
+
'client-side event publishing without Admin API writes',
|
|
1500
|
+
],
|
|
1501
|
+
binding: {
|
|
1502
|
+
name: 'SHOPIFY_CONNECTOR',
|
|
1503
|
+
source: 'platform:shopify_connector',
|
|
1504
|
+
interface: 'ShopifyConnector',
|
|
1505
|
+
callShape: 'await this.env.SHOPIFY_CONNECTOR.invoke("shopify.graphql", { query, variables })',
|
|
1506
|
+
},
|
|
1507
|
+
sourceProject: {
|
|
1508
|
+
durableIdentity: 'connector.sourceProject.sourceArtifactId',
|
|
1509
|
+
targetPackId: 'shopify-connector-source',
|
|
1510
|
+
visibility: 'internal_source_artifact_not_customer_facing_app',
|
|
1511
|
+
},
|
|
1512
|
+
guardrails: [
|
|
1513
|
+
'Use the account profile because connectors are account-level resources.',
|
|
1514
|
+
'Use minimum Shopify scopes for the requested job.',
|
|
1515
|
+
'Never commit client secrets, Admin API tokens, app automation tokens, Tender tokens, or customer data exports.',
|
|
1516
|
+
'Do not create bindings/<id>/ files for Shopify connectors; use the platform connector source in app.json.',
|
|
1517
|
+
],
|
|
1518
|
+
},
|
|
1519
|
+
},
|
|
1310
1520
|
analyticsAuthoring: {
|
|
1311
1521
|
command: ANALYTICS_AUTHORING_COMMAND,
|
|
1312
1522
|
playbookId: ANALYTICS_AUTHORING_PLAYBOOK_ID,
|
|
@@ -1333,6 +1543,8 @@ function tenderCliCapabilities() {
|
|
|
1333
1543
|
'tender app db --help',
|
|
1334
1544
|
'tender app bindings outbound --help',
|
|
1335
1545
|
'tender app outbound-auth --help',
|
|
1546
|
+
'tender connectors --help',
|
|
1547
|
+
'tender connectors shopify source init --help',
|
|
1336
1548
|
],
|
|
1337
1549
|
},
|
|
1338
1550
|
workflows: [
|
|
@@ -1358,6 +1570,23 @@ function tenderCliCapabilities() {
|
|
|
1358
1570
|
'Do not write fetched playbook contents into the checkout or install them as skills.',
|
|
1359
1571
|
],
|
|
1360
1572
|
},
|
|
1573
|
+
{
|
|
1574
|
+
name: 'shopify_connector_discovery',
|
|
1575
|
+
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.',
|
|
1576
|
+
commands: [
|
|
1577
|
+
'tender capabilities --json',
|
|
1578
|
+
'tender playbooks list --json',
|
|
1579
|
+
'tender playbooks get shopify-connector-custom-app --json',
|
|
1580
|
+
'tender connectors list --profile account --json',
|
|
1581
|
+
'tender connectors bind SHOPIFY_CONNECTOR <connector-id> --instance default --json',
|
|
1582
|
+
],
|
|
1583
|
+
notes: [
|
|
1584
|
+
'Connectors are tenant-owned platform bindings, not generated binding workers.',
|
|
1585
|
+
'The merchant owns the Shopify custom app; Tender stores connector credentials and app source metadata.',
|
|
1586
|
+
'Use connector.sourceProject.sourceArtifactId as the durable Shopify app source identity.',
|
|
1587
|
+
'Fetch shopify-connector-customer-metadata-widget when the requested job involves customer metafields, metaobjects, or profile writes.',
|
|
1588
|
+
],
|
|
1589
|
+
},
|
|
1361
1590
|
{
|
|
1362
1591
|
name: 'create_new_app_with_preview',
|
|
1363
1592
|
when: 'When the user wants a new Tender App that is immediately visible.',
|
|
@@ -3077,643 +3306,682 @@ function parseShareCommonArgs(args, startIndex) {
|
|
|
3077
3306
|
}
|
|
3078
3307
|
return { baseUrl, flagToken, profileName, json, remaining };
|
|
3079
3308
|
}
|
|
3080
|
-
function
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
expires = parseRequiredFlagValue(parsed.remaining[index + 1], '--expires');
|
|
3309
|
+
function parseConnectorsCommonArgs(args, startIndex) {
|
|
3310
|
+
let baseUrl = null;
|
|
3311
|
+
let flagToken = null;
|
|
3312
|
+
let profileName = null;
|
|
3313
|
+
let json = false;
|
|
3314
|
+
const remaining = [];
|
|
3315
|
+
for (let index = startIndex; index < args.length; index += 1) {
|
|
3316
|
+
const arg = args[index];
|
|
3317
|
+
if (arg === '--json') {
|
|
3318
|
+
json = true;
|
|
3319
|
+
continue;
|
|
3320
|
+
}
|
|
3321
|
+
if (arg === '--base-url') {
|
|
3322
|
+
baseUrl = parseBaseUrlFlag(args, index);
|
|
3095
3323
|
index += 1;
|
|
3096
3324
|
continue;
|
|
3097
3325
|
}
|
|
3098
|
-
if (arg === '--
|
|
3099
|
-
|
|
3326
|
+
if (arg === '--token') {
|
|
3327
|
+
flagToken = parseTokenFlag(args, index);
|
|
3100
3328
|
index += 1;
|
|
3101
3329
|
continue;
|
|
3102
3330
|
}
|
|
3103
|
-
|
|
3331
|
+
if (arg === '--profile') {
|
|
3332
|
+
profileName = parseProfileName(args[index + 1], '--profile');
|
|
3333
|
+
index += 1;
|
|
3334
|
+
continue;
|
|
3335
|
+
}
|
|
3336
|
+
remaining.push(arg);
|
|
3104
3337
|
}
|
|
3105
|
-
return {
|
|
3338
|
+
return { baseUrl, flagToken, profileName, json, remaining };
|
|
3106
3339
|
}
|
|
3107
|
-
function
|
|
3108
|
-
if (
|
|
3109
|
-
|
|
3340
|
+
function parseConnectorId(value, usage) {
|
|
3341
|
+
if (!value || value.startsWith('-')) {
|
|
3342
|
+
throw new TenderCliUsageError(`connector id is required. Example: ${usage}`);
|
|
3110
3343
|
}
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3344
|
+
return value;
|
|
3345
|
+
}
|
|
3346
|
+
function parseConnectorInstanceId(value) {
|
|
3347
|
+
const normalized = (value ?? 'default').trim().toLowerCase();
|
|
3348
|
+
if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
|
|
3349
|
+
throw new TenderCliUsageError('--instance must start with a letter or number and contain only lowercase letters, numbers, underscores, or dashes.');
|
|
3114
3350
|
}
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3351
|
+
return normalized;
|
|
3352
|
+
}
|
|
3353
|
+
function parseBindingName(value) {
|
|
3354
|
+
if (!value || value.startsWith('-')) {
|
|
3355
|
+
throw new TenderCliUsageError('binding name is required. Example: tender connectors bind SHOPIFY_CONNECTOR conn_123 --instance default --json');
|
|
3118
3356
|
}
|
|
3119
|
-
|
|
3357
|
+
if (!/^[A-Z][A-Z0-9_]{0,79}$/.test(value)) {
|
|
3358
|
+
throw new TenderCliUsageError('binding name must look like SHOPIFY_CONNECTOR.');
|
|
3359
|
+
}
|
|
3360
|
+
return value;
|
|
3120
3361
|
}
|
|
3121
|
-
function
|
|
3362
|
+
function parseConnectorsListArgs(args) {
|
|
3122
3363
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3123
|
-
return { command: 'help', topic: '
|
|
3124
|
-
}
|
|
3125
|
-
const shareId = args[0];
|
|
3126
|
-
if (!shareId || shareId.startsWith('-')) {
|
|
3127
|
-
throw new TenderCliUsageError('share id is required. Example: tender app share revoke <share-id> --json');
|
|
3364
|
+
return { command: 'help', topic: 'connectors list' };
|
|
3128
3365
|
}
|
|
3129
|
-
const parsed =
|
|
3366
|
+
const parsed = parseConnectorsCommonArgs(args, 0);
|
|
3130
3367
|
if (parsed.remaining.length > 0) {
|
|
3131
|
-
throw new TenderCliUsageError(`Unknown
|
|
3368
|
+
throw new TenderCliUsageError(`Unknown connectors list option: ${parsed.remaining[0]}`);
|
|
3132
3369
|
}
|
|
3133
|
-
return { command: '
|
|
3370
|
+
return { command: 'connectors list', ...parsed };
|
|
3134
3371
|
}
|
|
3135
|
-
function
|
|
3372
|
+
function parseConnectorsShopifyCreateArgs(args) {
|
|
3136
3373
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3137
|
-
return { command: 'help', topic: '
|
|
3138
|
-
}
|
|
3139
|
-
const token = args[0];
|
|
3140
|
-
if (!token || token.startsWith('-')) {
|
|
3141
|
-
throw new TenderCliUsageError('share token is required. Example: tender app claim <token> --dir ./widget');
|
|
3374
|
+
return { command: 'help', topic: 'connectors shopify create' };
|
|
3142
3375
|
}
|
|
3143
|
-
const parsed =
|
|
3144
|
-
let
|
|
3145
|
-
let
|
|
3146
|
-
let
|
|
3376
|
+
const parsed = parseConnectorsCommonArgs(args, 0);
|
|
3377
|
+
let instanceId = 'default';
|
|
3378
|
+
let label = null;
|
|
3379
|
+
let appName = null;
|
|
3380
|
+
let adminApiVersion = null;
|
|
3381
|
+
let shopDomain = null;
|
|
3147
3382
|
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
3148
3383
|
const arg = parsed.remaining[index];
|
|
3149
|
-
if (arg === '--
|
|
3150
|
-
|
|
3384
|
+
if (arg === '--instance') {
|
|
3385
|
+
instanceId = parseConnectorInstanceId(parsed.remaining[index + 1]);
|
|
3151
3386
|
index += 1;
|
|
3152
3387
|
continue;
|
|
3153
3388
|
}
|
|
3154
|
-
if (arg === '--
|
|
3155
|
-
|
|
3389
|
+
if (arg === '--label') {
|
|
3390
|
+
label = parseRequiredFlagValue(parsed.remaining[index + 1], '--label');
|
|
3156
3391
|
index += 1;
|
|
3157
3392
|
continue;
|
|
3158
3393
|
}
|
|
3159
|
-
if (arg === '--
|
|
3160
|
-
|
|
3394
|
+
if (arg === '--app-name') {
|
|
3395
|
+
appName = parseRequiredFlagValue(parsed.remaining[index + 1], '--app-name');
|
|
3161
3396
|
index += 1;
|
|
3162
3397
|
continue;
|
|
3163
3398
|
}
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3399
|
+
if (arg === '--admin-api-version') {
|
|
3400
|
+
adminApiVersion = parseRequiredFlagValue(parsed.remaining[index + 1], '--admin-api-version');
|
|
3401
|
+
index += 1;
|
|
3402
|
+
continue;
|
|
3403
|
+
}
|
|
3404
|
+
if (arg === '--shop') {
|
|
3405
|
+
shopDomain = parseRequiredFlagValue(parsed.remaining[index + 1], '--shop');
|
|
3406
|
+
index += 1;
|
|
3407
|
+
continue;
|
|
3408
|
+
}
|
|
3409
|
+
throw new TenderCliUsageError(`Unknown connectors shopify create option: ${arg}`);
|
|
3168
3410
|
}
|
|
3169
|
-
return {
|
|
3411
|
+
return {
|
|
3412
|
+
command: 'connectors shopify create',
|
|
3413
|
+
instanceId,
|
|
3414
|
+
label,
|
|
3415
|
+
appName,
|
|
3416
|
+
adminApiVersion,
|
|
3417
|
+
shopDomain,
|
|
3418
|
+
...parsed,
|
|
3419
|
+
};
|
|
3170
3420
|
}
|
|
3171
|
-
function
|
|
3421
|
+
function parseConnectorsShopifySetupUrlArgs(args) {
|
|
3172
3422
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3173
|
-
return { command: 'help', topic: '
|
|
3423
|
+
return { command: 'help', topic: 'connectors shopify setup-url' };
|
|
3174
3424
|
}
|
|
3175
|
-
const
|
|
3176
|
-
|
|
3177
|
-
|
|
3425
|
+
const connectorId = parseConnectorId(args[0], 'tender connectors shopify setup-url <connector-id> --json');
|
|
3426
|
+
const parsed = parseConnectorsCommonArgs(args, 1);
|
|
3427
|
+
if (parsed.remaining.length > 0) {
|
|
3428
|
+
throw new TenderCliUsageError(`Unknown connectors shopify setup-url option: ${parsed.remaining[0]}`);
|
|
3178
3429
|
}
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
let
|
|
3188
|
-
let
|
|
3189
|
-
let
|
|
3190
|
-
for (let index =
|
|
3191
|
-
const arg =
|
|
3192
|
-
if (arg === '--
|
|
3193
|
-
|
|
3194
|
-
continue;
|
|
3195
|
-
}
|
|
3196
|
-
if (arg === '--dry-run') {
|
|
3197
|
-
dryRun = true;
|
|
3198
|
-
continue;
|
|
3199
|
-
}
|
|
3200
|
-
if (arg === '--force') {
|
|
3201
|
-
force = true;
|
|
3202
|
-
continue;
|
|
3203
|
-
}
|
|
3204
|
-
if (arg === '--preview') {
|
|
3205
|
-
preview = true;
|
|
3206
|
-
continue;
|
|
3207
|
-
}
|
|
3208
|
-
if (arg === '--no-scaffold') {
|
|
3209
|
-
scaffold = 'none';
|
|
3210
|
-
continue;
|
|
3211
|
-
}
|
|
3212
|
-
if (arg === '--dir') {
|
|
3213
|
-
const value = args[index + 1];
|
|
3214
|
-
if (!value || value.startsWith('-')) {
|
|
3215
|
-
throw new TenderCliUsageError('--dir requires a path.');
|
|
3216
|
-
}
|
|
3217
|
-
dir = value;
|
|
3430
|
+
return { command: 'connectors shopify setup-url', connectorId, ...parsed };
|
|
3431
|
+
}
|
|
3432
|
+
function parseConnectorsShopifyCredentialsSetArgs(args) {
|
|
3433
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
3434
|
+
return { command: 'help', topic: 'connectors shopify credentials set' };
|
|
3435
|
+
}
|
|
3436
|
+
const connectorId = parseConnectorId(args[0], 'tender connectors shopify credentials set <connector-id> --shop <domain> --client-id <id> --client-secret-stdin --json');
|
|
3437
|
+
const parsed = parseConnectorsCommonArgs(args, 1);
|
|
3438
|
+
let shopDomain = null;
|
|
3439
|
+
let clientId = null;
|
|
3440
|
+
let clientSecretSource = null;
|
|
3441
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
3442
|
+
const arg = parsed.remaining[index];
|
|
3443
|
+
if (arg === '--shop') {
|
|
3444
|
+
shopDomain = parseRequiredFlagValue(parsed.remaining[index + 1], '--shop');
|
|
3218
3445
|
index += 1;
|
|
3219
3446
|
continue;
|
|
3220
3447
|
}
|
|
3221
|
-
if (arg === '--
|
|
3222
|
-
|
|
3448
|
+
if (arg === '--client-id') {
|
|
3449
|
+
clientId = parseRequiredFlagValue(parsed.remaining[index + 1], '--client-id');
|
|
3223
3450
|
index += 1;
|
|
3224
3451
|
continue;
|
|
3225
3452
|
}
|
|
3226
|
-
if (arg === '--
|
|
3227
|
-
|
|
3228
|
-
index += 1;
|
|
3453
|
+
if (arg === '--client-secret-stdin') {
|
|
3454
|
+
clientSecretSource = { kind: 'stdin' };
|
|
3229
3455
|
continue;
|
|
3230
3456
|
}
|
|
3231
|
-
if (arg === '--
|
|
3232
|
-
|
|
3457
|
+
if (arg === '--client-secret-env') {
|
|
3458
|
+
clientSecretSource = {
|
|
3459
|
+
kind: 'env',
|
|
3460
|
+
name: parseRequiredFlagValue(parsed.remaining[index + 1], '--client-secret-env'),
|
|
3461
|
+
};
|
|
3233
3462
|
index += 1;
|
|
3234
3463
|
continue;
|
|
3235
3464
|
}
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3465
|
+
throw new TenderCliUsageError(`Unknown connectors shopify credentials set option: ${arg}`);
|
|
3466
|
+
}
|
|
3467
|
+
if (!shopDomain) {
|
|
3468
|
+
throw new TenderCliUsageError('--shop is required. Example: tender connectors shopify credentials set conn_123 --shop demo.myshopify.com --client-id abc --client-secret-stdin --json');
|
|
3469
|
+
}
|
|
3470
|
+
if (!clientId) {
|
|
3471
|
+
throw new TenderCliUsageError('--client-id is required. Example: tender connectors shopify credentials set conn_123 --shop demo.myshopify.com --client-id abc --client-secret-stdin --json');
|
|
3472
|
+
}
|
|
3473
|
+
if (!clientSecretSource) {
|
|
3474
|
+
throw new TenderCliUsageError('--client-secret-stdin or --client-secret-env is required.');
|
|
3475
|
+
}
|
|
3476
|
+
return {
|
|
3477
|
+
command: 'connectors shopify credentials set',
|
|
3478
|
+
connectorId,
|
|
3479
|
+
shopDomain,
|
|
3480
|
+
clientId,
|
|
3481
|
+
clientSecretSource,
|
|
3482
|
+
...parsed,
|
|
3483
|
+
};
|
|
3484
|
+
}
|
|
3485
|
+
function parseConnectorsShopifyAppAutomationTokenSetArgs(args) {
|
|
3486
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
3487
|
+
return { command: 'help', topic: 'connectors shopify app-automation-token set' };
|
|
3488
|
+
}
|
|
3489
|
+
const connectorId = parseConnectorId(args[0], 'tender connectors shopify app-automation-token set <connector-id> --token-stdin --json');
|
|
3490
|
+
const parsed = parseConnectorsCommonArgs(args, 1);
|
|
3491
|
+
let tokenSource = null;
|
|
3492
|
+
let expiresAt = null;
|
|
3493
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
3494
|
+
const arg = parsed.remaining[index];
|
|
3495
|
+
if (arg === '--token-stdin') {
|
|
3496
|
+
tokenSource = { kind: 'stdin' };
|
|
3239
3497
|
continue;
|
|
3240
3498
|
}
|
|
3241
|
-
if (arg === '--
|
|
3242
|
-
|
|
3499
|
+
if (arg === '--token-env') {
|
|
3500
|
+
tokenSource = {
|
|
3501
|
+
kind: 'env',
|
|
3502
|
+
name: parseRequiredFlagValue(parsed.remaining[index + 1], '--token-env'),
|
|
3503
|
+
};
|
|
3243
3504
|
index += 1;
|
|
3244
3505
|
continue;
|
|
3245
3506
|
}
|
|
3246
|
-
if (arg === '--
|
|
3247
|
-
|
|
3507
|
+
if (arg === '--expires-at') {
|
|
3508
|
+
expiresAt = parseRequiredFlagValue(parsed.remaining[index + 1], '--expires-at');
|
|
3248
3509
|
index += 1;
|
|
3249
3510
|
continue;
|
|
3250
3511
|
}
|
|
3251
|
-
throw new TenderCliUsageError(`Unknown app
|
|
3512
|
+
throw new TenderCliUsageError(`Unknown connectors shopify app-automation-token set option: ${arg}`);
|
|
3513
|
+
}
|
|
3514
|
+
if (!tokenSource) {
|
|
3515
|
+
throw new TenderCliUsageError('--token-stdin or --token-env is required.');
|
|
3252
3516
|
}
|
|
3253
3517
|
return {
|
|
3254
|
-
command: 'app
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
flagToken,
|
|
3260
|
-
profileName,
|
|
3261
|
-
ttl,
|
|
3262
|
-
scaffold,
|
|
3263
|
-
preview,
|
|
3264
|
-
dryRun,
|
|
3265
|
-
force,
|
|
3266
|
-
json,
|
|
3518
|
+
command: 'connectors shopify app-automation-token set',
|
|
3519
|
+
connectorId,
|
|
3520
|
+
tokenSource,
|
|
3521
|
+
expiresAt,
|
|
3522
|
+
...parsed,
|
|
3267
3523
|
};
|
|
3268
3524
|
}
|
|
3269
|
-
function
|
|
3525
|
+
function parseConnectorsShopifyAppAutomationTokenDeleteArgs(args) {
|
|
3270
3526
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3271
|
-
return { command: 'help', topic: 'app
|
|
3272
|
-
}
|
|
3273
|
-
const artifactId = args[0];
|
|
3274
|
-
if (!artifactId || artifactId.startsWith('-')) {
|
|
3275
|
-
throw new TenderCliUsageError('app id is required. Example: tender app context fetch <app-id> --dir ./widget --json');
|
|
3527
|
+
return { command: 'help', topic: 'connectors shopify app-automation-token delete' };
|
|
3276
3528
|
}
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
let
|
|
3280
|
-
let
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
for (let index = 1; index < args.length; index += 1) {
|
|
3285
|
-
const arg = args[index];
|
|
3286
|
-
if (arg === '--json') {
|
|
3287
|
-
json = true;
|
|
3288
|
-
continue;
|
|
3289
|
-
}
|
|
3290
|
-
if (arg === '--dry-run') {
|
|
3291
|
-
dryRun = true;
|
|
3292
|
-
continue;
|
|
3293
|
-
}
|
|
3294
|
-
if (arg === '--force') {
|
|
3295
|
-
force = true;
|
|
3296
|
-
continue;
|
|
3297
|
-
}
|
|
3298
|
-
if (arg === '--dir') {
|
|
3299
|
-
const value = args[index + 1];
|
|
3300
|
-
if (!value || value.startsWith('-')) {
|
|
3301
|
-
throw new TenderCliUsageError('--dir requires a path.');
|
|
3302
|
-
}
|
|
3303
|
-
dir = value;
|
|
3304
|
-
index += 1;
|
|
3305
|
-
continue;
|
|
3306
|
-
}
|
|
3307
|
-
if (arg === '--base-url') {
|
|
3308
|
-
baseUrl = parseBaseUrlFlag(args, index);
|
|
3309
|
-
index += 1;
|
|
3310
|
-
continue;
|
|
3311
|
-
}
|
|
3312
|
-
if (arg === '--token') {
|
|
3313
|
-
flagToken = parseTokenFlag(args, index);
|
|
3314
|
-
index += 1;
|
|
3315
|
-
continue;
|
|
3316
|
-
}
|
|
3317
|
-
if (arg === '--profile') {
|
|
3318
|
-
profileName = parseProfileName(args[index + 1], '--profile');
|
|
3529
|
+
const connectorId = parseConnectorId(args[0], 'tender connectors shopify app-automation-token delete <connector-id> --confirm delete --json');
|
|
3530
|
+
const parsed = parseConnectorsCommonArgs(args, 1);
|
|
3531
|
+
let confirm = null;
|
|
3532
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
3533
|
+
const arg = parsed.remaining[index];
|
|
3534
|
+
if (arg === '--confirm') {
|
|
3535
|
+
confirm = parseRequiredFlagValue(parsed.remaining[index + 1], '--confirm');
|
|
3319
3536
|
index += 1;
|
|
3320
3537
|
continue;
|
|
3321
3538
|
}
|
|
3322
|
-
throw new TenderCliUsageError(`Unknown
|
|
3539
|
+
throw new TenderCliUsageError(`Unknown connectors shopify app-automation-token delete option: ${arg}`);
|
|
3323
3540
|
}
|
|
3324
3541
|
return {
|
|
3325
|
-
command: 'app
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
flagToken,
|
|
3330
|
-
profileName,
|
|
3331
|
-
dryRun,
|
|
3332
|
-
force,
|
|
3333
|
-
json,
|
|
3542
|
+
command: 'connectors shopify app-automation-token delete',
|
|
3543
|
+
connectorId,
|
|
3544
|
+
confirm,
|
|
3545
|
+
...parsed,
|
|
3334
3546
|
};
|
|
3335
3547
|
}
|
|
3336
|
-
function
|
|
3548
|
+
function parseConnectorsShopifyVerifyArgs(args) {
|
|
3337
3549
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3338
|
-
return { command: 'help', topic: '
|
|
3550
|
+
return { command: 'help', topic: 'connectors shopify verify' };
|
|
3339
3551
|
}
|
|
3340
|
-
const
|
|
3341
|
-
|
|
3342
|
-
|
|
3552
|
+
const connectorId = parseConnectorId(args[0], 'tender connectors shopify verify <connector-id> --json');
|
|
3553
|
+
const parsed = parseConnectorsCommonArgs(args, 1);
|
|
3554
|
+
if (parsed.remaining.length > 0) {
|
|
3555
|
+
throw new TenderCliUsageError(`Unknown connectors shopify verify option: ${parsed.remaining[0]}`);
|
|
3343
3556
|
}
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3557
|
+
return { command: 'connectors shopify verify', connectorId, ...parsed };
|
|
3558
|
+
}
|
|
3559
|
+
function parseConnectorsShopifySourceInitArgs(args) {
|
|
3560
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
3561
|
+
return { command: 'help', topic: 'connectors shopify source init' };
|
|
3562
|
+
}
|
|
3563
|
+
const connectorId = parseConnectorId(args[0], 'tender connectors shopify source init <connector-id> --dir ./shopify-app --client-id abc --tender-app-url https://example.tenderprompt.app/client.js --tender-element tender-widget --json');
|
|
3564
|
+
const parsed = parseConnectorsCommonArgs(args, 1);
|
|
3565
|
+
let dir = null;
|
|
3566
|
+
let clientId = null;
|
|
3567
|
+
let sourceName = null;
|
|
3568
|
+
let tenderAppUrl = null;
|
|
3569
|
+
let tenderElement = null;
|
|
3570
|
+
let artifactId = null;
|
|
3571
|
+
let publicId = null;
|
|
3572
|
+
let themeExtensionHandle = 'tender-widget';
|
|
3573
|
+
let themeExtensionUid = null;
|
|
3574
|
+
let blockName = 'Tender Prompt Widget';
|
|
3575
|
+
let scopes = null;
|
|
3576
|
+
let force = false;
|
|
3577
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
3578
|
+
const arg = parsed.remaining[index];
|
|
3355
3579
|
if (arg === '--dir') {
|
|
3356
|
-
|
|
3357
|
-
if (!value || value.startsWith('-')) {
|
|
3358
|
-
throw new TenderCliUsageError('--dir requires a path.');
|
|
3359
|
-
}
|
|
3360
|
-
dir = value;
|
|
3580
|
+
dir = parseRequiredFlagValue(parsed.remaining[index + 1], '--dir');
|
|
3361
3581
|
index += 1;
|
|
3362
3582
|
continue;
|
|
3363
3583
|
}
|
|
3364
|
-
if (arg === '--
|
|
3365
|
-
|
|
3584
|
+
if (arg === '--client-id') {
|
|
3585
|
+
clientId = parseRequiredFlagValue(parsed.remaining[index + 1], '--client-id');
|
|
3366
3586
|
index += 1;
|
|
3367
3587
|
continue;
|
|
3368
3588
|
}
|
|
3369
|
-
if (arg === '--
|
|
3370
|
-
|
|
3589
|
+
if (arg === '--source-name') {
|
|
3590
|
+
sourceName = parseRequiredFlagValue(parsed.remaining[index + 1], '--source-name');
|
|
3371
3591
|
index += 1;
|
|
3372
3592
|
continue;
|
|
3373
3593
|
}
|
|
3374
|
-
if (arg === '--
|
|
3375
|
-
|
|
3594
|
+
if (arg === '--tender-app-url') {
|
|
3595
|
+
tenderAppUrl = parseRequiredFlagValue(parsed.remaining[index + 1], '--tender-app-url');
|
|
3376
3596
|
index += 1;
|
|
3377
3597
|
continue;
|
|
3378
3598
|
}
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
command: 'app context status',
|
|
3383
|
-
artifactId,
|
|
3384
|
-
dir,
|
|
3385
|
-
baseUrl,
|
|
3386
|
-
flagToken,
|
|
3387
|
-
profileName,
|
|
3388
|
-
json,
|
|
3389
|
-
};
|
|
3390
|
-
}
|
|
3391
|
-
function parseContextRefreshArgs(args) {
|
|
3392
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
3393
|
-
return { command: 'help', topic: 'app context refresh' };
|
|
3394
|
-
}
|
|
3395
|
-
const artifactId = args[0];
|
|
3396
|
-
if (!artifactId || artifactId.startsWith('-')) {
|
|
3397
|
-
throw new TenderCliUsageError('app id is required. Example: tender app context refresh <app-id> --dir ./widget --dry-run --json');
|
|
3398
|
-
}
|
|
3399
|
-
let dir = '.';
|
|
3400
|
-
let baseUrl = null;
|
|
3401
|
-
let flagToken = null;
|
|
3402
|
-
let profileName = null;
|
|
3403
|
-
let dryRun = false;
|
|
3404
|
-
let force = false;
|
|
3405
|
-
let json = false;
|
|
3406
|
-
for (let index = 1; index < args.length; index += 1) {
|
|
3407
|
-
const arg = args[index];
|
|
3408
|
-
if (arg === '--json') {
|
|
3409
|
-
json = true;
|
|
3599
|
+
if (arg === '--tender-element') {
|
|
3600
|
+
tenderElement = parseRequiredFlagValue(parsed.remaining[index + 1], '--tender-element');
|
|
3601
|
+
index += 1;
|
|
3410
3602
|
continue;
|
|
3411
3603
|
}
|
|
3412
|
-
if (arg === '--
|
|
3413
|
-
|
|
3604
|
+
if (arg === '--artifact-id') {
|
|
3605
|
+
artifactId = parseRequiredFlagValue(parsed.remaining[index + 1], '--artifact-id');
|
|
3606
|
+
index += 1;
|
|
3414
3607
|
continue;
|
|
3415
3608
|
}
|
|
3416
|
-
if (arg === '--
|
|
3417
|
-
|
|
3609
|
+
if (arg === '--public-id') {
|
|
3610
|
+
publicId = parseRequiredFlagValue(parsed.remaining[index + 1], '--public-id');
|
|
3611
|
+
index += 1;
|
|
3418
3612
|
continue;
|
|
3419
3613
|
}
|
|
3420
|
-
if (arg === '--
|
|
3421
|
-
|
|
3422
|
-
if (!value || value.startsWith('-')) {
|
|
3423
|
-
throw new TenderCliUsageError('--dir requires a path.');
|
|
3424
|
-
}
|
|
3425
|
-
dir = value;
|
|
3614
|
+
if (arg === '--theme-extension-handle') {
|
|
3615
|
+
themeExtensionHandle = parseRequiredFlagValue(parsed.remaining[index + 1], '--theme-extension-handle');
|
|
3426
3616
|
index += 1;
|
|
3427
3617
|
continue;
|
|
3428
3618
|
}
|
|
3429
|
-
if (arg === '--
|
|
3430
|
-
|
|
3619
|
+
if (arg === '--theme-extension-uid') {
|
|
3620
|
+
themeExtensionUid = parseRequiredFlagValue(parsed.remaining[index + 1], '--theme-extension-uid');
|
|
3431
3621
|
index += 1;
|
|
3432
3622
|
continue;
|
|
3433
3623
|
}
|
|
3434
|
-
if (arg === '--
|
|
3435
|
-
|
|
3624
|
+
if (arg === '--block-name') {
|
|
3625
|
+
blockName = parseRequiredFlagValue(parsed.remaining[index + 1], '--block-name');
|
|
3436
3626
|
index += 1;
|
|
3437
3627
|
continue;
|
|
3438
3628
|
}
|
|
3439
|
-
if (arg === '--
|
|
3440
|
-
|
|
3629
|
+
if (arg === '--scopes') {
|
|
3630
|
+
scopes = parseRequiredFlagValue(parsed.remaining[index + 1], '--scopes');
|
|
3441
3631
|
index += 1;
|
|
3442
3632
|
continue;
|
|
3443
3633
|
}
|
|
3444
|
-
|
|
3634
|
+
if (arg === '--force') {
|
|
3635
|
+
force = true;
|
|
3636
|
+
continue;
|
|
3637
|
+
}
|
|
3638
|
+
throw new TenderCliUsageError(`Unknown connectors shopify source init option: ${arg}`);
|
|
3639
|
+
}
|
|
3640
|
+
if (!dir) {
|
|
3641
|
+
throw new TenderCliUsageError('--dir is required. Example: tender connectors shopify source init conn_123 --dir ./shopify-app --client-id abc --tender-app-url https://example.tenderprompt.app/client.js --tender-element tender-widget --json');
|
|
3642
|
+
}
|
|
3643
|
+
if (!clientId) {
|
|
3644
|
+
throw new TenderCliUsageError('--client-id is required because Tender stores only the non-secret client ID prefix.');
|
|
3645
|
+
}
|
|
3646
|
+
if (!tenderAppUrl) {
|
|
3647
|
+
throw new TenderCliUsageError('--tender-app-url is required. Pass the Tender app client.js URL to load in the app block.');
|
|
3648
|
+
}
|
|
3649
|
+
if (!tenderElement) {
|
|
3650
|
+
throw new TenderCliUsageError('--tender-element is required. Pass the Tender custom element tag to mount in Shopify.');
|
|
3445
3651
|
}
|
|
3446
3652
|
return {
|
|
3447
|
-
command: '
|
|
3448
|
-
|
|
3653
|
+
command: 'connectors shopify source init',
|
|
3654
|
+
connectorId,
|
|
3449
3655
|
dir,
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3656
|
+
clientId,
|
|
3657
|
+
sourceName,
|
|
3658
|
+
template: 'dog-profile',
|
|
3659
|
+
tenderAppUrl,
|
|
3660
|
+
tenderElement,
|
|
3661
|
+
artifactId,
|
|
3662
|
+
publicId,
|
|
3663
|
+
themeExtensionHandle,
|
|
3664
|
+
themeExtensionUid,
|
|
3665
|
+
blockName,
|
|
3666
|
+
scopes,
|
|
3454
3667
|
force,
|
|
3455
|
-
|
|
3668
|
+
...parsed,
|
|
3456
3669
|
};
|
|
3457
3670
|
}
|
|
3458
|
-
function
|
|
3459
|
-
|
|
3460
|
-
|
|
3671
|
+
function parseConnectorsShopifySourceCheckoutArgs(args) {
|
|
3672
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
3673
|
+
return { command: 'help', topic: 'connectors shopify source checkout' };
|
|
3674
|
+
}
|
|
3675
|
+
const connectorId = parseConnectorId(args[0], 'tender connectors shopify source checkout <connector-id> --dir ./shopify-app --json');
|
|
3676
|
+
const parsed = parseConnectorsCommonArgs(args, 1);
|
|
3677
|
+
let dir = null;
|
|
3461
3678
|
let remoteName = 'tender';
|
|
3462
|
-
let
|
|
3463
|
-
|
|
3464
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
3465
|
-
const arg = args[index];
|
|
3466
|
-
if (arg === '--json') {
|
|
3467
|
-
json = true;
|
|
3468
|
-
continue;
|
|
3469
|
-
}
|
|
3679
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
3680
|
+
const arg = parsed.remaining[index];
|
|
3470
3681
|
if (arg === '--dir') {
|
|
3471
|
-
dir = parseRequiredFlagValue(
|
|
3472
|
-
index += 1;
|
|
3473
|
-
continue;
|
|
3474
|
-
}
|
|
3475
|
-
if (arg === '--artifact') {
|
|
3476
|
-
artifactId = parseRequiredFlagValue(args[index + 1], '--artifact');
|
|
3682
|
+
dir = parseRequiredFlagValue(parsed.remaining[index + 1], '--dir');
|
|
3477
3683
|
index += 1;
|
|
3478
3684
|
continue;
|
|
3479
3685
|
}
|
|
3480
3686
|
if (arg === '--remote') {
|
|
3481
|
-
remoteName = parseRemoteName(
|
|
3687
|
+
remoteName = parseRemoteName(parsed.remaining[index + 1]);
|
|
3482
3688
|
index += 1;
|
|
3483
3689
|
continue;
|
|
3484
3690
|
}
|
|
3485
|
-
|
|
3691
|
+
throw new TenderCliUsageError(`Unknown connectors shopify source checkout option: ${arg}`);
|
|
3486
3692
|
}
|
|
3487
|
-
|
|
3693
|
+
if (!dir) {
|
|
3694
|
+
throw new TenderCliUsageError('--dir is required. Example: tender connectors shopify source checkout conn_123 --dir ./shopify-app --json');
|
|
3695
|
+
}
|
|
3696
|
+
return { command: 'connectors shopify source checkout', connectorId, dir, remoteName, ...parsed };
|
|
3488
3697
|
}
|
|
3489
|
-
function
|
|
3698
|
+
function parseConnectorsShopifySourceValidateArgs(args) {
|
|
3490
3699
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3491
|
-
return { command: 'help', topic: '
|
|
3700
|
+
return { command: 'help', topic: 'connectors shopify source validate' };
|
|
3492
3701
|
}
|
|
3493
|
-
const
|
|
3494
|
-
|
|
3495
|
-
let
|
|
3702
|
+
const connectorId = parseConnectorId(args[0], 'tender connectors shopify source validate <connector-id> --dir ./shopify-app --execute --json');
|
|
3703
|
+
const parsed = parseConnectorsCommonArgs(args, 1);
|
|
3704
|
+
let dir = '.';
|
|
3705
|
+
let clientId = null;
|
|
3706
|
+
let execute = false;
|
|
3496
3707
|
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
3497
3708
|
const arg = parsed.remaining[index];
|
|
3498
|
-
if (arg === '--
|
|
3499
|
-
|
|
3709
|
+
if (arg === '--execute') {
|
|
3710
|
+
execute = true;
|
|
3711
|
+
continue;
|
|
3712
|
+
}
|
|
3713
|
+
if (arg === '--dir') {
|
|
3714
|
+
dir = parseRequiredFlagValue(parsed.remaining[index + 1], '--dir');
|
|
3500
3715
|
index += 1;
|
|
3501
3716
|
continue;
|
|
3502
3717
|
}
|
|
3503
|
-
if (arg === '--
|
|
3504
|
-
|
|
3718
|
+
if (arg === '--client-id') {
|
|
3719
|
+
clientId = parseRequiredFlagValue(parsed.remaining[index + 1], '--client-id');
|
|
3505
3720
|
index += 1;
|
|
3506
3721
|
continue;
|
|
3507
3722
|
}
|
|
3508
|
-
throw new TenderCliUsageError(`Unknown
|
|
3509
|
-
}
|
|
3510
|
-
if (!summary.trim()) {
|
|
3511
|
-
throw new TenderCliUsageError('memory start requires --summary.\nNext: tender memory start --summary "Current user goal"');
|
|
3723
|
+
throw new TenderCliUsageError(`Unknown connectors shopify source validate option: ${arg}`);
|
|
3512
3724
|
}
|
|
3513
|
-
return { command: '
|
|
3725
|
+
return { command: 'connectors shopify source validate', connectorId, dir, clientId, execute, ...parsed };
|
|
3514
3726
|
}
|
|
3515
|
-
function
|
|
3727
|
+
function parseConnectorsShopifySourceDeployArgs(args) {
|
|
3516
3728
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3517
|
-
return { command: 'help', topic: '
|
|
3729
|
+
return { command: 'help', topic: 'connectors shopify source deploy' };
|
|
3518
3730
|
}
|
|
3519
|
-
const
|
|
3520
|
-
|
|
3521
|
-
let
|
|
3522
|
-
let
|
|
3523
|
-
|
|
3731
|
+
const connectorId = parseConnectorId(args[0], 'tender connectors shopify source deploy <connector-id> --dir ./shopify-app --confirm deploy --json');
|
|
3732
|
+
const parsed = parseConnectorsCommonArgs(args, 1);
|
|
3733
|
+
let dir = '.';
|
|
3734
|
+
let clientId = null;
|
|
3735
|
+
let version = null;
|
|
3736
|
+
let message = null;
|
|
3737
|
+
let sourceControlUrl = null;
|
|
3738
|
+
let allowDeletes = false;
|
|
3739
|
+
let confirm = null;
|
|
3524
3740
|
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
3525
3741
|
const arg = parsed.remaining[index];
|
|
3526
|
-
if (arg === '--
|
|
3527
|
-
|
|
3742
|
+
if (arg === '--allow-deletes') {
|
|
3743
|
+
allowDeletes = true;
|
|
3744
|
+
continue;
|
|
3745
|
+
}
|
|
3746
|
+
if (arg === '--dir') {
|
|
3747
|
+
dir = parseRequiredFlagValue(parsed.remaining[index + 1], '--dir');
|
|
3528
3748
|
index += 1;
|
|
3529
3749
|
continue;
|
|
3530
3750
|
}
|
|
3531
|
-
if (arg === '--
|
|
3532
|
-
|
|
3751
|
+
if (arg === '--client-id') {
|
|
3752
|
+
clientId = parseRequiredFlagValue(parsed.remaining[index + 1], '--client-id');
|
|
3533
3753
|
index += 1;
|
|
3534
3754
|
continue;
|
|
3535
3755
|
}
|
|
3536
|
-
if (arg === '--
|
|
3537
|
-
|
|
3756
|
+
if (arg === '--version') {
|
|
3757
|
+
version = parseRequiredFlagValue(parsed.remaining[index + 1], '--version');
|
|
3538
3758
|
index += 1;
|
|
3539
3759
|
continue;
|
|
3540
3760
|
}
|
|
3541
|
-
if (arg === '--
|
|
3542
|
-
|
|
3761
|
+
if (arg === '--message') {
|
|
3762
|
+
message = parseRequiredFlagValue(parsed.remaining[index + 1], '--message');
|
|
3543
3763
|
index += 1;
|
|
3544
3764
|
continue;
|
|
3545
3765
|
}
|
|
3546
|
-
|
|
3766
|
+
if (arg === '--source-control-url') {
|
|
3767
|
+
sourceControlUrl = parseRequiredFlagValue(parsed.remaining[index + 1], '--source-control-url');
|
|
3768
|
+
index += 1;
|
|
3769
|
+
continue;
|
|
3770
|
+
}
|
|
3771
|
+
if (arg === '--confirm') {
|
|
3772
|
+
confirm = parseRequiredFlagValue(parsed.remaining[index + 1], '--confirm');
|
|
3773
|
+
index += 1;
|
|
3774
|
+
continue;
|
|
3775
|
+
}
|
|
3776
|
+
throw new TenderCliUsageError(`Unknown connectors shopify source deploy option: ${arg}`);
|
|
3547
3777
|
}
|
|
3548
|
-
return { command: '
|
|
3778
|
+
return { command: 'connectors shopify source deploy', connectorId, dir, clientId, version, message, sourceControlUrl, allowDeletes, confirm, ...parsed };
|
|
3549
3779
|
}
|
|
3550
|
-
function
|
|
3780
|
+
function parseConnectorsShopifyDeleteArgs(args) {
|
|
3551
3781
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3552
|
-
return { command: 'help', topic: '
|
|
3782
|
+
return { command: 'help', topic: 'connectors shopify delete' };
|
|
3553
3783
|
}
|
|
3554
|
-
const
|
|
3555
|
-
|
|
3556
|
-
let
|
|
3557
|
-
let body = null;
|
|
3784
|
+
const connectorId = parseConnectorId(args[0], 'tender connectors shopify delete <connector-id> --confirm delete --json');
|
|
3785
|
+
const parsed = parseConnectorsCommonArgs(args, 1);
|
|
3786
|
+
let confirm = null;
|
|
3558
3787
|
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
3559
3788
|
const arg = parsed.remaining[index];
|
|
3560
|
-
if (arg === '--
|
|
3561
|
-
|
|
3789
|
+
if (arg === '--confirm') {
|
|
3790
|
+
confirm = parseRequiredFlagValue(parsed.remaining[index + 1], '--confirm');
|
|
3562
3791
|
index += 1;
|
|
3563
3792
|
continue;
|
|
3564
3793
|
}
|
|
3565
|
-
|
|
3566
|
-
|
|
3794
|
+
throw new TenderCliUsageError(`Unknown connectors shopify delete option: ${arg}`);
|
|
3795
|
+
}
|
|
3796
|
+
return { command: 'connectors shopify delete', connectorId, confirm, ...parsed };
|
|
3797
|
+
}
|
|
3798
|
+
function parseConnectorsBindArgs(args) {
|
|
3799
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
3800
|
+
return { command: 'help', topic: 'connectors bind' };
|
|
3801
|
+
}
|
|
3802
|
+
const bindingName = parseBindingName(args[0]);
|
|
3803
|
+
const connectorId = parseConnectorId(args[1], 'tender connectors bind SHOPIFY_CONNECTOR conn_123 --instance default --json');
|
|
3804
|
+
let instanceId = 'default';
|
|
3805
|
+
let label = null;
|
|
3806
|
+
let json = false;
|
|
3807
|
+
for (let index = 2; index < args.length; index += 1) {
|
|
3808
|
+
const arg = args[index];
|
|
3809
|
+
if (arg === '--json') {
|
|
3810
|
+
json = true;
|
|
3811
|
+
continue;
|
|
3812
|
+
}
|
|
3813
|
+
if (arg === '--instance') {
|
|
3814
|
+
instanceId = parseConnectorInstanceId(args[index + 1]);
|
|
3567
3815
|
index += 1;
|
|
3568
3816
|
continue;
|
|
3569
3817
|
}
|
|
3570
|
-
if (arg === '--
|
|
3571
|
-
|
|
3818
|
+
if (arg === '--label') {
|
|
3819
|
+
label = parseRequiredFlagValue(args[index + 1], '--label');
|
|
3572
3820
|
index += 1;
|
|
3573
3821
|
continue;
|
|
3574
3822
|
}
|
|
3575
|
-
throw new TenderCliUsageError(`Unknown
|
|
3823
|
+
throw new TenderCliUsageError(`Unknown connectors bind option: ${arg}`);
|
|
3576
3824
|
}
|
|
3577
|
-
return { command: '
|
|
3825
|
+
return { command: 'connectors bind', bindingName, connectorId, instanceId, label, json };
|
|
3578
3826
|
}
|
|
3579
|
-
function
|
|
3827
|
+
function parseShareCreateArgs(args) {
|
|
3580
3828
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3581
|
-
return { command: 'help', topic: '
|
|
3829
|
+
return { command: 'help', topic: 'app share create' };
|
|
3582
3830
|
}
|
|
3583
|
-
const
|
|
3584
|
-
|
|
3831
|
+
const artifactId = args[0];
|
|
3832
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
3833
|
+
throw new TenderCliUsageError('app id is required. Example: tender app share create <app-id> --json');
|
|
3834
|
+
}
|
|
3835
|
+
const parsed = parseShareCommonArgs(args, 1);
|
|
3836
|
+
let expires = '7d';
|
|
3837
|
+
let claims = 1;
|
|
3585
3838
|
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
3586
3839
|
const arg = parsed.remaining[index];
|
|
3587
|
-
if (arg === '--
|
|
3588
|
-
|
|
3840
|
+
if (arg === '--expires') {
|
|
3841
|
+
expires = parseRequiredFlagValue(parsed.remaining[index + 1], '--expires');
|
|
3589
3842
|
index += 1;
|
|
3590
3843
|
continue;
|
|
3591
3844
|
}
|
|
3592
|
-
|
|
3845
|
+
if (arg === '--claims') {
|
|
3846
|
+
claims = parsePositiveIntegerFlag(parsed.remaining[index + 1], '--claims');
|
|
3847
|
+
index += 1;
|
|
3848
|
+
continue;
|
|
3849
|
+
}
|
|
3850
|
+
throw new TenderCliUsageError(`Unknown app share create option: ${arg}`);
|
|
3593
3851
|
}
|
|
3594
|
-
return { command: '
|
|
3852
|
+
return { command: 'app share create', artifactId, expires, claims, ...parsed };
|
|
3595
3853
|
}
|
|
3596
|
-
function
|
|
3854
|
+
function parseShareInspectArgs(args) {
|
|
3597
3855
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3598
|
-
return { command: 'help', topic: '
|
|
3856
|
+
return { command: 'help', topic: 'app share inspect' };
|
|
3599
3857
|
}
|
|
3600
|
-
const
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
const arg = parsed.remaining[index];
|
|
3604
|
-
if (arg === '--limit') {
|
|
3605
|
-
limit = parsePositiveIntegerFlag(parsed.remaining[index + 1], '--limit');
|
|
3606
|
-
index += 1;
|
|
3607
|
-
continue;
|
|
3608
|
-
}
|
|
3609
|
-
throw new TenderCliUsageError(`Unknown memory brief option: ${arg}`);
|
|
3858
|
+
const token = args[0];
|
|
3859
|
+
if (!token || token.startsWith('-')) {
|
|
3860
|
+
throw new TenderCliUsageError('share token is required. Example: tender app share inspect <token> --json');
|
|
3610
3861
|
}
|
|
3611
|
-
|
|
3862
|
+
const parsed = parseShareCommonArgs(args, 1);
|
|
3863
|
+
if (parsed.remaining.length > 0) {
|
|
3864
|
+
throw new TenderCliUsageError(`Unknown app share inspect option: ${parsed.remaining[0]}`);
|
|
3865
|
+
}
|
|
3866
|
+
return { command: 'app share inspect', token, ...parsed };
|
|
3612
3867
|
}
|
|
3613
|
-
function
|
|
3868
|
+
function parseShareRevokeArgs(args) {
|
|
3614
3869
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3615
|
-
return { command: 'help', topic: '
|
|
3870
|
+
return { command: 'help', topic: 'app share revoke' };
|
|
3616
3871
|
}
|
|
3617
|
-
const
|
|
3872
|
+
const shareId = args[0];
|
|
3873
|
+
if (!shareId || shareId.startsWith('-')) {
|
|
3874
|
+
throw new TenderCliUsageError('share id is required. Example: tender app share revoke <share-id> --json');
|
|
3875
|
+
}
|
|
3876
|
+
const parsed = parseShareCommonArgs(args, 1);
|
|
3618
3877
|
if (parsed.remaining.length > 0) {
|
|
3619
|
-
throw new TenderCliUsageError(`Unknown
|
|
3878
|
+
throw new TenderCliUsageError(`Unknown app share revoke option: ${parsed.remaining[0]}`);
|
|
3620
3879
|
}
|
|
3621
|
-
return { command: '
|
|
3880
|
+
return { command: 'app share revoke', shareId, ...parsed };
|
|
3622
3881
|
}
|
|
3623
|
-
function
|
|
3882
|
+
function parseClaimArgs(args) {
|
|
3624
3883
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3625
|
-
return { command: 'help', topic: '
|
|
3884
|
+
return { command: 'help', topic: 'app claim' };
|
|
3626
3885
|
}
|
|
3627
|
-
const
|
|
3628
|
-
|
|
3629
|
-
|
|
3886
|
+
const token = args[0];
|
|
3887
|
+
if (!token || token.startsWith('-')) {
|
|
3888
|
+
throw new TenderCliUsageError('share token is required. Example: tender app claim <token> --dir ./widget');
|
|
3889
|
+
}
|
|
3890
|
+
const parsed = parseShareCommonArgs(args, 1);
|
|
3891
|
+
let dir = null;
|
|
3892
|
+
let name = null;
|
|
3893
|
+
let confirm = null;
|
|
3630
3894
|
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
3631
3895
|
const arg = parsed.remaining[index];
|
|
3632
|
-
if (arg === '--
|
|
3633
|
-
|
|
3634
|
-
if (value !== 'jsonl') {
|
|
3635
|
-
throw new TenderCliUsageError(`Unsupported memory export format: ${value}.\nNext: tender memory export --format jsonl --dir ${parsed.dir}`);
|
|
3636
|
-
}
|
|
3637
|
-
format = value;
|
|
3896
|
+
if (arg === '--dir') {
|
|
3897
|
+
dir = parseRequiredFlagValue(parsed.remaining[index + 1], '--dir');
|
|
3638
3898
|
index += 1;
|
|
3639
3899
|
continue;
|
|
3640
3900
|
}
|
|
3641
|
-
if (arg === '--
|
|
3642
|
-
|
|
3901
|
+
if (arg === '--name') {
|
|
3902
|
+
name = parseRequiredFlagValue(parsed.remaining[index + 1], '--name');
|
|
3643
3903
|
index += 1;
|
|
3644
3904
|
continue;
|
|
3645
3905
|
}
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
}
|
|
3653
|
-
function parseTokenProfile(value) {
|
|
3654
|
-
if (value === 'edit-preview' || value === 'publish' || value === 'create' || value === 'db-read') {
|
|
3655
|
-
return value;
|
|
3656
|
-
}
|
|
3657
|
-
throw new TenderCliUsageError('--profile must be one of edit-preview, publish, create, or db-read.');
|
|
3658
|
-
}
|
|
3659
|
-
function parsePositiveIntegerFlag(value, flag) {
|
|
3660
|
-
const parsed = Number(value);
|
|
3661
|
-
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
3662
|
-
throw new TenderCliUsageError(`${flag} requires a positive integer.`);
|
|
3906
|
+
if (arg === '--confirm') {
|
|
3907
|
+
confirm = parseRequiredFlagValue(parsed.remaining[index + 1], '--confirm');
|
|
3908
|
+
index += 1;
|
|
3909
|
+
continue;
|
|
3910
|
+
}
|
|
3911
|
+
throw new TenderCliUsageError(`Unknown app claim option: ${arg}`);
|
|
3663
3912
|
}
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
function parseNonNegativeIntegerFlag(value, flag) {
|
|
3667
|
-
const parsed = Number(value);
|
|
3668
|
-
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
3669
|
-
throw new TenderCliUsageError(`${flag} requires a non-negative integer.`);
|
|
3913
|
+
if (!dir) {
|
|
3914
|
+
throw new TenderCliUsageError('--dir is required. Example: tender app claim tok_share_123 --dir ./widget');
|
|
3670
3915
|
}
|
|
3671
|
-
return parsed;
|
|
3916
|
+
return { command: 'app claim', token, dir, name, confirm, ...parsed };
|
|
3672
3917
|
}
|
|
3673
|
-
function
|
|
3918
|
+
function parseArtifactsInitArgs(args) {
|
|
3674
3919
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3675
|
-
return { command: 'help', topic: '
|
|
3920
|
+
return { command: 'help', topic: 'app init' };
|
|
3676
3921
|
}
|
|
3677
|
-
|
|
3922
|
+
const artifactId = args[0];
|
|
3923
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
3924
|
+
throw new TenderCliUsageError('app id is required. Example: tender app init <app-id> --dir ./widget --json');
|
|
3925
|
+
}
|
|
3926
|
+
let dir = '.';
|
|
3927
|
+
let remoteName = 'tender';
|
|
3678
3928
|
let baseUrl = null;
|
|
3679
|
-
let
|
|
3680
|
-
let
|
|
3681
|
-
let artifactId = null;
|
|
3929
|
+
let flagToken = null;
|
|
3930
|
+
let profileName = null;
|
|
3682
3931
|
let ttl = '7d';
|
|
3932
|
+
let scaffold = 'server-backed';
|
|
3933
|
+
let preview = false;
|
|
3934
|
+
let dryRun = false;
|
|
3935
|
+
let force = false;
|
|
3683
3936
|
let json = false;
|
|
3684
|
-
let
|
|
3685
|
-
let pollTimeoutSeconds = 600;
|
|
3686
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
3937
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
3687
3938
|
const arg = args[index];
|
|
3688
|
-
if (arg === '--device') {
|
|
3689
|
-
device = true;
|
|
3690
|
-
continue;
|
|
3691
|
-
}
|
|
3692
3939
|
if (arg === '--json') {
|
|
3693
3940
|
json = true;
|
|
3694
3941
|
continue;
|
|
3695
3942
|
}
|
|
3696
|
-
if (arg === '--
|
|
3697
|
-
|
|
3943
|
+
if (arg === '--dry-run') {
|
|
3944
|
+
dryRun = true;
|
|
3698
3945
|
continue;
|
|
3699
3946
|
}
|
|
3700
|
-
if (arg === '--
|
|
3701
|
-
|
|
3947
|
+
if (arg === '--force') {
|
|
3948
|
+
force = true;
|
|
3949
|
+
continue;
|
|
3950
|
+
}
|
|
3951
|
+
if (arg === '--preview') {
|
|
3952
|
+
preview = true;
|
|
3953
|
+
continue;
|
|
3954
|
+
}
|
|
3955
|
+
if (arg === '--no-scaffold') {
|
|
3956
|
+
scaffold = 'none';
|
|
3957
|
+
continue;
|
|
3958
|
+
}
|
|
3959
|
+
if (arg === '--dir') {
|
|
3960
|
+
const value = args[index + 1];
|
|
3961
|
+
if (!value || value.startsWith('-')) {
|
|
3962
|
+
throw new TenderCliUsageError('--dir requires a path.');
|
|
3963
|
+
}
|
|
3964
|
+
dir = value;
|
|
3702
3965
|
index += 1;
|
|
3703
3966
|
continue;
|
|
3704
3967
|
}
|
|
3705
|
-
if (arg === '--
|
|
3706
|
-
|
|
3968
|
+
if (arg === '--remote') {
|
|
3969
|
+
remoteName = parseRemoteName(args[index + 1]);
|
|
3707
3970
|
index += 1;
|
|
3708
3971
|
continue;
|
|
3709
3972
|
}
|
|
3710
|
-
if (arg === '--
|
|
3711
|
-
|
|
3973
|
+
if (arg === '--base-url') {
|
|
3974
|
+
baseUrl = parseBaseUrlFlag(args, index);
|
|
3712
3975
|
index += 1;
|
|
3713
3976
|
continue;
|
|
3714
3977
|
}
|
|
3715
|
-
if (arg === '--
|
|
3716
|
-
|
|
3978
|
+
if (arg === '--token') {
|
|
3979
|
+
flagToken = parseTokenFlag(args, index);
|
|
3980
|
+
index += 1;
|
|
3981
|
+
continue;
|
|
3982
|
+
}
|
|
3983
|
+
if (arg === '--profile') {
|
|
3984
|
+
profileName = parseProfileName(args[index + 1], '--profile');
|
|
3717
3985
|
index += 1;
|
|
3718
3986
|
continue;
|
|
3719
3987
|
}
|
|
@@ -3722,72 +3990,124 @@ function parseAuthLoginArgs(args) {
|
|
|
3722
3990
|
index += 1;
|
|
3723
3991
|
continue;
|
|
3724
3992
|
}
|
|
3725
|
-
if (arg === '--
|
|
3726
|
-
|
|
3993
|
+
if (arg === '--scaffold') {
|
|
3994
|
+
scaffold = parseScaffoldTemplate(args[index + 1]);
|
|
3727
3995
|
index += 1;
|
|
3728
3996
|
continue;
|
|
3729
3997
|
}
|
|
3730
|
-
throw new TenderCliUsageError(`Unknown
|
|
3731
|
-
}
|
|
3732
|
-
if (!device) {
|
|
3733
|
-
throw new TenderCliUsageError('auth login currently requires --device. Example: tender auth login --device --profile edit-preview --artifact artifact_123');
|
|
3734
|
-
}
|
|
3735
|
-
if (tokenProfile === 'create' && artifactId) {
|
|
3736
|
-
throw new TenderCliUsageError('--profile create must be account-scoped; remove --artifact.');
|
|
3737
|
-
}
|
|
3738
|
-
if (tokenProfile === 'db-read' && !artifactId) {
|
|
3739
|
-
throw new TenderCliUsageError('--profile db-read requires --artifact <artifact-id>.');
|
|
3998
|
+
throw new TenderCliUsageError(`Unknown app init option: ${arg}`);
|
|
3740
3999
|
}
|
|
3741
4000
|
return {
|
|
3742
|
-
command: '
|
|
3743
|
-
baseUrl,
|
|
3744
|
-
device,
|
|
3745
|
-
tokenProfile,
|
|
3746
|
-
saveProfile,
|
|
4001
|
+
command: 'app init',
|
|
3747
4002
|
artifactId,
|
|
4003
|
+
dir,
|
|
4004
|
+
remoteName,
|
|
4005
|
+
baseUrl,
|
|
4006
|
+
flagToken,
|
|
4007
|
+
profileName,
|
|
3748
4008
|
ttl,
|
|
4009
|
+
scaffold,
|
|
4010
|
+
preview,
|
|
4011
|
+
dryRun,
|
|
4012
|
+
force,
|
|
3749
4013
|
json,
|
|
3750
|
-
noPoll,
|
|
3751
|
-
pollTimeoutSeconds,
|
|
3752
4014
|
};
|
|
3753
4015
|
}
|
|
3754
|
-
function
|
|
4016
|
+
function parseContextFetchArgs(args) {
|
|
3755
4017
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3756
|
-
return { command: 'help', topic: '
|
|
4018
|
+
return { command: 'help', topic: 'app context fetch' };
|
|
4019
|
+
}
|
|
4020
|
+
const artifactId = args[0];
|
|
4021
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
4022
|
+
throw new TenderCliUsageError('app id is required. Example: tender app context fetch <app-id> --dir ./widget --json');
|
|
3757
4023
|
}
|
|
4024
|
+
let dir = '.';
|
|
4025
|
+
let baseUrl = null;
|
|
4026
|
+
let flagToken = null;
|
|
3758
4027
|
let profileName = null;
|
|
4028
|
+
let dryRun = false;
|
|
4029
|
+
let force = false;
|
|
3759
4030
|
let json = false;
|
|
3760
|
-
for (let index =
|
|
4031
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
3761
4032
|
const arg = args[index];
|
|
3762
4033
|
if (arg === '--json') {
|
|
3763
4034
|
json = true;
|
|
3764
4035
|
continue;
|
|
3765
4036
|
}
|
|
4037
|
+
if (arg === '--dry-run') {
|
|
4038
|
+
dryRun = true;
|
|
4039
|
+
continue;
|
|
4040
|
+
}
|
|
4041
|
+
if (arg === '--force') {
|
|
4042
|
+
force = true;
|
|
4043
|
+
continue;
|
|
4044
|
+
}
|
|
4045
|
+
if (arg === '--dir') {
|
|
4046
|
+
const value = args[index + 1];
|
|
4047
|
+
if (!value || value.startsWith('-')) {
|
|
4048
|
+
throw new TenderCliUsageError('--dir requires a path.');
|
|
4049
|
+
}
|
|
4050
|
+
dir = value;
|
|
4051
|
+
index += 1;
|
|
4052
|
+
continue;
|
|
4053
|
+
}
|
|
4054
|
+
if (arg === '--base-url') {
|
|
4055
|
+
baseUrl = parseBaseUrlFlag(args, index);
|
|
4056
|
+
index += 1;
|
|
4057
|
+
continue;
|
|
4058
|
+
}
|
|
4059
|
+
if (arg === '--token') {
|
|
4060
|
+
flagToken = parseTokenFlag(args, index);
|
|
4061
|
+
index += 1;
|
|
4062
|
+
continue;
|
|
4063
|
+
}
|
|
3766
4064
|
if (arg === '--profile') {
|
|
3767
4065
|
profileName = parseProfileName(args[index + 1], '--profile');
|
|
3768
4066
|
index += 1;
|
|
3769
4067
|
continue;
|
|
3770
4068
|
}
|
|
3771
|
-
throw new TenderCliUsageError(`Unknown
|
|
4069
|
+
throw new TenderCliUsageError(`Unknown context fetch option: ${arg}`);
|
|
3772
4070
|
}
|
|
3773
4071
|
return {
|
|
3774
|
-
command: '
|
|
4072
|
+
command: 'app context fetch',
|
|
4073
|
+
artifactId,
|
|
4074
|
+
dir,
|
|
4075
|
+
baseUrl,
|
|
4076
|
+
flagToken,
|
|
3775
4077
|
profileName,
|
|
4078
|
+
dryRun,
|
|
4079
|
+
force,
|
|
3776
4080
|
json,
|
|
3777
4081
|
};
|
|
3778
4082
|
}
|
|
3779
|
-
function
|
|
4083
|
+
function parseContextStatusArgs(args) {
|
|
4084
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4085
|
+
return { command: 'help', topic: 'app context status' };
|
|
4086
|
+
}
|
|
4087
|
+
const artifactId = args[0];
|
|
4088
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
4089
|
+
throw new TenderCliUsageError('app id is required. Example: tender app context status <app-id> --dir ./widget --json');
|
|
4090
|
+
}
|
|
4091
|
+
let dir = '.';
|
|
3780
4092
|
let baseUrl = null;
|
|
3781
4093
|
let flagToken = null;
|
|
3782
4094
|
let profileName = null;
|
|
3783
4095
|
let json = false;
|
|
3784
|
-
|
|
3785
|
-
for (let index = startIndex; index < args.length; index += 1) {
|
|
4096
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
3786
4097
|
const arg = args[index];
|
|
3787
4098
|
if (arg === '--json') {
|
|
3788
4099
|
json = true;
|
|
3789
4100
|
continue;
|
|
3790
4101
|
}
|
|
4102
|
+
if (arg === '--dir') {
|
|
4103
|
+
const value = args[index + 1];
|
|
4104
|
+
if (!value || value.startsWith('-')) {
|
|
4105
|
+
throw new TenderCliUsageError('--dir requires a path.');
|
|
4106
|
+
}
|
|
4107
|
+
dir = value;
|
|
4108
|
+
index += 1;
|
|
4109
|
+
continue;
|
|
4110
|
+
}
|
|
3791
4111
|
if (arg === '--base-url') {
|
|
3792
4112
|
baseUrl = parseBaseUrlFlag(args, index);
|
|
3793
4113
|
index += 1;
|
|
@@ -3803,93 +4123,91 @@ function parsePlaybooksCommonArgs(args, startIndex) {
|
|
|
3803
4123
|
index += 1;
|
|
3804
4124
|
continue;
|
|
3805
4125
|
}
|
|
3806
|
-
|
|
3807
|
-
}
|
|
3808
|
-
return { baseUrl, flagToken, profileName, json, extras };
|
|
3809
|
-
}
|
|
3810
|
-
function parsePlaybooksListArgs(args) {
|
|
3811
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
3812
|
-
return { command: 'help', topic: 'playbooks list' };
|
|
3813
|
-
}
|
|
3814
|
-
const parsed = parsePlaybooksCommonArgs(args, 0);
|
|
3815
|
-
if (parsed.extras.length > 0) {
|
|
3816
|
-
throw new TenderCliUsageError(`Unknown playbooks list option: ${parsed.extras[0]}`);
|
|
3817
|
-
}
|
|
3818
|
-
return {
|
|
3819
|
-
command: 'playbooks list',
|
|
3820
|
-
baseUrl: parsed.baseUrl,
|
|
3821
|
-
flagToken: parsed.flagToken,
|
|
3822
|
-
profileName: parsed.profileName,
|
|
3823
|
-
json: parsed.json,
|
|
3824
|
-
};
|
|
3825
|
-
}
|
|
3826
|
-
function parsePlaybooksGetArgs(args) {
|
|
3827
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
3828
|
-
return { command: 'help', topic: 'playbooks get' };
|
|
3829
|
-
}
|
|
3830
|
-
const playbookId = args[0];
|
|
3831
|
-
if (!playbookId || playbookId.startsWith('-')) {
|
|
3832
|
-
throw new TenderCliUsageError('playbook id is required. Example: tender playbooks get recharge-bundle-builder --json');
|
|
3833
|
-
}
|
|
3834
|
-
const parsed = parsePlaybooksCommonArgs(args, 1);
|
|
3835
|
-
if (parsed.extras.length > 0) {
|
|
3836
|
-
throw new TenderCliUsageError(`Unknown playbooks get option: ${parsed.extras[0]}`);
|
|
4126
|
+
throw new TenderCliUsageError(`Unknown context status option: ${arg}`);
|
|
3837
4127
|
}
|
|
3838
4128
|
return {
|
|
3839
|
-
command: '
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
4129
|
+
command: 'app context status',
|
|
4130
|
+
artifactId,
|
|
4131
|
+
dir,
|
|
4132
|
+
baseUrl,
|
|
4133
|
+
flagToken,
|
|
4134
|
+
profileName,
|
|
4135
|
+
json,
|
|
3845
4136
|
};
|
|
3846
4137
|
}
|
|
3847
|
-
function
|
|
4138
|
+
function parseContextRefreshArgs(args) {
|
|
3848
4139
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3849
|
-
return { command: 'help', topic: '
|
|
4140
|
+
return { command: 'help', topic: 'app context refresh' };
|
|
3850
4141
|
}
|
|
3851
|
-
const
|
|
3852
|
-
if (!
|
|
3853
|
-
throw new TenderCliUsageError('
|
|
4142
|
+
const artifactId = args[0];
|
|
4143
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
4144
|
+
throw new TenderCliUsageError('app id is required. Example: tender app context refresh <app-id> --dir ./widget --dry-run --json');
|
|
3854
4145
|
}
|
|
3855
|
-
let
|
|
3856
|
-
|
|
4146
|
+
let dir = '.';
|
|
4147
|
+
let baseUrl = null;
|
|
4148
|
+
let flagToken = null;
|
|
4149
|
+
let profileName = null;
|
|
4150
|
+
let dryRun = false;
|
|
4151
|
+
let force = false;
|
|
4152
|
+
let json = false;
|
|
3857
4153
|
for (let index = 1; index < args.length; index += 1) {
|
|
3858
4154
|
const arg = args[index];
|
|
3859
|
-
if (arg === '--
|
|
4155
|
+
if (arg === '--json') {
|
|
4156
|
+
json = true;
|
|
4157
|
+
continue;
|
|
4158
|
+
}
|
|
4159
|
+
if (arg === '--dry-run') {
|
|
4160
|
+
dryRun = true;
|
|
4161
|
+
continue;
|
|
4162
|
+
}
|
|
4163
|
+
if (arg === '--force') {
|
|
4164
|
+
force = true;
|
|
4165
|
+
continue;
|
|
4166
|
+
}
|
|
4167
|
+
if (arg === '--dir') {
|
|
3860
4168
|
const value = args[index + 1];
|
|
3861
4169
|
if (!value || value.startsWith('-')) {
|
|
3862
|
-
throw new TenderCliUsageError('--
|
|
4170
|
+
throw new TenderCliUsageError('--dir requires a path.');
|
|
3863
4171
|
}
|
|
3864
|
-
|
|
4172
|
+
dir = value;
|
|
3865
4173
|
index += 1;
|
|
3866
4174
|
continue;
|
|
3867
4175
|
}
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
4176
|
+
if (arg === '--base-url') {
|
|
4177
|
+
baseUrl = parseBaseUrlFlag(args, index);
|
|
4178
|
+
index += 1;
|
|
4179
|
+
continue;
|
|
4180
|
+
}
|
|
4181
|
+
if (arg === '--token') {
|
|
4182
|
+
flagToken = parseTokenFlag(args, index);
|
|
4183
|
+
index += 1;
|
|
4184
|
+
continue;
|
|
4185
|
+
}
|
|
4186
|
+
if (arg === '--profile') {
|
|
4187
|
+
profileName = parseProfileName(args[index + 1], '--profile');
|
|
4188
|
+
index += 1;
|
|
4189
|
+
continue;
|
|
4190
|
+
}
|
|
4191
|
+
throw new TenderCliUsageError(`Unknown context refresh option: ${arg}`);
|
|
3876
4192
|
}
|
|
3877
4193
|
return {
|
|
3878
|
-
command: '
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
baseUrl
|
|
3882
|
-
flagToken
|
|
3883
|
-
profileName
|
|
3884
|
-
|
|
4194
|
+
command: 'app context refresh',
|
|
4195
|
+
artifactId,
|
|
4196
|
+
dir,
|
|
4197
|
+
baseUrl,
|
|
4198
|
+
flagToken,
|
|
4199
|
+
profileName,
|
|
4200
|
+
dryRun,
|
|
4201
|
+
force,
|
|
4202
|
+
json,
|
|
3885
4203
|
};
|
|
3886
4204
|
}
|
|
3887
|
-
function
|
|
3888
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
3889
|
-
return { command: 'help', topic: 'app generate-env-types' };
|
|
3890
|
-
}
|
|
4205
|
+
function parseMemoryCommonArgs(args) {
|
|
3891
4206
|
let dir = '.';
|
|
4207
|
+
let artifactId = null;
|
|
4208
|
+
let remoteName = 'tender';
|
|
3892
4209
|
let json = false;
|
|
4210
|
+
const remaining = [];
|
|
3893
4211
|
for (let index = 0; index < args.length; index += 1) {
|
|
3894
4212
|
const arg = args[index];
|
|
3895
4213
|
if (arg === '--json') {
|
|
@@ -3897,187 +4215,320 @@ function parseGenerateEnvTypesArgs(args) {
|
|
|
3897
4215
|
continue;
|
|
3898
4216
|
}
|
|
3899
4217
|
if (arg === '--dir') {
|
|
3900
|
-
|
|
3901
|
-
if (!value) {
|
|
3902
|
-
throw new TenderCliUsageError('--dir requires a path.');
|
|
3903
|
-
}
|
|
3904
|
-
dir = value;
|
|
4218
|
+
dir = parseRequiredFlagValue(args[index + 1], '--dir');
|
|
3905
4219
|
index += 1;
|
|
3906
4220
|
continue;
|
|
3907
4221
|
}
|
|
3908
|
-
|
|
4222
|
+
if (arg === '--artifact') {
|
|
4223
|
+
artifactId = parseRequiredFlagValue(args[index + 1], '--artifact');
|
|
4224
|
+
index += 1;
|
|
4225
|
+
continue;
|
|
4226
|
+
}
|
|
4227
|
+
if (arg === '--remote') {
|
|
4228
|
+
remoteName = parseRemoteName(args[index + 1]);
|
|
4229
|
+
index += 1;
|
|
4230
|
+
continue;
|
|
4231
|
+
}
|
|
4232
|
+
remaining.push(arg);
|
|
3909
4233
|
}
|
|
3910
|
-
return {
|
|
3911
|
-
command: 'app generate-env-types',
|
|
3912
|
-
dir,
|
|
3913
|
-
json,
|
|
3914
|
-
};
|
|
4234
|
+
return { dir, artifactId, remoteName, json, remaining };
|
|
3915
4235
|
}
|
|
3916
|
-
function
|
|
3917
|
-
if (
|
|
3918
|
-
|
|
4236
|
+
function parseMemoryStartArgs(args) {
|
|
4237
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4238
|
+
return { command: 'help', topic: 'memory start' };
|
|
3919
4239
|
}
|
|
3920
|
-
|
|
3921
|
-
|
|
4240
|
+
const parsed = parseMemoryCommonArgs(args);
|
|
4241
|
+
let summary = '';
|
|
4242
|
+
let body = null;
|
|
4243
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
4244
|
+
const arg = parsed.remaining[index];
|
|
4245
|
+
if (arg === '--summary') {
|
|
4246
|
+
summary = parseRequiredFlagValue(parsed.remaining[index + 1], '--summary');
|
|
4247
|
+
index += 1;
|
|
4248
|
+
continue;
|
|
4249
|
+
}
|
|
4250
|
+
if (arg === '--body') {
|
|
4251
|
+
body = parseRequiredFlagValue(parsed.remaining[index + 1], '--body');
|
|
4252
|
+
index += 1;
|
|
4253
|
+
continue;
|
|
4254
|
+
}
|
|
4255
|
+
throw new TenderCliUsageError(`Unknown memory start option: ${arg}`);
|
|
3922
4256
|
}
|
|
3923
|
-
if (
|
|
3924
|
-
|
|
4257
|
+
if (!summary.trim()) {
|
|
4258
|
+
throw new TenderCliUsageError('memory start requires --summary.\nNext: tender memory start --summary "Current user goal"');
|
|
3925
4259
|
}
|
|
3926
|
-
|
|
4260
|
+
return { command: 'memory start', summary, body, ...parsed };
|
|
3927
4261
|
}
|
|
3928
|
-
function
|
|
3929
|
-
if (
|
|
3930
|
-
|
|
4262
|
+
function parseMemoryAddArgs(args) {
|
|
4263
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4264
|
+
return { command: 'help', topic: 'memory add' };
|
|
3931
4265
|
}
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
4266
|
+
const parsed = parseMemoryCommonArgs(args);
|
|
4267
|
+
let kind = 'note';
|
|
4268
|
+
let summary = '';
|
|
4269
|
+
let body = null;
|
|
4270
|
+
const tags = [];
|
|
4271
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
4272
|
+
const arg = parsed.remaining[index];
|
|
4273
|
+
if (arg === '--kind') {
|
|
4274
|
+
kind = parseRequiredFlagValue(parsed.remaining[index + 1], '--kind');
|
|
4275
|
+
index += 1;
|
|
4276
|
+
continue;
|
|
4277
|
+
}
|
|
4278
|
+
if (arg === '--summary') {
|
|
4279
|
+
summary = parseRequiredFlagValue(parsed.remaining[index + 1], '--summary');
|
|
4280
|
+
index += 1;
|
|
4281
|
+
continue;
|
|
4282
|
+
}
|
|
4283
|
+
if (arg === '--body') {
|
|
4284
|
+
body = parseRequiredFlagValue(parsed.remaining[index + 1], '--body');
|
|
4285
|
+
index += 1;
|
|
4286
|
+
continue;
|
|
4287
|
+
}
|
|
4288
|
+
if (arg === '--tag') {
|
|
4289
|
+
tags.push(parseRequiredFlagValue(parsed.remaining[index + 1], '--tag'));
|
|
4290
|
+
index += 1;
|
|
4291
|
+
continue;
|
|
4292
|
+
}
|
|
4293
|
+
throw new TenderCliUsageError(`Unknown memory add option: ${arg}`);
|
|
3937
4294
|
}
|
|
3938
|
-
return
|
|
4295
|
+
return { command: 'memory add', kind, summary, body, tags, ...parsed };
|
|
3939
4296
|
}
|
|
3940
|
-
function
|
|
4297
|
+
function parseMemoryHandoffArgs(args) {
|
|
3941
4298
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3942
|
-
return { command: 'help', topic: '
|
|
4299
|
+
return { command: 'help', topic: 'memory handoff' };
|
|
3943
4300
|
}
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
4301
|
+
const parsed = parseMemoryCommonArgs(args);
|
|
4302
|
+
let reason = null;
|
|
4303
|
+
let summary = null;
|
|
4304
|
+
let body = null;
|
|
4305
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
4306
|
+
const arg = parsed.remaining[index];
|
|
4307
|
+
if (arg === '--reason') {
|
|
4308
|
+
reason = parseRequiredFlagValue(parsed.remaining[index + 1], '--reason');
|
|
4309
|
+
index += 1;
|
|
3948
4310
|
continue;
|
|
3949
4311
|
}
|
|
3950
|
-
|
|
4312
|
+
if (arg === '--summary') {
|
|
4313
|
+
summary = parseRequiredFlagValue(parsed.remaining[index + 1], '--summary');
|
|
4314
|
+
index += 1;
|
|
4315
|
+
continue;
|
|
4316
|
+
}
|
|
4317
|
+
if (arg === '--body') {
|
|
4318
|
+
body = parseRequiredFlagValue(parsed.remaining[index + 1], '--body');
|
|
4319
|
+
index += 1;
|
|
4320
|
+
continue;
|
|
4321
|
+
}
|
|
4322
|
+
throw new TenderCliUsageError(`Unknown memory handoff option: ${arg}`);
|
|
3951
4323
|
}
|
|
3952
|
-
return { command: '
|
|
4324
|
+
return { command: 'memory handoff', reason, summary, body, ...parsed };
|
|
3953
4325
|
}
|
|
3954
|
-
function
|
|
4326
|
+
function parseMemoryShowArgs(args) {
|
|
3955
4327
|
if (args.includes('--help') || args.includes('-h')) {
|
|
3956
|
-
return { command: 'help', topic: '
|
|
4328
|
+
return { command: 'help', topic: 'memory show' };
|
|
3957
4329
|
}
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
4330
|
+
const parsed = parseMemoryCommonArgs(args);
|
|
4331
|
+
let commitSha = null;
|
|
4332
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
4333
|
+
const arg = parsed.remaining[index];
|
|
4334
|
+
if (arg === '--commit') {
|
|
4335
|
+
commitSha = parseRequiredFlagValue(parsed.remaining[index + 1], '--commit');
|
|
4336
|
+
index += 1;
|
|
3962
4337
|
continue;
|
|
3963
4338
|
}
|
|
3964
|
-
throw new TenderCliUsageError(`Unknown
|
|
4339
|
+
throw new TenderCliUsageError(`Unknown memory show option: ${arg}`);
|
|
3965
4340
|
}
|
|
3966
|
-
return { command: '
|
|
4341
|
+
return { command: 'memory show', commitSha, ...parsed };
|
|
3967
4342
|
}
|
|
3968
|
-
function
|
|
3969
|
-
if (
|
|
3970
|
-
|
|
4343
|
+
function parseMemoryBriefArgs(args) {
|
|
4344
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4345
|
+
return { command: 'help', topic: 'memory brief' };
|
|
3971
4346
|
}
|
|
3972
|
-
|
|
3973
|
-
|
|
4347
|
+
const parsed = parseMemoryCommonArgs(args);
|
|
4348
|
+
let limit = 12;
|
|
4349
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
4350
|
+
const arg = parsed.remaining[index];
|
|
4351
|
+
if (arg === '--limit') {
|
|
4352
|
+
limit = parsePositiveIntegerFlag(parsed.remaining[index + 1], '--limit');
|
|
4353
|
+
index += 1;
|
|
4354
|
+
continue;
|
|
4355
|
+
}
|
|
4356
|
+
throw new TenderCliUsageError(`Unknown memory brief option: ${arg}`);
|
|
3974
4357
|
}
|
|
3975
|
-
return
|
|
4358
|
+
return { command: 'memory brief', limit, ...parsed };
|
|
3976
4359
|
}
|
|
3977
|
-
function
|
|
3978
|
-
if (
|
|
3979
|
-
|
|
4360
|
+
function parseMemoryFlushArgs(args) {
|
|
4361
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4362
|
+
return { command: 'help', topic: 'memory flush' };
|
|
3980
4363
|
}
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
value.includes('?') ||
|
|
3985
|
-
value.includes('#') ||
|
|
3986
|
-
value.includes('*') ||
|
|
3987
|
-
value !== value.toLowerCase()) {
|
|
3988
|
-
throw new TenderCliUsageError('--host must be an explicit lowercase hostname without protocol, wildcard, path, query, or port. Example: api.example.com');
|
|
4364
|
+
const parsed = parseMemoryCommonArgs(args);
|
|
4365
|
+
if (parsed.remaining.length > 0) {
|
|
4366
|
+
throw new TenderCliUsageError(`Unknown memory flush option: ${parsed.remaining[0]}`);
|
|
3989
4367
|
}
|
|
3990
|
-
return
|
|
4368
|
+
return { command: 'memory flush', ...parsed };
|
|
3991
4369
|
}
|
|
3992
|
-
function
|
|
3993
|
-
if (
|
|
3994
|
-
|
|
4370
|
+
function parseMemoryExportArgs(args) {
|
|
4371
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4372
|
+
return { command: 'help', topic: 'memory export' };
|
|
3995
4373
|
}
|
|
3996
|
-
|
|
3997
|
-
|
|
4374
|
+
const parsed = parseMemoryCommonArgs(args);
|
|
4375
|
+
let format = null;
|
|
4376
|
+
let commitSha = null;
|
|
4377
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
4378
|
+
const arg = parsed.remaining[index];
|
|
4379
|
+
if (arg === '--format') {
|
|
4380
|
+
const value = parseRequiredFlagValue(parsed.remaining[index + 1], '--format');
|
|
4381
|
+
if (value !== 'jsonl') {
|
|
4382
|
+
throw new TenderCliUsageError(`Unsupported memory export format: ${value}.\nNext: tender memory export --format jsonl --dir ${parsed.dir}`);
|
|
4383
|
+
}
|
|
4384
|
+
format = value;
|
|
4385
|
+
index += 1;
|
|
4386
|
+
continue;
|
|
4387
|
+
}
|
|
4388
|
+
if (arg === '--commit') {
|
|
4389
|
+
commitSha = parseRequiredFlagValue(parsed.remaining[index + 1], '--commit');
|
|
4390
|
+
index += 1;
|
|
4391
|
+
continue;
|
|
4392
|
+
}
|
|
4393
|
+
throw new TenderCliUsageError(`Unknown memory export option: ${arg}`);
|
|
3998
4394
|
}
|
|
3999
|
-
|
|
4395
|
+
if (format === null) {
|
|
4396
|
+
throw new TenderCliUsageError(`memory export requires --format jsonl.\nNext: tender memory export --format jsonl --dir ${parsed.dir}`);
|
|
4397
|
+
}
|
|
4398
|
+
return { command: 'memory export', format, commitSha, ...parsed };
|
|
4000
4399
|
}
|
|
4001
|
-
function
|
|
4002
|
-
if (
|
|
4003
|
-
|
|
4400
|
+
function parseTokenProfile(value) {
|
|
4401
|
+
if (value === 'edit-preview' || value === 'publish' || value === 'create' || value === 'db-read') {
|
|
4402
|
+
return value;
|
|
4004
4403
|
}
|
|
4005
|
-
|
|
4006
|
-
|
|
4404
|
+
throw new TenderCliUsageError('--profile must be one of edit-preview, publish, create, or db-read.');
|
|
4405
|
+
}
|
|
4406
|
+
function parsePositiveIntegerFlag(value, flag) {
|
|
4407
|
+
const parsed = Number(value);
|
|
4408
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
4409
|
+
throw new TenderCliUsageError(`${flag} requires a positive integer.`);
|
|
4007
4410
|
}
|
|
4008
|
-
return
|
|
4411
|
+
return parsed;
|
|
4009
4412
|
}
|
|
4010
|
-
function
|
|
4413
|
+
function parseNonNegativeIntegerFlag(value, flag) {
|
|
4414
|
+
const parsed = Number(value);
|
|
4415
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
4416
|
+
throw new TenderCliUsageError(`${flag} requires a non-negative integer.`);
|
|
4417
|
+
}
|
|
4418
|
+
return parsed;
|
|
4419
|
+
}
|
|
4420
|
+
function parseAuthLoginArgs(args) {
|
|
4011
4421
|
if (args.includes('--help') || args.includes('-h')) {
|
|
4012
|
-
return { command: 'help', topic: '
|
|
4422
|
+
return { command: 'help', topic: 'auth login' };
|
|
4013
4423
|
}
|
|
4014
|
-
let
|
|
4015
|
-
let
|
|
4016
|
-
let
|
|
4017
|
-
let
|
|
4018
|
-
let
|
|
4019
|
-
let
|
|
4424
|
+
let device = false;
|
|
4425
|
+
let baseUrl = null;
|
|
4426
|
+
let tokenProfile = 'edit-preview';
|
|
4427
|
+
let saveProfile = DEFAULT_PROFILE_NAME;
|
|
4428
|
+
let artifactId = null;
|
|
4429
|
+
let ttl = '7d';
|
|
4020
4430
|
let json = false;
|
|
4431
|
+
let noPoll = false;
|
|
4432
|
+
let pollTimeoutSeconds = 600;
|
|
4021
4433
|
for (let index = 0; index < args.length; index += 1) {
|
|
4022
4434
|
const arg = args[index];
|
|
4435
|
+
if (arg === '--device') {
|
|
4436
|
+
device = true;
|
|
4437
|
+
continue;
|
|
4438
|
+
}
|
|
4023
4439
|
if (arg === '--json') {
|
|
4024
4440
|
json = true;
|
|
4025
4441
|
continue;
|
|
4026
4442
|
}
|
|
4027
|
-
if (arg === '--
|
|
4028
|
-
|
|
4443
|
+
if (arg === '--no-poll') {
|
|
4444
|
+
noPoll = true;
|
|
4029
4445
|
continue;
|
|
4030
4446
|
}
|
|
4031
|
-
if (arg === '--
|
|
4032
|
-
|
|
4447
|
+
if (arg === '--base-url') {
|
|
4448
|
+
baseUrl = parseBaseUrlFlag(args, index);
|
|
4033
4449
|
index += 1;
|
|
4034
4450
|
continue;
|
|
4035
4451
|
}
|
|
4036
|
-
if (arg === '--
|
|
4037
|
-
|
|
4452
|
+
if (arg === '--profile') {
|
|
4453
|
+
tokenProfile = parseTokenProfile(args[index + 1]);
|
|
4038
4454
|
index += 1;
|
|
4039
4455
|
continue;
|
|
4040
4456
|
}
|
|
4041
|
-
if (arg === '--
|
|
4042
|
-
|
|
4457
|
+
if (arg === '--save-profile') {
|
|
4458
|
+
saveProfile = parseProfileName(args[index + 1], '--save-profile');
|
|
4043
4459
|
index += 1;
|
|
4044
4460
|
continue;
|
|
4045
4461
|
}
|
|
4046
|
-
if (arg === '--
|
|
4047
|
-
|
|
4462
|
+
if (arg === '--artifact') {
|
|
4463
|
+
artifactId = parseProfileName(args[index + 1], '--artifact');
|
|
4048
4464
|
index += 1;
|
|
4049
4465
|
continue;
|
|
4050
4466
|
}
|
|
4051
|
-
if (arg === '--
|
|
4052
|
-
|
|
4467
|
+
if (arg === '--ttl') {
|
|
4468
|
+
ttl = parseTtl(args[index + 1]);
|
|
4053
4469
|
index += 1;
|
|
4054
4470
|
continue;
|
|
4055
4471
|
}
|
|
4056
|
-
|
|
4472
|
+
if (arg === '--poll-timeout') {
|
|
4473
|
+
pollTimeoutSeconds = parsePositiveIntegerFlag(args[index + 1], '--poll-timeout');
|
|
4474
|
+
index += 1;
|
|
4475
|
+
continue;
|
|
4476
|
+
}
|
|
4477
|
+
throw new TenderCliUsageError(`Unknown auth login option: ${arg}`);
|
|
4057
4478
|
}
|
|
4058
|
-
if (!
|
|
4059
|
-
throw new TenderCliUsageError('
|
|
4479
|
+
if (!device) {
|
|
4480
|
+
throw new TenderCliUsageError('auth login currently requires --device. Example: tender auth login --device --profile edit-preview --artifact artifact_123');
|
|
4060
4481
|
}
|
|
4061
|
-
if (
|
|
4062
|
-
throw new TenderCliUsageError('
|
|
4482
|
+
if (tokenProfile === 'create' && artifactId) {
|
|
4483
|
+
throw new TenderCliUsageError('--profile create must be account-scoped; remove --artifact.');
|
|
4484
|
+
}
|
|
4485
|
+
if (tokenProfile === 'db-read' && !artifactId) {
|
|
4486
|
+
throw new TenderCliUsageError('--profile db-read requires --artifact <artifact-id>.');
|
|
4063
4487
|
}
|
|
4064
4488
|
return {
|
|
4065
|
-
command: '
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4489
|
+
command: 'auth login',
|
|
4490
|
+
baseUrl,
|
|
4491
|
+
device,
|
|
4492
|
+
tokenProfile,
|
|
4493
|
+
saveProfile,
|
|
4494
|
+
artifactId,
|
|
4495
|
+
ttl,
|
|
4072
4496
|
json,
|
|
4497
|
+
noPoll,
|
|
4498
|
+
pollTimeoutSeconds,
|
|
4073
4499
|
};
|
|
4074
4500
|
}
|
|
4075
|
-
function
|
|
4501
|
+
function parseAuthStatusArgs(args) {
|
|
4502
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4503
|
+
return { command: 'help', topic: 'auth status' };
|
|
4504
|
+
}
|
|
4505
|
+
let profileName = null;
|
|
4506
|
+
let json = false;
|
|
4507
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
4508
|
+
const arg = args[index];
|
|
4509
|
+
if (arg === '--json') {
|
|
4510
|
+
json = true;
|
|
4511
|
+
continue;
|
|
4512
|
+
}
|
|
4513
|
+
if (arg === '--profile') {
|
|
4514
|
+
profileName = parseProfileName(args[index + 1], '--profile');
|
|
4515
|
+
index += 1;
|
|
4516
|
+
continue;
|
|
4517
|
+
}
|
|
4518
|
+
throw new TenderCliUsageError(`Unknown auth status option: ${arg}`);
|
|
4519
|
+
}
|
|
4520
|
+
return {
|
|
4521
|
+
command: 'auth status',
|
|
4522
|
+
profileName,
|
|
4523
|
+
json,
|
|
4524
|
+
};
|
|
4525
|
+
}
|
|
4526
|
+
function parsePlaybooksCommonArgs(args, startIndex) {
|
|
4076
4527
|
let baseUrl = null;
|
|
4077
4528
|
let flagToken = null;
|
|
4078
4529
|
let profileName = null;
|
|
4079
4530
|
let json = false;
|
|
4080
|
-
const
|
|
4531
|
+
const extras = [];
|
|
4081
4532
|
for (let index = startIndex; index < args.length; index += 1) {
|
|
4082
4533
|
const arg = args[index];
|
|
4083
4534
|
if (arg === '--json') {
|
|
@@ -4099,90 +4550,143 @@ function parseAnalyticsCommonArgs(args, startIndex) {
|
|
|
4099
4550
|
index += 1;
|
|
4100
4551
|
continue;
|
|
4101
4552
|
}
|
|
4102
|
-
|
|
4553
|
+
extras.push(arg);
|
|
4103
4554
|
}
|
|
4104
|
-
return { baseUrl, flagToken, profileName, json,
|
|
4555
|
+
return { baseUrl, flagToken, profileName, json, extras };
|
|
4105
4556
|
}
|
|
4106
|
-
function
|
|
4557
|
+
function parsePlaybooksListArgs(args) {
|
|
4107
4558
|
if (args.includes('--help') || args.includes('-h')) {
|
|
4108
|
-
return { command: 'help', topic: '
|
|
4559
|
+
return { command: 'help', topic: 'playbooks list' };
|
|
4109
4560
|
}
|
|
4110
|
-
const
|
|
4111
|
-
if (
|
|
4112
|
-
throw new TenderCliUsageError(
|
|
4561
|
+
const parsed = parsePlaybooksCommonArgs(args, 0);
|
|
4562
|
+
if (parsed.extras.length > 0) {
|
|
4563
|
+
throw new TenderCliUsageError(`Unknown playbooks list option: ${parsed.extras[0]}`);
|
|
4113
4564
|
}
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4565
|
+
return {
|
|
4566
|
+
command: 'playbooks list',
|
|
4567
|
+
baseUrl: parsed.baseUrl,
|
|
4568
|
+
flagToken: parsed.flagToken,
|
|
4569
|
+
profileName: parsed.profileName,
|
|
4570
|
+
json: parsed.json,
|
|
4571
|
+
};
|
|
4572
|
+
}
|
|
4573
|
+
function parsePlaybooksGetArgs(args) {
|
|
4574
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4575
|
+
return { command: 'help', topic: 'playbooks get' };
|
|
4124
4576
|
}
|
|
4125
|
-
|
|
4577
|
+
const playbookId = args[0];
|
|
4578
|
+
if (!playbookId || playbookId.startsWith('-')) {
|
|
4579
|
+
throw new TenderCliUsageError('playbook id is required. Example: tender playbooks get recharge-bundle-builder --json');
|
|
4580
|
+
}
|
|
4581
|
+
const parsed = parsePlaybooksCommonArgs(args, 1);
|
|
4582
|
+
if (parsed.extras.length > 0) {
|
|
4583
|
+
throw new TenderCliUsageError(`Unknown playbooks get option: ${parsed.extras[0]}`);
|
|
4584
|
+
}
|
|
4585
|
+
return {
|
|
4586
|
+
command: 'playbooks get',
|
|
4587
|
+
playbookId,
|
|
4588
|
+
baseUrl: parsed.baseUrl,
|
|
4589
|
+
flagToken: parsed.flagToken,
|
|
4590
|
+
profileName: parsed.profileName,
|
|
4591
|
+
json: parsed.json,
|
|
4592
|
+
};
|
|
4126
4593
|
}
|
|
4127
|
-
function
|
|
4594
|
+
function parsePlaybooksFileArgs(args) {
|
|
4128
4595
|
if (args.includes('--help') || args.includes('-h')) {
|
|
4129
|
-
return { command: 'help', topic: '
|
|
4596
|
+
return { command: 'help', topic: 'playbooks file' };
|
|
4130
4597
|
}
|
|
4131
|
-
const
|
|
4132
|
-
if (!
|
|
4133
|
-
throw new TenderCliUsageError('
|
|
4598
|
+
const playbookId = args[0];
|
|
4599
|
+
if (!playbookId || playbookId.startsWith('-')) {
|
|
4600
|
+
throw new TenderCliUsageError('playbook id is required. Example: tender playbooks file recharge-bundle-builder --path references/recharge-payloads.md --json');
|
|
4134
4601
|
}
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
for (let index =
|
|
4138
|
-
const arg =
|
|
4139
|
-
if (arg === '--
|
|
4140
|
-
|
|
4602
|
+
let filePath = null;
|
|
4603
|
+
const rest = [];
|
|
4604
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
4605
|
+
const arg = args[index];
|
|
4606
|
+
if (arg === '--path') {
|
|
4607
|
+
const value = args[index + 1];
|
|
4608
|
+
if (!value || value.startsWith('-')) {
|
|
4609
|
+
throw new TenderCliUsageError('--path requires a playbook reference path.');
|
|
4610
|
+
}
|
|
4611
|
+
filePath = value;
|
|
4141
4612
|
index += 1;
|
|
4142
4613
|
continue;
|
|
4143
4614
|
}
|
|
4144
|
-
|
|
4615
|
+
rest.push(arg);
|
|
4145
4616
|
}
|
|
4146
|
-
|
|
4147
|
-
|
|
4617
|
+
const parsed = parsePlaybooksCommonArgs(rest, 0);
|
|
4618
|
+
if (parsed.extras.length > 0) {
|
|
4619
|
+
throw new TenderCliUsageError(`Unknown playbooks file option: ${parsed.extras[0]}`);
|
|
4148
4620
|
}
|
|
4149
|
-
|
|
4621
|
+
if (!filePath) {
|
|
4622
|
+
throw new TenderCliUsageError('--path is required. Example: tender playbooks file recharge-bundle-builder --path references/recharge-payloads.md --json');
|
|
4623
|
+
}
|
|
4624
|
+
return {
|
|
4625
|
+
command: 'playbooks file',
|
|
4626
|
+
playbookId,
|
|
4627
|
+
filePath,
|
|
4628
|
+
baseUrl: parsed.baseUrl,
|
|
4629
|
+
flagToken: parsed.flagToken,
|
|
4630
|
+
profileName: parsed.profileName,
|
|
4631
|
+
json: parsed.json,
|
|
4632
|
+
};
|
|
4150
4633
|
}
|
|
4151
|
-
function
|
|
4634
|
+
function parseGenerateEnvTypesArgs(args) {
|
|
4152
4635
|
if (args.includes('--help') || args.includes('-h')) {
|
|
4153
|
-
return { command: 'help', topic: 'app
|
|
4154
|
-
}
|
|
4155
|
-
const artifactId = args[0];
|
|
4156
|
-
if (!artifactId || artifactId.startsWith('-')) {
|
|
4157
|
-
throw new TenderCliUsageError('app id is required. Example: tender app analytics capabilities <app-id> --include-catalog --json');
|
|
4636
|
+
return { command: 'help', topic: 'app generate-env-types' };
|
|
4158
4637
|
}
|
|
4159
|
-
|
|
4160
|
-
let
|
|
4161
|
-
let
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
includeCatalog = true;
|
|
4638
|
+
let dir = '.';
|
|
4639
|
+
let json = false;
|
|
4640
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
4641
|
+
const arg = args[index];
|
|
4642
|
+
if (arg === '--json') {
|
|
4643
|
+
json = true;
|
|
4166
4644
|
continue;
|
|
4167
4645
|
}
|
|
4168
|
-
if (arg === '--
|
|
4169
|
-
|
|
4646
|
+
if (arg === '--dir') {
|
|
4647
|
+
const value = args[index + 1];
|
|
4648
|
+
if (!value) {
|
|
4649
|
+
throw new TenderCliUsageError('--dir requires a path.');
|
|
4650
|
+
}
|
|
4651
|
+
dir = value;
|
|
4170
4652
|
index += 1;
|
|
4171
4653
|
continue;
|
|
4172
4654
|
}
|
|
4173
|
-
throw new TenderCliUsageError(`Unknown
|
|
4655
|
+
throw new TenderCliUsageError(`Unknown generate-env-types option: ${arg}`);
|
|
4174
4656
|
}
|
|
4175
4657
|
return {
|
|
4176
|
-
command: 'app
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
range,
|
|
4180
|
-
...parsed,
|
|
4658
|
+
command: 'app generate-env-types',
|
|
4659
|
+
dir,
|
|
4660
|
+
json,
|
|
4181
4661
|
};
|
|
4182
4662
|
}
|
|
4183
|
-
function
|
|
4663
|
+
function parseAnalyticsRange(value) {
|
|
4664
|
+
if (!value || value.startsWith('-')) {
|
|
4665
|
+
throw new TenderCliUsageError('--range requires 7d, 30d, 90d, or YYYY-MM-DD..YYYY-MM-DD.');
|
|
4666
|
+
}
|
|
4667
|
+
if (value === '7d' || value === '30d' || value === '90d') {
|
|
4668
|
+
return value;
|
|
4669
|
+
}
|
|
4670
|
+
if (/^\d{4}-\d{2}-\d{2}\.\.\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
4671
|
+
return value;
|
|
4672
|
+
}
|
|
4673
|
+
throw new TenderCliUsageError('--range must be 7d, 30d, 90d, or YYYY-MM-DD..YYYY-MM-DD.');
|
|
4674
|
+
}
|
|
4675
|
+
function parseSpecPath(value) {
|
|
4676
|
+
if (!value || (value !== '-' && value.startsWith('-'))) {
|
|
4677
|
+
throw new TenderCliUsageError('--spec requires a JSON file path or - for stdin.');
|
|
4678
|
+
}
|
|
4679
|
+
return value;
|
|
4680
|
+
}
|
|
4681
|
+
function parseRequiredFlagValue(value, flag) {
|
|
4682
|
+
if (!value || value.startsWith('-')) {
|
|
4683
|
+
throw new TenderCliUsageError(`${flag} requires a value.`);
|
|
4684
|
+
}
|
|
4685
|
+
return value;
|
|
4686
|
+
}
|
|
4687
|
+
function parseCapabilitiesArgs(args) {
|
|
4184
4688
|
if (args.includes('--help') || args.includes('-h')) {
|
|
4185
|
-
return { command: 'help', topic: '
|
|
4689
|
+
return { command: 'help', topic: 'capabilities' };
|
|
4186
4690
|
}
|
|
4187
4691
|
let json = false;
|
|
4188
4692
|
for (const arg of args) {
|
|
@@ -4190,49 +4694,292 @@ function parseAnalyticsAuthoringArgs(args) {
|
|
|
4190
4694
|
json = true;
|
|
4191
4695
|
continue;
|
|
4192
4696
|
}
|
|
4193
|
-
throw new TenderCliUsageError(`Unknown
|
|
4697
|
+
throw new TenderCliUsageError(`Unknown capabilities option: ${arg}`);
|
|
4194
4698
|
}
|
|
4195
|
-
return { command: '
|
|
4699
|
+
return { command: 'capabilities', json };
|
|
4196
4700
|
}
|
|
4197
|
-
function
|
|
4701
|
+
function parseVersionArgs(args) {
|
|
4198
4702
|
if (args.includes('--help') || args.includes('-h')) {
|
|
4199
|
-
return { command: 'help', topic: '
|
|
4200
|
-
}
|
|
4201
|
-
const artifactId = args[0];
|
|
4202
|
-
if (!artifactId || artifactId.startsWith('-')) {
|
|
4203
|
-
throw new TenderCliUsageError('app id is required. Example: tender app analytics suggestions <app-id> --range 30d --json');
|
|
4703
|
+
return { command: 'help', topic: 'version' };
|
|
4204
4704
|
}
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
if (arg === '--range') {
|
|
4210
|
-
range = parseAnalyticsRange(parsed.remaining[index + 1]);
|
|
4211
|
-
index += 1;
|
|
4705
|
+
let json = false;
|
|
4706
|
+
for (const arg of args) {
|
|
4707
|
+
if (arg === '--json') {
|
|
4708
|
+
json = true;
|
|
4212
4709
|
continue;
|
|
4213
4710
|
}
|
|
4214
|
-
throw new TenderCliUsageError(`Unknown
|
|
4711
|
+
throw new TenderCliUsageError(`Unknown version option: ${arg}`);
|
|
4215
4712
|
}
|
|
4216
|
-
return { command: '
|
|
4713
|
+
return { command: 'version', json };
|
|
4217
4714
|
}
|
|
4218
|
-
function
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
.split(',')
|
|
4222
|
-
.map((step) => step.trim())
|
|
4223
|
-
.filter((step) => step.length > 0);
|
|
4224
|
-
if (steps.length < 2 || steps.length > 25 || new Set(steps).size !== steps.length) {
|
|
4225
|
-
throw new TenderCliUsageError('--steps must contain 2-25 unique comma-separated flow steps.');
|
|
4715
|
+
function parseBindingId(value) {
|
|
4716
|
+
if (!value || value.startsWith('-')) {
|
|
4717
|
+
throw new TenderCliUsageError('--id requires a binding id.');
|
|
4226
4718
|
}
|
|
4227
|
-
|
|
4719
|
+
if (!/^[a-z][a-z0-9-]*$/.test(value)) {
|
|
4720
|
+
throw new TenderCliUsageError('--id must start with a lowercase letter and contain only lowercase letters, numbers, and dashes. Example: shopify-api');
|
|
4721
|
+
}
|
|
4722
|
+
return value;
|
|
4228
4723
|
}
|
|
4229
|
-
function
|
|
4230
|
-
if (
|
|
4231
|
-
|
|
4724
|
+
function parseAllowedHost(value) {
|
|
4725
|
+
if (!value || value.startsWith('-')) {
|
|
4726
|
+
throw new TenderCliUsageError('--host requires a hostname.');
|
|
4232
4727
|
}
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4728
|
+
if (value.length === 0 ||
|
|
4729
|
+
value.includes('/') ||
|
|
4730
|
+
value.includes(':') ||
|
|
4731
|
+
value.includes('?') ||
|
|
4732
|
+
value.includes('#') ||
|
|
4733
|
+
value.includes('*') ||
|
|
4734
|
+
value !== value.toLowerCase()) {
|
|
4735
|
+
throw new TenderCliUsageError('--host must be an explicit lowercase hostname without protocol, wildcard, path, query, or port. Example: api.example.com');
|
|
4736
|
+
}
|
|
4737
|
+
return value;
|
|
4738
|
+
}
|
|
4739
|
+
function parseEnvBindingName(value) {
|
|
4740
|
+
if (!value || value.startsWith('-')) {
|
|
4741
|
+
throw new TenderCliUsageError('--name requires an env binding name.');
|
|
4742
|
+
}
|
|
4743
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(value)) {
|
|
4744
|
+
throw new TenderCliUsageError('--name must be UPPER_SNAKE_CASE. Example: SHOPIFY_API');
|
|
4745
|
+
}
|
|
4746
|
+
return value;
|
|
4747
|
+
}
|
|
4748
|
+
function parseTypeName(value, flag) {
|
|
4749
|
+
if (!value || value.startsWith('-')) {
|
|
4750
|
+
throw new TenderCliUsageError(`${flag} requires a TypeScript type name.`);
|
|
4751
|
+
}
|
|
4752
|
+
if (!/^[A-Z][A-Za-z0-9]*$/.test(value)) {
|
|
4753
|
+
throw new TenderCliUsageError(`${flag} must be a PascalCase TypeScript type name. Example: ShopifyApiBinding`);
|
|
4754
|
+
}
|
|
4755
|
+
return value;
|
|
4756
|
+
}
|
|
4757
|
+
function parseBindingsOutboundArgs(args) {
|
|
4758
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4759
|
+
return { command: 'help', topic: 'app bindings outbound' };
|
|
4760
|
+
}
|
|
4761
|
+
let id = null;
|
|
4762
|
+
let host = null;
|
|
4763
|
+
let name = null;
|
|
4764
|
+
let label = null;
|
|
4765
|
+
let interfaceName = null;
|
|
4766
|
+
let dryRun = false;
|
|
4767
|
+
let json = false;
|
|
4768
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
4769
|
+
const arg = args[index];
|
|
4770
|
+
if (arg === '--json') {
|
|
4771
|
+
json = true;
|
|
4772
|
+
continue;
|
|
4773
|
+
}
|
|
4774
|
+
if (arg === '--dry-run') {
|
|
4775
|
+
dryRun = true;
|
|
4776
|
+
continue;
|
|
4777
|
+
}
|
|
4778
|
+
if (arg === '--id') {
|
|
4779
|
+
id = parseBindingId(args[index + 1]);
|
|
4780
|
+
index += 1;
|
|
4781
|
+
continue;
|
|
4782
|
+
}
|
|
4783
|
+
if (arg === '--host') {
|
|
4784
|
+
host = parseAllowedHost(args[index + 1]);
|
|
4785
|
+
index += 1;
|
|
4786
|
+
continue;
|
|
4787
|
+
}
|
|
4788
|
+
if (arg === '--name') {
|
|
4789
|
+
name = parseEnvBindingName(args[index + 1]);
|
|
4790
|
+
index += 1;
|
|
4791
|
+
continue;
|
|
4792
|
+
}
|
|
4793
|
+
if (arg === '--label') {
|
|
4794
|
+
label = parseRequiredFlagValue(args[index + 1], '--label');
|
|
4795
|
+
index += 1;
|
|
4796
|
+
continue;
|
|
4797
|
+
}
|
|
4798
|
+
if (arg === '--interface') {
|
|
4799
|
+
interfaceName = parseTypeName(args[index + 1], '--interface');
|
|
4800
|
+
index += 1;
|
|
4801
|
+
continue;
|
|
4802
|
+
}
|
|
4803
|
+
throw new TenderCliUsageError(`Unknown bindings outbound option: ${arg}`);
|
|
4804
|
+
}
|
|
4805
|
+
if (!id || !host) {
|
|
4806
|
+
throw new TenderCliUsageError('--id and --host are required. Example: tender app bindings outbound --id shopify --host admin.shopify.com --dry-run --json');
|
|
4807
|
+
}
|
|
4808
|
+
if (!dryRun) {
|
|
4809
|
+
throw new TenderCliUsageError('bindings outbound currently requires --dry-run. Example: tender app bindings outbound --id shopify --host admin.shopify.com --dry-run --json');
|
|
4810
|
+
}
|
|
4811
|
+
return {
|
|
4812
|
+
command: 'app bindings outbound',
|
|
4813
|
+
id,
|
|
4814
|
+
host,
|
|
4815
|
+
name,
|
|
4816
|
+
label,
|
|
4817
|
+
interfaceName,
|
|
4818
|
+
dryRun,
|
|
4819
|
+
json,
|
|
4820
|
+
};
|
|
4821
|
+
}
|
|
4822
|
+
function parseAnalyticsCommonArgs(args, startIndex) {
|
|
4823
|
+
let baseUrl = null;
|
|
4824
|
+
let flagToken = null;
|
|
4825
|
+
let profileName = null;
|
|
4826
|
+
let json = false;
|
|
4827
|
+
const remaining = [];
|
|
4828
|
+
for (let index = startIndex; index < args.length; index += 1) {
|
|
4829
|
+
const arg = args[index];
|
|
4830
|
+
if (arg === '--json') {
|
|
4831
|
+
json = true;
|
|
4832
|
+
continue;
|
|
4833
|
+
}
|
|
4834
|
+
if (arg === '--base-url') {
|
|
4835
|
+
baseUrl = parseBaseUrlFlag(args, index);
|
|
4836
|
+
index += 1;
|
|
4837
|
+
continue;
|
|
4838
|
+
}
|
|
4839
|
+
if (arg === '--token') {
|
|
4840
|
+
flagToken = parseTokenFlag(args, index);
|
|
4841
|
+
index += 1;
|
|
4842
|
+
continue;
|
|
4843
|
+
}
|
|
4844
|
+
if (arg === '--profile') {
|
|
4845
|
+
profileName = parseProfileName(args[index + 1], '--profile');
|
|
4846
|
+
index += 1;
|
|
4847
|
+
continue;
|
|
4848
|
+
}
|
|
4849
|
+
remaining.push(arg);
|
|
4850
|
+
}
|
|
4851
|
+
return { baseUrl, flagToken, profileName, json, remaining };
|
|
4852
|
+
}
|
|
4853
|
+
function parseAnalyticsSummaryArgs(args) {
|
|
4854
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4855
|
+
return { command: 'help', topic: 'app analytics summary' };
|
|
4856
|
+
}
|
|
4857
|
+
const artifactId = args[0];
|
|
4858
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
4859
|
+
throw new TenderCliUsageError('app id is required. Example: tender app analytics summary <app-id> --range 30d --json');
|
|
4860
|
+
}
|
|
4861
|
+
const parsed = parseAnalyticsCommonArgs(args, 1);
|
|
4862
|
+
let range = '7d';
|
|
4863
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
4864
|
+
const arg = parsed.remaining[index];
|
|
4865
|
+
if (arg === '--range') {
|
|
4866
|
+
range = parseAnalyticsRange(parsed.remaining[index + 1]);
|
|
4867
|
+
index += 1;
|
|
4868
|
+
continue;
|
|
4869
|
+
}
|
|
4870
|
+
throw new TenderCliUsageError(`Unknown analytics summary option: ${arg}`);
|
|
4871
|
+
}
|
|
4872
|
+
return { command: 'app analytics summary', artifactId, range, ...parsed };
|
|
4873
|
+
}
|
|
4874
|
+
function parseAnalyticsQueryArgs(args) {
|
|
4875
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4876
|
+
return { command: 'help', topic: 'app analytics query' };
|
|
4877
|
+
}
|
|
4878
|
+
const artifactId = args[0];
|
|
4879
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
4880
|
+
throw new TenderCliUsageError('app id is required. Example: tender app analytics query <app-id> --spec chart.json --json');
|
|
4881
|
+
}
|
|
4882
|
+
const parsed = parseAnalyticsCommonArgs(args, 1);
|
|
4883
|
+
let specPath = null;
|
|
4884
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
4885
|
+
const arg = parsed.remaining[index];
|
|
4886
|
+
if (arg === '--spec') {
|
|
4887
|
+
specPath = parseSpecPath(parsed.remaining[index + 1]);
|
|
4888
|
+
index += 1;
|
|
4889
|
+
continue;
|
|
4890
|
+
}
|
|
4891
|
+
throw new TenderCliUsageError(`Unknown analytics query option: ${arg}`);
|
|
4892
|
+
}
|
|
4893
|
+
if (!specPath) {
|
|
4894
|
+
throw new TenderCliUsageError('--spec is required. Example: tender app analytics query artifact_123 --spec chart.json --json');
|
|
4895
|
+
}
|
|
4896
|
+
return { command: 'app analytics query', artifactId, specPath, ...parsed };
|
|
4897
|
+
}
|
|
4898
|
+
function parseAnalyticsCapabilitiesArgs(args) {
|
|
4899
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4900
|
+
return { command: 'help', topic: 'app analytics capabilities' };
|
|
4901
|
+
}
|
|
4902
|
+
const artifactId = args[0];
|
|
4903
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
4904
|
+
throw new TenderCliUsageError('app id is required. Example: tender app analytics capabilities <app-id> --include-catalog --json');
|
|
4905
|
+
}
|
|
4906
|
+
const parsed = parseAnalyticsCommonArgs(args, 1);
|
|
4907
|
+
let range = '7d';
|
|
4908
|
+
let includeCatalog = false;
|
|
4909
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
4910
|
+
const arg = parsed.remaining[index];
|
|
4911
|
+
if (arg === '--include-catalog') {
|
|
4912
|
+
includeCatalog = true;
|
|
4913
|
+
continue;
|
|
4914
|
+
}
|
|
4915
|
+
if (arg === '--range') {
|
|
4916
|
+
range = parseAnalyticsRange(parsed.remaining[index + 1]);
|
|
4917
|
+
index += 1;
|
|
4918
|
+
continue;
|
|
4919
|
+
}
|
|
4920
|
+
throw new TenderCliUsageError(`Unknown analytics capabilities option: ${arg}`);
|
|
4921
|
+
}
|
|
4922
|
+
return {
|
|
4923
|
+
command: 'app analytics capabilities',
|
|
4924
|
+
artifactId,
|
|
4925
|
+
includeCatalog,
|
|
4926
|
+
range,
|
|
4927
|
+
...parsed,
|
|
4928
|
+
};
|
|
4929
|
+
}
|
|
4930
|
+
function parseAnalyticsAuthoringArgs(args) {
|
|
4931
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4932
|
+
return { command: 'help', topic: 'app analytics authoring' };
|
|
4933
|
+
}
|
|
4934
|
+
let json = false;
|
|
4935
|
+
for (const arg of args) {
|
|
4936
|
+
if (arg === '--json') {
|
|
4937
|
+
json = true;
|
|
4938
|
+
continue;
|
|
4939
|
+
}
|
|
4940
|
+
throw new TenderCliUsageError(`Unknown analytics authoring option: ${arg}`);
|
|
4941
|
+
}
|
|
4942
|
+
return { command: 'app analytics authoring', json };
|
|
4943
|
+
}
|
|
4944
|
+
function parseAnalyticsSuggestionsArgs(args) {
|
|
4945
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4946
|
+
return { command: 'help', topic: 'app analytics suggestions' };
|
|
4947
|
+
}
|
|
4948
|
+
const artifactId = args[0];
|
|
4949
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
4950
|
+
throw new TenderCliUsageError('app id is required. Example: tender app analytics suggestions <app-id> --range 30d --json');
|
|
4951
|
+
}
|
|
4952
|
+
const parsed = parseAnalyticsCommonArgs(args, 1);
|
|
4953
|
+
let range = '7d';
|
|
4954
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
4955
|
+
const arg = parsed.remaining[index];
|
|
4956
|
+
if (arg === '--range') {
|
|
4957
|
+
range = parseAnalyticsRange(parsed.remaining[index + 1]);
|
|
4958
|
+
index += 1;
|
|
4959
|
+
continue;
|
|
4960
|
+
}
|
|
4961
|
+
throw new TenderCliUsageError(`Unknown analytics suggestions option: ${arg}`);
|
|
4962
|
+
}
|
|
4963
|
+
return { command: 'app analytics suggestions', artifactId, range, ...parsed };
|
|
4964
|
+
}
|
|
4965
|
+
function parseAnalyticsSteps(value) {
|
|
4966
|
+
const raw = parseRequiredFlagValue(value, '--steps');
|
|
4967
|
+
const steps = raw
|
|
4968
|
+
.split(',')
|
|
4969
|
+
.map((step) => step.trim())
|
|
4970
|
+
.filter((step) => step.length > 0);
|
|
4971
|
+
if (steps.length < 2 || steps.length > 25 || new Set(steps).size !== steps.length) {
|
|
4972
|
+
throw new TenderCliUsageError('--steps must contain 2-25 unique comma-separated flow steps.');
|
|
4973
|
+
}
|
|
4974
|
+
return steps;
|
|
4975
|
+
}
|
|
4976
|
+
function parseAnalyticsExactFunnelArgs(args) {
|
|
4977
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4978
|
+
return { command: 'help', topic: 'app analytics exact-funnel' };
|
|
4979
|
+
}
|
|
4980
|
+
const artifactId = args[0];
|
|
4981
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
4982
|
+
throw new TenderCliUsageError('app id is required. Example: tender app analytics exact-funnel <app-id> --flow checkout_flow --steps started,completed --range 30d --json');
|
|
4236
4983
|
}
|
|
4237
4984
|
const parsed = parseAnalyticsCommonArgs(args, 1);
|
|
4238
4985
|
let range = '7d';
|
|
@@ -6022,7 +6769,67 @@ function parseTenderArgs(args) {
|
|
|
6022
6769
|
}
|
|
6023
6770
|
throw new TenderCliUsageError(`Unknown playbooks command: ${args[1]}`);
|
|
6024
6771
|
}
|
|
6025
|
-
if (args[0] === '
|
|
6772
|
+
if (args[0] === 'connectors') {
|
|
6773
|
+
if (args[1] === undefined || args[1] === '--help' || args[1] === '-h') {
|
|
6774
|
+
return { command: 'help', topic: 'connectors' };
|
|
6775
|
+
}
|
|
6776
|
+
if (args[1] === 'list') {
|
|
6777
|
+
return parseConnectorsListArgs(args.slice(2));
|
|
6778
|
+
}
|
|
6779
|
+
if (args[1] === 'bind') {
|
|
6780
|
+
return parseConnectorsBindArgs(args.slice(2));
|
|
6781
|
+
}
|
|
6782
|
+
if (args[1] === 'shopify') {
|
|
6783
|
+
if (args[2] === undefined || args[2] === '--help' || args[2] === '-h') {
|
|
6784
|
+
return { command: 'help', topic: 'connectors shopify' };
|
|
6785
|
+
}
|
|
6786
|
+
if (args[2] === 'create') {
|
|
6787
|
+
return parseConnectorsShopifyCreateArgs(args.slice(3));
|
|
6788
|
+
}
|
|
6789
|
+
if (args[2] === 'setup-url') {
|
|
6790
|
+
return parseConnectorsShopifySetupUrlArgs(args.slice(3));
|
|
6791
|
+
}
|
|
6792
|
+
if (args[2] === 'credentials' && (args[3] === undefined || args[3] === '--help' || args[3] === '-h')) {
|
|
6793
|
+
return { command: 'help', topic: 'connectors shopify credentials set' };
|
|
6794
|
+
}
|
|
6795
|
+
if (args[2] === 'credentials' && args[3] === 'set') {
|
|
6796
|
+
return parseConnectorsShopifyCredentialsSetArgs(args.slice(4));
|
|
6797
|
+
}
|
|
6798
|
+
if (args[2] === 'app-automation-token' && (args[3] === undefined || args[3] === '--help' || args[3] === '-h')) {
|
|
6799
|
+
return { command: 'help', topic: 'connectors shopify app-automation-token set' };
|
|
6800
|
+
}
|
|
6801
|
+
if (args[2] === 'app-automation-token' && args[3] === 'set') {
|
|
6802
|
+
return parseConnectorsShopifyAppAutomationTokenSetArgs(args.slice(4));
|
|
6803
|
+
}
|
|
6804
|
+
if (args[2] === 'app-automation-token' && args[3] === 'delete') {
|
|
6805
|
+
return parseConnectorsShopifyAppAutomationTokenDeleteArgs(args.slice(4));
|
|
6806
|
+
}
|
|
6807
|
+
if (args[2] === 'verify') {
|
|
6808
|
+
return parseConnectorsShopifyVerifyArgs(args.slice(3));
|
|
6809
|
+
}
|
|
6810
|
+
if (args[2] === 'source' && (args[3] === undefined || args[3] === '--help' || args[3] === '-h')) {
|
|
6811
|
+
return { command: 'help', topic: 'connectors shopify source init' };
|
|
6812
|
+
}
|
|
6813
|
+
if (args[2] === 'source' && args[3] === 'init') {
|
|
6814
|
+
return parseConnectorsShopifySourceInitArgs(args.slice(4));
|
|
6815
|
+
}
|
|
6816
|
+
if (args[2] === 'source' && args[3] === 'checkout') {
|
|
6817
|
+
return parseConnectorsShopifySourceCheckoutArgs(args.slice(4));
|
|
6818
|
+
}
|
|
6819
|
+
if (args[2] === 'source' && args[3] === 'validate') {
|
|
6820
|
+
return parseConnectorsShopifySourceValidateArgs(args.slice(4));
|
|
6821
|
+
}
|
|
6822
|
+
if (args[2] === 'source' && args[3] === 'deploy') {
|
|
6823
|
+
return parseConnectorsShopifySourceDeployArgs(args.slice(4));
|
|
6824
|
+
}
|
|
6825
|
+
if (args[2] === 'delete') {
|
|
6826
|
+
return parseConnectorsShopifyDeleteArgs(args.slice(3));
|
|
6827
|
+
}
|
|
6828
|
+
throw new TenderCliUsageError(`Unknown connectors shopify command: ${args[2]}`);
|
|
6829
|
+
}
|
|
6830
|
+
throw new TenderCliUsageError(`Unknown connectors command: ${args[1]}`);
|
|
6831
|
+
}
|
|
6832
|
+
if (args[0] === 'apps') {
|
|
6026
6833
|
throw new TenderCliUsageError('Unknown command: apps. Use `tender app ...` for Tender App workflows.');
|
|
6027
6834
|
}
|
|
6028
6835
|
if (args[0] !== 'app') {
|
|
@@ -6738,6 +7545,27 @@ async function requestPlaybooksApi(input) {
|
|
|
6738
7545
|
}
|
|
6739
7546
|
return payload;
|
|
6740
7547
|
}
|
|
7548
|
+
async function requestConnectorsApi(input) {
|
|
7549
|
+
const suffix = input.connectorsPath ? `/${input.connectorsPath.replace(/^\/+/, '')}` : '';
|
|
7550
|
+
const response = await input.fetcher(`${input.baseUrl}/api/account/connectors${suffix}`, {
|
|
7551
|
+
method: input.method,
|
|
7552
|
+
headers: {
|
|
7553
|
+
authorization: `Bearer ${input.token}`,
|
|
7554
|
+
accept: 'application/json',
|
|
7555
|
+
...(input.body === undefined ? {} : { 'content-type': 'application/json' }),
|
|
7556
|
+
},
|
|
7557
|
+
body: input.body === undefined ? undefined : JSON.stringify(input.body),
|
|
7558
|
+
});
|
|
7559
|
+
const payload = response.status === 204 ? { ok: true } : await parseJsonResponse(response);
|
|
7560
|
+
if (!response.ok || !payload?.ok) {
|
|
7561
|
+
const reasonCode = (typeof payload?.reasonCode === 'string' && payload.reasonCode) || `http_${response.status}`;
|
|
7562
|
+
const message = (typeof payload?.message === 'string' && payload.message) ||
|
|
7563
|
+
(typeof payload?.reasonCode === 'string' && payload.reasonCode) ||
|
|
7564
|
+
`Connector request failed with HTTP ${response.status}.`;
|
|
7565
|
+
throw new Error([message, `Reason: ${reasonCode}.`, 'Example: tender connectors list --profile account --json.'].join('\n'));
|
|
7566
|
+
}
|
|
7567
|
+
return payload;
|
|
7568
|
+
}
|
|
6741
7569
|
function dbCorrectedExample(artifactId, dbPath) {
|
|
6742
7570
|
if (dbPath === 'capabilities') {
|
|
6743
7571
|
return `tender app db capabilities ${artifactId} --json`;
|
|
@@ -8159,9 +8987,16 @@ async function runGitCommand(input) {
|
|
|
8159
8987
|
function shellQuote(value) {
|
|
8160
8988
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
8161
8989
|
}
|
|
8990
|
+
function currentCliCredentialHelperCommandParts() {
|
|
8991
|
+
const scriptPath = process.argv[1];
|
|
8992
|
+
if (!scriptPath) {
|
|
8993
|
+
return ['tender'];
|
|
8994
|
+
}
|
|
8995
|
+
return [process.execPath, scriptPath];
|
|
8996
|
+
}
|
|
8162
8997
|
function createCredentialHelperCommand(input) {
|
|
8163
8998
|
const parts = [
|
|
8164
|
-
|
|
8999
|
+
...currentCliCredentialHelperCommandParts().map(shellQuote),
|
|
8165
9000
|
'app',
|
|
8166
9001
|
'git',
|
|
8167
9002
|
'credential',
|
|
@@ -9866,228 +10701,1349 @@ async function runMemoryBrief(command, io, runtime) {
|
|
|
9866
10701
|
repoPath,
|
|
9867
10702
|
runner: commandRunnerForMemory(runtime),
|
|
9868
10703
|
});
|
|
9869
|
-
const latestSummary = [...entries].reverse().find((entry) => entry.kind === 'summary') ?? null;
|
|
9870
|
-
const unresolved = entries
|
|
9871
|
-
.filter((entry) => entry.status === 'open' || entry.kind === 'failure' || entry.kind === 'open_question' || entry.kind === 'support_handoff')
|
|
9872
|
-
.slice(-command.limit);
|
|
9873
|
-
const briefEntries = latestSummary ? [latestSummary, ...unresolved.filter((entry) => entry.entryId !== latestSummary.entryId)] : unresolved;
|
|
10704
|
+
const latestSummary = [...entries].reverse().find((entry) => entry.kind === 'summary') ?? null;
|
|
10705
|
+
const unresolved = entries
|
|
10706
|
+
.filter((entry) => entry.status === 'open' || entry.kind === 'failure' || entry.kind === 'open_question' || entry.kind === 'support_handoff')
|
|
10707
|
+
.slice(-command.limit);
|
|
10708
|
+
const briefEntries = latestSummary ? [latestSummary, ...unresolved.filter((entry) => entry.entryId !== latestSummary.entryId)] : unresolved;
|
|
10709
|
+
const result = {
|
|
10710
|
+
ok: true,
|
|
10711
|
+
command: command.command,
|
|
10712
|
+
ref: ARTIFACT_MEMORY_NOTES_REF,
|
|
10713
|
+
entries: briefEntries,
|
|
10714
|
+
};
|
|
10715
|
+
if (command.json) {
|
|
10716
|
+
io.stdout(JSON.stringify(result, null, 2));
|
|
10717
|
+
}
|
|
10718
|
+
else {
|
|
10719
|
+
io.stdout(briefEntries.length > 0 ? briefEntries.map(formatArtifactMemoryEntry).join('\n') : 'No artifact memory brief is available yet.');
|
|
10720
|
+
}
|
|
10721
|
+
return 0;
|
|
10722
|
+
}
|
|
10723
|
+
async function runMemoryFlush(command, io, runtime) {
|
|
10724
|
+
const sync = await pushArtifactMemoryNotes({
|
|
10725
|
+
repoPath: path.resolve(command.dir),
|
|
10726
|
+
remoteName: command.remoteName,
|
|
10727
|
+
runner: commandRunnerForMemory(runtime),
|
|
10728
|
+
});
|
|
10729
|
+
const next = sync.status === 'failed'
|
|
10730
|
+
? `tender memory flush --dir ${command.dir} --remote ${command.remoteName}`
|
|
10731
|
+
: sync.status === 'not_configured'
|
|
10732
|
+
? memoryRecoveryForArtifact(command.artifactId, command.dir)
|
|
10733
|
+
: null;
|
|
10734
|
+
const result = {
|
|
10735
|
+
ok: sync.status === 'pushed',
|
|
10736
|
+
command: command.command,
|
|
10737
|
+
memorySync: sync,
|
|
10738
|
+
next,
|
|
10739
|
+
};
|
|
10740
|
+
if (command.json) {
|
|
10741
|
+
io.stdout(JSON.stringify(result, null, 2));
|
|
10742
|
+
}
|
|
10743
|
+
else {
|
|
10744
|
+
io.stdout([
|
|
10745
|
+
`memory_sync: ${sync.status}`,
|
|
10746
|
+
sync.error ? `error: ${sync.error}` : null,
|
|
10747
|
+
result.next ? `next: ${result.next}` : null,
|
|
10748
|
+
]
|
|
10749
|
+
.filter((line) => Boolean(line))
|
|
10750
|
+
.join('\n'));
|
|
10751
|
+
}
|
|
10752
|
+
return sync.status === 'pushed' ? 0 : 1;
|
|
10753
|
+
}
|
|
10754
|
+
async function runMemoryExport(command, io, runtime) {
|
|
10755
|
+
const entries = await readArtifactMemoryEntries({
|
|
10756
|
+
repoPath: path.resolve(command.dir),
|
|
10757
|
+
commitSha: command.commitSha,
|
|
10758
|
+
runner: commandRunnerForMemory(runtime),
|
|
10759
|
+
});
|
|
10760
|
+
if (command.format === 'jsonl') {
|
|
10761
|
+
io.stdout(entries.map((entry) => JSON.stringify(entry)).join('\n'));
|
|
10762
|
+
return 0;
|
|
10763
|
+
}
|
|
10764
|
+
throw new TenderCliUsageError(`Unsupported memory export format: ${command.format}.\nNext: tender memory export --format jsonl --dir ${command.dir}`);
|
|
10765
|
+
}
|
|
10766
|
+
async function readLocalChangedFiles(input) {
|
|
10767
|
+
const result = await runGitCommand({
|
|
10768
|
+
runtime: input.runtime,
|
|
10769
|
+
cwd: input.repoPath,
|
|
10770
|
+
args: ['status', '--porcelain'],
|
|
10771
|
+
allowFailure: true,
|
|
10772
|
+
});
|
|
10773
|
+
if (result.exitCode !== 0) {
|
|
10774
|
+
return [];
|
|
10775
|
+
}
|
|
10776
|
+
return result.stdout
|
|
10777
|
+
.split(/\r?\n/)
|
|
10778
|
+
.map((line) => line.slice(3).trim())
|
|
10779
|
+
.filter(Boolean)
|
|
10780
|
+
.slice(0, 50);
|
|
10781
|
+
}
|
|
10782
|
+
async function appendAutomaticMemory(input) {
|
|
10783
|
+
try {
|
|
10784
|
+
return await appendArtifactMemoryNote({
|
|
10785
|
+
repoPath: input.repoPath,
|
|
10786
|
+
artifactId: input.artifactId,
|
|
10787
|
+
kind: input.kind,
|
|
10788
|
+
summary: input.summary,
|
|
10789
|
+
body: input.body,
|
|
10790
|
+
tags: input.tags,
|
|
10791
|
+
severity: input.severity,
|
|
10792
|
+
status: input.status,
|
|
10793
|
+
related: input.related,
|
|
10794
|
+
source: {
|
|
10795
|
+
actor: 'cli',
|
|
10796
|
+
command: input.summary,
|
|
10797
|
+
},
|
|
10798
|
+
runner: commandRunnerForMemory(input.runtime),
|
|
10799
|
+
now: input.runtime.now,
|
|
10800
|
+
});
|
|
10801
|
+
}
|
|
10802
|
+
catch (error) {
|
|
10803
|
+
return {
|
|
10804
|
+
error: error instanceof Error ? error.message : String(error),
|
|
10805
|
+
next: `tender memory add --dir ${input.repoPath} --artifact ${input.artifactId} --kind ${input.kind} --summary ${JSON.stringify(input.summary)}`,
|
|
10806
|
+
};
|
|
10807
|
+
}
|
|
10808
|
+
}
|
|
10809
|
+
async function flushAutomaticMemory(input) {
|
|
10810
|
+
return pushArtifactMemoryNotes({
|
|
10811
|
+
repoPath: input.repoPath,
|
|
10812
|
+
remoteName: input.remoteName,
|
|
10813
|
+
runner: commandRunnerForMemory(input.runtime),
|
|
10814
|
+
});
|
|
10815
|
+
}
|
|
10816
|
+
async function resolveAnalyticsCredentials(command, runtime) {
|
|
10817
|
+
return await resolveApiCredentials({
|
|
10818
|
+
baseUrl: command.baseUrl,
|
|
10819
|
+
flagToken: command.flagToken,
|
|
10820
|
+
profileName: command.profileName,
|
|
10821
|
+
env: runtime.env ?? process.env,
|
|
10822
|
+
});
|
|
10823
|
+
}
|
|
10824
|
+
async function resolvePlaybooksCredentials(command, runtime) {
|
|
10825
|
+
try {
|
|
10826
|
+
return await resolveApiCredentials({
|
|
10827
|
+
baseUrl: command.baseUrl,
|
|
10828
|
+
flagToken: command.flagToken,
|
|
10829
|
+
profileName: command.profileName,
|
|
10830
|
+
env: runtime.env ?? process.env,
|
|
10831
|
+
});
|
|
10832
|
+
}
|
|
10833
|
+
catch (error) {
|
|
10834
|
+
if (error instanceof TenderCliUsageError) {
|
|
10835
|
+
throw new TenderCliUsageError([
|
|
10836
|
+
'Playbooks require Tender authentication.',
|
|
10837
|
+
'Run `tender auth login --device --profile edit-preview --save-profile agent --ttl 7d --json`, set TENDER_API_TOKEN, or pass --token <token>.',
|
|
10838
|
+
].join('\n'));
|
|
10839
|
+
}
|
|
10840
|
+
throw error;
|
|
10841
|
+
}
|
|
10842
|
+
}
|
|
10843
|
+
async function resolveDbCredentials(command, runtime) {
|
|
10844
|
+
return await resolveApiCredentials({
|
|
10845
|
+
baseUrl: command.baseUrl,
|
|
10846
|
+
flagToken: command.flagToken,
|
|
10847
|
+
profileName: command.profileName,
|
|
10848
|
+
env: runtime.env ?? process.env,
|
|
10849
|
+
});
|
|
10850
|
+
}
|
|
10851
|
+
function validateDbSql(sql) {
|
|
10852
|
+
if (!sql.trim()) {
|
|
10853
|
+
throw new TenderCliUsageError('SQL statement is required.');
|
|
10854
|
+
}
|
|
10855
|
+
const byteLength = new TextEncoder().encode(sql).byteLength;
|
|
10856
|
+
if (byteLength > DB_SQL_STATEMENT_MAX_BYTES) {
|
|
10857
|
+
throw new TenderCliUsageError(`SQL statement is limited to ${DB_SQL_STATEMENT_MAX_BYTES} bytes.`);
|
|
10858
|
+
}
|
|
10859
|
+
}
|
|
10860
|
+
async function readDbSql(command, runtime) {
|
|
10861
|
+
const sql = command.sqlSource.kind === 'inline'
|
|
10862
|
+
? command.sqlSource.value
|
|
10863
|
+
: command.sqlSource.value === '-'
|
|
10864
|
+
? runtime.stdin
|
|
10865
|
+
: await readFile(path.resolve(command.sqlSource.value), 'utf8');
|
|
10866
|
+
if (!sql) {
|
|
10867
|
+
throw new TenderCliUsageError('--file - requires SQL on stdin.');
|
|
10868
|
+
}
|
|
10869
|
+
validateDbSql(sql);
|
|
10870
|
+
return sql;
|
|
10871
|
+
}
|
|
10872
|
+
function printDbPayload(command, io, payload, fallbackLines) {
|
|
10873
|
+
if (command.json) {
|
|
10874
|
+
io.stdout(JSON.stringify(payload, null, 2));
|
|
10875
|
+
}
|
|
10876
|
+
else {
|
|
10877
|
+
io.stdout(fallbackLines.join('\n'));
|
|
10878
|
+
}
|
|
10879
|
+
}
|
|
10880
|
+
function printPlaybooksPayload(command, io, payload, fallback) {
|
|
10881
|
+
if (command.json) {
|
|
10882
|
+
io.stdout(JSON.stringify(payload, null, 2));
|
|
10883
|
+
}
|
|
10884
|
+
else {
|
|
10885
|
+
io.stdout(fallback);
|
|
10886
|
+
}
|
|
10887
|
+
}
|
|
10888
|
+
async function runPlaybooksList(command, io, runtime) {
|
|
10889
|
+
const credentials = await resolvePlaybooksCredentials(command, runtime);
|
|
10890
|
+
const payload = await requestPlaybooksApi({
|
|
10891
|
+
playbooksPath: 'list',
|
|
10892
|
+
baseUrl: credentials.baseUrl,
|
|
10893
|
+
token: credentials.token,
|
|
10894
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
10895
|
+
});
|
|
10896
|
+
const playbooks = Array.isArray(payload.playbooks) ? payload.playbooks : [];
|
|
10897
|
+
printPlaybooksPayload(command, io, payload, playbooks
|
|
10898
|
+
.map((playbook) => (isCliJsonRecord(playbook) ? `${String(playbook.id ?? '')}\t${String(playbook.description ?? '')}` : ''))
|
|
10899
|
+
.filter(Boolean)
|
|
10900
|
+
.join('\n') || 'No Tender Prompt playbooks found.');
|
|
10901
|
+
return 0;
|
|
10902
|
+
}
|
|
10903
|
+
async function runPlaybooksGet(command, io, runtime) {
|
|
10904
|
+
const credentials = await resolvePlaybooksCredentials(command, runtime);
|
|
10905
|
+
const payload = await requestPlaybooksApi({
|
|
10906
|
+
playbooksPath: 'get',
|
|
10907
|
+
playbookId: command.playbookId,
|
|
10908
|
+
baseUrl: credentials.baseUrl,
|
|
10909
|
+
token: credentials.token,
|
|
10910
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
10911
|
+
});
|
|
10912
|
+
printPlaybooksPayload(command, io, payload, typeof payload.body === 'string' ? payload.body : JSON.stringify(payload, null, 2));
|
|
10913
|
+
return 0;
|
|
10914
|
+
}
|
|
10915
|
+
async function runPlaybooksFile(command, io, runtime) {
|
|
10916
|
+
const credentials = await resolvePlaybooksCredentials(command, runtime);
|
|
10917
|
+
const payload = await requestPlaybooksApi({
|
|
10918
|
+
playbooksPath: 'file',
|
|
10919
|
+
playbookId: command.playbookId,
|
|
10920
|
+
filePath: command.filePath,
|
|
10921
|
+
baseUrl: credentials.baseUrl,
|
|
10922
|
+
token: credentials.token,
|
|
10923
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
10924
|
+
});
|
|
10925
|
+
printPlaybooksPayload(command, io, payload, typeof payload.content === 'string' ? payload.content : JSON.stringify(payload, null, 2));
|
|
10926
|
+
return 0;
|
|
10927
|
+
}
|
|
10928
|
+
async function resolveConnectorsCredentials(command, runtime) {
|
|
10929
|
+
try {
|
|
10930
|
+
return await resolveApiCredentials({
|
|
10931
|
+
baseUrl: command.baseUrl,
|
|
10932
|
+
flagToken: command.flagToken,
|
|
10933
|
+
profileName: command.profileName,
|
|
10934
|
+
env: runtime.env ?? process.env,
|
|
10935
|
+
});
|
|
10936
|
+
}
|
|
10937
|
+
catch (error) {
|
|
10938
|
+
if (error instanceof TenderCliUsageError) {
|
|
10939
|
+
throw new TenderCliUsageError([
|
|
10940
|
+
'Connectors require Tender account authentication.',
|
|
10941
|
+
'Run `tender auth login --device --profile create --ttl 7d --save-profile account --json`, set TENDER_API_TOKEN, or pass --token <token>.',
|
|
10942
|
+
].join('\n'));
|
|
10943
|
+
}
|
|
10944
|
+
throw error;
|
|
10945
|
+
}
|
|
10946
|
+
}
|
|
10947
|
+
function printConnectorsPayload(command, io, payload, fallbackLines) {
|
|
10948
|
+
if (command.json) {
|
|
10949
|
+
io.stdout(JSON.stringify(payload, null, 2));
|
|
10950
|
+
}
|
|
10951
|
+
else {
|
|
10952
|
+
io.stdout(fallbackLines.join('\n'));
|
|
10953
|
+
}
|
|
10954
|
+
}
|
|
10955
|
+
function readConnectorArray(payload) {
|
|
10956
|
+
return Array.isArray(payload.connectors) ? payload.connectors.filter(isCliJsonRecord) : [];
|
|
10957
|
+
}
|
|
10958
|
+
async function runConnectorsList(command, io, runtime) {
|
|
10959
|
+
const credentials = await resolveConnectorsCredentials(command, runtime);
|
|
10960
|
+
const payload = await requestConnectorsApi({
|
|
10961
|
+
connectorsPath: '',
|
|
10962
|
+
method: 'GET',
|
|
10963
|
+
baseUrl: credentials.baseUrl,
|
|
10964
|
+
token: credentials.token,
|
|
10965
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
10966
|
+
});
|
|
10967
|
+
const connectors = readConnectorArray(payload);
|
|
10968
|
+
printConnectorsPayload(command, io, payload, connectors.length > 0
|
|
10969
|
+
? connectors.map((connector) => {
|
|
10970
|
+
return [
|
|
10971
|
+
String(connector.connectorId ?? ''),
|
|
10972
|
+
String(connector.instanceId ?? ''),
|
|
10973
|
+
String(connector.status ?? ''),
|
|
10974
|
+
String(connector.adminApiVersion ?? ''),
|
|
10975
|
+
String(connector.shopDomain ?? ''),
|
|
10976
|
+
connector.hasAppAutomationToken ? 'app_automation_token:stored' : 'app_automation_token:missing',
|
|
10977
|
+
].join('\t');
|
|
10978
|
+
})
|
|
10979
|
+
: ['No connectors found.']);
|
|
10980
|
+
return 0;
|
|
10981
|
+
}
|
|
10982
|
+
async function runConnectorsShopifyCreate(command, io, runtime) {
|
|
10983
|
+
const credentials = await resolveConnectorsCredentials(command, runtime);
|
|
10984
|
+
const payload = await requestConnectorsApi({
|
|
10985
|
+
connectorsPath: '',
|
|
10986
|
+
method: 'POST',
|
|
10987
|
+
baseUrl: credentials.baseUrl,
|
|
10988
|
+
token: credentials.token,
|
|
10989
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
10990
|
+
body: {
|
|
10991
|
+
type: 'shopify',
|
|
10992
|
+
instanceId: command.instanceId,
|
|
10993
|
+
...(command.label ? { label: command.label } : {}),
|
|
10994
|
+
...(command.appName ? { appName: command.appName } : {}),
|
|
10995
|
+
...(command.adminApiVersion ? { adminApiVersion: command.adminApiVersion } : {}),
|
|
10996
|
+
...(command.shopDomain ? { shopDomain: command.shopDomain } : {}),
|
|
10997
|
+
},
|
|
10998
|
+
});
|
|
10999
|
+
const connector = isCliJsonRecord(payload.connector) ? payload.connector : {};
|
|
11000
|
+
printConnectorsPayload(command, io, payload, [
|
|
11001
|
+
`connector_id: ${String(connector.connectorId ?? '')}`,
|
|
11002
|
+
`instance: ${String(connector.instanceId ?? command.instanceId)}`,
|
|
11003
|
+
`status: ${String(connector.status ?? '')}`,
|
|
11004
|
+
`admin_api_version: ${String(connector.adminApiVersion ?? command.adminApiVersion ?? '')}`,
|
|
11005
|
+
`setup_url: ${String(payload.setupUrl ?? '')}`,
|
|
11006
|
+
]);
|
|
11007
|
+
return 0;
|
|
11008
|
+
}
|
|
11009
|
+
async function runConnectorsShopifySetupUrl(command, io, runtime) {
|
|
11010
|
+
const credentials = await resolveConnectorsCredentials(command, runtime);
|
|
11011
|
+
const payload = await requestConnectorsApi({
|
|
11012
|
+
connectorsPath: `${encodeURIComponent(command.connectorId)}/setup-sessions`,
|
|
11013
|
+
method: 'POST',
|
|
11014
|
+
baseUrl: credentials.baseUrl,
|
|
11015
|
+
token: credentials.token,
|
|
11016
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
11017
|
+
});
|
|
11018
|
+
printConnectorsPayload(command, io, payload, [
|
|
11019
|
+
`connector_id: ${command.connectorId}`,
|
|
11020
|
+
`setup_url: ${String(payload.setupUrl ?? '')}`,
|
|
11021
|
+
]);
|
|
11022
|
+
return 0;
|
|
11023
|
+
}
|
|
11024
|
+
function readClientSecret(command, runtime) {
|
|
11025
|
+
if (command.clientSecretSource.kind === 'stdin') {
|
|
11026
|
+
const secret = (runtime.stdin ?? '').trim();
|
|
11027
|
+
if (!secret) {
|
|
11028
|
+
throw new TenderCliUsageError('--client-secret-stdin requires a non-empty secret on stdin.');
|
|
11029
|
+
}
|
|
11030
|
+
return secret;
|
|
11031
|
+
}
|
|
11032
|
+
const secret = runtime.env?.[command.clientSecretSource.name] ?? process.env[command.clientSecretSource.name];
|
|
11033
|
+
if (!secret?.trim()) {
|
|
11034
|
+
throw new TenderCliUsageError(`Environment variable ${command.clientSecretSource.name} is empty or unset.`);
|
|
11035
|
+
}
|
|
11036
|
+
return secret.trim();
|
|
11037
|
+
}
|
|
11038
|
+
function readAppAutomationToken(command, runtime) {
|
|
11039
|
+
if (command.tokenSource.kind === 'stdin') {
|
|
11040
|
+
const token = (runtime.stdin ?? '').trim();
|
|
11041
|
+
if (!token) {
|
|
11042
|
+
throw new TenderCliUsageError('--token-stdin requires a non-empty token on stdin.');
|
|
11043
|
+
}
|
|
11044
|
+
return token;
|
|
11045
|
+
}
|
|
11046
|
+
const token = runtime.env?.[command.tokenSource.name] ?? process.env[command.tokenSource.name];
|
|
11047
|
+
if (!token?.trim()) {
|
|
11048
|
+
throw new TenderCliUsageError(`Environment variable ${command.tokenSource.name} is empty or unset.`);
|
|
11049
|
+
}
|
|
11050
|
+
return token.trim();
|
|
11051
|
+
}
|
|
11052
|
+
async function runConnectorsShopifyCredentialsSet(command, io, runtime) {
|
|
11053
|
+
const credentials = await resolveConnectorsCredentials(command, runtime);
|
|
11054
|
+
const clientSecret = readClientSecret(command, runtime);
|
|
11055
|
+
const payload = await requestConnectorsApi({
|
|
11056
|
+
connectorsPath: `${encodeURIComponent(command.connectorId)}/credentials`,
|
|
11057
|
+
method: 'POST',
|
|
11058
|
+
baseUrl: credentials.baseUrl,
|
|
11059
|
+
token: credentials.token,
|
|
11060
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
11061
|
+
body: {
|
|
11062
|
+
shopDomain: command.shopDomain,
|
|
11063
|
+
clientId: command.clientId,
|
|
11064
|
+
clientSecret,
|
|
11065
|
+
},
|
|
11066
|
+
});
|
|
11067
|
+
const connector = isCliJsonRecord(payload.connector) ? payload.connector : {};
|
|
11068
|
+
printConnectorsPayload(command, io, payload, [
|
|
11069
|
+
`connector_id: ${String(connector.connectorId ?? command.connectorId)}`,
|
|
11070
|
+
`status: ${String(connector.status ?? '')}`,
|
|
11071
|
+
`shop: ${String(connector.shopDomain ?? command.shopDomain)}`,
|
|
11072
|
+
`client_id_prefix: ${String(connector.clientIdPrefix ?? '')}`,
|
|
11073
|
+
]);
|
|
11074
|
+
return 0;
|
|
11075
|
+
}
|
|
11076
|
+
async function runConnectorsShopifyAppAutomationTokenSet(command, io, runtime) {
|
|
11077
|
+
const credentials = await resolveConnectorsCredentials(command, runtime);
|
|
11078
|
+
const appAutomationToken = readAppAutomationToken(command, runtime);
|
|
11079
|
+
const payload = await requestConnectorsApi({
|
|
11080
|
+
connectorsPath: `${encodeURIComponent(command.connectorId)}/app-automation-token`,
|
|
11081
|
+
method: 'POST',
|
|
11082
|
+
baseUrl: credentials.baseUrl,
|
|
11083
|
+
token: credentials.token,
|
|
11084
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
11085
|
+
body: {
|
|
11086
|
+
appAutomationToken,
|
|
11087
|
+
...(command.expiresAt ? { expiresAt: command.expiresAt } : {}),
|
|
11088
|
+
},
|
|
11089
|
+
});
|
|
11090
|
+
const connector = isCliJsonRecord(payload.connector) ? payload.connector : {};
|
|
11091
|
+
printConnectorsPayload(command, io, payload, [
|
|
11092
|
+
`connector_id: ${String(connector.connectorId ?? command.connectorId)}`,
|
|
11093
|
+
`app_automation_token: ${connector.hasAppAutomationToken ? 'stored' : 'missing'}`,
|
|
11094
|
+
`app_automation_token_expires_at: ${String(connector.appAutomationTokenExpiresAt ?? '')}`,
|
|
11095
|
+
]);
|
|
11096
|
+
return 0;
|
|
11097
|
+
}
|
|
11098
|
+
async function runConnectorsShopifyAppAutomationTokenDelete(command, io, runtime) {
|
|
11099
|
+
if (command.confirm !== 'delete') {
|
|
11100
|
+
throw new TenderCliUsageError('--confirm delete is required. Example: tender connectors shopify app-automation-token delete conn_123 --confirm delete --json');
|
|
11101
|
+
}
|
|
11102
|
+
const credentials = await resolveConnectorsCredentials(command, runtime);
|
|
11103
|
+
const payload = await requestConnectorsApi({
|
|
11104
|
+
connectorsPath: `${encodeURIComponent(command.connectorId)}/app-automation-token`,
|
|
11105
|
+
method: 'DELETE',
|
|
11106
|
+
baseUrl: credentials.baseUrl,
|
|
11107
|
+
token: credentials.token,
|
|
11108
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
11109
|
+
});
|
|
11110
|
+
const connector = isCliJsonRecord(payload.connector) ? payload.connector : {};
|
|
11111
|
+
printConnectorsPayload(command, io, payload, [
|
|
11112
|
+
`connector_id: ${String(connector.connectorId ?? command.connectorId)}`,
|
|
11113
|
+
'app_automation_token: deleted',
|
|
11114
|
+
]);
|
|
11115
|
+
return 0;
|
|
11116
|
+
}
|
|
11117
|
+
async function runConnectorsShopifyVerify(command, io, runtime) {
|
|
11118
|
+
const credentials = await resolveConnectorsCredentials(command, runtime);
|
|
11119
|
+
const payload = await requestConnectorsApi({
|
|
11120
|
+
connectorsPath: `${encodeURIComponent(command.connectorId)}/verify`,
|
|
11121
|
+
method: 'POST',
|
|
11122
|
+
baseUrl: credentials.baseUrl,
|
|
11123
|
+
token: credentials.token,
|
|
11124
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
11125
|
+
});
|
|
11126
|
+
const connector = isCliJsonRecord(payload.connector) ? payload.connector : {};
|
|
11127
|
+
const scopes = Array.isArray(connector.scopes) ? connector.scopes.join(',') : '';
|
|
11128
|
+
printConnectorsPayload(command, io, payload, [
|
|
11129
|
+
`connector_id: ${String(connector.connectorId ?? command.connectorId)}`,
|
|
11130
|
+
`status: ${String(connector.status ?? '')}`,
|
|
11131
|
+
`shop: ${String(connector.shopDomain ?? '')}`,
|
|
11132
|
+
`scopes: ${scopes}`,
|
|
11133
|
+
]);
|
|
11134
|
+
return 0;
|
|
11135
|
+
}
|
|
11136
|
+
function requireShopifySourceString(value, fallback = '') {
|
|
11137
|
+
return typeof value === 'string' && value.trim() ? value.trim() : fallback;
|
|
11138
|
+
}
|
|
11139
|
+
function normalizeTenderElementTag(value) {
|
|
11140
|
+
const normalized = value.trim().toLowerCase();
|
|
11141
|
+
if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)+$/.test(normalized)) {
|
|
11142
|
+
throw new TenderCliUsageError('--tender-element must be a valid custom element tag, for example tender-dog-profile.');
|
|
11143
|
+
}
|
|
11144
|
+
return normalized;
|
|
11145
|
+
}
|
|
11146
|
+
function normalizeThemeExtensionHandle(value) {
|
|
11147
|
+
const normalized = value.trim().toLowerCase();
|
|
11148
|
+
if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(normalized)) {
|
|
11149
|
+
throw new TenderCliUsageError('--theme-extension-handle must contain only lowercase letters, numbers, and dashes.');
|
|
11150
|
+
}
|
|
11151
|
+
return normalized;
|
|
11152
|
+
}
|
|
11153
|
+
function tomlString(value) {
|
|
11154
|
+
return JSON.stringify(value);
|
|
11155
|
+
}
|
|
11156
|
+
function uniqueSortedScopes(scopes) {
|
|
11157
|
+
return [...new Set(scopes.map((scope) => scope.trim()).filter(Boolean))].sort();
|
|
11158
|
+
}
|
|
11159
|
+
function parseScopeCsv(value) {
|
|
11160
|
+
return uniqueSortedScopes((value ?? '').split(/[,\s]+/));
|
|
11161
|
+
}
|
|
11162
|
+
function defaultDogProfileScopes(connectorScopes, override) {
|
|
11163
|
+
if (override) {
|
|
11164
|
+
return parseScopeCsv(override);
|
|
11165
|
+
}
|
|
11166
|
+
const fromConnector = Array.isArray(connectorScopes) ? connectorScopes.filter((scope) => typeof scope === 'string') : [];
|
|
11167
|
+
return uniqueSortedScopes([
|
|
11168
|
+
...fromConnector,
|
|
11169
|
+
'read_customers',
|
|
11170
|
+
'write_customers',
|
|
11171
|
+
'read_metaobject_definitions',
|
|
11172
|
+
'write_metaobject_definitions',
|
|
11173
|
+
'read_metaobjects',
|
|
11174
|
+
'write_metaobjects',
|
|
11175
|
+
]);
|
|
11176
|
+
}
|
|
11177
|
+
function tenderWidgetAssetJs() {
|
|
11178
|
+
return `(function () {
|
|
11179
|
+
const loader = (window.TenderPromptBlockLoader ||= {});
|
|
11180
|
+
loader.scripts ||= new Map();
|
|
11181
|
+
loader.stylesheets ||= new Map();
|
|
11182
|
+
|
|
11183
|
+
function loadScript(src) {
|
|
11184
|
+
if (!src) return Promise.reject(new Error("Missing Tender runtime URL"));
|
|
11185
|
+
|
|
11186
|
+
const existing = loader.scripts.get(src);
|
|
11187
|
+
if (existing) return existing.promise;
|
|
11188
|
+
|
|
11189
|
+
const promise = new Promise((resolve, reject) => {
|
|
11190
|
+
const script = document.createElement("script");
|
|
11191
|
+
script.type = "module";
|
|
11192
|
+
script.src = src;
|
|
11193
|
+
script.onload = resolve;
|
|
11194
|
+
script.onerror = () => reject(new Error(\`Failed to load \${src}\`));
|
|
11195
|
+
document.head.appendChild(script);
|
|
11196
|
+
});
|
|
11197
|
+
|
|
11198
|
+
loader.scripts.set(src, { status: "loading", promise });
|
|
11199
|
+
promise.then(
|
|
11200
|
+
() => loader.scripts.set(src, { status: "loaded", promise }),
|
|
11201
|
+
() => loader.scripts.delete(src),
|
|
11202
|
+
);
|
|
11203
|
+
|
|
11204
|
+
return promise;
|
|
11205
|
+
}
|
|
11206
|
+
|
|
11207
|
+
function preloadStylesheet(src) {
|
|
11208
|
+
if (!src) return Promise.resolve();
|
|
11209
|
+
const existing = loader.stylesheets.get(src);
|
|
11210
|
+
if (existing) return existing.promise;
|
|
11211
|
+
|
|
11212
|
+
const promise = new Promise((resolve) => {
|
|
11213
|
+
const link = document.createElement("link");
|
|
11214
|
+
link.rel = "preload";
|
|
11215
|
+
link.as = "style";
|
|
11216
|
+
link.href = src;
|
|
11217
|
+
link.onload = resolve;
|
|
11218
|
+
link.onerror = resolve;
|
|
11219
|
+
document.head.appendChild(link);
|
|
11220
|
+
});
|
|
11221
|
+
|
|
11222
|
+
loader.stylesheets.set(src, { status: "loading", promise });
|
|
11223
|
+
promise.then(() => loader.stylesheets.set(src, { status: "loaded", promise }));
|
|
11224
|
+
return promise;
|
|
11225
|
+
}
|
|
11226
|
+
|
|
11227
|
+
function afterFrame(callback) {
|
|
11228
|
+
requestAnimationFrame(() => requestAnimationFrame(callback));
|
|
11229
|
+
}
|
|
11230
|
+
|
|
11231
|
+
function stylesheetLoaded(href) {
|
|
11232
|
+
return performance.getEntriesByName(href).some((entry) => entry.responseEnd > 0);
|
|
11233
|
+
}
|
|
11234
|
+
|
|
11235
|
+
function waitForWidgetPaintReady(widget, stylesheetUrl) {
|
|
11236
|
+
const stylesheetHref = stylesheetUrl ? new URL(stylesheetUrl, document.baseURI).href : null;
|
|
11237
|
+
|
|
11238
|
+
return new Promise((resolve) => {
|
|
11239
|
+
let settled = false;
|
|
11240
|
+
let noShadowTimer = null;
|
|
11241
|
+
let rootWithoutStylesheetTimer = null;
|
|
11242
|
+
const finish = () => {
|
|
11243
|
+
if (settled) return;
|
|
11244
|
+
settled = true;
|
|
11245
|
+
clearInterval(interval);
|
|
11246
|
+
clearTimeout(timeout);
|
|
11247
|
+
clearTimeout(noShadowTimer);
|
|
11248
|
+
clearTimeout(rootWithoutStylesheetTimer);
|
|
11249
|
+
afterFrame(resolve);
|
|
11250
|
+
};
|
|
11251
|
+
|
|
11252
|
+
const check = () => {
|
|
11253
|
+
const root = widget.shadowRoot;
|
|
11254
|
+
if (!root) {
|
|
11255
|
+
noShadowTimer ||= setTimeout(finish, 350);
|
|
11256
|
+
return;
|
|
11257
|
+
}
|
|
11258
|
+
|
|
11259
|
+
clearTimeout(noShadowTimer);
|
|
11260
|
+
const links = Array.from(root.querySelectorAll('link[rel="stylesheet"]'));
|
|
11261
|
+
const stylesheet = stylesheetHref
|
|
11262
|
+
? links.find((link) => link.href === stylesheetHref)
|
|
11263
|
+
: links[0];
|
|
11264
|
+
|
|
11265
|
+
if (!stylesheet) {
|
|
11266
|
+
rootWithoutStylesheetTimer ||= setTimeout(finish, 500);
|
|
11267
|
+
return;
|
|
11268
|
+
}
|
|
11269
|
+
|
|
11270
|
+
clearTimeout(rootWithoutStylesheetTimer);
|
|
11271
|
+
if (stylesheet.sheet || stylesheetLoaded(stylesheet.href)) {
|
|
11272
|
+
finish();
|
|
11273
|
+
return;
|
|
11274
|
+
}
|
|
11275
|
+
|
|
11276
|
+
stylesheet.addEventListener("load", finish, { once: true });
|
|
11277
|
+
stylesheet.addEventListener("error", finish, { once: true });
|
|
11278
|
+
};
|
|
11279
|
+
|
|
11280
|
+
const interval = setInterval(check, 25);
|
|
11281
|
+
const timeout = setTimeout(finish, 2200);
|
|
11282
|
+
check();
|
|
11283
|
+
});
|
|
11284
|
+
}
|
|
11285
|
+
|
|
11286
|
+
function revealWidget(mount, widget) {
|
|
11287
|
+
const placeholder = mount.querySelector("[data-tender-placeholder]");
|
|
11288
|
+
widget.style.visibility = "";
|
|
11289
|
+
widget.style.position = "";
|
|
11290
|
+
widget.style.inset = "";
|
|
11291
|
+
widget.style.pointerEvents = "";
|
|
11292
|
+
placeholder?.remove();
|
|
11293
|
+
mount.dataset.tenderReady = "true";
|
|
11294
|
+
mount.setAttribute("aria-busy", "false");
|
|
11295
|
+
}
|
|
11296
|
+
|
|
11297
|
+
class TenderPromptWidgetBlock extends HTMLElement {
|
|
11298
|
+
connectedCallback() {
|
|
11299
|
+
this.mount();
|
|
11300
|
+
}
|
|
11301
|
+
|
|
11302
|
+
async mount() {
|
|
11303
|
+
const mount = this.querySelector("[data-tender-mount]");
|
|
11304
|
+
const elementTag = this.dataset.tenderElementTag;
|
|
11305
|
+
if (!mount || !elementTag || mount.dataset.tenderMounted === "true") return;
|
|
11306
|
+
|
|
11307
|
+
mount.setAttribute("aria-busy", "true");
|
|
11308
|
+
preloadStylesheet(this.dataset.stylesheetUrl);
|
|
11309
|
+
await loadScript(this.dataset.tenderRuntimeSrc);
|
|
11310
|
+
await Promise.race([
|
|
11311
|
+
customElements.whenDefined(elementTag),
|
|
11312
|
+
new Promise((resolve) => setTimeout(resolve, 1000)),
|
|
11313
|
+
]);
|
|
11314
|
+
|
|
11315
|
+
const widget = document.createElement(elementTag);
|
|
11316
|
+
for (const [key, value] of Object.entries(this.dataset)) {
|
|
11317
|
+
if (value == null || key === "tenderRuntimeSrc" || key === "tenderElementTag") continue;
|
|
11318
|
+
widget.dataset[key] = value;
|
|
11319
|
+
}
|
|
11320
|
+
|
|
11321
|
+
widget.style.visibility = "hidden";
|
|
11322
|
+
widget.style.position = "absolute";
|
|
11323
|
+
widget.style.inset = "0";
|
|
11324
|
+
widget.style.pointerEvents = "none";
|
|
11325
|
+
mount.appendChild(widget);
|
|
11326
|
+
mount.dataset.tenderMounted = "true";
|
|
11327
|
+
await waitForWidgetPaintReady(widget, this.dataset.stylesheetUrl);
|
|
11328
|
+
revealWidget(mount, widget);
|
|
11329
|
+
}
|
|
11330
|
+
}
|
|
11331
|
+
|
|
11332
|
+
if (!customElements.get("tender-prompt-widget-block")) {
|
|
11333
|
+
customElements.define("tender-prompt-widget-block", TenderPromptWidgetBlock);
|
|
11334
|
+
}
|
|
11335
|
+
})();
|
|
11336
|
+
`;
|
|
11337
|
+
}
|
|
11338
|
+
function buildShopifySourceFiles(input) {
|
|
11339
|
+
const appName = requireShopifySourceString(input.connector.appName, 'Tender Prompt Apps');
|
|
11340
|
+
const adminApiVersion = requireShopifySourceString(input.connector.adminApiVersion, '2026-04');
|
|
11341
|
+
const shopDomain = requireShopifySourceString(input.connector.shopDomain);
|
|
11342
|
+
const tenderElement = normalizeTenderElementTag(input.tenderElement);
|
|
11343
|
+
const extensionHandle = normalizeThemeExtensionHandle(input.themeExtensionHandle);
|
|
11344
|
+
const scopes = defaultDogProfileScopes(input.connector.scopes, input.scopes);
|
|
11345
|
+
const artifactId = input.artifactId ?? 'artifact_replace_me';
|
|
11346
|
+
const publicId = input.publicId ?? 'tender-widget';
|
|
11347
|
+
const stylesheetUrl = new URL('./styles.css', input.tenderAppUrl).href;
|
|
11348
|
+
const blockName = input.blockName.trim().slice(0, 120) || 'Tender Prompt Widget';
|
|
11349
|
+
const connectorManifest = {
|
|
11350
|
+
version: 1,
|
|
11351
|
+
kind: 'shopify_connector_source',
|
|
11352
|
+
connectorId: input.connectorId,
|
|
11353
|
+
instanceId: requireShopifySourceString(input.connector.instanceId, 'default'),
|
|
11354
|
+
template: input.template,
|
|
11355
|
+
shopDomain: shopDomain || null,
|
|
11356
|
+
adminApiVersion,
|
|
11357
|
+
clientId: input.clientId,
|
|
11358
|
+
themeExtensionHandle: extensionHandle,
|
|
11359
|
+
tender: {
|
|
11360
|
+
artifactId,
|
|
11361
|
+
publicId,
|
|
11362
|
+
elementTag: tenderElement,
|
|
11363
|
+
runtimeUrl: input.tenderAppUrl,
|
|
11364
|
+
stylesheetUrl,
|
|
11365
|
+
},
|
|
11366
|
+
secrets: {
|
|
11367
|
+
clientSecret: 'stored in Tender connector credentials, not in this source tree',
|
|
11368
|
+
appAutomationToken: 'store in SHOPIFY_APP_AUTOMATION_TOKEN or Tender connector secret store, not in git',
|
|
11369
|
+
},
|
|
11370
|
+
};
|
|
11371
|
+
const shopifyAppToml = `# Merchant-owned Shopify app source generated by Tender Prompt.
|
|
11372
|
+
# Do not commit client secrets or app automation tokens.
|
|
11373
|
+
|
|
11374
|
+
client_id = ${tomlString(input.clientId)}
|
|
11375
|
+
name = ${tomlString(appName)}
|
|
11376
|
+
application_url = "https://shopify.dev/apps/default-app-home"
|
|
11377
|
+
embedded = true
|
|
11378
|
+
|
|
11379
|
+
[access_scopes]
|
|
11380
|
+
scopes = ${tomlString(scopes.join(','))}
|
|
11381
|
+
|
|
11382
|
+
[auth]
|
|
11383
|
+
redirect_urls = [ "https://shopify.dev/apps/default-app-home/api/auth" ]
|
|
11384
|
+
|
|
11385
|
+
[webhooks]
|
|
11386
|
+
api_version = ${tomlString(adminApiVersion)}
|
|
11387
|
+
|
|
11388
|
+
[build]
|
|
11389
|
+
automatically_update_urls_on_dev = true
|
|
11390
|
+
|
|
11391
|
+
[metaobjects.app.dog_profile]
|
|
11392
|
+
name = "Dog profile"
|
|
11393
|
+
display_name_field = "dog_name"
|
|
11394
|
+
access.admin = "merchant_read_write"
|
|
11395
|
+
|
|
11396
|
+
[metaobjects.app.dog_profile.fields.dog_name]
|
|
11397
|
+
name = "Dog name"
|
|
11398
|
+
type = "single_line_text_field"
|
|
11399
|
+
required = true
|
|
11400
|
+
|
|
11401
|
+
[metaobjects.app.dog_profile.fields.preferences]
|
|
11402
|
+
name = "Preferences"
|
|
11403
|
+
type = "json"
|
|
11404
|
+
|
|
11405
|
+
[customer.metafields.app.dog_profile]
|
|
11406
|
+
name = "Dog profile"
|
|
11407
|
+
type = "metaobject_reference<$app:dog_profile>"
|
|
11408
|
+
access.admin = "merchant_read_write"
|
|
11409
|
+
`;
|
|
11410
|
+
const extensionToml = [
|
|
11411
|
+
`name = ${tomlString(extensionHandle)}`,
|
|
11412
|
+
'type = "theme"',
|
|
11413
|
+
input.themeExtensionUid ? `uid = ${tomlString(input.themeExtensionUid)}` : null,
|
|
11414
|
+
'',
|
|
11415
|
+
]
|
|
11416
|
+
.filter((line) => line !== null)
|
|
11417
|
+
.join('\n');
|
|
11418
|
+
const liquid = `{% doc %}
|
|
11419
|
+
Tender Prompt app block generated from connector ${input.connectorId}.
|
|
11420
|
+
|
|
11421
|
+
The block loads one Tender runtime script and mounts the configured Tender
|
|
11422
|
+
custom element. Keep merchant-editable copy and version pins in block settings.
|
|
11423
|
+
{% enddoc %}
|
|
11424
|
+
|
|
11425
|
+
{% liquid
|
|
11426
|
+
assign artifact_id = block.settings.artifact_id | default: ${tomlString(artifactId)}
|
|
11427
|
+
assign widget_version = block.settings.widget_version | default: 'latest'
|
|
11428
|
+
|
|
11429
|
+
case widget_version
|
|
11430
|
+
else
|
|
11431
|
+
assign runtime_src = ${tomlString(input.tenderAppUrl)}
|
|
11432
|
+
assign stylesheet_src = ${tomlString(stylesheetUrl)}
|
|
11433
|
+
endcase
|
|
11434
|
+
%}
|
|
11435
|
+
|
|
11436
|
+
<tender-prompt-widget-block
|
|
11437
|
+
{{ block.shopify_attributes }}
|
|
11438
|
+
class="tender-prompt-widget-block tender-prompt-widget-block--{{ block.settings.tone }}"
|
|
11439
|
+
data-tender-artifact-id="{{ artifact_id | escape }}"
|
|
11440
|
+
data-tender-public-id="{{ block.settings.public_id | escape }}"
|
|
11441
|
+
data-tender-version="{{ widget_version | escape }}"
|
|
11442
|
+
data-tender-runtime-src="{{ runtime_src | escape }}"
|
|
11443
|
+
data-stylesheet-url="{{ stylesheet_src | escape }}"
|
|
11444
|
+
data-tender-element-tag="{{ block.settings.element_tag | escape }}"
|
|
11445
|
+
data-tender-mode="shopify_app_block"
|
|
11446
|
+
data-surface="shopify_theme_app_extension"
|
|
11447
|
+
data-tender-block-id="{{ block.id | escape }}"
|
|
11448
|
+
data-tender-shop-domain="{{ shop.permanent_domain | escape }}"
|
|
11449
|
+
data-tender-heading="{{ block.settings.heading | escape }}"
|
|
11450
|
+
data-tender-intro="{{ block.settings.intro | escape }}"
|
|
11451
|
+
data-tender-tone="{{ block.settings.tone | escape }}"
|
|
11452
|
+
{% if customer %}
|
|
11453
|
+
data-tender-customer-id="{{ customer.id }}"
|
|
11454
|
+
{% endif %}
|
|
11455
|
+
{% if product %}
|
|
11456
|
+
data-tender-product-id="{{ product.id }}"
|
|
11457
|
+
data-tender-product-handle="{{ product.handle | escape }}"
|
|
11458
|
+
{% endif %}
|
|
11459
|
+
>
|
|
11460
|
+
<div data-tender-mount style="position: relative;">
|
|
11461
|
+
<div
|
|
11462
|
+
data-tender-placeholder
|
|
11463
|
+
style="box-sizing: border-box; display: grid; gap: 14px; min-height: 260px; padding: 28px; border: 1px solid rgba(0,0,0,.12); border-radius: 8px; background: rgba(255,255,255,.78); color: currentColor;"
|
|
11464
|
+
>
|
|
11465
|
+
<div style="display: grid; gap: 8px;">
|
|
11466
|
+
<p style="margin: 0; font: inherit; font-weight: 700;">{{ block.settings.heading }}</p>
|
|
11467
|
+
<p style="margin: 0; max-width: 42rem; opacity: .72;">{{ block.settings.intro }}</p>
|
|
11468
|
+
</div>
|
|
11469
|
+
<div style="display: grid; gap: 10px;" aria-hidden="true">
|
|
11470
|
+
<span style="display: block; width: 52%; height: 12px; border-radius: 999px; background: rgba(0,0,0,.10);"></span>
|
|
11471
|
+
<span style="display: block; width: 100%; height: 44px; border-radius: 8px; background: rgba(0,0,0,.07);"></span>
|
|
11472
|
+
<span style="display: block; width: 88%; height: 44px; border-radius: 8px; background: rgba(0,0,0,.07);"></span>
|
|
11473
|
+
</div>
|
|
11474
|
+
</div>
|
|
11475
|
+
</div>
|
|
11476
|
+
</tender-prompt-widget-block>
|
|
11477
|
+
|
|
11478
|
+
{% schema %}
|
|
11479
|
+
{
|
|
11480
|
+
"name": ${JSON.stringify(blockName)},
|
|
11481
|
+
"target": "section",
|
|
11482
|
+
"javascript": "tender-widget.js",
|
|
11483
|
+
"settings": [
|
|
11484
|
+
{
|
|
11485
|
+
"type": "text",
|
|
11486
|
+
"id": "element_tag",
|
|
11487
|
+
"label": "Tender custom element",
|
|
11488
|
+
"default": ${JSON.stringify(tenderElement)}
|
|
11489
|
+
},
|
|
11490
|
+
{
|
|
11491
|
+
"type": "text",
|
|
11492
|
+
"id": "artifact_id",
|
|
11493
|
+
"label": "Tender artifact ID",
|
|
11494
|
+
"default": ${JSON.stringify(artifactId)}
|
|
11495
|
+
},
|
|
11496
|
+
{
|
|
11497
|
+
"type": "text",
|
|
11498
|
+
"id": "public_id",
|
|
11499
|
+
"label": "Tender public ID",
|
|
11500
|
+
"default": ${JSON.stringify(publicId)}
|
|
11501
|
+
},
|
|
11502
|
+
{
|
|
11503
|
+
"type": "select",
|
|
11504
|
+
"id": "widget_version",
|
|
11505
|
+
"label": "Widget version",
|
|
11506
|
+
"options": [
|
|
11507
|
+
{ "value": "latest", "label": "Latest published version" }
|
|
11508
|
+
],
|
|
11509
|
+
"default": "latest"
|
|
11510
|
+
},
|
|
11511
|
+
{
|
|
11512
|
+
"type": "inline_richtext",
|
|
11513
|
+
"id": "heading",
|
|
11514
|
+
"label": "Heading",
|
|
11515
|
+
"default": "Tell us about your customer"
|
|
11516
|
+
},
|
|
11517
|
+
{
|
|
11518
|
+
"type": "text",
|
|
11519
|
+
"id": "intro",
|
|
11520
|
+
"label": "Intro",
|
|
11521
|
+
"default": "Answer a few quick questions and keep your profile up to date."
|
|
11522
|
+
},
|
|
11523
|
+
{
|
|
11524
|
+
"type": "select",
|
|
11525
|
+
"id": "tone",
|
|
11526
|
+
"label": "Tone",
|
|
11527
|
+
"options": [
|
|
11528
|
+
{ "value": "default", "label": "Default" },
|
|
11529
|
+
{ "value": "subtle", "label": "Subtle" },
|
|
11530
|
+
{ "value": "contrast", "label": "Contrast" }
|
|
11531
|
+
],
|
|
11532
|
+
"default": "default"
|
|
11533
|
+
}
|
|
11534
|
+
]
|
|
11535
|
+
}
|
|
11536
|
+
{% endschema %}
|
|
11537
|
+
`;
|
|
11538
|
+
const readme = `# ${appName}
|
|
11539
|
+
|
|
11540
|
+
This is the merchant-owned Shopify app source generated by Tender Prompt for
|
|
11541
|
+
connector \`${input.connectorId}\`.
|
|
11542
|
+
|
|
11543
|
+
## What lives here
|
|
11544
|
+
|
|
11545
|
+
- \`shopify.app.toml\`: Shopify app configuration, scopes, and app-owned dog-profile custom data definitions.
|
|
11546
|
+
- \`extensions/${extensionHandle}\`: Theme App Extension with a Tender app block.
|
|
11547
|
+
- \`connector.json\`: Non-secret Tender metadata that maps this source tree back to the connector.
|
|
11548
|
+
|
|
11549
|
+
## Secrets
|
|
11550
|
+
|
|
11551
|
+
Do not commit the Shopify client secret, Admin API token, or App Automation
|
|
11552
|
+
Token. Runtime credentials stay in the Tender connector secret store. Deployment
|
|
11553
|
+
uses \`SHOPIFY_APP_AUTOMATION_TOKEN\` at command time.
|
|
11554
|
+
|
|
11555
|
+
## Validate
|
|
11556
|
+
|
|
11557
|
+
\`\`\`sh
|
|
11558
|
+
npx --yes @shopify/cli@latest app config validate --path . --json
|
|
11559
|
+
\`\`\`
|
|
11560
|
+
|
|
11561
|
+
## Deploy
|
|
11562
|
+
|
|
11563
|
+
\`\`\`sh
|
|
11564
|
+
SHOPIFY_APP_AUTOMATION_TOKEN=... npx --yes @shopify/cli@latest app deploy --path . --allow-updates --version tender-widget
|
|
11565
|
+
\`\`\`
|
|
11566
|
+
`;
|
|
11567
|
+
return {
|
|
11568
|
+
extensionHandle,
|
|
11569
|
+
manifest: connectorManifest,
|
|
11570
|
+
files: new Map([
|
|
11571
|
+
['.gitignore', '.shopify/\nnode_modules/\n.env\n.env.*\n'],
|
|
11572
|
+
['README.md', readme],
|
|
11573
|
+
['connector.json', `${JSON.stringify(connectorManifest, null, 2)}\n`],
|
|
11574
|
+
['shopify.app.toml', shopifyAppToml],
|
|
11575
|
+
[`extensions/${extensionHandle}/shopify.extension.toml`, extensionToml],
|
|
11576
|
+
[`extensions/${extensionHandle}/blocks/tender_widget.liquid`, liquid],
|
|
11577
|
+
[`extensions/${extensionHandle}/assets/tender-widget.js`, tenderWidgetAssetJs()],
|
|
11578
|
+
[`extensions/${extensionHandle}/locales/en.default.json`, `${JSON.stringify({ tender: { block: { name: blockName } } }, null, 2)}\n`],
|
|
11579
|
+
]),
|
|
11580
|
+
};
|
|
11581
|
+
}
|
|
11582
|
+
async function fileExists(filePath) {
|
|
11583
|
+
try {
|
|
11584
|
+
await readFile(filePath, 'utf8');
|
|
11585
|
+
return true;
|
|
11586
|
+
}
|
|
11587
|
+
catch {
|
|
11588
|
+
return false;
|
|
11589
|
+
}
|
|
11590
|
+
}
|
|
11591
|
+
async function writeShopifySourceFiles(input) {
|
|
11592
|
+
const written = [];
|
|
11593
|
+
for (const [relativePath, content] of input.files.entries()) {
|
|
11594
|
+
const absolutePath = path.join(input.dir, relativePath);
|
|
11595
|
+
if (!input.force && (await fileExists(absolutePath))) {
|
|
11596
|
+
throw new TenderCliUsageError(`Refusing to overwrite ${relativePath}. Pass --force to regenerate.`);
|
|
11597
|
+
}
|
|
11598
|
+
await mkdir(path.dirname(absolutePath), { recursive: true });
|
|
11599
|
+
await writeFile(absolutePath, content, 'utf8');
|
|
11600
|
+
written.push(relativePath);
|
|
11601
|
+
}
|
|
11602
|
+
return written;
|
|
11603
|
+
}
|
|
11604
|
+
const SHOPIFY_CONNECTOR_SOURCE_TARGET_PACK_ID = 'shopify-connector-source';
|
|
11605
|
+
const SHOPIFY_CONNECTOR_SOURCE_TARGET_PACK_VERSION = '1';
|
|
11606
|
+
function readConnectorSourceArtifactId(connector) {
|
|
11607
|
+
const sourceProject = isCliJsonRecord(connector.sourceProject) ? connector.sourceProject : null;
|
|
11608
|
+
const sourceArtifactId = typeof sourceProject?.sourceArtifactId === 'string' ? sourceProject.sourceArtifactId.trim() : '';
|
|
11609
|
+
return sourceArtifactId || null;
|
|
11610
|
+
}
|
|
11611
|
+
function defaultShopifySourceName(connector) {
|
|
11612
|
+
const appName = typeof connector.appName === 'string' && connector.appName.trim() ? connector.appName.trim() : 'Tender Prompt Apps';
|
|
11613
|
+
return `${appName} Shopify Source`.slice(0, 120);
|
|
11614
|
+
}
|
|
11615
|
+
async function requestConnector(command, runtime) {
|
|
11616
|
+
const credentials = await resolveConnectorsCredentials(command, runtime);
|
|
11617
|
+
const payload = await requestConnectorsApi({
|
|
11618
|
+
connectorsPath: encodeURIComponent(command.connectorId),
|
|
11619
|
+
method: 'GET',
|
|
11620
|
+
baseUrl: credentials.baseUrl,
|
|
11621
|
+
token: credentials.token,
|
|
11622
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
11623
|
+
});
|
|
11624
|
+
const connector = isCliJsonRecord(payload.connector) ? payload.connector : {};
|
|
11625
|
+
return { credentials, payload, connector };
|
|
11626
|
+
}
|
|
11627
|
+
async function createShopifySourceArtifact(input) {
|
|
11628
|
+
const response = await input.fetcher(`${input.credentials.baseUrl}/api/v1/artifacts`, {
|
|
11629
|
+
method: 'POST',
|
|
11630
|
+
headers: {
|
|
11631
|
+
authorization: `Bearer ${input.credentials.token}`,
|
|
11632
|
+
'content-type': 'application/json',
|
|
11633
|
+
},
|
|
11634
|
+
body: JSON.stringify({
|
|
11635
|
+
name: input.name,
|
|
11636
|
+
targetPackId: SHOPIFY_CONNECTOR_SOURCE_TARGET_PACK_ID,
|
|
11637
|
+
targetPackVersion: SHOPIFY_CONNECTOR_SOURCE_TARGET_PACK_VERSION,
|
|
11638
|
+
}),
|
|
11639
|
+
});
|
|
11640
|
+
const payload = await parseJsonResponse(response);
|
|
11641
|
+
if (!response.ok || !payload?.ok || !payload.artifact?.artifactId) {
|
|
11642
|
+
throw new Error(payload?.message ?? payload?.reasonCode ?? `Connector source artifact create failed with HTTP ${response.status}.`);
|
|
11643
|
+
}
|
|
11644
|
+
return payload.artifact;
|
|
11645
|
+
}
|
|
11646
|
+
async function patchConnectorSourceProject(input) {
|
|
11647
|
+
const sourceProject = {
|
|
11648
|
+
version: 1,
|
|
11649
|
+
kind: 'shopify_app_source',
|
|
11650
|
+
sourceArtifactId: input.sourceArtifactId,
|
|
11651
|
+
template: input.template,
|
|
11652
|
+
updatedAt: input.updatedAt,
|
|
11653
|
+
};
|
|
11654
|
+
const payload = await requestConnectorsApi({
|
|
11655
|
+
connectorsPath: encodeURIComponent(input.connectorId),
|
|
11656
|
+
method: 'PATCH',
|
|
11657
|
+
baseUrl: input.credentials.baseUrl,
|
|
11658
|
+
token: input.credentials.token,
|
|
11659
|
+
fetcher: input.fetcher,
|
|
11660
|
+
body: { sourceProject },
|
|
11661
|
+
});
|
|
11662
|
+
return { payload, sourceProject };
|
|
11663
|
+
}
|
|
11664
|
+
async function ensureConnectorSourceArtifact(input) {
|
|
11665
|
+
const existingSourceArtifactId = readConnectorSourceArtifactId(input.connector);
|
|
11666
|
+
if (existingSourceArtifactId) {
|
|
11667
|
+
return {
|
|
11668
|
+
sourceArtifactId: existingSourceArtifactId,
|
|
11669
|
+
artifact: null,
|
|
11670
|
+
created: false,
|
|
11671
|
+
};
|
|
11672
|
+
}
|
|
11673
|
+
const artifact = await createShopifySourceArtifact({
|
|
11674
|
+
name: input.command.sourceName?.trim() || defaultShopifySourceName(input.connector),
|
|
11675
|
+
credentials: input.credentials,
|
|
11676
|
+
fetcher: input.fetcher,
|
|
11677
|
+
});
|
|
11678
|
+
return {
|
|
11679
|
+
sourceArtifactId: artifact.artifactId,
|
|
11680
|
+
artifact,
|
|
11681
|
+
created: true,
|
|
11682
|
+
};
|
|
11683
|
+
}
|
|
11684
|
+
async function setupSourceArtifactCheckout(input) {
|
|
11685
|
+
const root = path.resolve(input.dir);
|
|
11686
|
+
const git = await requestGitRemoteLease({
|
|
11687
|
+
artifactId: input.artifactId,
|
|
11688
|
+
ttl: '7d',
|
|
11689
|
+
baseUrl: input.credentials.baseUrl,
|
|
11690
|
+
token: input.credentials.token,
|
|
11691
|
+
fetcher: input.fetcher,
|
|
11692
|
+
});
|
|
11693
|
+
const helper = createCredentialHelperCommand({
|
|
11694
|
+
artifactId: input.artifactId,
|
|
11695
|
+
baseUrl: input.credentials.baseUrl,
|
|
11696
|
+
profileName: input.credentials.profileName,
|
|
11697
|
+
ttl: '7d',
|
|
11698
|
+
remoteUrl: git.remoteUrl,
|
|
11699
|
+
});
|
|
11700
|
+
await mkdir(root, { recursive: true });
|
|
11701
|
+
const gitState = await runGitCommand({
|
|
11702
|
+
runtime: input.runtime,
|
|
11703
|
+
cwd: root,
|
|
11704
|
+
args: ['rev-parse', '--is-inside-work-tree'],
|
|
11705
|
+
allowFailure: true,
|
|
11706
|
+
});
|
|
11707
|
+
if (gitState.exitCode !== 0) {
|
|
11708
|
+
await runGitCommand({
|
|
11709
|
+
runtime: input.runtime,
|
|
11710
|
+
cwd: root,
|
|
11711
|
+
args: ['init', '-b', git.defaultBranch],
|
|
11712
|
+
});
|
|
11713
|
+
}
|
|
11714
|
+
await applyGitSetup({
|
|
11715
|
+
runtime: input.runtime,
|
|
11716
|
+
root,
|
|
11717
|
+
remoteName: input.remoteName,
|
|
11718
|
+
remoteUrl: git.remoteUrl,
|
|
11719
|
+
credentialHelper: helper,
|
|
11720
|
+
});
|
|
11721
|
+
const sourceCheckout = await maybeCheckoutInitialArtifactSource({
|
|
11722
|
+
runtime: input.runtime,
|
|
11723
|
+
root,
|
|
11724
|
+
remoteName: input.remoteName,
|
|
11725
|
+
defaultBranch: git.defaultBranch,
|
|
11726
|
+
});
|
|
11727
|
+
return {
|
|
11728
|
+
root,
|
|
11729
|
+
git,
|
|
11730
|
+
sourceCheckout,
|
|
11731
|
+
remoteName: input.remoteName,
|
|
11732
|
+
credentialHelper: helper,
|
|
11733
|
+
};
|
|
11734
|
+
}
|
|
11735
|
+
async function commitAndPushSourceFiles(input) {
|
|
11736
|
+
const paths = [...new Set(input.paths)].sort();
|
|
11737
|
+
if (paths.length === 0) {
|
|
11738
|
+
return {
|
|
11739
|
+
attempted: false,
|
|
11740
|
+
committed: false,
|
|
11741
|
+
pushed: false,
|
|
11742
|
+
skippedReason: 'no_files',
|
|
11743
|
+
commitSha: null,
|
|
11744
|
+
};
|
|
11745
|
+
}
|
|
11746
|
+
const status = await runGitCommand({
|
|
11747
|
+
runtime: input.runtime,
|
|
11748
|
+
cwd: input.dir,
|
|
11749
|
+
args: ['status', '--porcelain', '--', ...paths],
|
|
11750
|
+
});
|
|
11751
|
+
if (!status.stdout.trim()) {
|
|
11752
|
+
if (input.pushCleanHead) {
|
|
11753
|
+
const commitShaResult = await runGitCommand({
|
|
11754
|
+
runtime: input.runtime,
|
|
11755
|
+
cwd: input.dir,
|
|
11756
|
+
args: ['rev-parse', 'HEAD'],
|
|
11757
|
+
});
|
|
11758
|
+
await runGitCommand({
|
|
11759
|
+
runtime: input.runtime,
|
|
11760
|
+
cwd: input.dir,
|
|
11761
|
+
args: ['push', input.remoteName, input.defaultBranch],
|
|
11762
|
+
});
|
|
11763
|
+
return {
|
|
11764
|
+
attempted: true,
|
|
11765
|
+
committed: false,
|
|
11766
|
+
pushed: true,
|
|
11767
|
+
skippedReason: 'clean_head_pushed',
|
|
11768
|
+
commitSha: commitShaResult.stdout.trim() || null,
|
|
11769
|
+
};
|
|
11770
|
+
}
|
|
11771
|
+
return {
|
|
11772
|
+
attempted: true,
|
|
11773
|
+
committed: false,
|
|
11774
|
+
pushed: false,
|
|
11775
|
+
skippedReason: 'no_changes',
|
|
11776
|
+
commitSha: null,
|
|
11777
|
+
};
|
|
11778
|
+
}
|
|
11779
|
+
await runGitCommand({
|
|
11780
|
+
runtime: input.runtime,
|
|
11781
|
+
cwd: input.dir,
|
|
11782
|
+
args: ['add', '--', ...paths],
|
|
11783
|
+
});
|
|
11784
|
+
await runGitCommand({
|
|
11785
|
+
runtime: input.runtime,
|
|
11786
|
+
cwd: input.dir,
|
|
11787
|
+
args: ['commit', '-m', 'Update Shopify connector source'],
|
|
11788
|
+
});
|
|
11789
|
+
const commitShaResult = await runGitCommand({
|
|
11790
|
+
runtime: input.runtime,
|
|
11791
|
+
cwd: input.dir,
|
|
11792
|
+
args: ['rev-parse', 'HEAD'],
|
|
11793
|
+
});
|
|
11794
|
+
await runGitCommand({
|
|
11795
|
+
runtime: input.runtime,
|
|
11796
|
+
cwd: input.dir,
|
|
11797
|
+
args: ['push', input.remoteName, input.defaultBranch],
|
|
11798
|
+
});
|
|
11799
|
+
return {
|
|
11800
|
+
attempted: true,
|
|
11801
|
+
committed: true,
|
|
11802
|
+
pushed: true,
|
|
11803
|
+
skippedReason: null,
|
|
11804
|
+
commitSha: commitShaResult.stdout.trim() || null,
|
|
11805
|
+
};
|
|
11806
|
+
}
|
|
11807
|
+
function shopifyValidateArgs(input) {
|
|
11808
|
+
return [
|
|
11809
|
+
'--yes',
|
|
11810
|
+
'@shopify/cli@latest',
|
|
11811
|
+
'app',
|
|
11812
|
+
'config',
|
|
11813
|
+
'validate',
|
|
11814
|
+
'--path',
|
|
11815
|
+
input.dir,
|
|
11816
|
+
'--json',
|
|
11817
|
+
...(input.clientId ? ['--client-id', input.clientId] : []),
|
|
11818
|
+
];
|
|
11819
|
+
}
|
|
11820
|
+
function shopifyDeployArgs(input) {
|
|
11821
|
+
return [
|
|
11822
|
+
'--yes',
|
|
11823
|
+
'@shopify/cli@latest',
|
|
11824
|
+
'app',
|
|
11825
|
+
'deploy',
|
|
11826
|
+
'--path',
|
|
11827
|
+
input.dir,
|
|
11828
|
+
input.allowDeletes ? '--allow-deletes' : '--allow-updates',
|
|
11829
|
+
...(input.clientId ? ['--client-id', input.clientId] : []),
|
|
11830
|
+
...(input.version ? ['--version', input.version] : []),
|
|
11831
|
+
...(input.message ? ['--message', input.message] : []),
|
|
11832
|
+
...(input.sourceControlUrl ? ['--source-control-url', input.sourceControlUrl] : []),
|
|
11833
|
+
];
|
|
11834
|
+
}
|
|
11835
|
+
function commandLine(command, args) {
|
|
11836
|
+
return [command, ...args.map(shellQuote)].join(' ');
|
|
11837
|
+
}
|
|
11838
|
+
async function runNpxCommand(input) {
|
|
11839
|
+
const runner = input.runtime.commandRunner ?? defaultCommandRunner;
|
|
11840
|
+
const result = await runner('npx', input.args, { cwd: input.cwd });
|
|
11841
|
+
if (result.exitCode !== 0) {
|
|
11842
|
+
throw new Error([`npx ${input.args.join(' ')} failed with exit code ${result.exitCode}.`, result.stderr.trim() || result.stdout.trim()].filter(Boolean).join('\n'));
|
|
11843
|
+
}
|
|
11844
|
+
return result;
|
|
11845
|
+
}
|
|
11846
|
+
async function runConnectorsShopifySourceInit(command, io, runtime) {
|
|
11847
|
+
const fetcher = runtime.fetcher ?? fetch;
|
|
11848
|
+
const { credentials, payload, connector } = await requestConnector(command, runtime);
|
|
11849
|
+
const sourceArtifact = await ensureConnectorSourceArtifact({
|
|
11850
|
+
command,
|
|
11851
|
+
connector,
|
|
11852
|
+
credentials,
|
|
11853
|
+
fetcher,
|
|
11854
|
+
});
|
|
11855
|
+
const checkout = await setupSourceArtifactCheckout({
|
|
11856
|
+
artifactId: sourceArtifact.sourceArtifactId,
|
|
11857
|
+
dir: command.dir,
|
|
11858
|
+
remoteName: 'tender',
|
|
11859
|
+
credentials,
|
|
11860
|
+
runtime,
|
|
11861
|
+
fetcher,
|
|
11862
|
+
});
|
|
11863
|
+
const dir = path.resolve(command.dir);
|
|
11864
|
+
const source = buildShopifySourceFiles({
|
|
11865
|
+
connectorId: command.connectorId,
|
|
11866
|
+
connector,
|
|
11867
|
+
clientId: command.clientId,
|
|
11868
|
+
template: command.template,
|
|
11869
|
+
tenderAppUrl: command.tenderAppUrl,
|
|
11870
|
+
tenderElement: command.tenderElement,
|
|
11871
|
+
artifactId: command.artifactId,
|
|
11872
|
+
publicId: command.publicId,
|
|
11873
|
+
themeExtensionHandle: command.themeExtensionHandle,
|
|
11874
|
+
themeExtensionUid: command.themeExtensionUid,
|
|
11875
|
+
blockName: command.blockName,
|
|
11876
|
+
scopes: command.scopes,
|
|
11877
|
+
});
|
|
11878
|
+
const files = await writeShopifySourceFiles({ dir, files: source.files, force: command.force });
|
|
11879
|
+
const gitCommit = await commitAndPushSourceFiles({
|
|
11880
|
+
dir,
|
|
11881
|
+
paths: files,
|
|
11882
|
+
remoteName: checkout.remoteName,
|
|
11883
|
+
defaultBranch: checkout.git.defaultBranch,
|
|
11884
|
+
pushCleanHead: sourceArtifact.created || command.force,
|
|
11885
|
+
runtime,
|
|
11886
|
+
});
|
|
11887
|
+
const patched = await patchConnectorSourceProject({
|
|
11888
|
+
connectorId: command.connectorId,
|
|
11889
|
+
sourceArtifactId: sourceArtifact.sourceArtifactId,
|
|
11890
|
+
template: command.template,
|
|
11891
|
+
credentials,
|
|
11892
|
+
fetcher,
|
|
11893
|
+
updatedAt: runtime.now ? runtime.now() : Date.now(),
|
|
11894
|
+
});
|
|
9874
11895
|
const result = {
|
|
9875
11896
|
ok: true,
|
|
9876
11897
|
command: command.command,
|
|
9877
|
-
|
|
9878
|
-
|
|
11898
|
+
connectorId: command.connectorId,
|
|
11899
|
+
sourceArtifactId: sourceArtifact.sourceArtifactId,
|
|
11900
|
+
sourceArtifactCreated: sourceArtifact.created,
|
|
11901
|
+
sourceArtifact: sourceArtifact.artifact,
|
|
11902
|
+
dir,
|
|
11903
|
+
files,
|
|
11904
|
+
sourceProject: patched.sourceProject,
|
|
11905
|
+
connector: patched.payload.connector ?? payload.connector ?? null,
|
|
11906
|
+
git: {
|
|
11907
|
+
remoteName: checkout.remoteName,
|
|
11908
|
+
remoteUrl: checkout.git.remoteUrl,
|
|
11909
|
+
defaultBranch: checkout.git.defaultBranch,
|
|
11910
|
+
sourceCheckout: checkout.sourceCheckout,
|
|
11911
|
+
commit: gitCommit,
|
|
11912
|
+
},
|
|
11913
|
+
validateCommand: commandLine('npx', shopifyValidateArgs({ dir, clientId: command.clientId })),
|
|
11914
|
+
deployCommand: commandLine('npx', shopifyDeployArgs({ dir, clientId: command.clientId, version: 'tender-widget', message: null, sourceControlUrl: null, allowDeletes: false })),
|
|
9879
11915
|
};
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
|
|
11916
|
+
printConnectorsPayload(command, io, result, [
|
|
11917
|
+
`connector_id: ${command.connectorId}`,
|
|
11918
|
+
`source_artifact_id: ${sourceArtifact.sourceArtifactId}`,
|
|
11919
|
+
`source_dir: ${dir}`,
|
|
11920
|
+
`files: ${files.length}`,
|
|
11921
|
+
`git_pushed: ${gitCommit.pushed ? 'true' : 'false'}`,
|
|
11922
|
+
`next_validate: ${result.validateCommand}`,
|
|
11923
|
+
]);
|
|
9886
11924
|
return 0;
|
|
9887
11925
|
}
|
|
9888
|
-
async function
|
|
9889
|
-
const
|
|
9890
|
-
|
|
11926
|
+
async function runConnectorsShopifySourceCheckout(command, io, runtime) {
|
|
11927
|
+
const fetcher = runtime.fetcher ?? fetch;
|
|
11928
|
+
const { credentials, connector } = await requestConnector(command, runtime);
|
|
11929
|
+
const sourceArtifactId = readConnectorSourceArtifactId(connector);
|
|
11930
|
+
if (!sourceArtifactId) {
|
|
11931
|
+
throw new TenderCliUsageError('Connector does not have a source artifact yet. Run tender connectors shopify source init <connector-id> --dir ./shopify-app first.');
|
|
11932
|
+
}
|
|
11933
|
+
const checkout = await setupSourceArtifactCheckout({
|
|
11934
|
+
artifactId: sourceArtifactId,
|
|
11935
|
+
dir: command.dir,
|
|
9891
11936
|
remoteName: command.remoteName,
|
|
9892
|
-
|
|
11937
|
+
credentials,
|
|
11938
|
+
runtime,
|
|
11939
|
+
fetcher,
|
|
9893
11940
|
});
|
|
9894
|
-
const next = sync.status === 'failed'
|
|
9895
|
-
? `tender memory flush --dir ${command.dir} --remote ${command.remoteName}`
|
|
9896
|
-
: sync.status === 'not_configured'
|
|
9897
|
-
? memoryRecoveryForArtifact(command.artifactId, command.dir)
|
|
9898
|
-
: null;
|
|
9899
11941
|
const result = {
|
|
9900
|
-
ok:
|
|
11942
|
+
ok: true,
|
|
9901
11943
|
command: command.command,
|
|
9902
|
-
|
|
9903
|
-
|
|
11944
|
+
connectorId: command.connectorId,
|
|
11945
|
+
sourceArtifactId,
|
|
11946
|
+
dir: checkout.root,
|
|
11947
|
+
remoteName: checkout.remoteName,
|
|
11948
|
+
remoteUrl: checkout.git.remoteUrl,
|
|
11949
|
+
defaultBranch: checkout.git.defaultBranch,
|
|
11950
|
+
sourceCheckout: checkout.sourceCheckout,
|
|
9904
11951
|
};
|
|
9905
|
-
|
|
9906
|
-
|
|
9907
|
-
|
|
9908
|
-
|
|
9909
|
-
|
|
9910
|
-
|
|
9911
|
-
|
|
9912
|
-
result.next ? `next: ${result.next}` : null,
|
|
9913
|
-
]
|
|
9914
|
-
.filter((line) => Boolean(line))
|
|
9915
|
-
.join('\n'));
|
|
9916
|
-
}
|
|
9917
|
-
return sync.status === 'pushed' ? 0 : 1;
|
|
9918
|
-
}
|
|
9919
|
-
async function runMemoryExport(command, io, runtime) {
|
|
9920
|
-
const entries = await readArtifactMemoryEntries({
|
|
9921
|
-
repoPath: path.resolve(command.dir),
|
|
9922
|
-
commitSha: command.commitSha,
|
|
9923
|
-
runner: commandRunnerForMemory(runtime),
|
|
9924
|
-
});
|
|
9925
|
-
if (command.format === 'jsonl') {
|
|
9926
|
-
io.stdout(entries.map((entry) => JSON.stringify(entry)).join('\n'));
|
|
9927
|
-
return 0;
|
|
9928
|
-
}
|
|
9929
|
-
throw new TenderCliUsageError(`Unsupported memory export format: ${command.format}.\nNext: tender memory export --format jsonl --dir ${command.dir}`);
|
|
9930
|
-
}
|
|
9931
|
-
async function readLocalChangedFiles(input) {
|
|
9932
|
-
const result = await runGitCommand({
|
|
9933
|
-
runtime: input.runtime,
|
|
9934
|
-
cwd: input.repoPath,
|
|
9935
|
-
args: ['status', '--porcelain'],
|
|
9936
|
-
allowFailure: true,
|
|
9937
|
-
});
|
|
9938
|
-
if (result.exitCode !== 0) {
|
|
9939
|
-
return [];
|
|
9940
|
-
}
|
|
9941
|
-
return result.stdout
|
|
9942
|
-
.split(/\r?\n/)
|
|
9943
|
-
.map((line) => line.slice(3).trim())
|
|
9944
|
-
.filter(Boolean)
|
|
9945
|
-
.slice(0, 50);
|
|
9946
|
-
}
|
|
9947
|
-
async function appendAutomaticMemory(input) {
|
|
9948
|
-
try {
|
|
9949
|
-
return await appendArtifactMemoryNote({
|
|
9950
|
-
repoPath: input.repoPath,
|
|
9951
|
-
artifactId: input.artifactId,
|
|
9952
|
-
kind: input.kind,
|
|
9953
|
-
summary: input.summary,
|
|
9954
|
-
body: input.body,
|
|
9955
|
-
tags: input.tags,
|
|
9956
|
-
severity: input.severity,
|
|
9957
|
-
status: input.status,
|
|
9958
|
-
related: input.related,
|
|
9959
|
-
source: {
|
|
9960
|
-
actor: 'cli',
|
|
9961
|
-
command: input.summary,
|
|
9962
|
-
},
|
|
9963
|
-
runner: commandRunnerForMemory(input.runtime),
|
|
9964
|
-
now: input.runtime.now,
|
|
9965
|
-
});
|
|
9966
|
-
}
|
|
9967
|
-
catch (error) {
|
|
9968
|
-
return {
|
|
9969
|
-
error: error instanceof Error ? error.message : String(error),
|
|
9970
|
-
next: `tender memory add --dir ${input.repoPath} --artifact ${input.artifactId} --kind ${input.kind} --summary ${JSON.stringify(input.summary)}`,
|
|
9971
|
-
};
|
|
9972
|
-
}
|
|
9973
|
-
}
|
|
9974
|
-
async function flushAutomaticMemory(input) {
|
|
9975
|
-
return pushArtifactMemoryNotes({
|
|
9976
|
-
repoPath: input.repoPath,
|
|
9977
|
-
remoteName: input.remoteName,
|
|
9978
|
-
runner: commandRunnerForMemory(input.runtime),
|
|
9979
|
-
});
|
|
9980
|
-
}
|
|
9981
|
-
async function resolveAnalyticsCredentials(command, runtime) {
|
|
9982
|
-
return await resolveApiCredentials({
|
|
9983
|
-
baseUrl: command.baseUrl,
|
|
9984
|
-
flagToken: command.flagToken,
|
|
9985
|
-
profileName: command.profileName,
|
|
9986
|
-
env: runtime.env ?? process.env,
|
|
9987
|
-
});
|
|
11952
|
+
printConnectorsPayload(command, io, result, [
|
|
11953
|
+
`connector_id: ${command.connectorId}`,
|
|
11954
|
+
`source_artifact_id: ${sourceArtifactId}`,
|
|
11955
|
+
`source_dir: ${checkout.root}`,
|
|
11956
|
+
`remote: ${checkout.remoteName}`,
|
|
11957
|
+
]);
|
|
11958
|
+
return 0;
|
|
9988
11959
|
}
|
|
9989
|
-
async function
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
|
|
9993
|
-
|
|
9994
|
-
profileName: command.profileName,
|
|
9995
|
-
env: runtime.env ?? process.env,
|
|
9996
|
-
});
|
|
11960
|
+
async function runConnectorsShopifySourceValidate(command, io, runtime) {
|
|
11961
|
+
const { connector } = await requestConnector(command, runtime);
|
|
11962
|
+
const sourceArtifactId = readConnectorSourceArtifactId(connector);
|
|
11963
|
+
if (!sourceArtifactId) {
|
|
11964
|
+
throw new TenderCliUsageError('Connector does not have a source artifact yet. Run tender connectors shopify source init <connector-id> --dir ./shopify-app first.');
|
|
9997
11965
|
}
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
|
|
10003
|
-
|
|
11966
|
+
const dir = path.resolve(command.dir);
|
|
11967
|
+
const args = shopifyValidateArgs({ dir, clientId: command.clientId });
|
|
11968
|
+
const required = ['shopify.app.toml', 'connector.json'];
|
|
11969
|
+
for (const file of required) {
|
|
11970
|
+
if (!(await fileExists(path.join(dir, file)))) {
|
|
11971
|
+
throw new TenderCliUsageError(`Missing ${file}. Run tender connectors shopify source init first.`);
|
|
10004
11972
|
}
|
|
10005
|
-
throw error;
|
|
10006
|
-
}
|
|
10007
|
-
}
|
|
10008
|
-
async function resolveDbCredentials(command, runtime) {
|
|
10009
|
-
return await resolveApiCredentials({
|
|
10010
|
-
baseUrl: command.baseUrl,
|
|
10011
|
-
flagToken: command.flagToken,
|
|
10012
|
-
profileName: command.profileName,
|
|
10013
|
-
env: runtime.env ?? process.env,
|
|
10014
|
-
});
|
|
10015
|
-
}
|
|
10016
|
-
function validateDbSql(sql) {
|
|
10017
|
-
if (!sql.trim()) {
|
|
10018
|
-
throw new TenderCliUsageError('SQL statement is required.');
|
|
10019
|
-
}
|
|
10020
|
-
const byteLength = new TextEncoder().encode(sql).byteLength;
|
|
10021
|
-
if (byteLength > DB_SQL_STATEMENT_MAX_BYTES) {
|
|
10022
|
-
throw new TenderCliUsageError(`SQL statement is limited to ${DB_SQL_STATEMENT_MAX_BYTES} bytes.`);
|
|
10023
|
-
}
|
|
10024
|
-
}
|
|
10025
|
-
async function readDbSql(command, runtime) {
|
|
10026
|
-
const sql = command.sqlSource.kind === 'inline'
|
|
10027
|
-
? command.sqlSource.value
|
|
10028
|
-
: command.sqlSource.value === '-'
|
|
10029
|
-
? runtime.stdin
|
|
10030
|
-
: await readFile(path.resolve(command.sqlSource.value), 'utf8');
|
|
10031
|
-
if (!sql) {
|
|
10032
|
-
throw new TenderCliUsageError('--file - requires SQL on stdin.');
|
|
10033
|
-
}
|
|
10034
|
-
validateDbSql(sql);
|
|
10035
|
-
return sql;
|
|
10036
|
-
}
|
|
10037
|
-
function printDbPayload(command, io, payload, fallbackLines) {
|
|
10038
|
-
if (command.json) {
|
|
10039
|
-
io.stdout(JSON.stringify(payload, null, 2));
|
|
10040
11973
|
}
|
|
10041
|
-
|
|
10042
|
-
io.
|
|
11974
|
+
if (!command.execute) {
|
|
11975
|
+
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)}`]);
|
|
11976
|
+
return 0;
|
|
10043
11977
|
}
|
|
11978
|
+
const result = await runNpxCommand({ args, cwd: dir, runtime });
|
|
11979
|
+
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']);
|
|
11980
|
+
return 0;
|
|
10044
11981
|
}
|
|
10045
|
-
function
|
|
10046
|
-
|
|
10047
|
-
|
|
11982
|
+
async function runConnectorsShopifySourceDeploy(command, io, runtime) {
|
|
11983
|
+
const { connector } = await requestConnector(command, runtime);
|
|
11984
|
+
const sourceArtifactId = readConnectorSourceArtifactId(connector);
|
|
11985
|
+
if (!sourceArtifactId) {
|
|
11986
|
+
throw new TenderCliUsageError('Connector does not have a source artifact yet. Run tender connectors shopify source init <connector-id> --dir ./shopify-app first.');
|
|
10048
11987
|
}
|
|
10049
|
-
|
|
10050
|
-
|
|
11988
|
+
const dir = path.resolve(command.dir);
|
|
11989
|
+
const args = shopifyDeployArgs({
|
|
11990
|
+
dir,
|
|
11991
|
+
clientId: command.clientId,
|
|
11992
|
+
version: command.version,
|
|
11993
|
+
message: command.message,
|
|
11994
|
+
sourceControlUrl: command.sourceControlUrl,
|
|
11995
|
+
allowDeletes: command.allowDeletes,
|
|
11996
|
+
});
|
|
11997
|
+
if (command.confirm !== 'deploy') {
|
|
11998
|
+
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']);
|
|
11999
|
+
return 0;
|
|
10051
12000
|
}
|
|
10052
|
-
}
|
|
10053
|
-
|
|
10054
|
-
const credentials = await resolvePlaybooksCredentials(command, runtime);
|
|
10055
|
-
const payload = await requestPlaybooksApi({
|
|
10056
|
-
playbooksPath: 'list',
|
|
10057
|
-
baseUrl: credentials.baseUrl,
|
|
10058
|
-
token: credentials.token,
|
|
10059
|
-
fetcher: runtime.fetcher ?? fetch,
|
|
10060
|
-
});
|
|
10061
|
-
const playbooks = Array.isArray(payload.playbooks) ? payload.playbooks : [];
|
|
10062
|
-
printPlaybooksPayload(command, io, payload, playbooks
|
|
10063
|
-
.map((playbook) => (isCliJsonRecord(playbook) ? `${String(playbook.id ?? '')}\t${String(playbook.description ?? '')}` : ''))
|
|
10064
|
-
.filter(Boolean)
|
|
10065
|
-
.join('\n') || 'No Tender Prompt playbooks found.');
|
|
12001
|
+
const result = await runNpxCommand({ args, cwd: dir, runtime });
|
|
12002
|
+
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']);
|
|
10066
12003
|
return 0;
|
|
10067
12004
|
}
|
|
10068
|
-
async function
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10072
|
-
|
|
12005
|
+
async function runConnectorsShopifyDelete(command, io, runtime) {
|
|
12006
|
+
if (command.confirm !== 'delete') {
|
|
12007
|
+
throw new TenderCliUsageError('--confirm delete is required. Example: tender connectors shopify delete conn_123 --confirm delete --json');
|
|
12008
|
+
}
|
|
12009
|
+
const credentials = await resolveConnectorsCredentials(command, runtime);
|
|
12010
|
+
const payload = await requestConnectorsApi({
|
|
12011
|
+
connectorsPath: encodeURIComponent(command.connectorId),
|
|
12012
|
+
method: 'DELETE',
|
|
10073
12013
|
baseUrl: credentials.baseUrl,
|
|
10074
12014
|
token: credentials.token,
|
|
10075
12015
|
fetcher: runtime.fetcher ?? fetch,
|
|
10076
12016
|
});
|
|
10077
|
-
|
|
12017
|
+
printConnectorsPayload(command, io, { ...payload, connectorId: command.connectorId }, [`connector_id: ${command.connectorId}`, 'status: deleted']);
|
|
10078
12018
|
return 0;
|
|
10079
12019
|
}
|
|
10080
|
-
|
|
10081
|
-
const
|
|
10082
|
-
|
|
10083
|
-
|
|
10084
|
-
|
|
10085
|
-
|
|
10086
|
-
|
|
10087
|
-
|
|
10088
|
-
|
|
10089
|
-
|
|
10090
|
-
|
|
12020
|
+
function runConnectorsBind(command, io) {
|
|
12021
|
+
const binding = {
|
|
12022
|
+
id: `shopify-${command.instanceId}`,
|
|
12023
|
+
label: command.label ?? (command.instanceId === 'default' ? 'Shopify connector' : `Shopify connector (${command.instanceId})`),
|
|
12024
|
+
name: command.bindingName,
|
|
12025
|
+
source: `platform:shopify_connector/${command.instanceId}`,
|
|
12026
|
+
interface: 'ShopifyConnector',
|
|
12027
|
+
metadata: {
|
|
12028
|
+
connectorId: command.connectorId,
|
|
12029
|
+
},
|
|
12030
|
+
};
|
|
12031
|
+
const payload = {
|
|
12032
|
+
ok: true,
|
|
12033
|
+
command: command.command,
|
|
12034
|
+
connectorId: command.connectorId,
|
|
12035
|
+
instanceId: command.instanceId,
|
|
12036
|
+
binding,
|
|
12037
|
+
appJsonPatch: {
|
|
12038
|
+
bindingsAppend: binding,
|
|
12039
|
+
},
|
|
12040
|
+
};
|
|
12041
|
+
if (command.json) {
|
|
12042
|
+
io.stdout(JSON.stringify(payload, null, 2));
|
|
12043
|
+
}
|
|
12044
|
+
else {
|
|
12045
|
+
io.stdout(JSON.stringify(binding, null, 2));
|
|
12046
|
+
}
|
|
10091
12047
|
return 0;
|
|
10092
12048
|
}
|
|
10093
12049
|
async function runDbCapabilities(command, io, runtime) {
|
|
@@ -11032,6 +12988,51 @@ export async function runTenderCli(args, io = {
|
|
|
11032
12988
|
else if (command.topic === 'playbooks file') {
|
|
11033
12989
|
io.stdout(playbooksFileHelp());
|
|
11034
12990
|
}
|
|
12991
|
+
else if (command.topic === 'connectors') {
|
|
12992
|
+
io.stdout(connectorsHelp());
|
|
12993
|
+
}
|
|
12994
|
+
else if (command.topic === 'connectors list') {
|
|
12995
|
+
io.stdout(connectorsHelp());
|
|
12996
|
+
}
|
|
12997
|
+
else if (command.topic === 'connectors shopify') {
|
|
12998
|
+
io.stdout(connectorsHelp());
|
|
12999
|
+
}
|
|
13000
|
+
else if (command.topic === 'connectors shopify create') {
|
|
13001
|
+
io.stdout(connectorsShopifyCreateHelp());
|
|
13002
|
+
}
|
|
13003
|
+
else if (command.topic === 'connectors shopify setup-url') {
|
|
13004
|
+
io.stdout(connectorsShopifySetupUrlHelp());
|
|
13005
|
+
}
|
|
13006
|
+
else if (command.topic === 'connectors shopify credentials set') {
|
|
13007
|
+
io.stdout(connectorsShopifyCredentialsSetHelp());
|
|
13008
|
+
}
|
|
13009
|
+
else if (command.topic === 'connectors shopify app-automation-token set') {
|
|
13010
|
+
io.stdout(connectorsShopifyAppAutomationTokenSetHelp());
|
|
13011
|
+
}
|
|
13012
|
+
else if (command.topic === 'connectors shopify app-automation-token delete') {
|
|
13013
|
+
io.stdout(connectorsShopifyAppAutomationTokenDeleteHelp());
|
|
13014
|
+
}
|
|
13015
|
+
else if (command.topic === 'connectors shopify verify') {
|
|
13016
|
+
io.stdout(connectorsShopifyVerifyHelp());
|
|
13017
|
+
}
|
|
13018
|
+
else if (command.topic === 'connectors shopify source init') {
|
|
13019
|
+
io.stdout(connectorsShopifySourceInitHelp());
|
|
13020
|
+
}
|
|
13021
|
+
else if (command.topic === 'connectors shopify source checkout') {
|
|
13022
|
+
io.stdout(connectorsShopifySourceCheckoutHelp());
|
|
13023
|
+
}
|
|
13024
|
+
else if (command.topic === 'connectors shopify source validate') {
|
|
13025
|
+
io.stdout(connectorsShopifySourceValidateHelp());
|
|
13026
|
+
}
|
|
13027
|
+
else if (command.topic === 'connectors shopify source deploy') {
|
|
13028
|
+
io.stdout(connectorsShopifySourceDeployHelp());
|
|
13029
|
+
}
|
|
13030
|
+
else if (command.topic === 'connectors shopify delete') {
|
|
13031
|
+
io.stdout(connectorsShopifyDeleteHelp());
|
|
13032
|
+
}
|
|
13033
|
+
else if (command.topic === 'connectors bind') {
|
|
13034
|
+
io.stdout(connectorsBindHelp());
|
|
13035
|
+
}
|
|
11035
13036
|
else if (command.topic === 'auth') {
|
|
11036
13037
|
io.stdout(authHelp());
|
|
11037
13038
|
}
|
|
@@ -11259,6 +13260,45 @@ export async function runTenderCli(args, io = {
|
|
|
11259
13260
|
if (command.command === 'playbooks file') {
|
|
11260
13261
|
return await runPlaybooksFile(command, io, runtime);
|
|
11261
13262
|
}
|
|
13263
|
+
if (command.command === 'connectors list') {
|
|
13264
|
+
return await runConnectorsList(command, io, runtime);
|
|
13265
|
+
}
|
|
13266
|
+
if (command.command === 'connectors shopify create') {
|
|
13267
|
+
return await runConnectorsShopifyCreate(command, io, runtime);
|
|
13268
|
+
}
|
|
13269
|
+
if (command.command === 'connectors shopify setup-url') {
|
|
13270
|
+
return await runConnectorsShopifySetupUrl(command, io, runtime);
|
|
13271
|
+
}
|
|
13272
|
+
if (command.command === 'connectors shopify credentials set') {
|
|
13273
|
+
return await runConnectorsShopifyCredentialsSet(command, io, runtime);
|
|
13274
|
+
}
|
|
13275
|
+
if (command.command === 'connectors shopify app-automation-token set') {
|
|
13276
|
+
return await runConnectorsShopifyAppAutomationTokenSet(command, io, runtime);
|
|
13277
|
+
}
|
|
13278
|
+
if (command.command === 'connectors shopify app-automation-token delete') {
|
|
13279
|
+
return await runConnectorsShopifyAppAutomationTokenDelete(command, io, runtime);
|
|
13280
|
+
}
|
|
13281
|
+
if (command.command === 'connectors shopify verify') {
|
|
13282
|
+
return await runConnectorsShopifyVerify(command, io, runtime);
|
|
13283
|
+
}
|
|
13284
|
+
if (command.command === 'connectors shopify source init') {
|
|
13285
|
+
return await runConnectorsShopifySourceInit(command, io, runtime);
|
|
13286
|
+
}
|
|
13287
|
+
if (command.command === 'connectors shopify source checkout') {
|
|
13288
|
+
return await runConnectorsShopifySourceCheckout(command, io, runtime);
|
|
13289
|
+
}
|
|
13290
|
+
if (command.command === 'connectors shopify source validate') {
|
|
13291
|
+
return await runConnectorsShopifySourceValidate(command, io, runtime);
|
|
13292
|
+
}
|
|
13293
|
+
if (command.command === 'connectors shopify source deploy') {
|
|
13294
|
+
return await runConnectorsShopifySourceDeploy(command, io, runtime);
|
|
13295
|
+
}
|
|
13296
|
+
if (command.command === 'connectors shopify delete') {
|
|
13297
|
+
return await runConnectorsShopifyDelete(command, io, runtime);
|
|
13298
|
+
}
|
|
13299
|
+
if (command.command === 'connectors bind') {
|
|
13300
|
+
return runConnectorsBind(command, io);
|
|
13301
|
+
}
|
|
11262
13302
|
if (command.command === 'app list') {
|
|
11263
13303
|
return await runArtifactsList(command, io, runtime);
|
|
11264
13304
|
}
|
|
@@ -11425,7 +13465,7 @@ export async function runTenderCli(args, io = {
|
|
|
11425
13465
|
}
|
|
11426
13466
|
catch (error) {
|
|
11427
13467
|
if (args.includes('--json') &&
|
|
11428
|
-
((args[0] === 'app' && (args[1] === 'db' || (args[1] === 'agent' && args[2] === 'heartbeat'))) || args[0] === 'playbooks')) {
|
|
13468
|
+
((args[0] === 'app' && (args[1] === 'db' || (args[1] === 'agent' && args[2] === 'heartbeat'))) || args[0] === 'playbooks' || args[0] === 'connectors')) {
|
|
11429
13469
|
io.stdout(JSON.stringify(cliErrorPayload(error, args, command), null, 2));
|
|
11430
13470
|
return error instanceof TenderCliUsageError ? 2 : 1;
|
|
11431
13471
|
}
|
|
@@ -11439,13 +13479,23 @@ function hasFlagValue(args, flag, value) {
|
|
|
11439
13479
|
return args.some((arg, index) => (arg === flag && args[index + 1] === value) || arg === `${flag}=${value}`);
|
|
11440
13480
|
}
|
|
11441
13481
|
export function shouldReadEntrypointStdin(args) {
|
|
11442
|
-
return (args[0] === '
|
|
11443
|
-
|
|
11444
|
-
|
|
11445
|
-
|
|
11446
|
-
|
|
11447
|
-
|
|
11448
|
-
|
|
13482
|
+
return ((args[0] === 'connectors' &&
|
|
13483
|
+
args[1] === 'shopify' &&
|
|
13484
|
+
args[2] === 'credentials' &&
|
|
13485
|
+
args[3] === 'set' &&
|
|
13486
|
+
args.includes('--client-secret-stdin')) ||
|
|
13487
|
+
(args[0] === 'connectors' &&
|
|
13488
|
+
args[1] === 'shopify' &&
|
|
13489
|
+
args[2] === 'app-automation-token' &&
|
|
13490
|
+
args[3] === 'set' &&
|
|
13491
|
+
args.includes('--token-stdin')) ||
|
|
13492
|
+
(args[0] === 'app' &&
|
|
13493
|
+
((args[1] === 'git' && args[2] === 'credential') ||
|
|
13494
|
+
(args[1] === 'analytics' && hasFlagValue(args, '--spec', '-')) ||
|
|
13495
|
+
(args[1] === 'agent' && args[2] === 'heartbeat' && hasFlagValue(args, '--input', '-')) ||
|
|
13496
|
+
(args[1] === 'db' && (args[2] === 'resources' || args[2] === 'widgets') && hasFlagValue(args, '--spec', '-')) ||
|
|
13497
|
+
(args[1] === 'db' && args[2] === 'resources' && hasFlagValue(args, '--view', '-')) ||
|
|
13498
|
+
(args[1] === 'db' && args[2] === 'query' && hasFlagValue(args, '--file', '-')))));
|
|
11449
13499
|
}
|
|
11450
13500
|
async function readEntrypointStdin() {
|
|
11451
13501
|
if (process.stdin.isTTY) {
|