@extension.dev/deploy 0.2.5 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +35 -2
  3. package/dist/module.js +407 -369
  4. package/dist/module.mjs +409 -371
  5. package/dist/src/chrome.d.ts +6 -1
  6. package/dist/src/chrome.d.ts.map +1 -1
  7. package/dist/src/cli-commands.d.ts +5 -0
  8. package/dist/src/cli-commands.d.ts.map +1 -0
  9. package/dist/src/cli-parse.d.ts +3 -0
  10. package/dist/src/cli-parse.d.ts.map +1 -0
  11. package/dist/src/cli-usage.d.ts +2 -0
  12. package/dist/src/cli-usage.d.ts.map +1 -0
  13. package/dist/src/cli.d.ts +1 -2
  14. package/dist/src/cli.d.ts.map +1 -1
  15. package/dist/src/config.d.ts +3 -2
  16. package/dist/src/config.d.ts.map +1 -1
  17. package/dist/src/deploy.d.ts.map +1 -1
  18. package/dist/src/edge.d.ts.map +1 -1
  19. package/dist/src/firefox-api.d.ts +52 -0
  20. package/dist/src/firefox-api.d.ts.map +1 -0
  21. package/dist/src/firefox.d.ts +1 -32
  22. package/dist/src/firefox.d.ts.map +1 -1
  23. package/dist/src/init-chrome.d.ts +17 -0
  24. package/dist/src/init-chrome.d.ts.map +1 -0
  25. package/dist/src/init-edge.d.ts +8 -0
  26. package/dist/src/init-edge.d.ts.map +1 -0
  27. package/dist/src/init-firefox.d.ts +8 -0
  28. package/dist/src/init-firefox.d.ts.map +1 -0
  29. package/dist/src/init-io.d.ts.map +1 -1
  30. package/dist/src/init-prompt.d.ts +5 -0
  31. package/dist/src/init-prompt.d.ts.map +1 -0
  32. package/dist/src/init.d.ts +3 -26
  33. package/dist/src/init.d.ts.map +1 -1
  34. package/dist/src/types.d.ts +8 -8
  35. package/dist/src/types.d.ts.map +1 -1
  36. package/dist/src/utils/fetch.d.ts +2 -2
  37. package/dist/src/utils/fetch.d.ts.map +1 -1
  38. package/dist/src/watch.d.ts +1 -0
  39. package/dist/src/watch.d.ts.map +1 -1
  40. package/package.json +18 -8
  41. package/dist/__tests__/cli.test.d.ts +0 -2
  42. package/dist/__tests__/cli.test.d.ts.map +0 -1
  43. package/dist/__tests__/config.test.d.ts +0 -2
  44. package/dist/__tests__/config.test.d.ts.map +0 -1
  45. package/dist/__tests__/deploy.test.d.ts +0 -2
  46. package/dist/__tests__/deploy.test.d.ts.map +0 -1
  47. package/dist/__tests__/dry-run.test.d.ts +0 -2
  48. package/dist/__tests__/dry-run.test.d.ts.map +0 -1
  49. package/dist/__tests__/init.test.d.ts +0 -2
  50. package/dist/__tests__/init.test.d.ts.map +0 -1
  51. package/dist/__tests__/types.test.d.ts +0 -2
  52. package/dist/__tests__/types.test.d.ts.map +0 -1
  53. package/dist/__tests__/utils.test.d.ts +0 -2
  54. package/dist/__tests__/utils.test.d.ts.map +0 -1
  55. package/dist/__tests__/verify.test.d.ts +0 -2
  56. package/dist/__tests__/verify.test.d.ts.map +0 -1
  57. package/dist/__tests__/watch.test.d.ts +0 -2
  58. package/dist/__tests__/watch.test.d.ts.map +0 -1
package/dist/module.js CHANGED
@@ -62,10 +62,7 @@ const chromeOptionsSchema = external_zod_namespaceObject.z.object({
62
62
  clientId: external_zod_namespaceObject.z.string().min(1).trim(),
63
63
  clientSecret: external_zod_namespaceObject.z.string().min(1).trim(),
64
64
  refreshToken: external_zod_namespaceObject.z.string().min(1).trim(),
65
- publishTarget: external_zod_namespaceObject.z["enum"]([
66
- "default",
67
- "trustedTesters"
68
- ]).default("default"),
65
+ publisherId: external_zod_namespaceObject.z.string().min(1).trim(),
69
66
  deployPercentage: external_zod_namespaceObject.z.number().int().min(1).max(100).optional(),
70
67
  reviewExemption: external_zod_namespaceObject.z.boolean().default(false),
71
68
  skipSubmitReview: external_zod_namespaceObject.z.boolean().default(false)
@@ -115,7 +112,7 @@ function resolveConfig(flags) {
115
112
  clientId: flags.chromeClientId ?? envStr("CHROME_CLIENT_ID") ?? "",
116
113
  clientSecret: flags.chromeClientSecret ?? envStr("CHROME_CLIENT_SECRET") ?? "",
117
114
  refreshToken: flags.chromeRefreshToken ?? envStr("CHROME_REFRESH_TOKEN") ?? "",
118
- publishTarget: flags.chromePublishTarget ?? envStr("CHROME_PUBLISH_TARGET"),
115
+ publisherId: flags.chromePublisherId ?? envStr("CHROME_PUBLISHER_ID") ?? "",
119
116
  deployPercentage: flags.chromeDeployPercentage ?? envInt("CHROME_DEPLOY_PERCENTAGE"),
120
117
  reviewExemption: flags.chromeReviewExemption ?? envBool("CHROME_REVIEW_EXEMPTION"),
121
118
  skipSubmitReview: flags.chromeSkipSubmitReview ?? envBool("CHROME_SKIP_SUBMIT_REVIEW")
@@ -145,6 +142,7 @@ const SETUP_HINTS = {
145
142
  CHROME_CLIENT_ID: "Create OAuth 2.0 credentials at https://console.cloud.google.com/apis/credentials",
146
143
  CHROME_CLIENT_SECRET: "Generated alongside the Client ID in the Google Cloud Console.",
147
144
  CHROME_REFRESH_TOKEN: "Obtain by completing the OAuth consent flow. See the Chrome Web Store API docs.",
145
+ CHROME_PUBLISHER_ID: "The publisher UUID from your Chrome Web Store dev console URL (chrome.google.com/webstore/devconsole/<UUID>).",
148
146
  CHROME_ZIP: "Path to the .zip file built for Chrome Web Store upload.",
149
147
  FIREFOX_EXTENSION_ID: "The addon GUID ({uuid} format) or email-style ID from your addon's AMO page.",
150
148
  FIREFOX_JWT_ISSUER: "API key from https://addons.mozilla.org/developers/addon/api/key/",
@@ -187,16 +185,24 @@ function envInt(name) {
187
185
  }
188
186
  const promises_namespaceObject = require("node:fs/promises");
189
187
  var promises_default = /*#__PURE__*/ __webpack_require__.n(promises_namespaceObject);
190
- async function fetchJson(url, init) {
191
- const res = await fetch(url, init);
188
+ const DEFAULT_TIMEOUT_MS = 60000;
189
+ function withTimeout(init, timeoutMs = DEFAULT_TIMEOUT_MS) {
190
+ if (null == init ? void 0 : init.signal) return init;
191
+ return {
192
+ ...init || {},
193
+ signal: AbortSignal.timeout(timeoutMs)
194
+ };
195
+ }
196
+ async function fetchJson(url, init, timeoutMs) {
197
+ const res = await fetch(url, withTimeout(init, timeoutMs));
192
198
  if (!res.ok) {
193
199
  const body = await res.text().catch(()=>"");
194
200
  throw new Error(`HTTP ${res.status} ${res.statusText} \u{2014} ${(null == init ? void 0 : init.method) ?? "GET"} ${url}\n${body}`);
195
201
  }
196
202
  return res.json();
197
203
  }
198
- async function fetchRaw(url, init) {
199
- const res = await fetch(url, init);
204
+ async function fetchRaw(url, init, timeoutMs) {
205
+ const res = await fetch(url, withTimeout(init, timeoutMs));
200
206
  if (!res.ok) {
201
207
  const body = await res.text().catch(()=>"");
202
208
  throw new Error(`HTTP ${res.status} ${res.statusText} \u{2014} ${(null == init ? void 0 : init.method) ?? "GET"} ${url}\n${body}`);
@@ -249,10 +255,19 @@ function logDryStep(scope, step, details) {
249
255
  for (const [key, value] of Object.entries(details))if (null != value) console.log(labelLine(key, value));
250
256
  }
251
257
  }
258
+ const UPLOAD_TIMEOUT_MS = 120000;
259
+ const PUBLISH_TIMEOUT_MS = 60000;
252
260
  const CWS_OAUTH_URL = "https://oauth2.googleapis.com/token";
253
- const CWS_UPLOAD_BASE = "https://www.googleapis.com/upload/chromewebstore/v1.1/items";
254
- const CWS_PUBLISH_BASE = "https://www.googleapis.com/chromewebstore/v1.1/items";
255
- const CWS_ITEMS_BASE = "https://www.googleapis.com/chromewebstore/v1.1/items";
261
+ const CWS_V2_BASE = "https://chromewebstore.googleapis.com/v2";
262
+ const CWS_V2_UPLOAD_BASE = "https://chromewebstore.googleapis.com/upload/v2";
263
+ function v2ItemName(publisherId, extensionId) {
264
+ return `publishers/${encodeURIComponent(publisherId)}/items/${encodeURIComponent(extensionId)}`;
265
+ }
266
+ function requirePublisherId(publisherId, op) {
267
+ const id = String(publisherId || "").trim();
268
+ if (!id) throw new Error(`Chrome ${op} needs a publisher ID (the UUID from your Chrome Web Store dev console URL).`);
269
+ return id;
270
+ }
256
271
  function chromeStoreUrl(extensionId) {
257
272
  return `https://chromewebstore.google.com/detail/${extensionId}`;
258
273
  }
@@ -271,16 +286,17 @@ async function authenticate(clientId, clientSecret, refreshToken) {
271
286
  }
272
287
  });
273
288
  }
274
- async function chrome_upload(extensionId, zipPath, token) {
289
+ async function chrome_upload(extensionId, publisherId, zipPath, token) {
275
290
  const body = await promises_default().readFile(zipPath);
276
- const res = await fetch(`${CWS_UPLOAD_BASE}/${extensionId}`, {
277
- method: "PUT",
291
+ const url = `${CWS_V2_UPLOAD_BASE}/${v2ItemName(publisherId, extensionId)}:upload`;
292
+ const res = await fetch(url, {
293
+ method: "POST",
278
294
  body,
279
295
  headers: {
280
296
  Authorization: `${token.token_type} ${token.access_token}`,
281
- "x-goog-api-version": "2",
282
297
  "Content-Type": "application/zip"
283
- }
298
+ },
299
+ signal: AbortSignal.timeout(UPLOAD_TIMEOUT_MS)
284
300
  });
285
301
  if (!res.ok) {
286
302
  const text = await res.text().catch(()=>"");
@@ -288,17 +304,23 @@ async function chrome_upload(extensionId, zipPath, token) {
288
304
  }
289
305
  }
290
306
  async function publish(params) {
291
- const url = new URL(`${CWS_PUBLISH_BASE}/${params.extensionId}/publish`);
292
- url.searchParams.set("publishTarget", params.publishTarget);
293
- if (null != params.deployPercentage) url.searchParams.set("deployPercentage", String(params.deployPercentage));
294
- if (null != params.reviewExemption) url.searchParams.set("reviewExemption", String(params.reviewExemption));
295
- const res = await fetch(url.href, {
307
+ const reqBody = {
308
+ publishType: "DEFAULT_PUBLISH"
309
+ };
310
+ if (null != params.deployPercentage) reqBody.deployInfos = [
311
+ {
312
+ deployPercentage: params.deployPercentage
313
+ }
314
+ ];
315
+ if (params.reviewExemption) reqBody.skipReview = true;
316
+ const res = await fetch(`${CWS_V2_BASE}/${v2ItemName(params.publisherId, params.extensionId)}:publish`, {
296
317
  method: "POST",
297
318
  headers: {
298
319
  Authorization: `${params.token.token_type} ${params.token.access_token}`,
299
- "x-goog-api-version": "2",
300
- "Content-Length": "0"
301
- }
320
+ "Content-Type": "application/json"
321
+ },
322
+ body: JSON.stringify(reqBody),
323
+ signal: AbortSignal.timeout(PUBLISH_TIMEOUT_MS)
302
324
  });
303
325
  if (!res.ok) {
304
326
  const text = await res.text().catch(()=>"");
@@ -308,7 +330,8 @@ async function publish(params) {
308
330
  async function publishChrome(options, dryRun) {
309
331
  await assertFilePresent(options.zip);
310
332
  const zipSize = await getFileSize(options.zip);
311
- log("chrome", "Authenticating with Chrome Web Store");
333
+ const publisherId = requirePublisherId(options.publisherId, "submit");
334
+ log("chrome", "Authenticating with Chrome Web Store (API v2)");
312
335
  const token = await authenticate(options.clientId, options.clientSecret, options.refreshToken);
313
336
  const outcome = {
314
337
  storeUrl: chromeStoreUrl(options.extensionId),
@@ -321,21 +344,17 @@ async function publishChrome(options, dryRun) {
321
344
  });
322
345
  logDryStep("chrome", "Would upload ZIP", {
323
346
  file: options.zip,
324
- size: formatBytes(zipSize),
325
- target: `${CWS_UPLOAD_BASE}/${options.extensionId}`,
326
- method: "PUT"
347
+ size: formatBytes(zipSize)
327
348
  });
328
349
  options.skipSubmitReview ? logDryStep("chrome", "Would skip publish (skipSubmitReview=true)") : logDryStep("chrome", "Would publish for review", {
329
- target: `${CWS_PUBLISH_BASE}/${options.extensionId}/publish`,
330
- publishTarget: options.publishTarget ?? "default",
331
350
  deployPercentage: options.deployPercentage,
332
351
  reviewExemption: options.reviewExemption
333
352
  });
334
- log("chrome", "Dry run complete \u2014 no changes made");
353
+ log("chrome", "Dry run complete - no changes made");
335
354
  return outcome;
336
355
  }
337
356
  log("chrome", `Uploading ZIP file (${formatBytes(zipSize)})`);
338
- await chrome_upload(options.extensionId, options.zip, token);
357
+ await chrome_upload(options.extensionId, publisherId, options.zip, token);
339
358
  if (options.skipSubmitReview) {
340
359
  log("chrome", "Skipping publish step (skipSubmitReview=true)");
341
360
  return outcome;
@@ -343,7 +362,7 @@ async function publishChrome(options, dryRun) {
343
362
  log("chrome", "Publishing extension");
344
363
  await publish({
345
364
  extensionId: options.extensionId,
346
- publishTarget: options.publishTarget,
365
+ publisherId,
347
366
  token,
348
367
  deployPercentage: options.deployPercentage,
349
368
  reviewExemption: options.reviewExemption
@@ -351,14 +370,22 @@ async function publishChrome(options, dryRun) {
351
370
  return outcome;
352
371
  }
353
372
  async function getChromeItem(params) {
373
+ var _rev_distributionChannels_, _rev_distributionChannels;
354
374
  const token = await authenticate(params.clientId, params.clientSecret, params.refreshToken);
355
- const url = `${CWS_ITEMS_BASE}/${encodeURIComponent(params.extensionId)}?projection=${params.projection ?? "DRAFT"}`;
356
- return fetchJson(url, {
357
- headers: {
358
- Authorization: `${token.token_type} ${token.access_token}`,
359
- "x-goog-api-version": "2"
360
- }
375
+ const auth = {
376
+ Authorization: `${token.token_type} ${token.access_token}`
377
+ };
378
+ const pub = requirePublisherId(params.publisherId, "status");
379
+ const status = await fetchJson(`${CWS_V2_BASE}/${v2ItemName(pub, params.extensionId)}:fetchStatus`, {
380
+ headers: auth
361
381
  });
382
+ const rev = status.submittedItemRevisionStatus || status.publishedItemRevisionStatus;
383
+ return {
384
+ uploadState: status.lastAsyncUploadState,
385
+ crxVersion: null == rev ? void 0 : null == (_rev_distributionChannels = rev.distributionChannels) ? void 0 : null == (_rev_distributionChannels_ = _rev_distributionChannels[0]) ? void 0 : _rev_distributionChannels_.crxVersion,
386
+ publicKey: status.publicKey,
387
+ state: null == rev ? void 0 : rev.state
388
+ };
362
389
  }
363
390
  async function verifyChromeCredentials(params) {
364
391
  let token;
@@ -370,17 +397,18 @@ async function verifyChromeCredentials(params) {
370
397
  message: `OAuth token exchange failed. Verify your Client ID, Client Secret, and Refresh Token are correct.\n${err instanceof Error ? err.message : err}`
371
398
  };
372
399
  }
400
+ const auth = {
401
+ Authorization: `${token.token_type} ${token.access_token}`
402
+ };
373
403
  try {
374
- const item = await fetchJson(`${CWS_ITEMS_BASE}/${encodeURIComponent(params.extensionId)}?projection=DRAFT`, {
375
- headers: {
376
- Authorization: `${token.token_type} ${token.access_token}`
377
- }
404
+ const pub = requirePublisherId(params.publisherId, "verify");
405
+ const status = await fetchJson(`${CWS_V2_BASE}/${v2ItemName(pub, params.extensionId)}:fetchStatus`, {
406
+ headers: auth
378
407
  });
379
- const title = (null == item ? void 0 : item.title) || params.extensionId;
408
+ const rev = status.submittedItemRevisionStatus || status.publishedItemRevisionStatus;
380
409
  return {
381
410
  ok: true,
382
- message: `Credentials verified. You have access to "${title}".`,
383
- extensionTitle: title
411
+ message: `Credentials verified (Chrome Web Store API v2). Item state: ${(null == rev ? void 0 : rev.state) || "unknown"}.`
384
412
  };
385
413
  } catch (err) {
386
414
  const msg = err instanceof Error ? err.message : String(err);
@@ -390,7 +418,7 @@ async function verifyChromeCredentials(params) {
390
418
  };
391
419
  if (msg.includes("403")) return {
392
420
  ok: false,
393
- message: `Access denied to extension ${params.extensionId}. You may not be the owner or a developer on this item.`
421
+ message: "Access denied. Check the publisher ID matches this extension's account, and that the extension ID is correct."
394
422
  };
395
423
  return {
396
424
  ok: false,
@@ -398,20 +426,14 @@ async function verifyChromeCredentials(params) {
398
426
  };
399
427
  }
400
428
  }
401
- const external_node_crypto_namespaceObject = require("node:crypto");
402
- var external_node_crypto_default = /*#__PURE__*/ __webpack_require__.n(external_node_crypto_namespaceObject);
403
429
  function delay(ms) {
404
430
  return new Promise((resolve)=>{
405
431
  setTimeout(resolve, ms);
406
432
  });
407
433
  }
434
+ const external_node_crypto_namespaceObject = require("node:crypto");
435
+ var external_node_crypto_default = /*#__PURE__*/ __webpack_require__.n(external_node_crypto_namespaceObject);
408
436
  const AMO_BASE = "https://addons.mozilla.org/api/v5/addons";
409
- const AMO_PROFILE_URL = "https://addons.mozilla.org/api/v5/accounts/profile/";
410
- const POLL_INTERVAL_MS = 5000;
411
- const VALIDATION_TIMEOUT_MS = 600000;
412
- function firefoxStoreUrl(addonIdOrSlug) {
413
- return `https://addons.mozilla.org/firefox/addon/${addonIdOrSlug}`;
414
- }
415
437
  function signJwt(issuer, secret, expiresInSeconds = 30) {
416
438
  const header = {
417
439
  alg: "HS256",
@@ -496,6 +518,12 @@ async function createAddon(params) {
496
518
  headers: authHeaders(params.jwtIssuer, params.jwtSecret)
497
519
  });
498
520
  }
521
+ const AMO_PROFILE_URL = "https://addons.mozilla.org/api/v5/accounts/profile/";
522
+ const POLL_INTERVAL_MS = 5000;
523
+ const VALIDATION_TIMEOUT_MS = 600000;
524
+ function firefoxStoreUrl(addonIdOrSlug) {
525
+ return `https://addons.mozilla.org/firefox/addon/${addonIdOrSlug}`;
526
+ }
499
527
  async function publishFirefox(options, dryRun) {
500
528
  await assertFilePresent(options.zip);
501
529
  const zipSize = await getFileSize(options.zip);
@@ -861,8 +889,134 @@ async function deploy(config) {
861
889
  success
862
890
  };
863
891
  }
892
+ function usage() {
893
+ return `
894
+ extension-deploy \u{2014} Deploy browser extensions to Chrome, Firefox, and Edge.
895
+
896
+ USAGE
897
+ extension-deploy [options] submit to one or more stores
898
+ extension-deploy init [options] interactive credential setup wizard
899
+ extension-deploy watch --chrome [options] poll an in-flight submission
900
+ extension-deploy watch --firefox [options]
901
+ extension-deploy watch --edge [options]
902
+
903
+ GLOBAL
904
+ --dry-run Verify auth without uploading or publishing
905
+ --output-json <path> Write the DeployResult / WatchEvent as JSON to <path>
906
+ --help Show this help message
907
+
908
+ CHROME
909
+ --chrome-zip <path> Path to Chrome extension ZIP
910
+ --chrome-extension-id <id> Chrome extension ID
911
+ --chrome-publisher-id <uuid> Publisher UUID from the dev console (required)
912
+ --chrome-client-id <id> OAuth2 client ID
913
+ --chrome-client-secret <secret> OAuth2 client secret
914
+ --chrome-refresh-token <token> OAuth2 refresh token
915
+ --chrome-deploy-percentage <n> Staged rollout percentage (1-100)
916
+ --chrome-review-exemption Request expedited review
917
+ --chrome-skip-submit-review Upload only, skip publish
918
+
919
+ FIREFOX
920
+ --firefox-zip <path> Path to Firefox extension ZIP
921
+ --firefox-sources-zip <path> Path to sources ZIP (optional)
922
+ --firefox-extension-id <id> Addon GUID or email-style ID
923
+ --firefox-jwt-issuer <issuer> AMO JWT issuer
924
+ --firefox-jwt-secret <secret> AMO JWT secret
925
+ --firefox-channel <channel> "listed" or "unlisted"
926
+
927
+ EDGE
928
+ --edge-zip <path> Path to Edge extension ZIP
929
+ --edge-product-id <id> Partner Center product ID
930
+ --edge-client-id <id> Partner Center client ID
931
+ --edge-api-key <key> Partner Center API key (v1.1)
932
+ --edge-skip-submit-review Upload only, skip publish
933
+
934
+ WATCH
935
+ --watch-submission-id <id> Required for firefox/edge (version id / operation id)
936
+ --watch-interval <seconds> Poll interval (default 60)
937
+ --watch-timeout <seconds> Max time to wait for a terminal state (default 3600)
938
+
939
+ INIT
940
+ --init-stores <list> Comma-separated store list (skip picker)
941
+ --init-output <path> Destination file (default .env.submit)
942
+ --init-skip-verify Don't run verifyCredentials after collection
943
+ --init-force Overwrite existing .env.submit without asking
944
+
945
+ ENVIRONMENT VARIABLES
946
+ All flags can be set via SCREAMING_SNAKE_CASE env vars (e.g. CHROME_ZIP,
947
+ FIREFOX_JWT_SECRET, EDGE_API_KEY). Flags take precedence over env vars.
948
+ A .env.submit file in the current directory is loaded automatically.
949
+ `.trim();
950
+ }
951
+ const BOOLEAN_FLAGS = new Set([
952
+ "help",
953
+ "dryRun",
954
+ "chromeReviewExemption",
955
+ "chromeSkipSubmitReview",
956
+ "edgeSkipSubmitReview",
957
+ "initSkipVerify",
958
+ "initForce"
959
+ ]);
960
+ const INT_FLAGS = new Set([
961
+ "chromeDeployPercentage",
962
+ "watchInterval",
963
+ "watchTimeout"
964
+ ]);
965
+ function parseArgs(argv) {
966
+ const flags = {};
967
+ let i = 0;
968
+ if ("watch" === argv[0]) {
969
+ flags.command = "watch";
970
+ i = 1;
971
+ } else if ("init" === argv[0]) {
972
+ flags.command = "init";
973
+ i = 1;
974
+ }
975
+ while(i < argv.length){
976
+ const arg = argv[i];
977
+ if ("--help" === arg || "-h" === arg) {
978
+ flags.help = true;
979
+ i++;
980
+ continue;
981
+ }
982
+ if ("--chrome" === arg) {
983
+ flags.watchStore = "chrome";
984
+ i++;
985
+ continue;
986
+ }
987
+ if ("--firefox" === arg) {
988
+ flags.watchStore = "firefox";
989
+ i++;
990
+ continue;
991
+ }
992
+ if ("--edge" === arg) {
993
+ flags.watchStore = "edge";
994
+ i++;
995
+ continue;
996
+ }
997
+ if (!(null == arg ? void 0 : arg.startsWith("--"))) {
998
+ i++;
999
+ continue;
1000
+ }
1001
+ const key = arg.slice(2).replace(/-([a-z])/g, (_, c)=>c.toUpperCase());
1002
+ if (BOOLEAN_FLAGS.has(key)) {
1003
+ flags[key] = true;
1004
+ i++;
1005
+ continue;
1006
+ }
1007
+ const next = argv[i + 1];
1008
+ if (null == next || next.startsWith("--")) {
1009
+ flags[key] = true;
1010
+ i++;
1011
+ continue;
1012
+ }
1013
+ flags[key] = INT_FLAGS.has(key) ? parseInt(next, 10) : next;
1014
+ i += 2;
1015
+ }
1016
+ return flags;
1017
+ }
864
1018
  const DEFAULT_POLL_INTERVAL_MS = 60000;
865
- const DEFAULT_TIMEOUT_MS = 3600000;
1019
+ const watch_DEFAULT_TIMEOUT_MS = 3600000;
866
1020
  function watch_now() {
867
1021
  return new Date().toISOString();
868
1022
  }
@@ -989,7 +1143,7 @@ function watchFirefoxSubmission(input, options = {}) {
989
1143
  }
990
1144
  async function runWatchLoop(_store, probe, options) {
991
1145
  const pollInterval = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
992
- const timeout = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
1146
+ const timeout = options.timeoutMs ?? watch_DEFAULT_TIMEOUT_MS;
993
1147
  const deadline = Date.now() + timeout;
994
1148
  let lastEvent;
995
1149
  while(true){
@@ -1001,38 +1155,165 @@ async function runWatchLoop(_store, probe, options) {
1001
1155
  await delay(pollInterval);
1002
1156
  }
1003
1157
  }
1004
- const STORE_CHOICES = [
1005
- {
1006
- value: "chrome",
1007
- label: "Chrome Web Store"
1008
- },
1009
- {
1010
- value: "firefox",
1011
- label: "Firefox AMO (addons.mozilla.org)"
1012
- },
1013
- {
1014
- value: "edge",
1015
- label: "Edge Add-ons (Partner Center)"
1158
+ async function requireValue(io, label, hint, opts) {
1159
+ const question = hint ? `${label} ${hint}:` : `${label}:`;
1160
+ for(let attempt = 0; attempt < 3; attempt++){
1161
+ const answer = (await io.prompt(question, opts)).trim();
1162
+ if (answer) return answer;
1163
+ io.log(` ${label} is required.`);
1016
1164
  }
1017
- ];
1165
+ throw new Error(`init: ${label} is required.`);
1166
+ }
1018
1167
  const CHROME_OAUTH_TOKEN_URL = "https://oauth2.googleapis.com/token";
1019
1168
  const CHROME_OAUTH_AUTH_URL = "https://accounts.google.com/o/oauth2/auth";
1020
1169
  const CHROME_OOB_REDIRECT = "urn:ietf:wg:oauth:2.0:oob";
1021
1170
  const CHROME_SCOPE = "https://www.googleapis.com/auth/chromewebstore";
1022
- async function runInit(deps, options = {}) {
1171
+ async function collectChromeCredentials(deps) {
1023
1172
  const { io } = deps;
1173
+ io.log("Chrome Web Store needs OAuth credentials. You'll need:");
1174
+ io.log(" 1. A Google Cloud project with the Chrome Web Store API enabled:");
1175
+ io.log(" https://console.cloud.google.com/apis/library/chromewebstore.googleapis.com");
1176
+ io.log(" 2. OAuth 2.0 credentials (application type: Desktop app):");
1177
+ io.log(" https://console.cloud.google.com/apis/credentials");
1178
+ io.log(" 3. Your extension ID from the Chrome Web Store Developer Dashboard.");
1179
+ io.log(" 4. Your publisher ID (the UUID in your dev console URL: chrome.google.com/webstore/devconsole/<UUID>).");
1024
1180
  io.log("");
1025
- io.log(`${colors.green("\u23F5\u23F5\u23F5")} ${colors.blue("init")} ${colors.underline("extension-deploy \u2014 set up store credentials")}`);
1181
+ io.log("This wizard will take your Client ID + Secret and walk you through");
1182
+ io.log("generating a refresh token automatically.");
1026
1183
  io.log("");
1027
- io.log("This wizard walks you through creating credentials for each store and");
1028
- io.log("writes them to a .env.submit file. Press Ctrl+C at any time to abort.");
1184
+ const extensionId = await requireValue(io, "Chrome extension ID", "(32-character ID from the CWS dashboard URL)");
1185
+ const publisherId = await requireValue(io, "Chrome publisher ID", "(UUID from your dev console URL)");
1186
+ const clientId = await requireValue(io, "OAuth Client ID");
1187
+ const clientSecret = await requireValue(io, "OAuth Client Secret", void 0, {
1188
+ secret: true
1189
+ });
1190
+ const refreshToken = await obtainChromeRefreshToken(deps, clientId, clientSecret);
1191
+ return {
1192
+ extensionId,
1193
+ publisherId,
1194
+ clientId,
1195
+ clientSecret,
1196
+ refreshToken
1197
+ };
1198
+ }
1199
+ function buildChromeAuthUrl(clientId) {
1200
+ const params = new URLSearchParams({
1201
+ response_type: "code",
1202
+ client_id: clientId,
1203
+ redirect_uri: CHROME_OOB_REDIRECT,
1204
+ scope: CHROME_SCOPE,
1205
+ access_type: "offline",
1206
+ prompt: "consent"
1207
+ });
1208
+ return `${CHROME_OAUTH_AUTH_URL}?${params.toString()}`;
1209
+ }
1210
+ async function exchangeChromeAuthCode(input) {
1211
+ const fetchFn = input.fetchImpl ?? fetch;
1212
+ const body = new URLSearchParams({
1213
+ code: input.authCode,
1214
+ client_id: input.clientId,
1215
+ client_secret: input.clientSecret,
1216
+ redirect_uri: CHROME_OOB_REDIRECT,
1217
+ grant_type: "authorization_code"
1218
+ });
1219
+ const res = await fetchFn(CHROME_OAUTH_TOKEN_URL, {
1220
+ method: "POST",
1221
+ headers: {
1222
+ "Content-Type": "application/x-www-form-urlencoded"
1223
+ },
1224
+ body: body.toString()
1225
+ });
1226
+ if (!res.ok) {
1227
+ const text = await res.text().catch(()=>"");
1228
+ throw new Error(`Chrome token exchange failed (HTTP ${res.status}): ${text || res.statusText}`);
1229
+ }
1230
+ const json = await res.json();
1231
+ if (!json.refresh_token) throw new Error(`Chrome token exchange returned no refresh_token${json.error ? `: ${json.error} \u{2014} ${json.error_description ?? ""}` : ""}. This usually means the authorization code was already used or expired; try the init wizard again.`);
1232
+ return json.refresh_token;
1233
+ }
1234
+ async function obtainChromeRefreshToken(deps, clientId, clientSecret) {
1235
+ const { io } = deps;
1236
+ const authUrl = buildChromeAuthUrl(clientId);
1029
1237
  io.log("");
1030
- const stores = options.stores ? options.stores : await io.multiselect("Which stores do you want to configure?", STORE_CHOICES);
1031
- if (0 === stores.length) throw new Error("init: at least one store must be selected. Use --init-stores chrome,firefox,edge or make a selection.");
1032
- const envLines = [
1033
- "# Generated by `extension-deploy init`.",
1034
- "# Do not commit secrets to source control.",
1035
- ""
1238
+ io.log("Open the URL below in your browser, approve the scope, and paste the");
1239
+ io.log("authorization code shown on the page:");
1240
+ io.log("");
1241
+ io.log(` ${authUrl}`);
1242
+ io.log("");
1243
+ const authCode = await requireValue(io, "Authorization code", void 0, {
1244
+ secret: true
1245
+ });
1246
+ return exchangeChromeAuthCode({
1247
+ clientId,
1248
+ clientSecret,
1249
+ authCode,
1250
+ fetchImpl: deps.fetchImpl
1251
+ });
1252
+ }
1253
+ async function collectFirefoxCredentials(deps) {
1254
+ const { io } = deps;
1255
+ io.log("Firefox AMO needs JWT API credentials:");
1256
+ io.log(" 1. Sign in at https://addons.mozilla.org");
1257
+ io.log(" 2. Create API credentials at https://addons.mozilla.org/developers/addon/api/key/");
1258
+ io.log(" 3. Copy the JWT issuer and JWT secret (the secret is only shown once).");
1259
+ io.log("");
1260
+ const addonId = await requireValue(io, "Firefox addon ID", "({uuid} GUID or email-style ID from your addon page)");
1261
+ const jwtIssuer = await requireValue(io, "JWT issuer");
1262
+ const jwtSecret = await requireValue(io, "JWT secret", void 0, {
1263
+ secret: true
1264
+ });
1265
+ return {
1266
+ addonId,
1267
+ jwtIssuer,
1268
+ jwtSecret
1269
+ };
1270
+ }
1271
+ async function collectEdgeCredentials(deps) {
1272
+ const { io } = deps;
1273
+ io.log("Edge Partner Center needs the v1.1 API credentials:");
1274
+ io.log(" 1. Open the Partner Center dashboard: https://partner.microsoft.com/dashboard/microsoftedge/overview");
1275
+ io.log(" 2. Under your extension, go to Settings \u2192 API access \u2192 API v1.1 and generate credentials.");
1276
+ io.log(" 3. Copy the Client ID, API key, and Product ID.");
1277
+ io.log("");
1278
+ const productId = await requireValue(io, "Edge product ID");
1279
+ const clientId = await requireValue(io, "Edge client ID");
1280
+ const apiKey = await requireValue(io, "Edge API key", void 0, {
1281
+ secret: true
1282
+ });
1283
+ return {
1284
+ productId,
1285
+ clientId,
1286
+ apiKey
1287
+ };
1288
+ }
1289
+ const STORE_CHOICES = [
1290
+ {
1291
+ value: "chrome",
1292
+ label: "Chrome Web Store"
1293
+ },
1294
+ {
1295
+ value: "firefox",
1296
+ label: "Firefox AMO (addons.mozilla.org)"
1297
+ },
1298
+ {
1299
+ value: "edge",
1300
+ label: "Edge Add-ons (Partner Center)"
1301
+ }
1302
+ ];
1303
+ async function runInit(deps, options = {}) {
1304
+ const { io } = deps;
1305
+ io.log("");
1306
+ io.log(`${colors.green("\u23F5\u23F5\u23F5")} ${colors.blue("init")} ${colors.underline("extension-deploy \u2014 set up store credentials")}`);
1307
+ io.log("");
1308
+ io.log("This wizard walks you through creating credentials for each store and");
1309
+ io.log("writes them to a .env.submit file. Press Ctrl+C at any time to abort.");
1310
+ io.log("");
1311
+ const stores = options.stores ? options.stores : await io.multiselect("Which stores do you want to configure?", STORE_CHOICES);
1312
+ if (0 === stores.length) throw new Error("init: at least one store must be selected. Use --init-stores chrome,firefox,edge or make a selection.");
1313
+ const envLines = [
1314
+ "# Generated by `extension-deploy init`.",
1315
+ "# Do not commit secrets to source control.",
1316
+ ""
1036
1317
  ];
1037
1318
  const verified = {};
1038
1319
  for (const store of stores){
@@ -1041,7 +1322,7 @@ async function runInit(deps, options = {}) {
1041
1322
  io.log("");
1042
1323
  if ("chrome" === store) {
1043
1324
  const creds = await collectChromeCredentials(deps);
1044
- envLines.push(`CHROME_EXTENSION_ID=${creds.extensionId}`, `CHROME_CLIENT_ID=${creds.clientId}`, `CHROME_CLIENT_SECRET=${creds.clientSecret}`, `CHROME_REFRESH_TOKEN=${creds.refreshToken}`, "");
1325
+ envLines.push(`CHROME_EXTENSION_ID=${creds.extensionId}`, `CHROME_PUBLISHER_ID=${creds.publisherId}`, `CHROME_CLIENT_ID=${creds.clientId}`, `CHROME_CLIENT_SECRET=${creds.clientSecret}`, `CHROME_REFRESH_TOKEN=${creds.refreshToken}`, "");
1045
1326
  if (!options.skipVerify) {
1046
1327
  const verify = deps.verifyChrome ?? verifyChromeCredentials;
1047
1328
  verified.chrome = normalizeVerify(await verify(creds));
@@ -1109,121 +1390,6 @@ async function runInit(deps, options = {}) {
1109
1390
  wrote
1110
1391
  };
1111
1392
  }
1112
- async function collectChromeCredentials(deps) {
1113
- const { io } = deps;
1114
- io.log("Chrome Web Store needs OAuth credentials. You'll need:");
1115
- io.log(" 1. A Google Cloud project with the Chrome Web Store API enabled:");
1116
- io.log(" https://console.cloud.google.com/apis/library/chromewebstore.googleapis.com");
1117
- io.log(" 2. OAuth 2.0 credentials (application type: Desktop app):");
1118
- io.log(" https://console.cloud.google.com/apis/credentials");
1119
- io.log(" 3. Your extension ID from the Chrome Web Store Developer Dashboard.");
1120
- io.log("");
1121
- io.log("This wizard will take your Client ID + Secret and walk you through");
1122
- io.log("generating a refresh token automatically.");
1123
- io.log("");
1124
- const extensionId = await requireValue(io, "Chrome extension ID", "(32-character ID from the CWS dashboard URL)");
1125
- const clientId = await requireValue(io, "OAuth Client ID");
1126
- const clientSecret = await requireValue(io, "OAuth Client Secret", void 0, {
1127
- secret: true
1128
- });
1129
- const refreshToken = await obtainChromeRefreshToken(deps, clientId, clientSecret);
1130
- return {
1131
- extensionId,
1132
- clientId,
1133
- clientSecret,
1134
- refreshToken
1135
- };
1136
- }
1137
- function buildChromeAuthUrl(clientId) {
1138
- const params = new URLSearchParams({
1139
- response_type: "code",
1140
- client_id: clientId,
1141
- redirect_uri: CHROME_OOB_REDIRECT,
1142
- scope: CHROME_SCOPE,
1143
- access_type: "offline",
1144
- prompt: "consent"
1145
- });
1146
- return `${CHROME_OAUTH_AUTH_URL}?${params.toString()}`;
1147
- }
1148
- async function exchangeChromeAuthCode(input) {
1149
- const fetchFn = input.fetchImpl ?? fetch;
1150
- const body = new URLSearchParams({
1151
- code: input.authCode,
1152
- client_id: input.clientId,
1153
- client_secret: input.clientSecret,
1154
- redirect_uri: CHROME_OOB_REDIRECT,
1155
- grant_type: "authorization_code"
1156
- });
1157
- const res = await fetchFn(CHROME_OAUTH_TOKEN_URL, {
1158
- method: "POST",
1159
- headers: {
1160
- "Content-Type": "application/x-www-form-urlencoded"
1161
- },
1162
- body: body.toString()
1163
- });
1164
- if (!res.ok) {
1165
- const text = await res.text().catch(()=>"");
1166
- throw new Error(`Chrome token exchange failed (HTTP ${res.status}): ${text || res.statusText}`);
1167
- }
1168
- const json = await res.json();
1169
- if (!json.refresh_token) throw new Error(`Chrome token exchange returned no refresh_token${json.error ? `: ${json.error} \u{2014} ${json.error_description ?? ""}` : ""}. This usually means the authorization code was already used or expired; try the init wizard again.`);
1170
- return json.refresh_token;
1171
- }
1172
- async function obtainChromeRefreshToken(deps, clientId, clientSecret) {
1173
- const { io } = deps;
1174
- const authUrl = buildChromeAuthUrl(clientId);
1175
- io.log("");
1176
- io.log("Open the URL below in your browser, approve the scope, and paste the");
1177
- io.log("authorization code shown on the page:");
1178
- io.log("");
1179
- io.log(` ${authUrl}`);
1180
- io.log("");
1181
- const authCode = await requireValue(io, "Authorization code", void 0, {
1182
- secret: true
1183
- });
1184
- return exchangeChromeAuthCode({
1185
- clientId,
1186
- clientSecret,
1187
- authCode,
1188
- fetchImpl: deps.fetchImpl
1189
- });
1190
- }
1191
- async function collectFirefoxCredentials(deps) {
1192
- const { io } = deps;
1193
- io.log("Firefox AMO needs JWT API credentials:");
1194
- io.log(" 1. Sign in at https://addons.mozilla.org");
1195
- io.log(" 2. Create API credentials at https://addons.mozilla.org/developers/addon/api/key/");
1196
- io.log(" 3. Copy the JWT issuer and JWT secret (the secret is only shown once).");
1197
- io.log("");
1198
- const addonId = await requireValue(io, "Firefox addon ID", "({uuid} GUID or email-style ID from your addon page)");
1199
- const jwtIssuer = await requireValue(io, "JWT issuer");
1200
- const jwtSecret = await requireValue(io, "JWT secret", void 0, {
1201
- secret: true
1202
- });
1203
- return {
1204
- addonId,
1205
- jwtIssuer,
1206
- jwtSecret
1207
- };
1208
- }
1209
- async function collectEdgeCredentials(deps) {
1210
- const { io } = deps;
1211
- io.log("Edge Partner Center needs the v1.1 API credentials:");
1212
- io.log(" 1. Open the Partner Center dashboard: https://partner.microsoft.com/dashboard/microsoftedge/overview");
1213
- io.log(" 2. Under your extension, go to Settings \u2192 API access \u2192 API v1.1 and generate credentials.");
1214
- io.log(" 3. Copy the Client ID, API key, and Product ID.");
1215
- io.log("");
1216
- const productId = await requireValue(io, "Edge product ID");
1217
- const clientId = await requireValue(io, "Edge client ID");
1218
- const apiKey = await requireValue(io, "Edge API key", void 0, {
1219
- secret: true
1220
- });
1221
- return {
1222
- productId,
1223
- clientId,
1224
- apiKey
1225
- };
1226
- }
1227
1393
  function labelFor(store) {
1228
1394
  var _STORE_CHOICES_find;
1229
1395
  return (null == (_STORE_CHOICES_find = STORE_CHOICES.find((c)=>c.value === store)) ? void 0 : _STORE_CHOICES_find.label) ?? String(store);
@@ -1242,15 +1408,6 @@ function resolveEnvPath(cwd, outputPath) {
1242
1408
  const target = outputPath ?? ".env.submit";
1243
1409
  return external_node_path_default().isAbsolute(target) ? target : external_node_path_default().join(cwd, target);
1244
1410
  }
1245
- async function requireValue(io, label, hint, opts) {
1246
- const question = hint ? `${label} ${hint}:` : `${label}:`;
1247
- for(let attempt = 0; attempt < 3; attempt++){
1248
- const answer = (await io.prompt(question, opts)).trim();
1249
- if (answer) return answer;
1250
- io.log(` ${label} is required.`);
1251
- }
1252
- throw new Error(`init: ${label} is required.`);
1253
- }
1254
1411
  const external_node_readline_namespaceObject = require("node:readline");
1255
1412
  var external_node_readline_default = /*#__PURE__*/ __webpack_require__.n(external_node_readline_namespaceObject);
1256
1413
  function createStdinIO() {
@@ -1306,7 +1463,13 @@ function createStdinIO() {
1306
1463
  }
1307
1464
  const stdinFs = {
1308
1465
  async writeFile (filePath, content) {
1309
- await promises_default().writeFile(filePath, content, "utf-8");
1466
+ await promises_default().writeFile(filePath, content, {
1467
+ encoding: "utf-8",
1468
+ mode: 384
1469
+ });
1470
+ try {
1471
+ await promises_default().chmod(filePath, 384);
1472
+ } catch {}
1310
1473
  },
1311
1474
  async fileExists (filePath) {
1312
1475
  try {
@@ -1317,173 +1480,12 @@ const stdinFs = {
1317
1480
  }
1318
1481
  }
1319
1482
  };
1320
- function usage() {
1321
- return `
1322
- extension-deploy \u{2014} Deploy browser extensions to Chrome, Firefox, and Edge.
1323
-
1324
- USAGE
1325
- extension-deploy [options] submit to one or more stores
1326
- extension-deploy init [options] interactive credential setup wizard
1327
- extension-deploy watch --chrome [options] poll an in-flight submission
1328
- extension-deploy watch --firefox [options]
1329
- extension-deploy watch --edge [options]
1330
-
1331
- GLOBAL
1332
- --dry-run Verify auth without uploading or publishing
1333
- --output-json <path> Write the DeployResult / WatchEvent as JSON to <path>
1334
- --help Show this help message
1335
-
1336
- CHROME
1337
- --chrome-zip <path> Path to Chrome extension ZIP
1338
- --chrome-extension-id <id> Chrome extension ID
1339
- --chrome-client-id <id> OAuth2 client ID
1340
- --chrome-client-secret <secret> OAuth2 client secret
1341
- --chrome-refresh-token <token> OAuth2 refresh token
1342
- --chrome-publish-target <target> "default" or "trustedTesters"
1343
- --chrome-deploy-percentage <n> Staged rollout percentage (1-100)
1344
- --chrome-review-exemption Request expedited review
1345
- --chrome-skip-submit-review Upload only, skip publish
1346
-
1347
- FIREFOX
1348
- --firefox-zip <path> Path to Firefox extension ZIP
1349
- --firefox-sources-zip <path> Path to sources ZIP (optional)
1350
- --firefox-extension-id <id> Addon GUID or email-style ID
1351
- --firefox-jwt-issuer <issuer> AMO JWT issuer
1352
- --firefox-jwt-secret <secret> AMO JWT secret
1353
- --firefox-channel <channel> "listed" or "unlisted"
1354
-
1355
- EDGE
1356
- --edge-zip <path> Path to Edge extension ZIP
1357
- --edge-product-id <id> Partner Center product ID
1358
- --edge-client-id <id> Partner Center client ID
1359
- --edge-api-key <key> Partner Center API key (v1.1)
1360
- --edge-skip-submit-review Upload only, skip publish
1361
-
1362
- WATCH
1363
- --watch-submission-id <id> Required for firefox/edge (version id / operation id)
1364
- --watch-interval <seconds> Poll interval (default 60)
1365
- --watch-timeout <seconds> Max time to wait for a terminal state (default 3600)
1366
-
1367
- INIT
1368
- --init-stores <list> Comma-separated store list (skip picker)
1369
- --init-output <path> Destination file (default .env.submit)
1370
- --init-skip-verify Don't run verifyCredentials after collection
1371
- --init-force Overwrite existing .env.submit without asking
1372
-
1373
- ENVIRONMENT VARIABLES
1374
- All flags can be set via SCREAMING_SNAKE_CASE env vars (e.g. CHROME_ZIP,
1375
- FIREFOX_JWT_SECRET, EDGE_API_KEY). Flags take precedence over env vars.
1376
- A .env.submit file in the current directory is loaded automatically.
1377
- `.trim();
1378
- }
1379
- const BOOLEAN_FLAGS = new Set([
1380
- "help",
1381
- "dryRun",
1382
- "chromeReviewExemption",
1383
- "chromeSkipSubmitReview",
1384
- "edgeSkipSubmitReview",
1385
- "initSkipVerify",
1386
- "initForce"
1387
- ]);
1388
- const INT_FLAGS = new Set([
1389
- "chromeDeployPercentage",
1390
- "watchInterval",
1391
- "watchTimeout"
1392
- ]);
1393
- function parseArgs(argv) {
1394
- const flags = {};
1395
- let i = 0;
1396
- if ("watch" === argv[0]) {
1397
- flags.command = "watch";
1398
- i = 1;
1399
- } else if ("init" === argv[0]) {
1400
- flags.command = "init";
1401
- i = 1;
1402
- }
1403
- while(i < argv.length){
1404
- const arg = argv[i];
1405
- if ("--help" === arg || "-h" === arg) {
1406
- flags.help = true;
1407
- i++;
1408
- continue;
1409
- }
1410
- if ("--chrome" === arg) {
1411
- flags.watchStore = "chrome";
1412
- i++;
1413
- continue;
1414
- }
1415
- if ("--firefox" === arg) {
1416
- flags.watchStore = "firefox";
1417
- i++;
1418
- continue;
1419
- }
1420
- if ("--edge" === arg) {
1421
- flags.watchStore = "edge";
1422
- i++;
1423
- continue;
1424
- }
1425
- if (!(null == arg ? void 0 : arg.startsWith("--"))) {
1426
- i++;
1427
- continue;
1428
- }
1429
- const key = arg.slice(2).replace(/-([a-z])/g, (_, c)=>c.toUpperCase());
1430
- if (BOOLEAN_FLAGS.has(key)) {
1431
- flags[key] = true;
1432
- i++;
1433
- continue;
1434
- }
1435
- const next = argv[i + 1];
1436
- if (null == next || next.startsWith("--")) {
1437
- flags[key] = true;
1438
- i++;
1439
- continue;
1440
- }
1441
- flags[key] = INT_FLAGS.has(key) ? parseInt(next, 10) : next;
1442
- i += 2;
1443
- }
1444
- return flags;
1445
- }
1446
- function loadDotEnv() {
1447
- try {
1448
- const envPath = external_node_path_default().resolve(process.cwd(), ".env.submit");
1449
- const content = external_node_fs_default().readFileSync(envPath, "utf-8");
1450
- for (const line of content.split("\n")){
1451
- const trimmed = line.trim();
1452
- if (!trimmed || trimmed.startsWith("#")) continue;
1453
- const eqIdx = trimmed.indexOf("=");
1454
- if (-1 === eqIdx) continue;
1455
- const key = trimmed.slice(0, eqIdx).trim();
1456
- let value = trimmed.slice(eqIdx + 1).trim();
1457
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
1458
- if (!(key in process.env)) process.env[key] = value;
1459
- }
1460
- } catch {}
1461
- }
1462
1483
  function writeJsonOutput(filePath, payload) {
1463
1484
  external_node_fs_default().mkdirSync(external_node_path_default().dirname(external_node_path_default().resolve(filePath)), {
1464
1485
  recursive: true
1465
1486
  });
1466
1487
  external_node_fs_default().writeFileSync(filePath, JSON.stringify(payload, null, 2) + "\n", "utf-8");
1467
1488
  }
1468
- async function main() {
1469
- loadDotEnv();
1470
- const flags = parseArgs(process.argv.slice(2));
1471
- if (flags.help) {
1472
- console.log(usage());
1473
- process.exit(0);
1474
- }
1475
- if ("watch" === flags.command) return void await runWatch(flags);
1476
- if ("init" === flags.command) return void await runInitCommand(flags);
1477
- const config = resolveConfig(flags);
1478
- try {
1479
- const result = await deploy(config);
1480
- if (flags.outputJson) writeJsonOutput(flags.outputJson, result);
1481
- if (!result.success) process.exit(1);
1482
- } catch (err) {
1483
- logError("deploy", err instanceof Error ? err.message : String(err));
1484
- process.exit(1);
1485
- }
1486
- }
1487
1489
  async function runWatch(flags) {
1488
1490
  const store = flags.watchStore;
1489
1491
  if (!store) {
@@ -1506,7 +1508,8 @@ async function runWatch(flags) {
1506
1508
  clientId: mustEnv("CHROME_CLIENT_ID", flags.chromeClientId),
1507
1509
  clientSecret: mustEnv("CHROME_CLIENT_SECRET", flags.chromeClientSecret),
1508
1510
  refreshToken: mustEnv("CHROME_REFRESH_TOKEN", flags.chromeRefreshToken),
1509
- extensionId: mustEnv("CHROME_EXTENSION_ID", flags.chromeExtensionId)
1511
+ extensionId: mustEnv("CHROME_EXTENSION_ID", flags.chromeExtensionId),
1512
+ publisherId: mustEnv("CHROME_PUBLISHER_ID", flags.chromePublisherId)
1510
1513
  }, {
1511
1514
  pollIntervalMs,
1512
1515
  timeoutMs,
@@ -1579,6 +1582,41 @@ function parseInitStores(raw) {
1579
1582
  if (0 === parsed.length) throw new Error(`--init-stores: expected a comma-separated list of chrome,firefox,edge (got "${raw}")`);
1580
1583
  return parsed;
1581
1584
  }
1585
+ function loadDotEnv() {
1586
+ try {
1587
+ const envPath = external_node_path_default().resolve(process.cwd(), ".env.submit");
1588
+ const content = external_node_fs_default().readFileSync(envPath, "utf-8");
1589
+ for (const line of content.split("\n")){
1590
+ const trimmed = line.trim();
1591
+ if (!trimmed || trimmed.startsWith("#")) continue;
1592
+ const eqIdx = trimmed.indexOf("=");
1593
+ if (-1 === eqIdx) continue;
1594
+ const key = trimmed.slice(0, eqIdx).trim();
1595
+ let value = trimmed.slice(eqIdx + 1).trim();
1596
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
1597
+ if (!(key in process.env)) process.env[key] = value;
1598
+ }
1599
+ } catch {}
1600
+ }
1601
+ async function main() {
1602
+ loadDotEnv();
1603
+ const flags = parseArgs(process.argv.slice(2));
1604
+ if (flags.help) {
1605
+ console.log(usage());
1606
+ process.exit(0);
1607
+ }
1608
+ if ("watch" === flags.command) return void await runWatch(flags);
1609
+ if ("init" === flags.command) return void await runInitCommand(flags);
1610
+ const config = resolveConfig(flags);
1611
+ try {
1612
+ const result = await deploy(config);
1613
+ if (flags.outputJson) writeJsonOutput(flags.outputJson, result);
1614
+ if (!result.success) process.exit(1);
1615
+ } catch (err) {
1616
+ logError("deploy", err instanceof Error ? err.message : String(err));
1617
+ process.exit(1);
1618
+ }
1619
+ }
1582
1620
  exports.chromeOptionsSchema = __webpack_exports__.chromeOptionsSchema;
1583
1621
  exports.deploy = __webpack_exports__.deploy;
1584
1622
  exports.deployConfigSchema = __webpack_exports__.deployConfigSchema;