@tenderprompt/cli 0.1.29 → 0.1.30

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/README.md CHANGED
@@ -60,6 +60,7 @@ tender --version
60
60
  tender capabilities --json
61
61
 
62
62
  tender auth login --device --profile edit-preview --artifact artifact_123 --ttl 7d
63
+ tender auth login --device --profile create --shopify-store tender-prompt --json
63
64
 
64
65
  tender app init artifact_123 --dir ./widget --preview --json
65
66
 
package/dist/index.js CHANGED
@@ -295,18 +295,21 @@ Commands:
295
295
  Examples:
296
296
  tender auth login --device --profile edit-preview --artifact artifact_123 --ttl 7d
297
297
  tender auth login --device --profile publish --artifact artifact_123 --ttl 7d --save-profile publish
298
+ tender auth login --device --profile create --shopify-store tender-prompt --json
298
299
  tender auth status --json`;
299
300
  }
300
301
  function authLoginHelp() {
301
- return `Usage: tender auth login --device [--base-url <url>] [--profile <edit-preview|publish|create|db-read>] [--artifact <artifact-id>] [--ttl <7d|14d|30d>] [--save-profile <name>] [--no-poll] [--json]
302
+ return `Usage: tender auth login --device [--base-url <url>] [--shopify-store <store>] [--shopify-app-url <url>] [--profile <edit-preview|publish|create|db-read>] [--artifact <artifact-id>] [--ttl <7d|14d|30d>] [--save-profile <name>] [--no-poll] [--json]
302
303
 
303
304
  Options:
304
305
  --device Use the OAuth device-code flow.
305
306
  --base-url <url> Tender base URL. Defaults to TENDER_BASE_URL or https://app.tenderprompt.com.
307
+ --shopify-store <store> Approve the device code in Shopify Admin for this store handle.
308
+ --shopify-app-url <url> Approve the device code through a Tender Prompt Shopify App Home URL.
306
309
  --profile <value> Token permission profile: edit-preview, publish, create, or db-read. Defaults to edit-preview.
307
310
  --artifact <artifact-id> Scope the token to one artifact. Required for normal edit-preview agent work.
308
311
  --ttl <value> Token lifetime: 7d, 14d, or 30d. Defaults to 7d.
309
- --save-profile <name> Local config profile name. Defaults to default.
312
+ --save-profile <name> Local config profile name. Defaults to default, or shopify-<store> with Shopify approval.
310
313
  --no-poll Print the verification URL and exit without waiting for approval.
311
314
  --json Emit stable JSON for coding agents.
312
315
 
@@ -314,6 +317,7 @@ Examples:
314
317
  tender auth login --device --profile edit-preview --artifact artifact_123 --ttl 7d
315
318
  tender auth login --device --profile publish --artifact artifact_123 --ttl 7d --save-profile publish --json
316
319
  tender auth login --device --profile create --ttl 7d --save-profile account
320
+ tender auth login --device --profile create --shopify-store tender-prompt --json
317
321
  tender auth login --device --profile db-read --artifact artifact_123 --ttl 7d --save-profile db`;
318
322
  }
319
323
  function authStatusHelp() {
@@ -2131,6 +2135,55 @@ function parseBaseUrlFlag(args, index) {
2131
2135
  }
2132
2136
  return normalizeBaseUrl(value);
2133
2137
  }
2138
+ function parseShopifyStoreHandle(value, flag) {
2139
+ if (!value || value.startsWith('-')) {
2140
+ throw new TenderCliUsageError(`${flag} requires a Shopify Admin store handle.`);
2141
+ }
2142
+ const normalized = value.trim().toLowerCase();
2143
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(normalized)) {
2144
+ throw new TenderCliUsageError(`${flag} must be a Shopify Admin store handle, such as tender-prompt.`);
2145
+ }
2146
+ return normalized;
2147
+ }
2148
+ function shopifyAppUrlForStore(store) {
2149
+ return `https://admin.shopify.com/store/${store}/apps/tender-prompt/shopify`;
2150
+ }
2151
+ function parseShopifyAppUrlFlag(args, index) {
2152
+ const value = args[index + 1];
2153
+ if (!value || value.startsWith('-')) {
2154
+ throw new TenderCliUsageError('--shopify-app-url requires a Shopify Admin App Home URL.');
2155
+ }
2156
+ let url;
2157
+ try {
2158
+ url = new URL(value);
2159
+ }
2160
+ catch {
2161
+ throw new TenderCliUsageError('--shopify-app-url requires a valid Shopify Admin App Home URL.');
2162
+ }
2163
+ if (url.protocol !== 'https:' || url.hostname !== 'admin.shopify.com') {
2164
+ throw new TenderCliUsageError('--shopify-app-url must start with https://admin.shopify.com/.');
2165
+ }
2166
+ const parts = url.pathname.split('/').filter(Boolean);
2167
+ const deviceSuffix = parts[5] === 'device' ? parts[5] : null;
2168
+ if ((parts.length !== 5 && !(parts.length === 6 && deviceSuffix)) ||
2169
+ parts[0] !== 'store' ||
2170
+ parts[2] !== 'apps' ||
2171
+ parts[4] !== 'shopify') {
2172
+ throw new TenderCliUsageError('--shopify-app-url must look like https://admin.shopify.com/store/<store>/apps/tender-prompt/shopify.');
2173
+ }
2174
+ if (url.search || url.hash) {
2175
+ throw new TenderCliUsageError('--shopify-app-url should not include a query string or hash.');
2176
+ }
2177
+ const store = parseShopifyStoreHandle(parts[1], '--shopify-app-url');
2178
+ const appHandle = parseShopifyStoreHandle(parts[3], '--shopify-app-url');
2179
+ return {
2180
+ store,
2181
+ appUrl: `https://admin.shopify.com/store/${store}/apps/${appHandle}/shopify`,
2182
+ };
2183
+ }
2184
+ function shopifyDeviceVerificationUrl(input) {
2185
+ return `${input.shopifyAppUrl.replace(/\/+$/, '')}/device?user_code=${encodeURIComponent(input.userCode)}`;
2186
+ }
2134
2187
  function parseTokenFlag(args, index) {
2135
2188
  const value = args[index + 1];
2136
2189
  if (!value || value.startsWith('-')) {
@@ -4397,8 +4450,11 @@ function parseAuthLoginArgs(args) {
4397
4450
  }
4398
4451
  let device = false;
4399
4452
  let baseUrl = null;
4453
+ let shopifyAppUrl = null;
4454
+ let shopifyStore = null;
4400
4455
  let tokenProfile = 'edit-preview';
4401
4456
  let saveProfile = DEFAULT_PROFILE_NAME;
4457
+ let saveProfileExplicit = false;
4402
4458
  let artifactId = null;
4403
4459
  let ttl = '7d';
4404
4460
  let json = false;
@@ -4423,6 +4479,25 @@ function parseAuthLoginArgs(args) {
4423
4479
  index += 1;
4424
4480
  continue;
4425
4481
  }
4482
+ if (arg === '--shopify-store') {
4483
+ if (shopifyAppUrl) {
4484
+ throw new TenderCliUsageError('--shopify-store cannot be combined with --shopify-app-url.');
4485
+ }
4486
+ shopifyStore = parseShopifyStoreHandle(args[index + 1], '--shopify-store');
4487
+ shopifyAppUrl = shopifyAppUrlForStore(shopifyStore);
4488
+ index += 1;
4489
+ continue;
4490
+ }
4491
+ if (arg === '--shopify-app-url') {
4492
+ if (shopifyAppUrl) {
4493
+ throw new TenderCliUsageError('--shopify-app-url cannot be combined with --shopify-store.');
4494
+ }
4495
+ const parsed = parseShopifyAppUrlFlag(args, index);
4496
+ shopifyStore = parsed.store;
4497
+ shopifyAppUrl = parsed.appUrl;
4498
+ index += 1;
4499
+ continue;
4500
+ }
4426
4501
  if (arg === '--profile') {
4427
4502
  tokenProfile = parseTokenProfile(args[index + 1]);
4428
4503
  index += 1;
@@ -4430,6 +4505,7 @@ function parseAuthLoginArgs(args) {
4430
4505
  }
4431
4506
  if (arg === '--save-profile') {
4432
4507
  saveProfile = parseProfileName(args[index + 1], '--save-profile');
4508
+ saveProfileExplicit = true;
4433
4509
  index += 1;
4434
4510
  continue;
4435
4511
  }
@@ -4459,9 +4535,14 @@ function parseAuthLoginArgs(args) {
4459
4535
  if (tokenProfile === 'db-read' && !artifactId) {
4460
4536
  throw new TenderCliUsageError('--profile db-read requires --artifact <artifact-id>.');
4461
4537
  }
4538
+ if (shopifyStore && !saveProfileExplicit) {
4539
+ saveProfile = `shopify-${shopifyStore}`;
4540
+ }
4462
4541
  return {
4463
4542
  command: 'auth login',
4464
4543
  baseUrl,
4544
+ shopifyAppUrl,
4545
+ shopifyStore,
4465
4546
  device,
4466
4547
  tokenProfile,
4467
4548
  saveProfile,
@@ -8249,15 +8330,24 @@ async function runAuthLogin(command, io, runtime) {
8249
8330
  if (!deviceResponse.ok || !devicePayload?.device_code || !devicePayload.user_code || !devicePayload.verification_uri_complete) {
8250
8331
  throw new Error(devicePayload?.error_description ?? devicePayload?.error ?? `Device authorization failed with HTTP ${deviceResponse.status}.`);
8251
8332
  }
8333
+ const verificationUriComplete = command.shopifyAppUrl
8334
+ ? shopifyDeviceVerificationUrl({
8335
+ shopifyAppUrl: command.shopifyAppUrl,
8336
+ userCode: devicePayload.user_code,
8337
+ })
8338
+ : devicePayload.verification_uri_complete;
8339
+ const verificationUri = command.shopifyAppUrl ? `${command.shopifyAppUrl}/device` : devicePayload.verification_uri;
8252
8340
  const pendingResult = {
8253
8341
  ok: true,
8254
8342
  command: command.command,
8255
8343
  baseUrl,
8344
+ shopifyAppUrl: command.shopifyAppUrl,
8345
+ shopifyStore: command.shopifyStore,
8256
8346
  profile: command.tokenProfile,
8257
8347
  saveProfile: command.saveProfile,
8258
8348
  userCode: devicePayload.user_code,
8259
- verificationUri: devicePayload.verification_uri,
8260
- verificationUriComplete: devicePayload.verification_uri_complete,
8349
+ verificationUri,
8350
+ verificationUriComplete,
8261
8351
  expiresIn: devicePayload.expires_in ?? null,
8262
8352
  interval: devicePayload.interval ?? null,
8263
8353
  };
@@ -8266,13 +8356,13 @@ async function runAuthLogin(command, io, runtime) {
8266
8356
  io.stdout(JSON.stringify(pendingResult, null, 2));
8267
8357
  }
8268
8358
  else {
8269
- io.stdout([`verification_url: ${devicePayload.verification_uri_complete}`, `user_code: ${devicePayload.user_code}`].join('\n'));
8359
+ io.stdout([`verification_url: ${verificationUriComplete}`, `user_code: ${devicePayload.user_code}`].join('\n'));
8270
8360
  }
8271
8361
  return 0;
8272
8362
  }
8273
8363
  if (!command.json) {
8274
8364
  io.stdout([
8275
- `Open this URL to approve Tender CLI access: ${devicePayload.verification_uri_complete}`,
8365
+ `Open this URL to approve Tender CLI access: ${verificationUriComplete}`,
8276
8366
  `User code: ${devicePayload.user_code}`,
8277
8367
  ].join('\n'));
8278
8368
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenderprompt/cli",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "tender": "bin/tender.js"
@@ -23,7 +23,7 @@
23
23
  "prepack": "yarn build"
24
24
  },
25
25
  "dependencies": {
26
- "@tenderprompt/tender-app-validator": "0.1.4"
26
+ "@tenderprompt/tender-app-validator": "0.1.5"
27
27
  },
28
28
  "engines": {
29
29
  "node": ">=20"