@extension.dev/deploy 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -22,8 +22,38 @@ Deploy browser extensions to Chrome Web Store, Firefox AMO, and Edge Partner Cen
22
22
  npm install @extension.dev/deploy
23
23
  ```
24
24
 
25
+ ## Quick start
26
+
27
+ If you're setting up a new project, run the interactive wizard. It walks you
28
+ through each store's credential acquisition (including auto-generating a
29
+ Chrome refresh token from your OAuth client), runs a verification check
30
+ against each store's API, and writes everything to a `.env.submit` file:
31
+
32
+ ```bash
33
+ npx extension-deploy init
34
+ ```
35
+
36
+ Non-interactive use (e.g. CI setup scripts):
37
+
38
+ ```bash
39
+ npx extension-deploy init \
40
+ --init-stores chrome,firefox \
41
+ --init-output config/.env.submit \
42
+ --init-skip-verify \
43
+ --init-force
44
+ ```
45
+
46
+ After `init` has written `.env.submit`, a full submission is a one-liner:
47
+
48
+ ```bash
49
+ npx extension-deploy --dry-run # validate creds and ZIPs
50
+ npx extension-deploy # actually submit
51
+ ```
52
+
25
53
  ## CLI usage
26
54
 
55
+ If you'd rather skip the wizard and pass credentials directly:
56
+
27
57
  ```bash
28
58
  npx extension-deploy \
29
59
  --chrome-zip dist/chrome.zip \
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=init.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.test.d.ts","sourceRoot":"","sources":["../../__tests__/init.test.ts"],"names":[],"mappings":""}
package/dist/module.js CHANGED
@@ -33,7 +33,8 @@ var __webpack_require__ = {};
33
33
  var __webpack_exports__ = {};
34
34
  __webpack_require__.r(__webpack_exports__);
35
35
  __webpack_require__.d(__webpack_exports__, {
36
- getEdgeSubmissionEvent: ()=>getEdgeSubmissionEvent,
36
+ watchChromeSubmission: ()=>watchChromeSubmission,
37
+ main: ()=>main,
37
38
  resolveConfig: ()=>resolveConfig,
38
39
  chromeOptionsSchema: ()=>chromeOptionsSchema,
39
40
  getFirefoxSubmissionEvent: ()=>getFirefoxSubmissionEvent,
@@ -48,8 +49,12 @@ __webpack_require__.d(__webpack_exports__, {
48
49
  verifyFirefoxCredentials: ()=>verifyFirefoxCredentials,
49
50
  verifyEdgeCredentials: ()=>verifyEdgeCredentials,
50
51
  verifyChromeCredentials: ()=>verifyChromeCredentials,
51
- watchChromeSubmission: ()=>watchChromeSubmission
52
+ getEdgeSubmissionEvent: ()=>getEdgeSubmissionEvent
52
53
  });
54
+ const external_node_fs_namespaceObject = require("node:fs");
55
+ var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
56
+ const external_node_path_namespaceObject = require("node:path");
57
+ var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
53
58
  const external_zod_namespaceObject = require("zod");
54
59
  const chromeOptionsSchema = external_zod_namespaceObject.z.object({
55
60
  zip: external_zod_namespaceObject.z.string().min(1),
@@ -937,6 +942,580 @@ async function runWatchLoop(_store, probe, options) {
937
942
  await delay(pollInterval);
938
943
  }
939
944
  }
945
+ const STORE_CHOICES = [
946
+ {
947
+ value: "chrome",
948
+ label: "Chrome Web Store"
949
+ },
950
+ {
951
+ value: "firefox",
952
+ label: "Firefox AMO (addons.mozilla.org)"
953
+ },
954
+ {
955
+ value: "edge",
956
+ label: "Edge Add-ons (Partner Center)"
957
+ }
958
+ ];
959
+ const CHROME_OAUTH_TOKEN_URL = "https://oauth2.googleapis.com/token";
960
+ const CHROME_OAUTH_AUTH_URL = "https://accounts.google.com/o/oauth2/auth";
961
+ const CHROME_OOB_REDIRECT = "urn:ietf:wg:oauth:2.0:oob";
962
+ const CHROME_SCOPE = "https://www.googleapis.com/auth/chromewebstore";
963
+ async function runInit(deps, options = {}) {
964
+ const { io } = deps;
965
+ io.log("");
966
+ io.log("extension-deploy init \u2014 set up store credentials");
967
+ io.log("");
968
+ io.log("This wizard walks you through creating credentials for each store and");
969
+ io.log("writes them to a .env.submit file. Press Ctrl+C at any time to abort.");
970
+ io.log("");
971
+ const stores = options.stores ? options.stores : await io.multiselect("Which stores do you want to configure?", STORE_CHOICES);
972
+ 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.");
973
+ const envLines = [
974
+ "# Generated by `extension-deploy init`.",
975
+ "# Do not commit secrets to source control.",
976
+ ""
977
+ ];
978
+ const verified = {};
979
+ for (const store of stores){
980
+ io.log("");
981
+ io.log(`=== ${labelFor(store)} ===`);
982
+ io.log("");
983
+ if ("chrome" === store) {
984
+ const creds = await collectChromeCredentials(deps);
985
+ envLines.push(`CHROME_EXTENSION_ID=${creds.extensionId}`, `CHROME_CLIENT_ID=${creds.clientId}`, `CHROME_CLIENT_SECRET=${creds.clientSecret}`, `CHROME_REFRESH_TOKEN=${creds.refreshToken}`, "");
986
+ if (!options.skipVerify) {
987
+ const verify = deps.verifyChrome ?? verifyChromeCredentials;
988
+ verified.chrome = normalizeVerify(await verify(creds));
989
+ logVerify(io, "chrome", verified.chrome);
990
+ }
991
+ } else if ("firefox" === store) {
992
+ const creds = await collectFirefoxCredentials(deps);
993
+ envLines.push(`FIREFOX_EXTENSION_ID=${creds.addonId}`, `FIREFOX_JWT_ISSUER=${creds.jwtIssuer}`, `FIREFOX_JWT_SECRET=${creds.jwtSecret}`, "");
994
+ if (!options.skipVerify) {
995
+ const verify = deps.verifyFirefox ?? verifyFirefoxCredentials;
996
+ verified.firefox = normalizeVerify(await verify({
997
+ jwtIssuer: creds.jwtIssuer,
998
+ jwtSecret: creds.jwtSecret,
999
+ addonId: creds.addonId
1000
+ }));
1001
+ logVerify(io, "firefox", verified.firefox);
1002
+ }
1003
+ } else {
1004
+ const creds = await collectEdgeCredentials(deps);
1005
+ envLines.push(`EDGE_PRODUCT_ID=${creds.productId}`, `EDGE_CLIENT_ID=${creds.clientId}`, `EDGE_API_KEY=${creds.apiKey}`, "");
1006
+ if (!options.skipVerify) {
1007
+ const verify = deps.verifyEdge ?? verifyEdgeCredentials;
1008
+ verified.edge = normalizeVerify(await verify({
1009
+ clientId: creds.clientId,
1010
+ apiKey: creds.apiKey,
1011
+ productId: creds.productId
1012
+ }));
1013
+ logVerify(io, "edge", verified.edge);
1014
+ }
1015
+ }
1016
+ }
1017
+ const envContent = envLines.join("\n");
1018
+ const envPath = resolveEnvPath(deps.cwd, options.outputPath);
1019
+ const exists = await deps.fileExists(envPath);
1020
+ let wrote = false;
1021
+ if (exists && !options.force) {
1022
+ const overwrite = await deps.io.confirm(`${envPath} already exists. Overwrite?`, false);
1023
+ if (overwrite) {
1024
+ await deps.writeFile(envPath, envContent);
1025
+ wrote = true;
1026
+ } else {
1027
+ io.log("");
1028
+ io.log(`Skipped writing ${envPath}. Credential block:`);
1029
+ io.log("");
1030
+ io.log(envContent);
1031
+ }
1032
+ } else {
1033
+ await deps.writeFile(envPath, envContent);
1034
+ wrote = true;
1035
+ }
1036
+ if (wrote) {
1037
+ io.log("");
1038
+ io.log(`Wrote ${envPath}`);
1039
+ io.log("");
1040
+ io.log("Next steps:");
1041
+ io.log(" - Add .env.submit to .gitignore if it isn't already (it contains secrets).");
1042
+ io.log(" - Run `extension-deploy --dry-run` to sanity-check the full submit flow.");
1043
+ io.log("");
1044
+ }
1045
+ return {
1046
+ stores,
1047
+ envPath,
1048
+ envContent,
1049
+ verified,
1050
+ wrote
1051
+ };
1052
+ }
1053
+ async function collectChromeCredentials(deps) {
1054
+ const { io } = deps;
1055
+ io.log("Chrome Web Store needs OAuth credentials. You'll need:");
1056
+ io.log(" 1. A Google Cloud project with the Chrome Web Store API enabled:");
1057
+ io.log(" https://console.cloud.google.com/apis/library/chromewebstore.googleapis.com");
1058
+ io.log(" 2. OAuth 2.0 credentials (application type: Desktop app):");
1059
+ io.log(" https://console.cloud.google.com/apis/credentials");
1060
+ io.log(" 3. Your extension ID from the Chrome Web Store Developer Dashboard.");
1061
+ io.log("");
1062
+ io.log("This wizard will take your Client ID + Secret and walk you through");
1063
+ io.log("generating a refresh token automatically.");
1064
+ io.log("");
1065
+ const extensionId = await requireValue(io, "Chrome extension ID", "(32-character ID from the CWS dashboard URL)");
1066
+ const clientId = await requireValue(io, "OAuth Client ID");
1067
+ const clientSecret = await requireValue(io, "OAuth Client Secret", void 0, {
1068
+ secret: true
1069
+ });
1070
+ const refreshToken = await obtainChromeRefreshToken(deps, clientId, clientSecret);
1071
+ return {
1072
+ extensionId,
1073
+ clientId,
1074
+ clientSecret,
1075
+ refreshToken
1076
+ };
1077
+ }
1078
+ function buildChromeAuthUrl(clientId) {
1079
+ const params = new URLSearchParams({
1080
+ response_type: "code",
1081
+ client_id: clientId,
1082
+ redirect_uri: CHROME_OOB_REDIRECT,
1083
+ scope: CHROME_SCOPE,
1084
+ access_type: "offline",
1085
+ prompt: "consent"
1086
+ });
1087
+ return `${CHROME_OAUTH_AUTH_URL}?${params.toString()}`;
1088
+ }
1089
+ async function exchangeChromeAuthCode(input) {
1090
+ const fetchFn = input.fetchImpl ?? fetch;
1091
+ const body = new URLSearchParams({
1092
+ code: input.authCode,
1093
+ client_id: input.clientId,
1094
+ client_secret: input.clientSecret,
1095
+ redirect_uri: CHROME_OOB_REDIRECT,
1096
+ grant_type: "authorization_code"
1097
+ });
1098
+ const res = await fetchFn(CHROME_OAUTH_TOKEN_URL, {
1099
+ method: "POST",
1100
+ headers: {
1101
+ "Content-Type": "application/x-www-form-urlencoded"
1102
+ },
1103
+ body: body.toString()
1104
+ });
1105
+ if (!res.ok) {
1106
+ const text = await res.text().catch(()=>"");
1107
+ throw new Error(`Chrome token exchange failed (HTTP ${res.status}): ${text || res.statusText}`);
1108
+ }
1109
+ const json = await res.json();
1110
+ 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.`);
1111
+ return json.refresh_token;
1112
+ }
1113
+ async function obtainChromeRefreshToken(deps, clientId, clientSecret) {
1114
+ const { io } = deps;
1115
+ const authUrl = buildChromeAuthUrl(clientId);
1116
+ io.log("");
1117
+ io.log("Open the URL below in your browser, approve the scope, and paste the");
1118
+ io.log("authorization code shown on the page:");
1119
+ io.log("");
1120
+ io.log(` ${authUrl}`);
1121
+ io.log("");
1122
+ const authCode = await requireValue(io, "Authorization code", void 0, {
1123
+ secret: true
1124
+ });
1125
+ return exchangeChromeAuthCode({
1126
+ clientId,
1127
+ clientSecret,
1128
+ authCode,
1129
+ fetchImpl: deps.fetchImpl
1130
+ });
1131
+ }
1132
+ async function collectFirefoxCredentials(deps) {
1133
+ const { io } = deps;
1134
+ io.log("Firefox AMO needs JWT API credentials:");
1135
+ io.log(" 1. Sign in at https://addons.mozilla.org");
1136
+ io.log(" 2. Create API credentials at https://addons.mozilla.org/developers/addon/api/key/");
1137
+ io.log(" 3. Copy the JWT issuer and JWT secret (the secret is only shown once).");
1138
+ io.log("");
1139
+ const addonId = await requireValue(io, "Firefox addon ID", "({uuid} GUID or email-style ID from your addon page)");
1140
+ const jwtIssuer = await requireValue(io, "JWT issuer");
1141
+ const jwtSecret = await requireValue(io, "JWT secret", void 0, {
1142
+ secret: true
1143
+ });
1144
+ return {
1145
+ addonId,
1146
+ jwtIssuer,
1147
+ jwtSecret
1148
+ };
1149
+ }
1150
+ async function collectEdgeCredentials(deps) {
1151
+ const { io } = deps;
1152
+ io.log("Edge Partner Center needs the v1.1 API credentials:");
1153
+ io.log(" 1. Open the Partner Center dashboard: https://partner.microsoft.com/dashboard/microsoftedge/overview");
1154
+ io.log(" 2. Under your extension, go to Settings \u2192 API access \u2192 API v1.1 and generate credentials.");
1155
+ io.log(" 3. Copy the Client ID, API key, and Product ID.");
1156
+ io.log("");
1157
+ const productId = await requireValue(io, "Edge product ID");
1158
+ const clientId = await requireValue(io, "Edge client ID");
1159
+ const apiKey = await requireValue(io, "Edge API key", void 0, {
1160
+ secret: true
1161
+ });
1162
+ return {
1163
+ productId,
1164
+ clientId,
1165
+ apiKey
1166
+ };
1167
+ }
1168
+ function labelFor(store) {
1169
+ var _STORE_CHOICES_find;
1170
+ return (null == (_STORE_CHOICES_find = STORE_CHOICES.find((c)=>c.value === store)) ? void 0 : _STORE_CHOICES_find.label) ?? String(store);
1171
+ }
1172
+ function normalizeVerify(result) {
1173
+ return {
1174
+ ok: result.ok,
1175
+ message: result.message
1176
+ };
1177
+ }
1178
+ function logVerify(io, store, record) {
1179
+ const marker = record.ok ? "[ok]" : "[fail]";
1180
+ io.log(` ${marker} verify ${store}: ${record.message}`);
1181
+ }
1182
+ function resolveEnvPath(cwd, outputPath) {
1183
+ const target = outputPath ?? ".env.submit";
1184
+ return external_node_path_default().isAbsolute(target) ? target : external_node_path_default().join(cwd, target);
1185
+ }
1186
+ async function requireValue(io, label, hint, opts) {
1187
+ const question = hint ? `${label} ${hint}:` : `${label}:`;
1188
+ for(let attempt = 0; attempt < 3; attempt++){
1189
+ const answer = (await io.prompt(question, opts)).trim();
1190
+ if (answer) return answer;
1191
+ io.log(` ${label} is required.`);
1192
+ }
1193
+ throw new Error(`init: ${label} is required.`);
1194
+ }
1195
+ const external_node_readline_namespaceObject = require("node:readline");
1196
+ var external_node_readline_default = /*#__PURE__*/ __webpack_require__.n(external_node_readline_namespaceObject);
1197
+ function createStdinIO() {
1198
+ const rl = external_node_readline_default().createInterface({
1199
+ input: process.stdin,
1200
+ output: process.stdout,
1201
+ terminal: false
1202
+ });
1203
+ const lineIter = rl[Symbol.asyncIterator]();
1204
+ async function readLine() {
1205
+ const { value, done } = await lineIter.next();
1206
+ if (done) return "";
1207
+ return value;
1208
+ }
1209
+ function write(chunk) {
1210
+ process.stdout.write(chunk);
1211
+ }
1212
+ return {
1213
+ async prompt (question, opts) {
1214
+ const suffix = (null == opts ? void 0 : opts.default) ? ` [${opts.default}]` : "";
1215
+ const secretNote = (null == opts ? void 0 : opts.secret) ? " (input visible)" : "";
1216
+ write(`${question}${suffix}${secretNote} `);
1217
+ const answer = await readLine();
1218
+ if (0 === answer.length && (null == opts ? void 0 : opts.default)) return opts.default;
1219
+ return answer;
1220
+ },
1221
+ async confirm (question, defaultValue = false) {
1222
+ const suffix = defaultValue ? "[Y/n]" : "[y/N]";
1223
+ write(`${question} ${suffix} `);
1224
+ const raw = (await readLine()).trim().toLowerCase();
1225
+ if ("" === raw) return defaultValue;
1226
+ return "y" === raw || "yes" === raw;
1227
+ },
1228
+ async multiselect (question, choices) {
1229
+ write(`${question}\n`);
1230
+ choices.forEach((choice, idx)=>{
1231
+ write(` ${idx + 1}) ${choice.label}\n`);
1232
+ });
1233
+ write(' Enter comma-separated numbers (e.g. 1,2 or "all"): ');
1234
+ const raw = (await readLine()).trim().toLowerCase();
1235
+ if ("all" === raw) return choices.map((c)=>c.value);
1236
+ const indices = raw.split(/[,\s]+/).map((token)=>Number.parseInt(token, 10)).filter((n)=>Number.isInteger(n) && n >= 1 && n <= choices.length);
1237
+ const unique = Array.from(new Set(indices));
1238
+ return unique.map((i)=>choices[i - 1].value);
1239
+ },
1240
+ log (message) {
1241
+ write(`${message}\n`);
1242
+ },
1243
+ dispose () {
1244
+ rl.close();
1245
+ }
1246
+ };
1247
+ }
1248
+ const stdinFs = {
1249
+ async writeFile (filePath, content) {
1250
+ await promises_default().writeFile(filePath, content, "utf-8");
1251
+ },
1252
+ async fileExists (filePath) {
1253
+ try {
1254
+ await promises_default().access(filePath);
1255
+ return true;
1256
+ } catch {
1257
+ return false;
1258
+ }
1259
+ }
1260
+ };
1261
+ function usage() {
1262
+ return `
1263
+ extension-deploy \u{2014} Deploy browser extensions to Chrome, Firefox, and Edge.
1264
+
1265
+ USAGE
1266
+ extension-deploy [options] submit to one or more stores
1267
+ extension-deploy init [options] interactive credential setup wizard
1268
+ extension-deploy watch --chrome [options] poll an in-flight submission
1269
+ extension-deploy watch --firefox [options]
1270
+ extension-deploy watch --edge [options]
1271
+
1272
+ GLOBAL
1273
+ --dry-run Verify auth without uploading or publishing
1274
+ --output-json <path> Write the DeployResult / WatchEvent as JSON to <path>
1275
+ --help Show this help message
1276
+
1277
+ CHROME
1278
+ --chrome-zip <path> Path to Chrome extension ZIP
1279
+ --chrome-extension-id <id> Chrome extension ID
1280
+ --chrome-client-id <id> OAuth2 client ID
1281
+ --chrome-client-secret <secret> OAuth2 client secret
1282
+ --chrome-refresh-token <token> OAuth2 refresh token
1283
+ --chrome-publish-target <target> "default" or "trustedTesters"
1284
+ --chrome-deploy-percentage <n> Staged rollout percentage (1-100)
1285
+ --chrome-review-exemption Request expedited review
1286
+ --chrome-skip-submit-review Upload only, skip publish
1287
+
1288
+ FIREFOX
1289
+ --firefox-zip <path> Path to Firefox extension ZIP
1290
+ --firefox-sources-zip <path> Path to sources ZIP (optional)
1291
+ --firefox-extension-id <id> Addon GUID or email-style ID
1292
+ --firefox-jwt-issuer <issuer> AMO JWT issuer
1293
+ --firefox-jwt-secret <secret> AMO JWT secret
1294
+ --firefox-channel <channel> "listed" or "unlisted"
1295
+
1296
+ EDGE
1297
+ --edge-zip <path> Path to Edge extension ZIP
1298
+ --edge-product-id <id> Partner Center product ID
1299
+ --edge-client-id <id> Partner Center client ID
1300
+ --edge-api-key <key> Partner Center API key (v1.1)
1301
+ --edge-skip-submit-review Upload only, skip publish
1302
+
1303
+ WATCH
1304
+ --watch-submission-id <id> Required for firefox/edge (version id / operation id)
1305
+ --watch-interval <seconds> Poll interval (default 60)
1306
+ --watch-timeout <seconds> Max time to wait for a terminal state (default 3600)
1307
+
1308
+ INIT
1309
+ --init-stores <list> Comma-separated store list (skip picker)
1310
+ --init-output <path> Destination file (default .env.submit)
1311
+ --init-skip-verify Don't run verifyCredentials after collection
1312
+ --init-force Overwrite existing .env.submit without asking
1313
+
1314
+ ENVIRONMENT VARIABLES
1315
+ All flags can be set via SCREAMING_SNAKE_CASE env vars (e.g. CHROME_ZIP,
1316
+ FIREFOX_JWT_SECRET, EDGE_API_KEY). Flags take precedence over env vars.
1317
+ A .env.submit file in the current directory is loaded automatically.
1318
+ `.trim();
1319
+ }
1320
+ const BOOLEAN_FLAGS = new Set([
1321
+ "help",
1322
+ "dryRun",
1323
+ "chromeReviewExemption",
1324
+ "chromeSkipSubmitReview",
1325
+ "edgeSkipSubmitReview",
1326
+ "initSkipVerify",
1327
+ "initForce"
1328
+ ]);
1329
+ const INT_FLAGS = new Set([
1330
+ "chromeDeployPercentage",
1331
+ "watchInterval",
1332
+ "watchTimeout"
1333
+ ]);
1334
+ function parseArgs(argv) {
1335
+ const flags = {};
1336
+ let i = 0;
1337
+ if ("watch" === argv[0]) {
1338
+ flags.command = "watch";
1339
+ i = 1;
1340
+ } else if ("init" === argv[0]) {
1341
+ flags.command = "init";
1342
+ i = 1;
1343
+ }
1344
+ while(i < argv.length){
1345
+ const arg = argv[i];
1346
+ if ("--help" === arg || "-h" === arg) {
1347
+ flags.help = true;
1348
+ i++;
1349
+ continue;
1350
+ }
1351
+ if ("--chrome" === arg) {
1352
+ flags.watchStore = "chrome";
1353
+ i++;
1354
+ continue;
1355
+ }
1356
+ if ("--firefox" === arg) {
1357
+ flags.watchStore = "firefox";
1358
+ i++;
1359
+ continue;
1360
+ }
1361
+ if ("--edge" === arg) {
1362
+ flags.watchStore = "edge";
1363
+ i++;
1364
+ continue;
1365
+ }
1366
+ if (!(null == arg ? void 0 : arg.startsWith("--"))) {
1367
+ i++;
1368
+ continue;
1369
+ }
1370
+ const key = arg.slice(2).replace(/-([a-z])/g, (_, c)=>c.toUpperCase());
1371
+ if (BOOLEAN_FLAGS.has(key)) {
1372
+ flags[key] = true;
1373
+ i++;
1374
+ continue;
1375
+ }
1376
+ const next = argv[i + 1];
1377
+ if (null == next || next.startsWith("--")) {
1378
+ flags[key] = true;
1379
+ i++;
1380
+ continue;
1381
+ }
1382
+ flags[key] = INT_FLAGS.has(key) ? parseInt(next, 10) : next;
1383
+ i += 2;
1384
+ }
1385
+ return flags;
1386
+ }
1387
+ function loadDotEnv() {
1388
+ try {
1389
+ const envPath = external_node_path_default().resolve(process.cwd(), ".env.submit");
1390
+ const content = external_node_fs_default().readFileSync(envPath, "utf-8");
1391
+ for (const line of content.split("\n")){
1392
+ const trimmed = line.trim();
1393
+ if (!trimmed || trimmed.startsWith("#")) continue;
1394
+ const eqIdx = trimmed.indexOf("=");
1395
+ if (-1 === eqIdx) continue;
1396
+ const key = trimmed.slice(0, eqIdx).trim();
1397
+ let value = trimmed.slice(eqIdx + 1).trim();
1398
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
1399
+ if (!(key in process.env)) process.env[key] = value;
1400
+ }
1401
+ } catch {}
1402
+ }
1403
+ function writeJsonOutput(filePath, payload) {
1404
+ external_node_fs_default().mkdirSync(external_node_path_default().dirname(external_node_path_default().resolve(filePath)), {
1405
+ recursive: true
1406
+ });
1407
+ external_node_fs_default().writeFileSync(filePath, JSON.stringify(payload, null, 2) + "\n", "utf-8");
1408
+ }
1409
+ async function main() {
1410
+ loadDotEnv();
1411
+ const flags = parseArgs(process.argv.slice(2));
1412
+ if (flags.help) {
1413
+ console.log(usage());
1414
+ process.exit(0);
1415
+ }
1416
+ if ("watch" === flags.command) return void await runWatch(flags);
1417
+ if ("init" === flags.command) return void await runInitCommand(flags);
1418
+ const config = resolveConfig(flags);
1419
+ try {
1420
+ const result = await deploy(config);
1421
+ if (flags.outputJson) writeJsonOutput(flags.outputJson, result);
1422
+ if (!result.success) process.exit(1);
1423
+ } catch (err) {
1424
+ console.error(err instanceof Error ? err.message : err);
1425
+ process.exit(1);
1426
+ }
1427
+ }
1428
+ async function runWatch(flags) {
1429
+ const store = flags.watchStore;
1430
+ if (!store) {
1431
+ console.error("watch: one of --chrome, --firefox, or --edge must be provided.");
1432
+ process.exit(2);
1433
+ }
1434
+ const onEvent = (event)=>{
1435
+ const line = `[watch:${event.store}] ${event.status}${event.nativeStatus ? ` (${event.nativeStatus})` : ""}${event.message ? ` \u{2014} ${event.message}` : ""}`;
1436
+ console.log(line);
1437
+ };
1438
+ const pollIntervalMs = (flags.watchInterval ?? 60) * 1000;
1439
+ const timeoutMs = (flags.watchTimeout ?? 3600) * 1000;
1440
+ let terminal;
1441
+ try {
1442
+ terminal = "chrome" === store ? await watchChromeSubmission({
1443
+ clientId: mustEnv("CHROME_CLIENT_ID", flags.chromeClientId),
1444
+ clientSecret: mustEnv("CHROME_CLIENT_SECRET", flags.chromeClientSecret),
1445
+ refreshToken: mustEnv("CHROME_REFRESH_TOKEN", flags.chromeRefreshToken),
1446
+ extensionId: mustEnv("CHROME_EXTENSION_ID", flags.chromeExtensionId)
1447
+ }, {
1448
+ pollIntervalMs,
1449
+ timeoutMs,
1450
+ onEvent
1451
+ }) : "firefox" === store ? await watchFirefoxSubmission({
1452
+ jwtIssuer: mustEnv("FIREFOX_JWT_ISSUER", flags.firefoxJwtIssuer),
1453
+ jwtSecret: mustEnv("FIREFOX_JWT_SECRET", flags.firefoxJwtSecret),
1454
+ extensionId: mustEnv("FIREFOX_EXTENSION_ID", flags.firefoxExtensionId),
1455
+ versionId: mustValue("--watch-submission-id", flags.watchSubmissionId)
1456
+ }, {
1457
+ pollIntervalMs,
1458
+ timeoutMs,
1459
+ onEvent
1460
+ }) : await watchEdgeSubmission({
1461
+ clientId: mustEnv("EDGE_CLIENT_ID", flags.edgeClientId),
1462
+ apiKey: mustEnv("EDGE_API_KEY", flags.edgeApiKey),
1463
+ productId: mustEnv("EDGE_PRODUCT_ID", flags.edgeProductId),
1464
+ operationId: mustValue("--watch-submission-id", flags.watchSubmissionId)
1465
+ }, {
1466
+ pollIntervalMs,
1467
+ timeoutMs,
1468
+ onEvent
1469
+ });
1470
+ } catch (err) {
1471
+ console.error(err instanceof Error ? err.message : err);
1472
+ process.exit(1);
1473
+ }
1474
+ if (flags.outputJson) writeJsonOutput(flags.outputJson, terminal);
1475
+ if ("failed" === terminal.status || "rejected" === terminal.status) process.exit(1);
1476
+ }
1477
+ function mustEnv(envName, flagValue) {
1478
+ const v = flagValue ?? process.env[envName];
1479
+ if (!v) throw new Error(`Missing required credential: ${envName}. Set the env var or pass the matching flag.`);
1480
+ return v;
1481
+ }
1482
+ function mustValue(flagName, value) {
1483
+ if (!value) throw new Error(`Missing required flag: ${flagName}`);
1484
+ return value;
1485
+ }
1486
+ async function runInitCommand(flags) {
1487
+ const stores = parseInitStores(flags.initStores);
1488
+ const io = createStdinIO();
1489
+ try {
1490
+ await runInit({
1491
+ io,
1492
+ cwd: process.cwd(),
1493
+ writeFile: stdinFs.writeFile,
1494
+ fileExists: stdinFs.fileExists
1495
+ }, {
1496
+ stores,
1497
+ outputPath: flags.initOutput,
1498
+ skipVerify: flags.initSkipVerify,
1499
+ force: flags.initForce
1500
+ });
1501
+ } catch (err) {
1502
+ console.error(err instanceof Error ? err.message : err);
1503
+ process.exit(1);
1504
+ } finally{
1505
+ io.dispose();
1506
+ }
1507
+ }
1508
+ function parseInitStores(raw) {
1509
+ if (!raw) return;
1510
+ const allowed = [
1511
+ "chrome",
1512
+ "firefox",
1513
+ "edge"
1514
+ ];
1515
+ const parsed = raw.split(",").map((s)=>s.trim().toLowerCase()).filter((s)=>allowed.includes(s));
1516
+ if (0 === parsed.length) throw new Error(`--init-stores: expected a comma-separated list of chrome,firefox,edge (got "${raw}")`);
1517
+ return parsed;
1518
+ }
940
1519
  exports.chromeOptionsSchema = __webpack_exports__.chromeOptionsSchema;
941
1520
  exports.deploy = __webpack_exports__.deploy;
942
1521
  exports.deployConfigSchema = __webpack_exports__.deployConfigSchema;
@@ -945,6 +1524,7 @@ exports.firefoxOptionsSchema = __webpack_exports__.firefoxOptionsSchema;
945
1524
  exports.getChromeSubmissionEvent = __webpack_exports__.getChromeSubmissionEvent;
946
1525
  exports.getEdgeSubmissionEvent = __webpack_exports__.getEdgeSubmissionEvent;
947
1526
  exports.getFirefoxSubmissionEvent = __webpack_exports__.getFirefoxSubmissionEvent;
1527
+ exports.main = __webpack_exports__.main;
948
1528
  exports.resolveConfig = __webpack_exports__.resolveConfig;
949
1529
  exports.validateConfig = __webpack_exports__.validateConfig;
950
1530
  exports.verifyChromeCredentials = __webpack_exports__.verifyChromeCredentials;
@@ -962,6 +1542,7 @@ for(var __webpack_i__ in __webpack_exports__)if (-1 === [
962
1542
  "getChromeSubmissionEvent",
963
1543
  "getEdgeSubmissionEvent",
964
1544
  "getFirefoxSubmissionEvent",
1545
+ "main",
965
1546
  "resolveConfig",
966
1547
  "validateConfig",
967
1548
  "verifyChromeCredentials",