@anvil-works/anvil-cli 0.5.1 → 0.5.3

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/cli.js CHANGED
@@ -34509,7 +34509,7 @@ var __webpack_exports__ = {};
34509
34509
  };
34510
34510
  const eraseLine = ESC + '2K';
34511
34511
  const eraseLines = (lines)=>lines > 0 ? (eraseLine + cursorUp(1)).repeat(lines - 1) + eraseLine + cursorLeft : '';
34512
- const height = (content)=>content.split('\n').length;
34512
+ const screen_manager_height = (content)=>content.split('\n').length;
34513
34513
  const lastLine = (content)=>content.split('\n').pop() ?? '';
34514
34514
  class ScreenManager {
34515
34515
  height = 0;
@@ -34538,12 +34538,12 @@ var __webpack_exports__ = {};
34538
34538
  if (rawPromptLine.length % width === 0) content += '\n';
34539
34539
  let output = content + (bottomContent ? '\n' + bottomContent : '');
34540
34540
  const promptLineUpDiff = Math.floor(rawPromptLine.length / width) - this.cursorPos.rows;
34541
- const bottomContentHeight = promptLineUpDiff + (bottomContent ? height(bottomContent) : 0);
34541
+ const bottomContentHeight = promptLineUpDiff + (bottomContent ? screen_manager_height(bottomContent) : 0);
34542
34542
  if (bottomContentHeight > 0) output += cursorUp(bottomContentHeight);
34543
34543
  output += cursorTo(this.cursorPos.cols);
34544
34544
  this.write(cursorDown(this.extraLinesUnderPrompt) + eraseLines(this.height) + output);
34545
34545
  this.extraLinesUnderPrompt = bottomContentHeight;
34546
- this.height = height(output);
34546
+ this.height = screen_manager_height(output);
34547
34547
  }
34548
34548
  checkCursorPos() {
34549
34549
  const cursorPos = this.rl.getCursorPos();
@@ -37768,20 +37768,20 @@ var __webpack_exports__ = {};
37768
37768
  }), mod);
37769
37769
  function pathspec(...paths) {
37770
37770
  const key = new String(paths);
37771
- cache.set(key, paths);
37771
+ esm_cache.set(key, paths);
37772
37772
  return key;
37773
37773
  }
37774
37774
  function isPathSpec(path) {
37775
- return path instanceof String && cache.has(path);
37775
+ return path instanceof String && esm_cache.has(path);
37776
37776
  }
37777
37777
  function toPaths(pathSpec) {
37778
- return cache.get(pathSpec) || [];
37778
+ return esm_cache.get(pathSpec) || [];
37779
37779
  }
37780
- var cache;
37780
+ var esm_cache;
37781
37781
  var init_pathspec = __esm({
37782
37782
  "src/lib/args/pathspec.ts" () {
37783
37783
  "use strict";
37784
- cache = /* @__PURE__ */ new WeakMap();
37784
+ esm_cache = /* @__PURE__ */ new WeakMap();
37785
37785
  }
37786
37786
  });
37787
37787
  var GitError;
@@ -42572,6 +42572,57 @@ var __webpack_exports__ = {};
42572
42572
  if (preferredEditors.includes(normalized)) return preferredEditorCommandByValue[normalized];
42573
42573
  return preferredEditorValue;
42574
42574
  }
42575
+ function extractExecutableFromCommand(command) {
42576
+ const trimmed = command.trim();
42577
+ if (!trimmed) return "";
42578
+ if (trimmed.startsWith('"')) {
42579
+ const closingIndex = trimmed.indexOf('"', 1);
42580
+ return closingIndex > 1 ? trimmed.slice(1, closingIndex) : "";
42581
+ }
42582
+ if (trimmed.startsWith("'")) {
42583
+ const closingIndex = trimmed.indexOf("'", 1);
42584
+ return closingIndex > 1 ? trimmed.slice(1, closingIndex) : "";
42585
+ }
42586
+ const firstSpace = trimmed.search(/\s/);
42587
+ return -1 === firstSpace ? trimmed : trimmed.slice(0, firstSpace);
42588
+ }
42589
+ function canExecutePath(filePath) {
42590
+ try {
42591
+ external_fs_.accessSync(filePath, "win32" === process.platform ? external_fs_.constants.F_OK : external_fs_.constants.X_OK);
42592
+ return true;
42593
+ } catch {
42594
+ return false;
42595
+ }
42596
+ }
42597
+ function isCommandAvailable(command, env = process.env) {
42598
+ const executable = extractExecutableFromCommand(command);
42599
+ if (!executable) return false;
42600
+ const hasPathSeparator = executable.includes(external_path_default().sep) || executable.includes("/");
42601
+ if (hasPathSeparator) return canExecutePath(executable);
42602
+ const pathEnv = env.PATH || "";
42603
+ if (!pathEnv) return false;
42604
+ const pathEntries = pathEnv.split(external_path_default().delimiter).filter(Boolean);
42605
+ const pathext = (env.PATHEXT || ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
42606
+ const extensions = "win32" === process.platform ? pathext : [
42607
+ ""
42608
+ ];
42609
+ const hasKnownExtension = "win32" === process.platform && pathext.some((ext)=>executable.toUpperCase().endsWith(ext.toUpperCase()));
42610
+ for (const dir of pathEntries){
42611
+ if ("win32" === process.platform && hasKnownExtension) {
42612
+ const candidate = external_path_default().join(dir, executable);
42613
+ if (canExecutePath(candidate)) return true;
42614
+ continue;
42615
+ }
42616
+ for (const ext of extensions){
42617
+ const candidate = external_path_default().join(dir, `${executable}${ext}`);
42618
+ if (canExecutePath(candidate)) return true;
42619
+ }
42620
+ }
42621
+ return false;
42622
+ }
42623
+ function getInstalledPreferredEditors() {
42624
+ return preferredEditors.filter((editor)=>isCommandAvailable(getPreferredEditorCommand(editor)));
42625
+ }
42575
42626
  function getAllConfig() {
42576
42627
  return config_config.store;
42577
42628
  }
@@ -42878,13 +42929,15 @@ var __webpack_exports__ = {};
42878
42929
  name: "value",
42879
42930
  message,
42880
42931
  choices,
42881
- default: defaultValue
42932
+ default: defaultValue,
42933
+ loop: false
42882
42934
  }
42883
42935
  ]);
42884
42936
  return answer.value;
42885
42937
  }
42886
42938
  }
42887
42939
  const external_readline_namespaceObject = require("readline");
42940
+ var external_readline_default = /*#__PURE__*/ __webpack_require__.n(external_readline_namespaceObject);
42888
42941
  /*! js-yaml 4.1.1 https://github.com/nodeca/js-yaml @license MIT */ function isNothing(subject) {
42889
42942
  return null == subject;
42890
42943
  }
@@ -47403,6 +47456,37 @@ var __webpack_exports__ = {};
47403
47456
  throw errors_createNetworkError.network(error.message);
47404
47457
  }
47405
47458
  }
47459
+ async function listAppsForCheckout(options = {}) {
47460
+ const anvilUrl = options.anvilUrl ?? anvil_api_getDefaultAnvilUrl();
47461
+ const authToken = await auth_getValidAuthToken(anvilUrl, options.username);
47462
+ const params = new URLSearchParams();
47463
+ if ("number" == typeof options.limit) params.set("limit", String(options.limit));
47464
+ if (options.cursor) params.set("cursor", options.cursor);
47465
+ if (options.q && options.q.trim()) params.set("q", options.q.trim());
47466
+ const query = params.toString();
47467
+ const url = `${anvilUrl}/ide/api/_/apps${query ? `?${query}` : ""}`;
47468
+ try {
47469
+ const resp = await fetch(url, {
47470
+ method: "GET",
47471
+ headers: {
47472
+ Authorization: `Bearer ${authToken}`
47473
+ },
47474
+ signal: options.signal
47475
+ });
47476
+ if (!resp.ok) {
47477
+ if (401 === resp.status) throw errors_createAuthError.invalid("Authentication failed");
47478
+ throw errors_createNetworkError.server(resp.status, resp.statusText);
47479
+ }
47480
+ const data = await resp.json();
47481
+ return {
47482
+ apps: Array.isArray(data.apps) ? data.apps : [],
47483
+ next_cursor: "string" == typeof data.next_cursor ? data.next_cursor : null
47484
+ };
47485
+ } catch (error) {
47486
+ if (error.type) throw error;
47487
+ throw errors_createNetworkError.network(error.message);
47488
+ }
47489
+ }
47406
47490
  function getGitFetchUrl(appId, authToken, anvilUrl = anvil_api_getDefaultAnvilUrl()) {
47407
47491
  const url = new URL(anvilUrl);
47408
47492
  const encodedToken = encodeURIComponent(authToken);
@@ -50117,1224 +50201,1320 @@ var __webpack_exports__ = {};
50117
50201
  const match = normalizedPath.match(/\/git\/([A-Z0-9]+)\.git(?:$|\/)/);
50118
50202
  return match ? match[1] : void 0;
50119
50203
  }
50120
- function decideFallbackUrl(explicitUrl, availableUrls = getAvailableAnvilUrls()) {
50121
- if (explicitUrl) return {
50122
- source: "explicit",
50123
- url: normalizeAnvilUrl(explicitUrl)
50124
- };
50125
- if (availableUrls.length > 1) return {
50126
- source: "available-multiple",
50127
- urls: availableUrls
50128
- };
50129
- if (1 === availableUrls.length) return {
50130
- source: "available-single",
50131
- url: availableUrls[0]
50132
- };
50133
- const fromConfig = getConfig("anvilUrl");
50134
- if ("string" == typeof fromConfig && fromConfig.trim()) return {
50135
- source: "config",
50136
- url: normalizeAnvilUrl(fromConfig.trim())
50137
- };
50138
- return {
50139
- source: "default",
50140
- url: isDevMode() ? "http://localhost:3000" : "https://anvil.works"
50141
- };
50142
- }
50143
- function decideUsernameForUrl(accounts) {
50144
- if (0 === accounts.length) return {
50145
- source: "none"
50146
- };
50147
- if (1 === accounts.length) return {
50148
- source: "single",
50149
- username: accounts[0]
50150
- };
50151
- return {
50152
- source: "multiple",
50153
- usernames: [
50154
- ...accounts
50155
- ]
50156
- };
50157
- }
50158
- async function resolveUsernameForUrl(anvilUrl, explicitUsername, promptMessage = "Multiple accounts found. Which account owns this app?") {
50159
- if (explicitUsername) return explicitUsername;
50160
- const decision = decideUsernameForUrl(auth_getAccountsForUrl(anvilUrl));
50161
- if ("none" === decision.source) return;
50162
- if ("single" === decision.source) {
50163
- logger_logger.verbose(chalk_source.cyan("Auto-selected account: ") + chalk_source.bold(decision.username));
50164
- return decision.username;
50204
+ const external_node_url_namespaceObject = require("node:url");
50205
+ var external_node_child_process_ = __webpack_require__("node:child_process");
50206
+ let isDockerCached;
50207
+ function hasDockerEnv() {
50208
+ try {
50209
+ external_node_fs_.statSync('/.dockerenv');
50210
+ return true;
50211
+ } catch {
50212
+ return false;
50165
50213
  }
50166
- const choices = decision.usernames.map((acct)=>({
50167
- name: acct,
50168
- value: acct
50169
- }));
50170
- choices.push({
50171
- name: "Cancel",
50172
- value: null
50173
- });
50174
- return logger_logger.select(promptMessage, choices, decision.usernames[0]);
50175
- }
50176
- async function confirmReverseLookupWithResolvedUser(anvilUrl, username) {
50177
- const resolvedUsername = await resolveUsernameForUrl(anvilUrl, username, `Multiple accounts found for ${anvilUrl}. Which account should be used for app lookup?`);
50178
- if (null === resolvedUsername) return null;
50179
- const shouldContinue = await logger_logger.confirm(`Search ${anvilUrl} ${resolvedUsername ? `for ${resolvedUsername}` : ""} for matching app IDs? (slower)`, true);
50180
- return {
50181
- username: resolvedUsername,
50182
- shouldContinue
50183
- };
50184
- }
50185
- async function resolveUrlForFallback(explicitUrl) {
50186
- const decision = decideFallbackUrl(explicitUrl);
50187
- if ("available-multiple" !== decision.source) return decision.url;
50188
- const choices = decision.urls.map((url)=>({
50189
- name: url,
50190
- value: url
50191
- }));
50192
- choices.push({
50193
- name: "Cancel",
50194
- value: null
50195
- });
50196
- const selectedUrl = await logger_logger.select("Multiple logged-in Anvil URLs found. Which one would you like to use?", choices, decision.urls[0]);
50197
- return selectedUrl;
50198
50214
  }
50199
- async function selectAppId(candidates) {
50200
- if (0 === candidates.length) {
50201
- logger_logger.warn("Could not auto-detect app ID from repository.");
50202
- const rl = external_readline_namespaceObject.createInterface({
50203
- input: process.stdin,
50204
- output: process.stdout
50205
- });
50206
- const manualAppId = await new Promise((resolve)=>{
50207
- const handleInterrupt = ()=>{
50208
- rl.close();
50209
- logger_logger.warn("Operation cancelled by user.");
50210
- resolve(null);
50211
- };
50212
- rl.on("SIGINT", handleInterrupt);
50213
- rl.question(chalk_source.cyan("Please enter app ID manually (or press Enter to exit): "), (answer)=>{
50214
- rl.removeListener("SIGINT", handleInterrupt);
50215
- rl.close();
50216
- const trimmed = answer.trim();
50217
- trimmed ? resolve(trimmed) : resolve(null);
50218
- });
50219
- });
50220
- if (manualAppId) return {
50221
- appId: manualAppId,
50222
- source: "config",
50223
- description: "Manual entry"
50224
- };
50225
- return null;
50226
- }
50227
- const choices = candidates.map((candidate, index)=>({
50228
- name: formatCandidateLabel(candidate),
50229
- value: index
50230
- }));
50231
- choices.push({
50232
- name: "Cancel",
50233
- value: null
50234
- });
50215
+ function hasDockerCGroup() {
50235
50216
  try {
50236
- const promptText = 1 === candidates.length ? "Confirm the detected app ID:" : "Select an app ID:";
50237
- const answer = await logger_logger.prompt([
50238
- {
50239
- type: "list",
50240
- name: "appId",
50241
- message: promptText,
50242
- choices: choices,
50243
- pageSize: 10
50244
- }
50245
- ]);
50246
- if (null === answer.appId) {
50247
- const rl = external_readline_namespaceObject.createInterface({
50248
- input: process.stdin,
50249
- output: process.stdout
50250
- });
50251
- const manualAppId = await new Promise((resolve)=>{
50252
- rl.question(chalk_source.cyan("Please enter app ID manually (or press Enter to exit): "), (answer)=>{
50253
- rl.close();
50254
- const trimmed = answer.trim();
50255
- trimmed ? resolve(trimmed) : resolve(null);
50256
- });
50257
- });
50258
- if (manualAppId) return {
50259
- appId: manualAppId,
50260
- source: "config",
50261
- description: "Manual entry"
50262
- };
50263
- return null;
50264
- }
50265
- return candidates[answer.appId];
50266
- } catch (error) {
50267
- const errorObj = error;
50268
- if ("ExitPromptError" === errorObj.name || errorObj.message.includes("User force closed")) logger_logger.warn("Operation cancelled by user.");
50269
- return null;
50217
+ return external_node_fs_.readFileSync('/proc/self/cgroup', 'utf8').includes('docker');
50218
+ } catch {
50219
+ return false;
50270
50220
  }
50271
50221
  }
50272
- async function pushToAnvil(options) {
50222
+ function isDocker() {
50223
+ if (void 0 === isDockerCached) isDockerCached = hasDockerEnv() || hasDockerCGroup();
50224
+ return isDockerCached;
50225
+ }
50226
+ let cachedResult;
50227
+ const hasContainerEnv = ()=>{
50273
50228
  try {
50274
- const authToken = await auth_getValidAuthToken(options.anvilUrl, options.username);
50275
- const pushUrl = getGitPushUrl(options.appId, authToken, options.anvilUrl);
50276
- const git = new GitService(options.repoPath);
50277
- const refSpec = `${options.branchName}:${options.branchName}`;
50278
- logger_logger.progress("push", "Pushing to Anvil...");
50279
- await git.push(pushUrl, refSpec, options.force ?? false);
50280
- logger_logger.progressEnd("push", "Pushed to Anvil");
50229
+ external_node_fs_.statSync('/run/.containerenv');
50281
50230
  return true;
50282
- } catch (e) {
50283
- logger_logger.progressEnd("push", "Push failed");
50284
- logger_logger.error(`Failed to push: ${errors_getErrorMessage(e)}`);
50231
+ } catch {
50285
50232
  return false;
50286
50233
  }
50234
+ };
50235
+ function isInsideContainer() {
50236
+ if (void 0 === cachedResult) cachedResult = hasContainerEnv() || isDocker();
50237
+ return cachedResult;
50287
50238
  }
50288
- async function fetchAndRebaseFromAnvil(options) {
50289
- const git = new GitService(options.repoPath);
50290
- const tempRef = `anvil-rebase-temp-${Date.now()}`;
50291
- let didStash = false;
50239
+ const isWsl = ()=>{
50240
+ if ('linux' !== external_node_process_.platform) return false;
50241
+ if (external_node_os_namespaceObject.release().toLowerCase().includes('microsoft')) {
50242
+ if (isInsideContainer()) return false;
50243
+ return true;
50244
+ }
50292
50245
  try {
50293
- const authToken = await auth_getValidAuthToken(options.anvilUrl, options.username);
50294
- const fetchUrl = getGitFetchUrl(options.appId, authToken, options.anvilUrl);
50295
- const hasChanges = await git.hasUncommittedChanges();
50296
- if (hasChanges) {
50297
- logger_logger.progress("rebase", "Stashing uncommitted changes...");
50298
- didStash = await git.stash("anvil-sync: stash before rebase");
50299
- }
50300
- logger_logger.progressUpdate("rebase", "Fetching from Anvil...");
50301
- await git.fetch(fetchUrl, `+${options.branchName}:${tempRef}`);
50302
- logger_logger.progressUpdate("rebase", "Rebasing...");
50303
- await git.rebase(tempRef);
50304
- logger_logger.progressUpdate("rebase", "Pushing rebased changes...");
50305
- const pushUrl = getGitPushUrl(options.appId, authToken, options.anvilUrl);
50306
- await git.push(pushUrl, `${options.branchName}:${options.branchName}`, true);
50307
- logger_logger.progressEnd("rebase", "Rebased and pushed to Anvil");
50308
- await git.deleteRef(`refs/heads/${tempRef}`);
50309
- if (didStash) try {
50310
- await git.stashPop();
50311
- logger_logger.verbose(chalk_source.green("Restored uncommitted changes"));
50312
- } catch (e) {
50313
- logger_logger.warn("Uncommitted changes were stashed but could not be restored automatically.");
50314
- logger_logger.info(chalk_source.gray(" Run: git stash pop"));
50315
- }
50316
- return {
50317
- success: true,
50318
- conflicted: false
50319
- };
50320
- } catch (e) {
50321
- const msg = errors_getErrorMessage(e);
50322
- if (msg.includes("rebase") && (msg.includes("conflict") || msg.includes("CONFLICT") || msg.includes("could not apply"))) {
50323
- try {
50324
- await git.rebaseAbort();
50325
- } catch {}
50326
- await git.deleteRef(`refs/heads/${tempRef}`);
50327
- if (didStash) try {
50328
- await git.stashPop();
50329
- } catch {
50330
- logger_logger.warn("Your uncommitted changes are in the stash. Run: git stash pop");
50331
- }
50332
- logger_logger.progressEnd("rebase", "Rebase failed - conflicts");
50333
- return {
50334
- success: false,
50335
- conflicted: true
50336
- };
50337
- }
50338
- await git.deleteRef(`refs/heads/${tempRef}`);
50339
- if (didStash) try {
50340
- await git.stashPop();
50341
- } catch {
50342
- logger_logger.warn("Your uncommitted changes are in the stash. Run: git stash pop");
50343
- }
50344
- logger_logger.progressEnd("rebase", "Rebase failed");
50345
- logger_logger.error(`Failed to rebase: ${msg}`);
50346
- return {
50347
- success: false,
50348
- conflicted: false
50349
- };
50350
- }
50351
- }
50352
- async function fetchAndHardResetFromAnvil(options) {
50353
- const git = new GitService(options.repoPath);
50354
- const tempRef = `anvil-reset-temp-${Date.now()}`;
50355
- try {
50356
- const authToken = await auth_getValidAuthToken(options.anvilUrl, options.username);
50357
- const fetchUrl = getGitFetchUrl(options.appId, authToken, options.anvilUrl);
50358
- logger_logger.progress("reset", "Fetching from Anvil...");
50359
- await git.fetch(fetchUrl, `+${options.branchName}:${tempRef}`);
50360
- logger_logger.progressUpdate("reset", "Resetting to Anvil's version...");
50361
- await git.reset(tempRef, "hard");
50362
- logger_logger.progressEnd("reset", "Reset to Anvil's version");
50363
- await git.deleteRef(`refs/heads/${tempRef}`);
50364
- return true;
50365
- } catch (e) {
50366
- await git.deleteRef(`refs/heads/${tempRef}`);
50367
- logger_logger.progressEnd("reset", "Reset failed");
50368
- logger_logger.error(`Failed to reset: ${errors_getErrorMessage(e)}`);
50246
+ return external_node_fs_.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft') ? !isInsideContainer() : false;
50247
+ } catch {
50369
50248
  return false;
50370
50249
  }
50250
+ };
50251
+ const is_wsl = external_node_process_.env.__IS_WSL_TEST__ ? isWsl : isWsl();
50252
+ const wslDrivesMountPoint = (()=>{
50253
+ const defaultMountPoint = '/mnt/';
50254
+ let mountPoint;
50255
+ return async function() {
50256
+ if (mountPoint) return mountPoint;
50257
+ const configFilePath = '/etc/wsl.conf';
50258
+ let isConfigFileExists = false;
50259
+ try {
50260
+ await external_node_fs_promises_namespaceObject.access(configFilePath, external_node_fs_promises_namespaceObject.constants.F_OK);
50261
+ isConfigFileExists = true;
50262
+ } catch {}
50263
+ if (!isConfigFileExists) return defaultMountPoint;
50264
+ const configContent = await external_node_fs_promises_namespaceObject.readFile(configFilePath, {
50265
+ encoding: 'utf8'
50266
+ });
50267
+ const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
50268
+ if (!configMountPoint) return defaultMountPoint;
50269
+ mountPoint = configMountPoint.groups.mountPoint.trim();
50270
+ mountPoint = mountPoint.endsWith('/') ? mountPoint : `${mountPoint}/`;
50271
+ return mountPoint;
50272
+ };
50273
+ })();
50274
+ const powerShellPathFromWsl = async ()=>{
50275
+ const mountPoint = await wslDrivesMountPoint();
50276
+ return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
50277
+ };
50278
+ const powerShellPath = async ()=>{
50279
+ if (is_wsl) return powerShellPathFromWsl();
50280
+ return `${external_node_process_.env.SYSTEMROOT || external_node_process_.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
50281
+ };
50282
+ function defineLazyProperty(object, propertyName, valueGetter) {
50283
+ const define = (value)=>Object.defineProperty(object, propertyName, {
50284
+ value,
50285
+ enumerable: true,
50286
+ writable: true
50287
+ });
50288
+ Object.defineProperty(object, propertyName, {
50289
+ configurable: true,
50290
+ enumerable: true,
50291
+ get () {
50292
+ const result = valueGetter();
50293
+ define(result);
50294
+ return result;
50295
+ },
50296
+ set (value) {
50297
+ define(value);
50298
+ }
50299
+ });
50300
+ return object;
50371
50301
  }
50372
- function getSyncStateCategory(syncStatus) {
50373
- if (syncStatus?.branchMissing) return "branch-missing";
50374
- if (syncStatus?.diverged) return "diverged";
50375
- if (syncStatus?.ahead && !syncStatus?.behind) return "ahead";
50376
- if (syncStatus?.behind && !syncStatus?.ahead) return "behind";
50377
- if (syncStatus?.ahead && syncStatus?.behind) return "diverged";
50378
- return "in-sync";
50302
+ const execFileAsync = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_.execFile);
50303
+ async function defaultBrowserId() {
50304
+ if ('darwin' !== external_node_process_.platform) throw new Error('macOS only');
50305
+ const { stdout } = await execFileAsync('defaults', [
50306
+ 'read',
50307
+ 'com.apple.LaunchServices/com.apple.launchservices.secure',
50308
+ 'LSHandlers'
50309
+ ]);
50310
+ const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
50311
+ const browserId = match?.groups.id ?? 'com.apple.Safari';
50312
+ if ('com.apple.safari' === browserId) return 'com.apple.Safari';
50313
+ return browserId;
50379
50314
  }
50380
- async function recheckSyncStatus(previousCategory, previousBranch, options) {
50381
- try {
50382
- logger_logger.progress("verify", "Verifying repository status...");
50383
- const freshSession = await api_watch(options.repoPath, options.appId, options.anvilUrl, options.stagedOnly, options.username);
50384
- logger_logger.progressEnd("verify");
50385
- const currentCategory = getSyncStateCategory(freshSession.syncStatus);
50386
- const currentBranch = freshSession.getBranchName() || "master";
50387
- if (currentCategory !== previousCategory || currentBranch !== previousBranch) {
50388
- logger_logger.warn("Repository status has changed. Re-evaluating...");
50389
- return freshSession;
50390
- }
50391
- freshSession.cleanup();
50392
- return null;
50393
- } catch (e) {
50394
- logger_logger.progressEnd("verify");
50395
- logger_logger.verbose(chalk_source.gray(`Could not re-verify status: ${errors_getErrorMessage(e)}`));
50396
- return null;
50397
- }
50315
+ const run_applescript_execFileAsync = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_.execFile);
50316
+ async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
50317
+ if ('darwin' !== external_node_process_.platform) throw new Error('macOS only');
50318
+ const outputArguments = humanReadableOutput ? [] : [
50319
+ '-ss'
50320
+ ];
50321
+ const execOptions = {};
50322
+ if (signal) execOptions.signal = signal;
50323
+ const { stdout } = await run_applescript_execFileAsync("osascript", [
50324
+ '-e',
50325
+ script,
50326
+ outputArguments
50327
+ ], execOptions);
50328
+ return stdout.trim();
50398
50329
  }
50399
- async function recreateSessionAndValidate(options) {
50400
- logger_logger.progress("init", "Restarting watch session...");
50401
- try {
50402
- const newSession = await api_watch(options.repoPath, options.appId, options.anvilUrl, options.stagedOnly, options.username);
50403
- logger_logger.progressEnd("init");
50404
- return await checkSyncStatusAndStart(newSession, options);
50405
- } catch (e) {
50406
- logger_logger.progressEnd("init", "Failed");
50407
- logger_logger.error(`Failed to restart: ${errors_getErrorMessage(e)}`);
50408
- return false;
50330
+ async function bundleName(bundleId) {
50331
+ return runAppleScript(`tell application "Finder" to set app_path to application file id "${bundleId}" as string\ntell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`);
50332
+ }
50333
+ const windows_execFileAsync = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_.execFile);
50334
+ const windowsBrowserProgIds = {
50335
+ MSEdgeHTM: {
50336
+ name: 'Edge',
50337
+ id: 'com.microsoft.edge'
50338
+ },
50339
+ MSEdgeBHTML: {
50340
+ name: 'Edge Beta',
50341
+ id: 'com.microsoft.edge.beta'
50342
+ },
50343
+ MSEdgeDHTML: {
50344
+ name: 'Edge Dev',
50345
+ id: 'com.microsoft.edge.dev'
50346
+ },
50347
+ AppXq0fevzme2pys62n3e0fbqa7peapykr8v: {
50348
+ name: 'Edge',
50349
+ id: 'com.microsoft.edge.old'
50350
+ },
50351
+ ChromeHTML: {
50352
+ name: 'Chrome',
50353
+ id: 'com.google.chrome'
50354
+ },
50355
+ ChromeBHTML: {
50356
+ name: 'Chrome Beta',
50357
+ id: 'com.google.chrome.beta'
50358
+ },
50359
+ ChromeDHTML: {
50360
+ name: 'Chrome Dev',
50361
+ id: 'com.google.chrome.dev'
50362
+ },
50363
+ ChromiumHTM: {
50364
+ name: 'Chromium',
50365
+ id: 'org.chromium.Chromium'
50366
+ },
50367
+ BraveHTML: {
50368
+ name: 'Brave',
50369
+ id: 'com.brave.Browser'
50370
+ },
50371
+ BraveBHTML: {
50372
+ name: 'Brave Beta',
50373
+ id: 'com.brave.Browser.beta'
50374
+ },
50375
+ BraveDHTML: {
50376
+ name: 'Brave Dev',
50377
+ id: 'com.brave.Browser.dev'
50378
+ },
50379
+ BraveSSHTM: {
50380
+ name: 'Brave Nightly',
50381
+ id: 'com.brave.Browser.nightly'
50382
+ },
50383
+ FirefoxURL: {
50384
+ name: 'Firefox',
50385
+ id: 'org.mozilla.firefox'
50386
+ },
50387
+ OperaStable: {
50388
+ name: 'Opera',
50389
+ id: 'com.operasoftware.Opera'
50390
+ },
50391
+ VivaldiHTM: {
50392
+ name: 'Vivaldi',
50393
+ id: 'com.vivaldi.Vivaldi'
50394
+ },
50395
+ 'IE.HTTP': {
50396
+ name: 'Internet Explorer',
50397
+ id: 'com.microsoft.ie'
50409
50398
  }
50399
+ };
50400
+ new Map(Object.entries(windowsBrowserProgIds));
50401
+ class UnknownBrowserError extends Error {
50410
50402
  }
50411
- async function checkSyncStatusAndStart(session, options) {
50412
- const syncStatus = session.syncStatus;
50413
- const branchName = session.getBranchName() || "master";
50414
- const hasUncommitted = session.hasUncommittedChanges;
50415
- const stateCategory = getSyncStateCategory(syncStatus);
50416
- if (syncStatus?.branchMissing) {
50417
- logger_logger.warn(`Branch '${branchName}' doesn't exist on Anvil yet.`);
50418
- if (hasUncommitted) logger_logger.info(chalk_source.gray(" You also have uncommitted changes."));
50419
- let shouldPush = options.autoMode;
50420
- if (options.autoMode) logger_logger.info(chalk_source.cyan("→ Auto-pushing new branch to Anvil..."));
50421
- else {
50422
- shouldPush = await logger_logger.confirm("Would you like to push this branch to Anvil?", true);
50423
- if (shouldPush) {
50424
- const changed = await recheckSyncStatus(stateCategory, branchName, options);
50425
- if (changed) return await checkSyncStatusAndStart(changed, options);
50426
- }
50427
- }
50428
- if (!shouldPush) {
50429
- logger_logger.warn("Watch cancelled. Push your branch to Anvil, then try again.");
50430
- session.cleanup();
50431
- return false;
50432
- }
50433
- session.cleanup();
50434
- const pushed = await pushToAnvil({
50435
- repoPath: options.repoPath,
50436
- appId: options.appId,
50437
- anvilUrl: options.anvilUrl,
50438
- branchName,
50439
- username: options.username
50440
- });
50441
- if (!pushed) return false;
50442
- return await recreateSessionAndValidate(options);
50403
+ async function defaultBrowser(_execFileAsync = windows_execFileAsync) {
50404
+ const { stdout } = await _execFileAsync('reg', [
50405
+ 'QUERY',
50406
+ ' HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice',
50407
+ '/v',
50408
+ 'ProgId'
50409
+ ]);
50410
+ const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
50411
+ if (!match) throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
50412
+ const { id } = match.groups;
50413
+ const browser = windowsBrowserProgIds[id];
50414
+ if (!browser) throw new UnknownBrowserError(`Unknown browser ID: ${id}`);
50415
+ return browser;
50416
+ }
50417
+ const default_browser_execFileAsync = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_.execFile);
50418
+ const titleize = (string)=>string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x)=>x.toUpperCase());
50419
+ async function default_browser_defaultBrowser() {
50420
+ if ('darwin' === external_node_process_.platform) {
50421
+ const id = await defaultBrowserId();
50422
+ const name = await bundleName(id);
50423
+ return {
50424
+ name,
50425
+ id
50426
+ };
50443
50427
  }
50444
- if (syncStatus?.behind || syncStatus?.ahead) if (syncStatus.ahead && !syncStatus.behind) {
50445
- logger_logger.warn(`Your local repository is ${syncStatus.ahead} commit(s) ahead of Anvil.`);
50446
- if (hasUncommitted) logger_logger.info(chalk_source.gray(" You also have uncommitted changes."));
50447
- let shouldPush = options.autoMode;
50448
- if (options.autoMode) logger_logger.info(chalk_source.cyan("→ Auto-pushing to Anvil..."));
50449
- else {
50450
- const action = await logger_logger.select("What would you like to do?", [
50451
- {
50452
- name: "Push your changes to Anvil (recommended)",
50453
- value: "push"
50454
- },
50455
- {
50456
- name: "Exit and handle manually",
50457
- value: "exit"
50458
- }
50459
- ], "push");
50460
- shouldPush = "push" === action;
50461
- if (shouldPush) {
50462
- const changed = await recheckSyncStatus(stateCategory, branchName, options);
50463
- if (changed) return await checkSyncStatusAndStart(changed, options);
50428
+ if ('linux' === external_node_process_.platform) {
50429
+ const { stdout } = await default_browser_execFileAsync('xdg-mime', [
50430
+ 'query',
50431
+ 'default',
50432
+ 'x-scheme-handler/http'
50433
+ ]);
50434
+ const id = stdout.trim();
50435
+ const name = titleize(id.replace(/.desktop$/, '').replace('-', ' '));
50436
+ return {
50437
+ name,
50438
+ id
50439
+ };
50440
+ }
50441
+ if ('win32' === external_node_process_.platform) return defaultBrowser();
50442
+ throw new Error('Only macOS, Linux, and Windows are supported');
50443
+ }
50444
+ const execFile = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_.execFile);
50445
+ const open_dirname = external_node_path_.dirname((0, external_node_url_namespaceObject.fileURLToPath)(__rslib_import_meta_url__));
50446
+ const localXdgOpenPath = external_node_path_.join(open_dirname, 'xdg-open');
50447
+ const { platform, arch } = external_node_process_;
50448
+ async function getWindowsDefaultBrowserFromWsl() {
50449
+ const powershellPath = await powerShellPath();
50450
+ const rawCommand = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
50451
+ const encodedCommand = external_node_buffer_namespaceObject.Buffer.from(rawCommand, 'utf16le').toString('base64');
50452
+ const { stdout } = await execFile(powershellPath, [
50453
+ '-NoProfile',
50454
+ '-NonInteractive',
50455
+ '-ExecutionPolicy',
50456
+ 'Bypass',
50457
+ '-EncodedCommand',
50458
+ encodedCommand
50459
+ ], {
50460
+ encoding: 'utf8'
50461
+ });
50462
+ const progId = stdout.trim();
50463
+ const browserMap = {
50464
+ ChromeHTML: 'com.google.chrome',
50465
+ BraveHTML: 'com.brave.Browser',
50466
+ MSEdgeHTM: 'com.microsoft.edge',
50467
+ FirefoxURL: 'org.mozilla.firefox'
50468
+ };
50469
+ return browserMap[progId] ? {
50470
+ id: browserMap[progId]
50471
+ } : {};
50472
+ }
50473
+ const pTryEach = async (array, mapper)=>{
50474
+ let latestError;
50475
+ for (const item of array)try {
50476
+ return await mapper(item);
50477
+ } catch (error) {
50478
+ latestError = error;
50479
+ }
50480
+ throw latestError;
50481
+ };
50482
+ const baseOpen = async (options)=>{
50483
+ options = {
50484
+ wait: false,
50485
+ background: false,
50486
+ newInstance: false,
50487
+ allowNonzeroExitCode: false,
50488
+ ...options
50489
+ };
50490
+ if (Array.isArray(options.app)) return pTryEach(options.app, (singleApp)=>baseOpen({
50491
+ ...options,
50492
+ app: singleApp
50493
+ }));
50494
+ let { name: app, arguments: appArguments = [] } = options.app ?? {};
50495
+ appArguments = [
50496
+ ...appArguments
50497
+ ];
50498
+ if (Array.isArray(app)) return pTryEach(app, (appName)=>baseOpen({
50499
+ ...options,
50500
+ app: {
50501
+ name: appName,
50502
+ arguments: appArguments
50464
50503
  }
50465
- }
50466
- if (!shouldPush) {
50467
- logger_logger.warn("Watch cancelled.");
50468
- session.cleanup();
50469
- return false;
50470
- }
50471
- session.cleanup();
50472
- const pushed = await pushToAnvil({
50473
- repoPath: options.repoPath,
50474
- appId: options.appId,
50475
- anvilUrl: options.anvilUrl,
50476
- branchName,
50477
- username: options.username
50478
- });
50479
- if (!pushed) return false;
50480
- return await recreateSessionAndValidate(options);
50481
- } else if (syncStatus.diverged) {
50482
- logger_logger.warn("Your local repository has diverged from Anvil.");
50483
- logger_logger.info(chalk_source.gray(` You are ${syncStatus.ahead} commit(s) ahead and ${syncStatus.behind} commit(s) behind.`));
50484
- if (hasUncommitted) logger_logger.info(chalk_source.gray(" You also have uncommitted changes."));
50485
- session.cleanup();
50486
- if (options.autoMode) {
50487
- logger_logger.info(chalk_source.cyan("→ Auto-rebasing onto Anvil's version..."));
50488
- const result = await fetchAndRebaseFromAnvil({
50489
- repoPath: options.repoPath,
50490
- appId: options.appId,
50491
- anvilUrl: options.anvilUrl,
50492
- branchName,
50493
- username: options.username
50504
+ }));
50505
+ if ('browser' === app || 'browserPrivate' === app) {
50506
+ const ids = {
50507
+ 'com.google.chrome': 'chrome',
50508
+ 'google-chrome.desktop': 'chrome',
50509
+ 'com.brave.Browser': 'brave',
50510
+ 'org.mozilla.firefox': 'firefox',
50511
+ 'firefox.desktop': 'firefox',
50512
+ 'com.microsoft.msedge': 'edge',
50513
+ 'com.microsoft.edge': 'edge',
50514
+ 'com.microsoft.edgemac': 'edge',
50515
+ 'microsoft-edge.desktop': 'edge'
50516
+ };
50517
+ const flags = {
50518
+ chrome: '--incognito',
50519
+ brave: '--incognito',
50520
+ firefox: '--private-window',
50521
+ edge: '--inPrivate'
50522
+ };
50523
+ const browser = is_wsl ? await getWindowsDefaultBrowserFromWsl() : await default_browser_defaultBrowser();
50524
+ if (browser.id in ids) {
50525
+ const browserName = ids[browser.id];
50526
+ if ('browserPrivate' === app) appArguments.push(flags[browserName]);
50527
+ return baseOpen({
50528
+ ...options,
50529
+ app: {
50530
+ name: open_apps[browserName],
50531
+ arguments: appArguments
50532
+ }
50494
50533
  });
50495
- if (result.conflicted) {
50496
- logger_logger.error("Rebase failed due to conflicts. Please resolve manually:");
50497
- logger_logger.info(chalk_source.gray(" 1. Run: git fetch anvil && git rebase anvil/" + branchName));
50498
- logger_logger.info(chalk_source.gray(" 2. Resolve conflicts, then: git rebase --continue"));
50499
- logger_logger.info(chalk_source.gray(" 3. Push: git push anvil " + branchName));
50500
- return false;
50501
- }
50502
- if (!result.success) return false;
50503
- return await recreateSessionAndValidate(options);
50504
50534
  }
50505
- const action = await logger_logger.select("How would you like to resolve this?", [
50506
- {
50507
- name: "Pull from Anvil and rebase your changes on top (recommended)",
50508
- value: "rebase"
50509
- },
50510
- {
50511
- name: "Discard your local commits and use Anvil's version",
50512
- value: "reset"
50513
- },
50514
- {
50515
- name: "Push your version to Anvil (overwrites remote)",
50516
- value: "push"
50517
- },
50518
- {
50519
- name: "Exit and handle manually",
50520
- value: "exit"
50521
- }
50522
- ], "rebase");
50523
- if ("exit" === action) {
50524
- logger_logger.warn("Watch cancelled.");
50525
- return false;
50535
+ throw new Error(`${browser.name} is not supported as a default browser`);
50536
+ }
50537
+ let command;
50538
+ const cliArguments = [];
50539
+ const childProcessOptions = {};
50540
+ if ('darwin' === platform) {
50541
+ command = 'open';
50542
+ if (options.wait) cliArguments.push('--wait-apps');
50543
+ if (options.background) cliArguments.push('--background');
50544
+ if (options.newInstance) cliArguments.push('--new');
50545
+ if (app) cliArguments.push('-a', app);
50546
+ } else if ('win32' !== platform && (!is_wsl || isInsideContainer() || app)) {
50547
+ if (app) command = app;
50548
+ else {
50549
+ const isBundled = !open_dirname || '/' === open_dirname;
50550
+ let exeLocalXdgOpen = false;
50551
+ try {
50552
+ await external_node_fs_promises_namespaceObject.access(localXdgOpenPath, external_node_fs_promises_namespaceObject.constants.X_OK);
50553
+ exeLocalXdgOpen = true;
50554
+ } catch {}
50555
+ const useSystemXdgOpen = external_node_process_.versions.electron ?? ('android' === platform || isBundled || !exeLocalXdgOpen);
50556
+ command = useSystemXdgOpen ? 'xdg-open' : localXdgOpenPath;
50526
50557
  }
50527
- const changed = await recheckSyncStatus(stateCategory, branchName, options);
50528
- if (changed) return await checkSyncStatusAndStart(changed, options);
50529
- if ("rebase" === action) {
50530
- const result = await fetchAndRebaseFromAnvil({
50531
- repoPath: options.repoPath,
50532
- appId: options.appId,
50533
- anvilUrl: options.anvilUrl,
50534
- branchName,
50535
- username: options.username
50536
- });
50537
- if (result.conflicted) {
50538
- logger_logger.error("Rebase failed due to conflicts. Please resolve manually:");
50539
- logger_logger.info(chalk_source.gray(" 1. Run: git fetch anvil && git rebase anvil/" + branchName));
50540
- logger_logger.info(chalk_source.gray(" 2. Resolve conflicts, then: git rebase --continue"));
50541
- logger_logger.info(chalk_source.gray(" 3. Push: git push anvil " + branchName));
50542
- return false;
50543
- }
50544
- if (!result.success) return false;
50545
- return await recreateSessionAndValidate(options);
50546
- }
50547
- if ("reset" === action) {
50548
- const resetWarning = hasUncommitted ? `This will discard ${syncStatus.ahead} local commit(s) and your uncommitted changes. Are you sure?` : `This will discard ${syncStatus.ahead} local commit(s). Are you sure?`;
50549
- const confirmed = await logger_logger.confirm(resetWarning, false);
50550
- if (!confirmed) {
50551
- logger_logger.warn("Watch cancelled.");
50552
- return false;
50553
- }
50554
- const reset = await fetchAndHardResetFromAnvil({
50555
- repoPath: options.repoPath,
50556
- appId: options.appId,
50557
- anvilUrl: options.anvilUrl,
50558
- branchName,
50559
- username: options.username
50560
- });
50561
- if (!reset) return false;
50562
- return await recreateSessionAndValidate(options);
50563
- }
50564
- if ("push" === action) {
50565
- const confirmed = await logger_logger.confirm("This will overwrite Anvil's version. Are you sure?", false);
50566
- if (!confirmed) {
50567
- logger_logger.warn("Watch cancelled.");
50568
- return false;
50569
- }
50570
- const pushed = await pushToAnvil({
50571
- repoPath: options.repoPath,
50572
- appId: options.appId,
50573
- anvilUrl: options.anvilUrl,
50574
- branchName,
50575
- username: options.username,
50576
- force: true
50577
- });
50578
- if (!pushed) return false;
50579
- return await recreateSessionAndValidate(options);
50558
+ if (appArguments.length > 0) cliArguments.push(...appArguments);
50559
+ if (!options.wait) {
50560
+ childProcessOptions.stdio = 'ignore';
50561
+ childProcessOptions.detached = true;
50580
50562
  }
50581
50563
  } else {
50582
- logger_logger.warn(`Your local repository is ${syncStatus.behind} commit(s) behind Anvil.`);
50583
- logger_logger.verbose(chalk_source.gray(" You need to sync to the latest version before watching."));
50584
- if (hasUncommitted) logger_logger.info(chalk_source.gray(" You also have uncommitted changes."));
50585
- let shouldSync = options.autoMode;
50586
- if (options.autoMode) logger_logger.info(chalk_source.cyan("→ Auto-syncing to latest version..."));
50587
- else {
50588
- shouldSync = await logger_logger.confirm("Would you like to sync to the latest version from Anvil now?", true);
50589
- if (shouldSync) {
50590
- const changed = await recheckSyncStatus(stateCategory, branchName, options);
50591
- if (changed) return await checkSyncStatusAndStart(changed, options);
50592
- }
50593
- }
50594
- if (!shouldSync) {
50595
- logger_logger.warn("Watch cancelled. Please sync manually before watching.");
50596
- session.cleanup();
50597
- return false;
50564
+ command = await powerShellPath();
50565
+ cliArguments.push('-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-EncodedCommand');
50566
+ if (!is_wsl) childProcessOptions.windowsVerbatimArguments = true;
50567
+ const encodedArguments = [
50568
+ 'Start'
50569
+ ];
50570
+ if (options.wait) encodedArguments.push('-Wait');
50571
+ if (app) {
50572
+ encodedArguments.push(`"\`"${app}\`""`);
50573
+ if (options.target) appArguments.push(options.target);
50574
+ } else if (options.target) encodedArguments.push(`"${options.target}"`);
50575
+ if (appArguments.length > 0) {
50576
+ appArguments = appArguments.map((argument)=>`"\`"${argument}\`""`);
50577
+ encodedArguments.push('-ArgumentList', appArguments.join(','));
50598
50578
  }
50599
- session.cleanup();
50600
- let authToken;
50601
- try {
50602
- authToken = await auth_getValidAuthToken(options.anvilUrl, options.username);
50603
- } catch (e) {
50604
- logger_logger.error("Not logged in. Please log in first.");
50605
- process.exit(1);
50579
+ options.target = external_node_buffer_namespaceObject.Buffer.from(encodedArguments.join(' '), 'utf16le').toString('base64');
50580
+ }
50581
+ if ('darwin' === platform && appArguments.length > 0) cliArguments.push('--args', ...appArguments);
50582
+ if (options.target) cliArguments.push(options.target);
50583
+ const subprocess = external_node_child_process_.spawn(command, cliArguments, childProcessOptions);
50584
+ if (options.wait) return new Promise((resolve, reject)=>{
50585
+ subprocess.once('error', reject);
50586
+ subprocess.once('close', (exitCode)=>{
50587
+ if (!options.allowNonzeroExitCode && exitCode > 0) return void reject(new Error(`Exited with code ${exitCode}`));
50588
+ resolve(subprocess);
50589
+ });
50590
+ });
50591
+ subprocess.unref();
50592
+ return subprocess;
50593
+ };
50594
+ const open_open = (target, options)=>{
50595
+ if ('string' != typeof target) throw new TypeError('Expected a `target`');
50596
+ return baseOpen({
50597
+ ...options,
50598
+ target
50599
+ });
50600
+ };
50601
+ function detectArchBinary(binary) {
50602
+ if ('string' == typeof binary || Array.isArray(binary)) return binary;
50603
+ const { [arch]: archBinary } = binary;
50604
+ if (!archBinary) throw new Error(`${arch} is not supported`);
50605
+ return archBinary;
50606
+ }
50607
+ function detectPlatformBinary({ [platform]: platformBinary }, { wsl }) {
50608
+ if (wsl && is_wsl) return detectArchBinary(wsl);
50609
+ if (!platformBinary) throw new Error(`${platform} is not supported`);
50610
+ return detectArchBinary(platformBinary);
50611
+ }
50612
+ const open_apps = {};
50613
+ defineLazyProperty(open_apps, 'chrome', ()=>detectPlatformBinary({
50614
+ darwin: 'google chrome',
50615
+ win32: 'chrome',
50616
+ linux: [
50617
+ 'google-chrome',
50618
+ 'google-chrome-stable',
50619
+ 'chromium'
50620
+ ]
50621
+ }, {
50622
+ wsl: {
50623
+ ia32: '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe',
50624
+ x64: [
50625
+ '/mnt/c/Program Files/Google/Chrome/Application/chrome.exe',
50626
+ '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe'
50627
+ ]
50606
50628
  }
50607
- logger_logger.progress("sync-latest", "Syncing to latest version from Anvil...");
50608
- try {
50609
- const newCommitId = await syncToLatest(options.repoPath, options.appId, options.anvilUrl, authToken, branchName, options.username);
50610
- logger_logger.progressEnd("sync-latest", `Synced to latest version: ${newCommitId.substring(0, 8)}`);
50611
- } catch (e) {
50612
- logger_logger.progressEnd("sync-latest", "Failed");
50613
- logger_logger.error(`Failed to sync: ${errors_getErrorMessage(e)}`);
50614
- process.exit(1);
50629
+ }));
50630
+ defineLazyProperty(open_apps, 'brave', ()=>detectPlatformBinary({
50631
+ darwin: 'brave browser',
50632
+ win32: 'brave',
50633
+ linux: [
50634
+ 'brave-browser',
50635
+ 'brave'
50636
+ ]
50637
+ }, {
50638
+ wsl: {
50639
+ ia32: '/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe',
50640
+ x64: [
50641
+ '/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe',
50642
+ '/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe'
50643
+ ]
50615
50644
  }
50616
- return await recreateSessionAndValidate(options);
50645
+ }));
50646
+ defineLazyProperty(open_apps, 'firefox', ()=>detectPlatformBinary({
50647
+ darwin: 'firefox',
50648
+ win32: String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,
50649
+ linux: 'firefox'
50650
+ }, {
50651
+ wsl: '/mnt/c/Program Files/Mozilla Firefox/firefox.exe'
50652
+ }));
50653
+ defineLazyProperty(open_apps, 'edge', ()=>detectPlatformBinary({
50654
+ darwin: 'microsoft edge',
50655
+ win32: 'msedge',
50656
+ linux: [
50657
+ 'microsoft-edge',
50658
+ 'microsoft-edge-dev'
50659
+ ]
50660
+ }, {
50661
+ wsl: '/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe'
50662
+ }));
50663
+ defineLazyProperty(open_apps, 'browser', ()=>'browser');
50664
+ defineLazyProperty(open_apps, 'browserPrivate', ()=>'browserPrivate');
50665
+ const node_modules_open = open_open;
50666
+ const defaultDeps = {
50667
+ openSystem: async (pathToOpen)=>node_modules_open(pathToOpen),
50668
+ isCommandAvailable: isCommandAvailable,
50669
+ spawnShellCommand: async (commandLine)=>{
50670
+ await new Promise((resolve, reject)=>{
50671
+ const child = (0, external_child_process_.spawn)(commandLine, {
50672
+ shell: true,
50673
+ stdio: "inherit"
50674
+ });
50675
+ child.on("error", reject);
50676
+ child.on("exit", (code)=>{
50677
+ if (0 === code || null === code) resolve();
50678
+ else reject(new Error(`Editor command exited with code ${code}`));
50679
+ });
50680
+ });
50617
50681
  }
50618
- await startWatchingWithEventHandlers(session, options);
50619
- return true;
50620
- }
50621
- async function startWatchingWithEventHandlers(session, options) {
50622
- const { autoMode, repoPath, appId, anvilUrl, stagedOnly } = options;
50623
- session.on("branch-changed", async ({ oldBranch, newBranch })=>{
50624
- try {
50625
- session.cleanup();
50626
- logger_logger.warn(`Branch changed: ${chalk_source.bold(oldBranch)} ${chalk_source.bold(newBranch)}`);
50627
- logger_logger.info(chalk_source.cyan("→ Restarting watch on new branch..."));
50628
- const git = esm_default(repoPath);
50629
- const actualBranch = (await git.revparse([
50630
- "--abbrev-ref",
50631
- "HEAD"
50632
- ])).trim();
50633
- if (actualBranch !== newBranch) logger_logger.verbose(chalk_source.yellow(`Branch changed again to ${chalk_source.bold(actualBranch)}, using that instead`));
50634
- logger_logger.progress("init", "Initializing watch session...");
50635
- try {
50636
- const newSession = await api_watch(repoPath, appId, anvilUrl, stagedOnly, options.username);
50637
- logger_logger.progressEnd("init");
50638
- const started = await checkSyncStatusAndStart(newSession, options);
50639
- if (!started) process.exit(0);
50640
- } catch (e) {
50641
- logger_logger.progressEnd("init", "Failed");
50642
- logger_logger.error("Failed to restart watch: " + errors_getErrorMessage(e));
50643
- process.exit(1);
50644
- }
50645
- } catch (error) {
50646
- logger_logger.error(`Error handling branch change: ${error.message}`);
50647
- process.exit(1);
50682
+ };
50683
+ function parseCommandTokens(command) {
50684
+ const input = command.trim();
50685
+ if (!input) return [];
50686
+ const tokens = [];
50687
+ let current = "";
50688
+ let quote = null;
50689
+ let escaping = false;
50690
+ for(let i = 0; i < input.length; i += 1){
50691
+ const ch = input[i];
50692
+ if (escaping) {
50693
+ current += ch;
50694
+ escaping = false;
50695
+ continue;
50648
50696
  }
50649
- });
50650
- session.on("validation-failed", ({ reason, currentBranch })=>{
50651
- logger_logger.error(chalk_source.red(`Validation failed: ${reason}`));
50652
- logger_logger.verbose(chalk_source.yellow(` Current branch: ${currentBranch}`));
50653
- logger_logger.verbose(chalk_source.yellow(" Please restart anvil to re-validate the branch."));
50654
- session.cleanup();
50655
- process.exit(1);
50656
- });
50657
- session.on("max-retries-exceeded", async ()=>{
50658
- if (autoMode) {
50659
- logger_logger.info(chalk_source.cyan("→ Auto-restarting watch session..."));
50660
- session.cleanup();
50661
- const restarted = await recreateSessionAndValidate(options);
50662
- if (!restarted) process.exit(1);
50663
- } else {
50664
- const shouldRestart = await logger_logger.confirm("Would you like to restart the watch session?", true);
50665
- if (shouldRestart) {
50666
- session.cleanup();
50667
- const restarted = await recreateSessionAndValidate(options);
50668
- if (!restarted) process.exit(1);
50669
- }
50697
+ if ("\\" === ch) {
50698
+ escaping = true;
50699
+ continue;
50670
50700
  }
50671
- });
50672
- if (session.hasUncommittedChanges) {
50673
- if (!autoMode) {
50674
- const shouldContinue = await logger_logger.confirm("Continue? Your uncommitted changes will be synced to Anvil.", true);
50675
- if (!shouldContinue) {
50676
- logger_logger.warn("Watch cancelled. Commit, stash, or discard your changes, then try again.");
50677
- session.cleanup();
50678
- process.exit(0);
50679
- }
50701
+ if (quote) {
50702
+ if (ch === quote) quote = null;
50703
+ else current += ch;
50704
+ continue;
50680
50705
  }
50681
- logger_logger.progress("save", "Saving uncommitted changes to Anvil...");
50682
- try {
50683
- await session.forceSave();
50684
- logger_logger.progressEnd("save");
50685
- } catch (e) {
50686
- logger_logger.progressEnd("save", "Failed");
50687
- logger_logger.error(`Failed to save: ${errors_getErrorMessage(e)}`);
50706
+ if ('"' === ch || "'" === ch) {
50707
+ quote = ch;
50708
+ continue;
50709
+ }
50710
+ if (/\s/.test(ch)) {
50711
+ if (current) {
50712
+ tokens.push(current);
50713
+ current = "";
50714
+ }
50715
+ continue;
50688
50716
  }
50717
+ current += ch;
50718
+ }
50719
+ if (escaping) current += "\\";
50720
+ if (quote) throw new Error(`Unterminated quote in command: ${command}`);
50721
+ if (current) tokens.push(current);
50722
+ return tokens;
50723
+ }
50724
+ function quoteForPosixShell(value) {
50725
+ if ("" === value) return "''";
50726
+ return `'${value.replace(/'/g, "'\"'\"'")}'`;
50727
+ }
50728
+ function quoteForCmdShell(value) {
50729
+ const escaped = value.replace(/[%^&|<>()"!]/g, "^$&");
50730
+ return `"${escaped}"`;
50731
+ }
50732
+ function buildShellCommandLine(tokens) {
50733
+ if ("win32" === process.platform) return tokens.map(quoteForCmdShell).join(" ");
50734
+ return tokens.map(quoteForPosixShell).join(" ");
50735
+ }
50736
+ async function openPathInEditorOrDefault(targetPath, preferredEditorCommand, deps = defaultDeps) {
50737
+ if (preferredEditorCommand) if (deps.isCommandAvailable(preferredEditorCommand)) try {
50738
+ const tokens = parseCommandTokens(preferredEditorCommand);
50739
+ if (0 === tokens.length) throw new Error("empty command");
50740
+ await deps.spawnShellCommand(buildShellCommandLine([
50741
+ ...tokens,
50742
+ targetPath
50743
+ ]));
50744
+ return;
50745
+ } catch (error) {
50746
+ logger_logger.warn(`Failed to open preferred editor '${preferredEditorCommand}': ${errors_getErrorMessage(error)}. Falling back to the system default app.`);
50689
50747
  }
50690
- await session.startWatching();
50748
+ else logger_logger.warn(`Preferred editor command '${preferredEditorCommand}' was not found in PATH. Falling back to the system default app.`);
50749
+ await deps.openSystem(targetPath);
50691
50750
  }
50692
- async function handleWatchCommand(options) {
50693
- const invoked = process.argv[2];
50694
- if ("sync" === invoked) {
50695
- logger_logger.error("'sync' has been renamed to 'watch'. Please use 'anvil watch' instead.");
50696
- process.exit(1);
50697
- }
50698
- const { path: repoPath = process.cwd(), appid: explicitAppId, useFirst = false, stagedOnly = false, autoMode = false, url: explicitUrl, user: explicitUsername } = options;
50699
- try {
50700
- const validationResult = await validateAnvilApp(repoPath);
50701
- if (validationResult.appName) logger_logger.info(chalk_source.green("Anvil app: ") + chalk_source.bold(validationResult.appName));
50702
- const detectedFromAllRemotes = await detectAppIdsFromAllRemotes(repoPath);
50703
- let filteredCandidates = filterCandidates(detectedFromAllRemotes, explicitUrl, explicitUsername);
50704
- logger_logger.verbose(chalk_source.cyan(`Detected ${detectedFromAllRemotes.length} app ID(s) from git remotes`));
50705
- if (filteredCandidates.length !== detectedFromAllRemotes.length) logger_logger.verbose(chalk_source.cyan(`After filtering: ${filteredCandidates.length} candidate(s)`));
50706
- let finalAppId;
50707
- let anvilUrl;
50708
- let username = explicitUsername;
50709
- let fallbackUrl;
50710
- let shouldPersistUsernameBinding = false;
50711
- if (explicitAppId) {
50712
- finalAppId = explicitAppId;
50713
- const binding = await getAppAuthBinding(repoPath, explicitAppId);
50714
- if (binding.url && !explicitUrl) {
50715
- anvilUrl = normalizeAnvilUrl(binding.url);
50716
- logger_logger.verbose(chalk_source.cyan("Resolved URL from binding for app ID: ") + chalk_source.bold(anvilUrl));
50717
- }
50718
- if (binding.username && !explicitUsername) {
50719
- username = binding.username;
50720
- logger_logger.verbose(chalk_source.cyan("Resolved username from binding for app ID: ") + chalk_source.bold(username));
50721
- }
50722
- const remoteInfo = lookupRemoteInfoForAppId(explicitAppId, detectedFromAllRemotes);
50723
- if (remoteInfo.detectedUrl && !explicitUrl && !anvilUrl) {
50724
- anvilUrl = normalizeAnvilUrl(remoteInfo.detectedUrl);
50725
- logger_logger.verbose(chalk_source.cyan("Resolved URL from remote for app ID: ") + chalk_source.bold(anvilUrl));
50726
- }
50727
- if (remoteInfo.detectedUsername && !explicitUsername && !username) {
50728
- username = remoteInfo.detectedUsername;
50729
- logger_logger.verbose(chalk_source.cyan("Resolved username from remote for app ID: ") + chalk_source.bold(username));
50730
- }
50731
- } else {
50732
- logger_logger.verbose(chalk_source.cyan("No app ID provided, attempting auto-detection..."));
50733
- if (0 === filteredCandidates.length) {
50734
- logger_logger.verbose(chalk_source.gray("No app IDs found in git remotes."));
50735
- const resolvedFallbackUrl = await resolveUrlForFallback(explicitUrl);
50736
- if (null === resolvedFallbackUrl) {
50737
- logger_logger.warn("Operation cancelled.");
50738
- process.exit(0);
50739
- }
50740
- fallbackUrl = normalizeAnvilUrl(resolvedFallbackUrl);
50741
- const lookupDecision = await confirmReverseLookupWithResolvedUser(fallbackUrl, username);
50742
- if (null === lookupDecision) {
50743
- logger_logger.warn("Operation cancelled.");
50744
- process.exit(0);
50745
- }
50746
- username = lookupDecision.username;
50747
- if (lookupDecision.shouldContinue) {
50748
- logger_logger.progress("detect", `Searching ${fallbackUrl} ${username ? `for ${username}` : ""} for matching app IDs...`);
50749
- const reverseLookupCandidates = await detectAppIdsByCommitLookup(repoPath, {
50750
- anvilUrl: fallbackUrl,
50751
- username,
50752
- includeRemotes: false
50753
- });
50754
- logger_logger.progressEnd("detect");
50755
- for (const c of reverseLookupCandidates)filteredCandidates.push({
50756
- ...c,
50757
- detectedUrl: fallbackUrl
50758
- });
50759
- }
50760
- }
50761
- if (filteredCandidates.length > 0) {
50762
- for (const c of filteredCandidates)logger_logger.verbose(chalk_source.gray(` Found: ${formatCandidateLabel(c)}`));
50763
- if (filteredCandidates.length > 1) logger_logger.verbose(chalk_source.yellow(`Found ${filteredCandidates.length} potential app IDs`));
50764
- }
50765
- if (useFirst && filteredCandidates.length > 0) {
50766
- const selected = filteredCandidates[0];
50767
- finalAppId = selected.appId;
50768
- if (selected.detectedUrl) anvilUrl = normalizeAnvilUrl(selected.detectedUrl);
50769
- if (selected.detectedUsername && !username) username = selected.detectedUsername;
50770
- logger_logger.success("Auto-selected first app ID: " + chalk_source.bold(finalAppId));
50771
- } else {
50772
- const selected = await selectAppId(filteredCandidates);
50773
- if (!selected) {
50774
- logger_logger.error("No app ID provided. Cannot continue without an app ID.");
50775
- process.exit(1);
50776
- }
50777
- finalAppId = selected.appId;
50778
- if (selected.detectedUrl) anvilUrl = normalizeAnvilUrl(selected.detectedUrl);
50779
- if (selected.detectedUsername && !username) username = selected.detectedUsername;
50780
- }
50781
- }
50782
- const binding = await getAppAuthBinding(repoPath, finalAppId);
50783
- if (binding.url && !explicitUrl) {
50784
- anvilUrl = normalizeAnvilUrl(binding.url);
50785
- logger_logger.verbose(chalk_source.cyan("Using app binding URL: ") + chalk_source.bold(anvilUrl));
50786
- }
50787
- if (binding.username && !explicitUsername) {
50788
- username = binding.username;
50789
- logger_logger.verbose(chalk_source.cyan("Using app binding username: ") + chalk_source.bold(username));
50790
- }
50791
- shouldPersistUsernameBinding = !binding.username && !explicitUsername;
50792
- if (explicitUrl) anvilUrl = normalizeAnvilUrl(explicitUrl);
50793
- else if (!anvilUrl) if (fallbackUrl) anvilUrl = fallbackUrl;
50794
- else {
50795
- const resolvedFallbackUrl = await resolveUrlForFallback();
50796
- if (null === resolvedFallbackUrl) {
50797
- logger_logger.warn("Operation cancelled.");
50798
- process.exit(0);
50799
- }
50800
- anvilUrl = normalizeAnvilUrl(resolvedFallbackUrl);
50801
- }
50802
- anvilUrl = normalizeAnvilUrl(anvilUrl);
50803
- logger_logger.verbose(chalk_source.green("Using app ID: ") + chalk_source.bold(finalAppId));
50804
- logger_logger.verbose(chalk_source.cyan("Using Anvil URL: ") + chalk_source.bold(anvilUrl));
50805
- const resolvedUsername = await resolveUsernameForUrl(anvilUrl, username);
50806
- if (null === resolvedUsername) {
50807
- logger_logger.warn("Operation cancelled.");
50808
- process.exit(0);
50809
- }
50810
- username = resolvedUsername;
50811
- if (shouldPersistUsernameBinding && username && auth_getAccountsForUrl(anvilUrl).length > 1) {
50812
- await setAppAuthBinding(repoPath, finalAppId, {
50813
- url: anvilUrl,
50814
- username
50815
- });
50816
- logger_logger.verbose(chalk_source.cyan("Saved app account binding for future non-interactive use."));
50817
- }
50818
- if (username) logger_logger.verbose(chalk_source.cyan("Using account: ") + chalk_source.bold(username));
50819
- if (!hasTokensForUrl(anvilUrl, username)) {
50820
- if (username) logger_logger.error(`Not logged in to ${anvilUrl} as ${username}`);
50821
- else logger_logger.error(`Not logged in to ${anvilUrl}`);
50822
- logger_logger.verbose(chalk_source.yellow("Please log in first:"));
50823
- console.log(chalk_source.cyan(` anvil login ${anvilUrl.replace(/^https?:\/\//, "")}`));
50824
- process.exit(1);
50825
- }
50826
- logger_logger.verbose(chalk_source.green("✓ Authentication tokens found"));
50827
- logger_logger.progress("validate", `Validating app ID: ${finalAppId}`);
50828
- const appIdValidation = await validateAppId(finalAppId, anvilUrl, username);
50829
- logger_logger.progressEnd("validate");
50830
- if (!appIdValidation.valid) {
50831
- logger_logger.error(`App ID validation failed: ${appIdValidation.error}`);
50832
- logger_logger.verbose(chalk_source.yellow("This could mean:"));
50833
- logger_logger.verbose(chalk_source.yellow(" • The app ID doesn't exist on the server"));
50834
- logger_logger.verbose(chalk_source.yellow(" • You don't have access to this app"));
50835
- logger_logger.verbose(chalk_source.yellow(" • There's a network or authentication issue"));
50836
- process.exit(1);
50837
- }
50838
- logger_logger.verbose(chalk_source.green(`✔ App ID validated successfully`));
50839
- if (appIdValidation.app_name) logger_logger.verbose(chalk_source.gray(` App name: ${appIdValidation.app_name}`));
50840
- if (stagedOnly) logger_logger.info(chalk_source.yellow("▸ Staged-only mode: Only staged changes will be synced"));
50841
- if (autoMode) logger_logger.info(chalk_source.cyan("▸ Auto mode: Will automatically restart on branch changes and sync when behind"));
50842
- logger_logger.progress("init", "Initializing watch session...");
50843
- const session = await api_watch(repoPath, finalAppId, anvilUrl, stagedOnly, username);
50844
- logger_logger.progressEnd("init");
50845
- logger_logger.verbose(chalk_source.blue("Watching for file changes..."));
50846
- const started = await checkSyncStatusAndStart(session, {
50847
- autoMode,
50848
- repoPath,
50849
- appId: finalAppId,
50850
- anvilUrl,
50851
- stagedOnly,
50852
- username
50853
- });
50854
- if (!started) process.exit(1);
50855
- } catch (e) {
50856
- logger_logger.error("Error: " + errors_getErrorMessage(e));
50857
- process.exit(1);
50858
- }
50859
- }
50860
- function registerWatchCommand(program) {
50861
- const watchCommand = program.command("watch [path]").description("Watch for file changes and sync to Anvil").alias("sync").alias("w").option("-A, --appid <APP_ID>", "Specify app ID directly").option("-f, --first", "Auto-select first detected app ID without confirmation").option("-s, --staged-only", "Only sync staged changes (use git add to stage files)").option("-a, --auto", "Auto mode: restart on branch changes and sync when behind").option("-V, --verbose", "Show detailed output").option("-u, --url <ANVIL_URL>", "Specify Anvil server URL (e.g., anvil.works, localhost)").option("-U, --user <USERNAME>", "Specify which user account to use").action(async (path, options)=>{
50862
- if (void 0 !== options.verbose && logger_logger instanceof CLILogger) logger_logger.setVerbose(options.verbose);
50863
- await handleWatchCommand({
50864
- path,
50865
- appid: options.appid,
50866
- useFirst: options.first,
50867
- stagedOnly: options.stagedOnly,
50868
- autoMode: options.auto,
50869
- verbose: options.verbose,
50870
- url: options.url,
50871
- user: options.user
50872
- });
50873
- });
50874
- return watchCommand;
50751
+ function decideFallbackUrl(explicitUrl, availableUrls = getAvailableAnvilUrls()) {
50752
+ if (explicitUrl) return {
50753
+ source: "explicit",
50754
+ url: normalizeAnvilUrl(explicitUrl)
50755
+ };
50756
+ if (availableUrls.length > 1) return {
50757
+ source: "available-multiple",
50758
+ urls: availableUrls
50759
+ };
50760
+ if (1 === availableUrls.length) return {
50761
+ source: "available-single",
50762
+ url: availableUrls[0]
50763
+ };
50764
+ const fromConfig = getConfig("anvilUrl");
50765
+ if ("string" == typeof fromConfig && fromConfig.trim()) return {
50766
+ source: "config",
50767
+ url: normalizeAnvilUrl(fromConfig.trim())
50768
+ };
50769
+ return {
50770
+ source: "default",
50771
+ url: isDevMode() ? "http://localhost:3000" : "https://anvil.works"
50772
+ };
50875
50773
  }
50876
- const external_node_url_namespaceObject = require("node:url");
50877
- var external_node_child_process_ = __webpack_require__("node:child_process");
50878
- let isDockerCached;
50879
- function hasDockerEnv() {
50880
- try {
50881
- external_node_fs_.statSync('/.dockerenv');
50882
- return true;
50883
- } catch {
50884
- return false;
50885
- }
50774
+ function decideUsernameForUrl(accounts) {
50775
+ if (0 === accounts.length) return {
50776
+ source: "none"
50777
+ };
50778
+ if (1 === accounts.length) return {
50779
+ source: "single",
50780
+ username: accounts[0]
50781
+ };
50782
+ return {
50783
+ source: "multiple",
50784
+ usernames: [
50785
+ ...accounts
50786
+ ]
50787
+ };
50886
50788
  }
50887
- function hasDockerCGroup() {
50888
- try {
50889
- return external_node_fs_.readFileSync('/proc/self/cgroup', 'utf8').includes('docker');
50890
- } catch {
50891
- return false;
50789
+ async function resolveUsernameForUrl(anvilUrl, explicitUsername, promptMessage = "Multiple accounts found. Which account owns this app?") {
50790
+ if (explicitUsername) return explicitUsername;
50791
+ const decision = decideUsernameForUrl(auth_getAccountsForUrl(anvilUrl));
50792
+ if ("none" === decision.source) return;
50793
+ if ("single" === decision.source) {
50794
+ logger_logger.verbose(chalk_source.cyan("Auto-selected account: ") + chalk_source.bold(decision.username));
50795
+ return decision.username;
50892
50796
  }
50797
+ const choices = decision.usernames.map((acct)=>({
50798
+ name: acct,
50799
+ value: acct
50800
+ }));
50801
+ choices.push({
50802
+ name: "Cancel",
50803
+ value: null
50804
+ });
50805
+ return logger_logger.select(promptMessage, choices, decision.usernames[0]);
50893
50806
  }
50894
- function isDocker() {
50895
- if (void 0 === isDockerCached) isDockerCached = hasDockerEnv() || hasDockerCGroup();
50896
- return isDockerCached;
50807
+ async function confirmReverseLookupWithResolvedUser(anvilUrl, username) {
50808
+ const resolvedUsername = await resolveUsernameForUrl(anvilUrl, username, `Multiple accounts found for ${anvilUrl}. Which account should be used for app lookup?`);
50809
+ if (null === resolvedUsername) return null;
50810
+ const shouldContinue = await logger_logger.confirm(`Search ${anvilUrl} ${resolvedUsername ? `for ${resolvedUsername}` : ""} for matching app IDs? (slower)`, true);
50811
+ return {
50812
+ username: resolvedUsername,
50813
+ shouldContinue
50814
+ };
50897
50815
  }
50898
- let cachedResult;
50899
- const hasContainerEnv = ()=>{
50816
+ async function resolveUrlForFallback(explicitUrl) {
50817
+ const decision = decideFallbackUrl(explicitUrl);
50818
+ if ("available-multiple" !== decision.source) return decision.url;
50819
+ const choices = decision.urls.map((url)=>({
50820
+ name: url,
50821
+ value: url
50822
+ }));
50823
+ choices.push({
50824
+ name: "Cancel",
50825
+ value: null
50826
+ });
50827
+ const selectedUrl = await logger_logger.select("Multiple logged-in Anvil URLs found. Which one would you like to use?", choices, decision.urls[0]);
50828
+ return selectedUrl;
50829
+ }
50830
+ function resolveWatchOpenPath(repoPath) {
50831
+ return external_path_default().resolve(repoPath);
50832
+ }
50833
+ async function openWatchPath(targetPath, deps) {
50834
+ const preferredEditor = String(getConfig("preferredEditor") || "").trim();
50835
+ const preferredEditorCommand = preferredEditor ? getPreferredEditorCommand(preferredEditor) : "";
50836
+ await openPathInEditorOrDefault(targetPath, preferredEditorCommand, deps);
50837
+ logger_logger.info(chalk_source.gray(`Opened ${targetPath}`));
50838
+ }
50839
+ async function selectAppId(candidates) {
50840
+ if (0 === candidates.length) {
50841
+ logger_logger.warn("Could not auto-detect app ID from repository.");
50842
+ const rl = external_readline_namespaceObject.createInterface({
50843
+ input: process.stdin,
50844
+ output: process.stdout
50845
+ });
50846
+ const manualAppId = await new Promise((resolve)=>{
50847
+ const handleInterrupt = ()=>{
50848
+ rl.close();
50849
+ logger_logger.warn("Operation cancelled by user.");
50850
+ resolve(null);
50851
+ };
50852
+ rl.on("SIGINT", handleInterrupt);
50853
+ rl.question(chalk_source.cyan("Please enter app ID manually (or press Enter to exit): "), (answer)=>{
50854
+ rl.removeListener("SIGINT", handleInterrupt);
50855
+ rl.close();
50856
+ const trimmed = answer.trim();
50857
+ trimmed ? resolve(trimmed) : resolve(null);
50858
+ });
50859
+ });
50860
+ if (manualAppId) return {
50861
+ appId: manualAppId,
50862
+ source: "config",
50863
+ description: "Manual entry"
50864
+ };
50865
+ return null;
50866
+ }
50867
+ const choices = candidates.map((candidate, index)=>({
50868
+ name: formatCandidateLabel(candidate),
50869
+ value: index
50870
+ }));
50871
+ choices.push({
50872
+ name: "Cancel",
50873
+ value: null
50874
+ });
50900
50875
  try {
50901
- external_node_fs_.statSync('/run/.containerenv');
50876
+ const promptText = 1 === candidates.length ? "Confirm the detected app ID:" : "Select an app ID:";
50877
+ const answer = await logger_logger.prompt([
50878
+ {
50879
+ type: "list",
50880
+ name: "appId",
50881
+ message: promptText,
50882
+ choices: choices,
50883
+ pageSize: 10
50884
+ }
50885
+ ]);
50886
+ if (null === answer.appId) {
50887
+ const rl = external_readline_namespaceObject.createInterface({
50888
+ input: process.stdin,
50889
+ output: process.stdout
50890
+ });
50891
+ const manualAppId = await new Promise((resolve)=>{
50892
+ rl.question(chalk_source.cyan("Please enter app ID manually (or press Enter to exit): "), (answer)=>{
50893
+ rl.close();
50894
+ const trimmed = answer.trim();
50895
+ trimmed ? resolve(trimmed) : resolve(null);
50896
+ });
50897
+ });
50898
+ if (manualAppId) return {
50899
+ appId: manualAppId,
50900
+ source: "config",
50901
+ description: "Manual entry"
50902
+ };
50903
+ return null;
50904
+ }
50905
+ return candidates[answer.appId];
50906
+ } catch (error) {
50907
+ const errorObj = error;
50908
+ if ("ExitPromptError" === errorObj.name || errorObj.message.includes("User force closed")) logger_logger.warn("Operation cancelled by user.");
50909
+ return null;
50910
+ }
50911
+ }
50912
+ async function pushToAnvil(options) {
50913
+ try {
50914
+ const authToken = await auth_getValidAuthToken(options.anvilUrl, options.username);
50915
+ const pushUrl = getGitPushUrl(options.appId, authToken, options.anvilUrl);
50916
+ const git = new GitService(options.repoPath);
50917
+ const refSpec = `${options.branchName}:${options.branchName}`;
50918
+ logger_logger.progress("push", "Pushing to Anvil...");
50919
+ await git.push(pushUrl, refSpec, options.force ?? false);
50920
+ logger_logger.progressEnd("push", "Pushed to Anvil");
50902
50921
  return true;
50903
- } catch {
50922
+ } catch (e) {
50923
+ logger_logger.progressEnd("push", "Push failed");
50924
+ logger_logger.error(`Failed to push: ${errors_getErrorMessage(e)}`);
50904
50925
  return false;
50905
50926
  }
50906
- };
50907
- function isInsideContainer() {
50908
- if (void 0 === cachedResult) cachedResult = hasContainerEnv() || isDocker();
50909
- return cachedResult;
50910
50927
  }
50911
- const isWsl = ()=>{
50912
- if ('linux' !== external_node_process_.platform) return false;
50913
- if (external_node_os_namespaceObject.release().toLowerCase().includes('microsoft')) {
50914
- if (isInsideContainer()) return false;
50915
- return true;
50928
+ async function fetchAndRebaseFromAnvil(options) {
50929
+ const git = new GitService(options.repoPath);
50930
+ const tempRef = `anvil-rebase-temp-${Date.now()}`;
50931
+ let didStash = false;
50932
+ try {
50933
+ const authToken = await auth_getValidAuthToken(options.anvilUrl, options.username);
50934
+ const fetchUrl = getGitFetchUrl(options.appId, authToken, options.anvilUrl);
50935
+ const hasChanges = await git.hasUncommittedChanges();
50936
+ if (hasChanges) {
50937
+ logger_logger.progress("rebase", "Stashing uncommitted changes...");
50938
+ didStash = await git.stash("anvil-sync: stash before rebase");
50939
+ }
50940
+ logger_logger.progressUpdate("rebase", "Fetching from Anvil...");
50941
+ await git.fetch(fetchUrl, `+${options.branchName}:${tempRef}`);
50942
+ logger_logger.progressUpdate("rebase", "Rebasing...");
50943
+ await git.rebase(tempRef);
50944
+ logger_logger.progressUpdate("rebase", "Pushing rebased changes...");
50945
+ const pushUrl = getGitPushUrl(options.appId, authToken, options.anvilUrl);
50946
+ await git.push(pushUrl, `${options.branchName}:${options.branchName}`, true);
50947
+ logger_logger.progressEnd("rebase", "Rebased and pushed to Anvil");
50948
+ await git.deleteRef(`refs/heads/${tempRef}`);
50949
+ if (didStash) try {
50950
+ await git.stashPop();
50951
+ logger_logger.verbose(chalk_source.green("Restored uncommitted changes"));
50952
+ } catch (e) {
50953
+ logger_logger.warn("Uncommitted changes were stashed but could not be restored automatically.");
50954
+ logger_logger.info(chalk_source.gray(" Run: git stash pop"));
50955
+ }
50956
+ return {
50957
+ success: true,
50958
+ conflicted: false
50959
+ };
50960
+ } catch (e) {
50961
+ const msg = errors_getErrorMessage(e);
50962
+ if (msg.includes("rebase") && (msg.includes("conflict") || msg.includes("CONFLICT") || msg.includes("could not apply"))) {
50963
+ try {
50964
+ await git.rebaseAbort();
50965
+ } catch {}
50966
+ await git.deleteRef(`refs/heads/${tempRef}`);
50967
+ if (didStash) try {
50968
+ await git.stashPop();
50969
+ } catch {
50970
+ logger_logger.warn("Your uncommitted changes are in the stash. Run: git stash pop");
50971
+ }
50972
+ logger_logger.progressEnd("rebase", "Rebase failed - conflicts");
50973
+ return {
50974
+ success: false,
50975
+ conflicted: true
50976
+ };
50977
+ }
50978
+ await git.deleteRef(`refs/heads/${tempRef}`);
50979
+ if (didStash) try {
50980
+ await git.stashPop();
50981
+ } catch {
50982
+ logger_logger.warn("Your uncommitted changes are in the stash. Run: git stash pop");
50983
+ }
50984
+ logger_logger.progressEnd("rebase", "Rebase failed");
50985
+ logger_logger.error(`Failed to rebase: ${msg}`);
50986
+ return {
50987
+ success: false,
50988
+ conflicted: false
50989
+ };
50916
50990
  }
50991
+ }
50992
+ async function fetchAndHardResetFromAnvil(options) {
50993
+ const git = new GitService(options.repoPath);
50994
+ const tempRef = `anvil-reset-temp-${Date.now()}`;
50917
50995
  try {
50918
- return external_node_fs_.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft') ? !isInsideContainer() : false;
50919
- } catch {
50996
+ const authToken = await auth_getValidAuthToken(options.anvilUrl, options.username);
50997
+ const fetchUrl = getGitFetchUrl(options.appId, authToken, options.anvilUrl);
50998
+ logger_logger.progress("reset", "Fetching from Anvil...");
50999
+ await git.fetch(fetchUrl, `+${options.branchName}:${tempRef}`);
51000
+ logger_logger.progressUpdate("reset", "Resetting to Anvil's version...");
51001
+ await git.reset(tempRef, "hard");
51002
+ logger_logger.progressEnd("reset", "Reset to Anvil's version");
51003
+ await git.deleteRef(`refs/heads/${tempRef}`);
51004
+ return true;
51005
+ } catch (e) {
51006
+ await git.deleteRef(`refs/heads/${tempRef}`);
51007
+ logger_logger.progressEnd("reset", "Reset failed");
51008
+ logger_logger.error(`Failed to reset: ${errors_getErrorMessage(e)}`);
50920
51009
  return false;
50921
51010
  }
50922
- };
50923
- const is_wsl = external_node_process_.env.__IS_WSL_TEST__ ? isWsl : isWsl();
50924
- const wslDrivesMountPoint = (()=>{
50925
- const defaultMountPoint = '/mnt/';
50926
- let mountPoint;
50927
- return async function() {
50928
- if (mountPoint) return mountPoint;
50929
- const configFilePath = '/etc/wsl.conf';
50930
- let isConfigFileExists = false;
50931
- try {
50932
- await external_node_fs_promises_namespaceObject.access(configFilePath, external_node_fs_promises_namespaceObject.constants.F_OK);
50933
- isConfigFileExists = true;
50934
- } catch {}
50935
- if (!isConfigFileExists) return defaultMountPoint;
50936
- const configContent = await external_node_fs_promises_namespaceObject.readFile(configFilePath, {
50937
- encoding: 'utf8'
50938
- });
50939
- const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
50940
- if (!configMountPoint) return defaultMountPoint;
50941
- mountPoint = configMountPoint.groups.mountPoint.trim();
50942
- mountPoint = mountPoint.endsWith('/') ? mountPoint : `${mountPoint}/`;
50943
- return mountPoint;
50944
- };
50945
- })();
50946
- const powerShellPathFromWsl = async ()=>{
50947
- const mountPoint = await wslDrivesMountPoint();
50948
- return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
50949
- };
50950
- const powerShellPath = async ()=>{
50951
- if (is_wsl) return powerShellPathFromWsl();
50952
- return `${external_node_process_.env.SYSTEMROOT || external_node_process_.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
50953
- };
50954
- function defineLazyProperty(object, propertyName, valueGetter) {
50955
- const define = (value)=>Object.defineProperty(object, propertyName, {
50956
- value,
50957
- enumerable: true,
50958
- writable: true
50959
- });
50960
- Object.defineProperty(object, propertyName, {
50961
- configurable: true,
50962
- enumerable: true,
50963
- get () {
50964
- const result = valueGetter();
50965
- define(result);
50966
- return result;
50967
- },
50968
- set (value) {
50969
- define(value);
50970
- }
50971
- });
50972
- return object;
50973
51011
  }
50974
- const execFileAsync = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_.execFile);
50975
- async function defaultBrowserId() {
50976
- if ('darwin' !== external_node_process_.platform) throw new Error('macOS only');
50977
- const { stdout } = await execFileAsync('defaults', [
50978
- 'read',
50979
- 'com.apple.LaunchServices/com.apple.launchservices.secure',
50980
- 'LSHandlers'
50981
- ]);
50982
- const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
50983
- const browserId = match?.groups.id ?? 'com.apple.Safari';
50984
- if ('com.apple.safari' === browserId) return 'com.apple.Safari';
50985
- return browserId;
50986
- }
50987
- const run_applescript_execFileAsync = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_.execFile);
50988
- async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
50989
- if ('darwin' !== external_node_process_.platform) throw new Error('macOS only');
50990
- const outputArguments = humanReadableOutput ? [] : [
50991
- '-ss'
50992
- ];
50993
- const execOptions = {};
50994
- if (signal) execOptions.signal = signal;
50995
- const { stdout } = await run_applescript_execFileAsync("osascript", [
50996
- '-e',
50997
- script,
50998
- outputArguments
50999
- ], execOptions);
51000
- return stdout.trim();
51001
- }
51002
- async function bundleName(bundleId) {
51003
- return runAppleScript(`tell application "Finder" to set app_path to application file id "${bundleId}" as string\ntell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`);
51012
+ function getSyncStateCategory(syncStatus) {
51013
+ if (syncStatus?.branchMissing) return "branch-missing";
51014
+ if (syncStatus?.diverged) return "diverged";
51015
+ if (syncStatus?.ahead && !syncStatus?.behind) return "ahead";
51016
+ if (syncStatus?.behind && !syncStatus?.ahead) return "behind";
51017
+ if (syncStatus?.ahead && syncStatus?.behind) return "diverged";
51018
+ return "in-sync";
51004
51019
  }
51005
- const windows_execFileAsync = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_.execFile);
51006
- const windowsBrowserProgIds = {
51007
- MSEdgeHTM: {
51008
- name: 'Edge',
51009
- id: 'com.microsoft.edge'
51010
- },
51011
- MSEdgeBHTML: {
51012
- name: 'Edge Beta',
51013
- id: 'com.microsoft.edge.beta'
51014
- },
51015
- MSEdgeDHTML: {
51016
- name: 'Edge Dev',
51017
- id: 'com.microsoft.edge.dev'
51018
- },
51019
- AppXq0fevzme2pys62n3e0fbqa7peapykr8v: {
51020
- name: 'Edge',
51021
- id: 'com.microsoft.edge.old'
51022
- },
51023
- ChromeHTML: {
51024
- name: 'Chrome',
51025
- id: 'com.google.chrome'
51026
- },
51027
- ChromeBHTML: {
51028
- name: 'Chrome Beta',
51029
- id: 'com.google.chrome.beta'
51030
- },
51031
- ChromeDHTML: {
51032
- name: 'Chrome Dev',
51033
- id: 'com.google.chrome.dev'
51034
- },
51035
- ChromiumHTM: {
51036
- name: 'Chromium',
51037
- id: 'org.chromium.Chromium'
51038
- },
51039
- BraveHTML: {
51040
- name: 'Brave',
51041
- id: 'com.brave.Browser'
51042
- },
51043
- BraveBHTML: {
51044
- name: 'Brave Beta',
51045
- id: 'com.brave.Browser.beta'
51046
- },
51047
- BraveDHTML: {
51048
- name: 'Brave Dev',
51049
- id: 'com.brave.Browser.dev'
51050
- },
51051
- BraveSSHTM: {
51052
- name: 'Brave Nightly',
51053
- id: 'com.brave.Browser.nightly'
51054
- },
51055
- FirefoxURL: {
51056
- name: 'Firefox',
51057
- id: 'org.mozilla.firefox'
51058
- },
51059
- OperaStable: {
51060
- name: 'Opera',
51061
- id: 'com.operasoftware.Opera'
51062
- },
51063
- VivaldiHTM: {
51064
- name: 'Vivaldi',
51065
- id: 'com.vivaldi.Vivaldi'
51066
- },
51067
- 'IE.HTTP': {
51068
- name: 'Internet Explorer',
51069
- id: 'com.microsoft.ie'
51020
+ async function recheckSyncStatus(previousCategory, previousBranch, options) {
51021
+ try {
51022
+ logger_logger.progress("verify", "Verifying repository status...");
51023
+ const freshSession = await api_watch(options.repoPath, options.appId, options.anvilUrl, options.stagedOnly, options.username);
51024
+ logger_logger.progressEnd("verify");
51025
+ const currentCategory = getSyncStateCategory(freshSession.syncStatus);
51026
+ const currentBranch = freshSession.getBranchName() || "master";
51027
+ if (currentCategory !== previousCategory || currentBranch !== previousBranch) {
51028
+ logger_logger.warn("Repository status has changed. Re-evaluating...");
51029
+ return freshSession;
51030
+ }
51031
+ freshSession.cleanup();
51032
+ return null;
51033
+ } catch (e) {
51034
+ logger_logger.progressEnd("verify");
51035
+ logger_logger.verbose(chalk_source.gray(`Could not re-verify status: ${errors_getErrorMessage(e)}`));
51036
+ return null;
51070
51037
  }
51071
- };
51072
- new Map(Object.entries(windowsBrowserProgIds));
51073
- class UnknownBrowserError extends Error {
51074
51038
  }
51075
- async function defaultBrowser(_execFileAsync = windows_execFileAsync) {
51076
- const { stdout } = await _execFileAsync('reg', [
51077
- 'QUERY',
51078
- ' HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice',
51079
- '/v',
51080
- 'ProgId'
51081
- ]);
51082
- const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
51083
- if (!match) throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
51084
- const { id } = match.groups;
51085
- const browser = windowsBrowserProgIds[id];
51086
- if (!browser) throw new UnknownBrowserError(`Unknown browser ID: ${id}`);
51087
- return browser;
51039
+ async function recreateSessionAndValidate(options) {
51040
+ logger_logger.progress("init", "Restarting watch session...");
51041
+ try {
51042
+ const newSession = await api_watch(options.repoPath, options.appId, options.anvilUrl, options.stagedOnly, options.username);
51043
+ logger_logger.progressEnd("init");
51044
+ return await checkSyncStatusAndStart(newSession, options);
51045
+ } catch (e) {
51046
+ logger_logger.progressEnd("init", "Failed");
51047
+ logger_logger.error(`Failed to restart: ${errors_getErrorMessage(e)}`);
51048
+ return false;
51049
+ }
51088
51050
  }
51089
- const default_browser_execFileAsync = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_.execFile);
51090
- const titleize = (string)=>string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x)=>x.toUpperCase());
51091
- async function default_browser_defaultBrowser() {
51092
- if ('darwin' === external_node_process_.platform) {
51093
- const id = await defaultBrowserId();
51094
- const name = await bundleName(id);
51095
- return {
51096
- name,
51097
- id
51098
- };
51051
+ async function checkSyncStatusAndStart(session, options) {
51052
+ const syncStatus = session.syncStatus;
51053
+ const branchName = session.getBranchName() || "master";
51054
+ const hasUncommitted = session.hasUncommittedChanges;
51055
+ const stateCategory = getSyncStateCategory(syncStatus);
51056
+ if (syncStatus?.branchMissing) {
51057
+ logger_logger.warn(`Branch '${branchName}' doesn't exist on Anvil yet.`);
51058
+ if (hasUncommitted) logger_logger.info(chalk_source.gray(" You also have uncommitted changes."));
51059
+ let shouldPush = options.autoMode;
51060
+ if (options.autoMode) logger_logger.info(chalk_source.cyan("→ Auto-pushing new branch to Anvil..."));
51061
+ else {
51062
+ shouldPush = await logger_logger.confirm("Would you like to push this branch to Anvil?", true);
51063
+ if (shouldPush) {
51064
+ const changed = await recheckSyncStatus(stateCategory, branchName, options);
51065
+ if (changed) return await checkSyncStatusAndStart(changed, options);
51066
+ }
51067
+ }
51068
+ if (!shouldPush) {
51069
+ logger_logger.warn("Watch cancelled. Push your branch to Anvil, then try again.");
51070
+ session.cleanup();
51071
+ return false;
51072
+ }
51073
+ session.cleanup();
51074
+ const pushed = await pushToAnvil({
51075
+ repoPath: options.repoPath,
51076
+ appId: options.appId,
51077
+ anvilUrl: options.anvilUrl,
51078
+ branchName,
51079
+ username: options.username
51080
+ });
51081
+ if (!pushed) return false;
51082
+ return await recreateSessionAndValidate(options);
51099
51083
  }
51100
- if ('linux' === external_node_process_.platform) {
51101
- const { stdout } = await default_browser_execFileAsync('xdg-mime', [
51102
- 'query',
51103
- 'default',
51104
- 'x-scheme-handler/http'
51105
- ]);
51106
- const id = stdout.trim();
51107
- const name = titleize(id.replace(/.desktop$/, '').replace('-', ' '));
51108
- return {
51109
- name,
51110
- id
51111
- };
51084
+ if (syncStatus?.behind || syncStatus?.ahead) if (syncStatus.ahead && !syncStatus.behind) {
51085
+ logger_logger.warn(`Your local repository is ${syncStatus.ahead} commit(s) ahead of Anvil.`);
51086
+ if (hasUncommitted) logger_logger.info(chalk_source.gray(" You also have uncommitted changes."));
51087
+ let shouldPush = options.autoMode;
51088
+ if (options.autoMode) logger_logger.info(chalk_source.cyan("→ Auto-pushing to Anvil..."));
51089
+ else {
51090
+ const action = await logger_logger.select("What would you like to do?", [
51091
+ {
51092
+ name: "Push your changes to Anvil (recommended)",
51093
+ value: "push"
51094
+ },
51095
+ {
51096
+ name: "Exit and handle manually",
51097
+ value: "exit"
51098
+ }
51099
+ ], "push");
51100
+ shouldPush = "push" === action;
51101
+ if (shouldPush) {
51102
+ const changed = await recheckSyncStatus(stateCategory, branchName, options);
51103
+ if (changed) return await checkSyncStatusAndStart(changed, options);
51104
+ }
51105
+ }
51106
+ if (!shouldPush) {
51107
+ logger_logger.warn("Watch cancelled.");
51108
+ session.cleanup();
51109
+ return false;
51110
+ }
51111
+ session.cleanup();
51112
+ const pushed = await pushToAnvil({
51113
+ repoPath: options.repoPath,
51114
+ appId: options.appId,
51115
+ anvilUrl: options.anvilUrl,
51116
+ branchName,
51117
+ username: options.username
51118
+ });
51119
+ if (!pushed) return false;
51120
+ return await recreateSessionAndValidate(options);
51121
+ } else if (syncStatus.diverged) {
51122
+ logger_logger.warn("Your local repository has diverged from Anvil.");
51123
+ logger_logger.info(chalk_source.gray(` You are ${syncStatus.ahead} commit(s) ahead and ${syncStatus.behind} commit(s) behind.`));
51124
+ if (hasUncommitted) logger_logger.info(chalk_source.gray(" You also have uncommitted changes."));
51125
+ session.cleanup();
51126
+ if (options.autoMode) {
51127
+ logger_logger.info(chalk_source.cyan("→ Auto-rebasing onto Anvil's version..."));
51128
+ const result = await fetchAndRebaseFromAnvil({
51129
+ repoPath: options.repoPath,
51130
+ appId: options.appId,
51131
+ anvilUrl: options.anvilUrl,
51132
+ branchName,
51133
+ username: options.username
51134
+ });
51135
+ if (result.conflicted) {
51136
+ logger_logger.error("Rebase failed due to conflicts. Please resolve manually:");
51137
+ logger_logger.info(chalk_source.gray(" 1. Run: git fetch anvil && git rebase anvil/" + branchName));
51138
+ logger_logger.info(chalk_source.gray(" 2. Resolve conflicts, then: git rebase --continue"));
51139
+ logger_logger.info(chalk_source.gray(" 3. Push: git push anvil " + branchName));
51140
+ return false;
51141
+ }
51142
+ if (!result.success) return false;
51143
+ return await recreateSessionAndValidate(options);
51144
+ }
51145
+ const action = await logger_logger.select("How would you like to resolve this?", [
51146
+ {
51147
+ name: "Pull from Anvil and rebase your changes on top (recommended)",
51148
+ value: "rebase"
51149
+ },
51150
+ {
51151
+ name: "Discard your local commits and use Anvil's version",
51152
+ value: "reset"
51153
+ },
51154
+ {
51155
+ name: "Push your version to Anvil (overwrites remote)",
51156
+ value: "push"
51157
+ },
51158
+ {
51159
+ name: "Exit and handle manually",
51160
+ value: "exit"
51161
+ }
51162
+ ], "rebase");
51163
+ if ("exit" === action) {
51164
+ logger_logger.warn("Watch cancelled.");
51165
+ return false;
51166
+ }
51167
+ const changed = await recheckSyncStatus(stateCategory, branchName, options);
51168
+ if (changed) return await checkSyncStatusAndStart(changed, options);
51169
+ if ("rebase" === action) {
51170
+ const result = await fetchAndRebaseFromAnvil({
51171
+ repoPath: options.repoPath,
51172
+ appId: options.appId,
51173
+ anvilUrl: options.anvilUrl,
51174
+ branchName,
51175
+ username: options.username
51176
+ });
51177
+ if (result.conflicted) {
51178
+ logger_logger.error("Rebase failed due to conflicts. Please resolve manually:");
51179
+ logger_logger.info(chalk_source.gray(" 1. Run: git fetch anvil && git rebase anvil/" + branchName));
51180
+ logger_logger.info(chalk_source.gray(" 2. Resolve conflicts, then: git rebase --continue"));
51181
+ logger_logger.info(chalk_source.gray(" 3. Push: git push anvil " + branchName));
51182
+ return false;
51183
+ }
51184
+ if (!result.success) return false;
51185
+ return await recreateSessionAndValidate(options);
51186
+ }
51187
+ if ("reset" === action) {
51188
+ const resetWarning = hasUncommitted ? `This will discard ${syncStatus.ahead} local commit(s) and your uncommitted changes. Are you sure?` : `This will discard ${syncStatus.ahead} local commit(s). Are you sure?`;
51189
+ const confirmed = await logger_logger.confirm(resetWarning, false);
51190
+ if (!confirmed) {
51191
+ logger_logger.warn("Watch cancelled.");
51192
+ return false;
51193
+ }
51194
+ const reset = await fetchAndHardResetFromAnvil({
51195
+ repoPath: options.repoPath,
51196
+ appId: options.appId,
51197
+ anvilUrl: options.anvilUrl,
51198
+ branchName,
51199
+ username: options.username
51200
+ });
51201
+ if (!reset) return false;
51202
+ return await recreateSessionAndValidate(options);
51203
+ }
51204
+ if ("push" === action) {
51205
+ const confirmed = await logger_logger.confirm("This will overwrite Anvil's version. Are you sure?", false);
51206
+ if (!confirmed) {
51207
+ logger_logger.warn("Watch cancelled.");
51208
+ return false;
51209
+ }
51210
+ const pushed = await pushToAnvil({
51211
+ repoPath: options.repoPath,
51212
+ appId: options.appId,
51213
+ anvilUrl: options.anvilUrl,
51214
+ branchName,
51215
+ username: options.username,
51216
+ force: true
51217
+ });
51218
+ if (!pushed) return false;
51219
+ return await recreateSessionAndValidate(options);
51220
+ }
51221
+ } else {
51222
+ logger_logger.warn(`Your local repository is ${syncStatus.behind} commit(s) behind Anvil.`);
51223
+ logger_logger.verbose(chalk_source.gray(" You need to sync to the latest version before watching."));
51224
+ if (hasUncommitted) logger_logger.info(chalk_source.gray(" You also have uncommitted changes."));
51225
+ let shouldSync = options.autoMode;
51226
+ if (options.autoMode) logger_logger.info(chalk_source.cyan("→ Auto-syncing to latest version..."));
51227
+ else {
51228
+ shouldSync = await logger_logger.confirm("Would you like to sync to the latest version from Anvil now?", true);
51229
+ if (shouldSync) {
51230
+ const changed = await recheckSyncStatus(stateCategory, branchName, options);
51231
+ if (changed) return await checkSyncStatusAndStart(changed, options);
51232
+ }
51233
+ }
51234
+ if (!shouldSync) {
51235
+ logger_logger.warn("Watch cancelled. Please sync manually before watching.");
51236
+ session.cleanup();
51237
+ return false;
51238
+ }
51239
+ session.cleanup();
51240
+ let authToken;
51241
+ try {
51242
+ authToken = await auth_getValidAuthToken(options.anvilUrl, options.username);
51243
+ } catch (e) {
51244
+ logger_logger.error("Not logged in. Please log in first.");
51245
+ process.exit(1);
51246
+ }
51247
+ logger_logger.progress("sync-latest", "Syncing to latest version from Anvil...");
51248
+ try {
51249
+ const newCommitId = await syncToLatest(options.repoPath, options.appId, options.anvilUrl, authToken, branchName, options.username);
51250
+ logger_logger.progressEnd("sync-latest", `Synced to latest version: ${newCommitId.substring(0, 8)}`);
51251
+ } catch (e) {
51252
+ logger_logger.progressEnd("sync-latest", "Failed");
51253
+ logger_logger.error(`Failed to sync: ${errors_getErrorMessage(e)}`);
51254
+ process.exit(1);
51255
+ }
51256
+ return await recreateSessionAndValidate(options);
51112
51257
  }
51113
- if ('win32' === external_node_process_.platform) return defaultBrowser();
51114
- throw new Error('Only macOS, Linux, and Windows are supported');
51258
+ await startWatchingWithEventHandlers(session, options);
51259
+ return true;
51115
51260
  }
51116
- const execFile = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_.execFile);
51117
- const open_dirname = external_node_path_.dirname((0, external_node_url_namespaceObject.fileURLToPath)(__rslib_import_meta_url__));
51118
- const localXdgOpenPath = external_node_path_.join(open_dirname, 'xdg-open');
51119
- const { platform, arch } = external_node_process_;
51120
- async function getWindowsDefaultBrowserFromWsl() {
51121
- const powershellPath = await powerShellPath();
51122
- const rawCommand = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
51123
- const encodedCommand = external_node_buffer_namespaceObject.Buffer.from(rawCommand, 'utf16le').toString('base64');
51124
- const { stdout } = await execFile(powershellPath, [
51125
- '-NoProfile',
51126
- '-NonInteractive',
51127
- '-ExecutionPolicy',
51128
- 'Bypass',
51129
- '-EncodedCommand',
51130
- encodedCommand
51131
- ], {
51132
- encoding: 'utf8'
51261
+ async function startWatchingWithEventHandlers(session, options) {
51262
+ const { autoMode, repoPath, appId, anvilUrl, stagedOnly } = options;
51263
+ session.on("branch-changed", async ({ oldBranch, newBranch })=>{
51264
+ try {
51265
+ session.cleanup();
51266
+ logger_logger.warn(`Branch changed: ${chalk_source.bold(oldBranch)} ${chalk_source.bold(newBranch)}`);
51267
+ logger_logger.info(chalk_source.cyan("→ Restarting watch on new branch..."));
51268
+ const git = esm_default(repoPath);
51269
+ const actualBranch = (await git.revparse([
51270
+ "--abbrev-ref",
51271
+ "HEAD"
51272
+ ])).trim();
51273
+ if (actualBranch !== newBranch) logger_logger.verbose(chalk_source.yellow(`Branch changed again to ${chalk_source.bold(actualBranch)}, using that instead`));
51274
+ logger_logger.progress("init", "Initializing watch session...");
51275
+ try {
51276
+ const newSession = await api_watch(repoPath, appId, anvilUrl, stagedOnly, options.username);
51277
+ logger_logger.progressEnd("init");
51278
+ const started = await checkSyncStatusAndStart(newSession, options);
51279
+ if (!started) process.exit(0);
51280
+ } catch (e) {
51281
+ logger_logger.progressEnd("init", "Failed");
51282
+ logger_logger.error("Failed to restart watch: " + errors_getErrorMessage(e));
51283
+ process.exit(1);
51284
+ }
51285
+ } catch (error) {
51286
+ logger_logger.error(`Error handling branch change: ${error.message}`);
51287
+ process.exit(1);
51288
+ }
51133
51289
  });
51134
- const progId = stdout.trim();
51135
- const browserMap = {
51136
- ChromeHTML: 'com.google.chrome',
51137
- BraveHTML: 'com.brave.Browser',
51138
- MSEdgeHTM: 'com.microsoft.edge',
51139
- FirefoxURL: 'org.mozilla.firefox'
51140
- };
51141
- return browserMap[progId] ? {
51142
- id: browserMap[progId]
51143
- } : {};
51290
+ session.on("validation-failed", ({ reason, currentBranch })=>{
51291
+ logger_logger.error(chalk_source.red(`Validation failed: ${reason}`));
51292
+ logger_logger.verbose(chalk_source.yellow(` Current branch: ${currentBranch}`));
51293
+ logger_logger.verbose(chalk_source.yellow(" Please restart anvil to re-validate the branch."));
51294
+ session.cleanup();
51295
+ process.exit(1);
51296
+ });
51297
+ session.on("max-retries-exceeded", async ()=>{
51298
+ if (autoMode) {
51299
+ logger_logger.info(chalk_source.cyan("→ Auto-restarting watch session..."));
51300
+ session.cleanup();
51301
+ const restarted = await recreateSessionAndValidate(options);
51302
+ if (!restarted) process.exit(1);
51303
+ } else {
51304
+ const shouldRestart = await logger_logger.confirm("Would you like to restart the watch session?", true);
51305
+ if (shouldRestart) {
51306
+ session.cleanup();
51307
+ const restarted = await recreateSessionAndValidate(options);
51308
+ if (!restarted) process.exit(1);
51309
+ }
51310
+ }
51311
+ });
51312
+ if (session.hasUncommittedChanges) {
51313
+ if (!autoMode) {
51314
+ const shouldContinue = await logger_logger.confirm("Continue? Your uncommitted changes will be synced to Anvil.", true);
51315
+ if (!shouldContinue) {
51316
+ logger_logger.warn("Watch cancelled. Commit, stash, or discard your changes, then try again.");
51317
+ session.cleanup();
51318
+ process.exit(0);
51319
+ }
51320
+ }
51321
+ logger_logger.progress("save", "Saving uncommitted changes to Anvil...");
51322
+ try {
51323
+ await session.forceSave();
51324
+ logger_logger.progressEnd("save");
51325
+ } catch (e) {
51326
+ logger_logger.progressEnd("save", "Failed");
51327
+ logger_logger.error(`Failed to save: ${errors_getErrorMessage(e)}`);
51328
+ }
51329
+ }
51330
+ await session.startWatching();
51144
51331
  }
51145
- const pTryEach = async (array, mapper)=>{
51146
- let latestError;
51147
- for (const item of array)try {
51148
- return await mapper(item);
51149
- } catch (error) {
51150
- latestError = error;
51332
+ async function handleWatchCommand(options) {
51333
+ const invoked = process.argv[2];
51334
+ if ("sync" === invoked) {
51335
+ logger_logger.error("'sync' has been renamed to 'watch'. Please use 'anvil watch' instead.");
51336
+ process.exit(1);
51151
51337
  }
51152
- throw latestError;
51153
- };
51154
- const baseOpen = async (options)=>{
51155
- options = {
51156
- wait: false,
51157
- background: false,
51158
- newInstance: false,
51159
- allowNonzeroExitCode: false,
51160
- ...options
51161
- };
51162
- if (Array.isArray(options.app)) return pTryEach(options.app, (singleApp)=>baseOpen({
51163
- ...options,
51164
- app: singleApp
51165
- }));
51166
- let { name: app, arguments: appArguments = [] } = options.app ?? {};
51167
- appArguments = [
51168
- ...appArguments
51169
- ];
51170
- if (Array.isArray(app)) return pTryEach(app, (appName)=>baseOpen({
51171
- ...options,
51172
- app: {
51173
- name: appName,
51174
- arguments: appArguments
51338
+ const { path: repoPath = process.cwd(), appid: explicitAppId, useFirst = false, stagedOnly = false, autoMode = false, url: explicitUrl, user: explicitUsername, open: openAfterValidation = false } = options;
51339
+ try {
51340
+ const validationResult = await validateAnvilApp(repoPath);
51341
+ if (validationResult.appName) logger_logger.info(chalk_source.green("Anvil app: ") + chalk_source.bold(validationResult.appName));
51342
+ const detectedFromAllRemotes = await detectAppIdsFromAllRemotes(repoPath);
51343
+ let filteredCandidates = filterCandidates(detectedFromAllRemotes, explicitUrl, explicitUsername);
51344
+ logger_logger.verbose(chalk_source.cyan(`Detected ${detectedFromAllRemotes.length} app ID(s) from git remotes`));
51345
+ if (filteredCandidates.length !== detectedFromAllRemotes.length) logger_logger.verbose(chalk_source.cyan(`After filtering: ${filteredCandidates.length} candidate(s)`));
51346
+ let finalAppId;
51347
+ let anvilUrl;
51348
+ let username = explicitUsername;
51349
+ let fallbackUrl;
51350
+ let shouldPersistUsernameBinding = false;
51351
+ if (explicitAppId) {
51352
+ finalAppId = explicitAppId;
51353
+ const binding = await getAppAuthBinding(repoPath, explicitAppId);
51354
+ if (binding.url && !explicitUrl) {
51355
+ anvilUrl = normalizeAnvilUrl(binding.url);
51356
+ logger_logger.verbose(chalk_source.cyan("Resolved URL from binding for app ID: ") + chalk_source.bold(anvilUrl));
51175
51357
  }
51176
- }));
51177
- if ('browser' === app || 'browserPrivate' === app) {
51178
- const ids = {
51179
- 'com.google.chrome': 'chrome',
51180
- 'google-chrome.desktop': 'chrome',
51181
- 'com.brave.Browser': 'brave',
51182
- 'org.mozilla.firefox': 'firefox',
51183
- 'firefox.desktop': 'firefox',
51184
- 'com.microsoft.msedge': 'edge',
51185
- 'com.microsoft.edge': 'edge',
51186
- 'com.microsoft.edgemac': 'edge',
51187
- 'microsoft-edge.desktop': 'edge'
51188
- };
51189
- const flags = {
51190
- chrome: '--incognito',
51191
- brave: '--incognito',
51192
- firefox: '--private-window',
51193
- edge: '--inPrivate'
51194
- };
51195
- const browser = is_wsl ? await getWindowsDefaultBrowserFromWsl() : await default_browser_defaultBrowser();
51196
- if (browser.id in ids) {
51197
- const browserName = ids[browser.id];
51198
- if ('browserPrivate' === app) appArguments.push(flags[browserName]);
51199
- return baseOpen({
51200
- ...options,
51201
- app: {
51202
- name: open_apps[browserName],
51203
- arguments: appArguments
51358
+ if (binding.username && !explicitUsername) {
51359
+ username = binding.username;
51360
+ logger_logger.verbose(chalk_source.cyan("Resolved username from binding for app ID: ") + chalk_source.bold(username));
51361
+ }
51362
+ const remoteInfo = lookupRemoteInfoForAppId(explicitAppId, detectedFromAllRemotes);
51363
+ if (remoteInfo.detectedUrl && !explicitUrl && !anvilUrl) {
51364
+ anvilUrl = normalizeAnvilUrl(remoteInfo.detectedUrl);
51365
+ logger_logger.verbose(chalk_source.cyan("Resolved URL from remote for app ID: ") + chalk_source.bold(anvilUrl));
51366
+ }
51367
+ if (remoteInfo.detectedUsername && !explicitUsername && !username) {
51368
+ username = remoteInfo.detectedUsername;
51369
+ logger_logger.verbose(chalk_source.cyan("Resolved username from remote for app ID: ") + chalk_source.bold(username));
51370
+ }
51371
+ } else {
51372
+ logger_logger.verbose(chalk_source.cyan("No app ID provided, attempting auto-detection..."));
51373
+ if (0 === filteredCandidates.length) {
51374
+ logger_logger.verbose(chalk_source.gray("No app IDs found in git remotes."));
51375
+ const resolvedFallbackUrl = await resolveUrlForFallback(explicitUrl);
51376
+ if (null === resolvedFallbackUrl) {
51377
+ logger_logger.warn("Operation cancelled.");
51378
+ process.exit(0);
51204
51379
  }
51205
- });
51380
+ fallbackUrl = normalizeAnvilUrl(resolvedFallbackUrl);
51381
+ const lookupDecision = await confirmReverseLookupWithResolvedUser(fallbackUrl, username);
51382
+ if (null === lookupDecision) {
51383
+ logger_logger.warn("Operation cancelled.");
51384
+ process.exit(0);
51385
+ }
51386
+ username = lookupDecision.username;
51387
+ if (lookupDecision.shouldContinue) {
51388
+ logger_logger.progress("detect", `Searching ${fallbackUrl} ${username ? `for ${username}` : ""} for matching app IDs...`);
51389
+ const reverseLookupCandidates = await detectAppIdsByCommitLookup(repoPath, {
51390
+ anvilUrl: fallbackUrl,
51391
+ username,
51392
+ includeRemotes: false
51393
+ });
51394
+ logger_logger.progressEnd("detect");
51395
+ for (const c of reverseLookupCandidates)filteredCandidates.push({
51396
+ ...c,
51397
+ detectedUrl: fallbackUrl
51398
+ });
51399
+ }
51400
+ }
51401
+ if (filteredCandidates.length > 0) {
51402
+ for (const c of filteredCandidates)logger_logger.verbose(chalk_source.gray(` Found: ${formatCandidateLabel(c)}`));
51403
+ if (filteredCandidates.length > 1) logger_logger.verbose(chalk_source.yellow(`Found ${filteredCandidates.length} potential app IDs`));
51404
+ }
51405
+ if (useFirst && filteredCandidates.length > 0) {
51406
+ const selected = filteredCandidates[0];
51407
+ finalAppId = selected.appId;
51408
+ if (selected.detectedUrl) anvilUrl = normalizeAnvilUrl(selected.detectedUrl);
51409
+ if (selected.detectedUsername && !username) username = selected.detectedUsername;
51410
+ logger_logger.success("Auto-selected first app ID: " + chalk_source.bold(finalAppId));
51411
+ } else {
51412
+ const selected = await selectAppId(filteredCandidates);
51413
+ if (!selected) {
51414
+ logger_logger.error("No app ID provided. Cannot continue without an app ID.");
51415
+ process.exit(1);
51416
+ }
51417
+ finalAppId = selected.appId;
51418
+ if (selected.detectedUrl) anvilUrl = normalizeAnvilUrl(selected.detectedUrl);
51419
+ if (selected.detectedUsername && !username) username = selected.detectedUsername;
51420
+ }
51206
51421
  }
51207
- throw new Error(`${browser.name} is not supported as a default browser`);
51208
- }
51209
- let command;
51210
- const cliArguments = [];
51211
- const childProcessOptions = {};
51212
- if ('darwin' === platform) {
51213
- command = 'open';
51214
- if (options.wait) cliArguments.push('--wait-apps');
51215
- if (options.background) cliArguments.push('--background');
51216
- if (options.newInstance) cliArguments.push('--new');
51217
- if (app) cliArguments.push('-a', app);
51218
- } else if ('win32' !== platform && (!is_wsl || isInsideContainer() || app)) {
51219
- if (app) command = app;
51422
+ const binding = await getAppAuthBinding(repoPath, finalAppId);
51423
+ if (binding.url && !explicitUrl) {
51424
+ anvilUrl = normalizeAnvilUrl(binding.url);
51425
+ logger_logger.verbose(chalk_source.cyan("Using app binding URL: ") + chalk_source.bold(anvilUrl));
51426
+ }
51427
+ if (binding.username && !explicitUsername) {
51428
+ username = binding.username;
51429
+ logger_logger.verbose(chalk_source.cyan("Using app binding username: ") + chalk_source.bold(username));
51430
+ }
51431
+ shouldPersistUsernameBinding = !binding.username && !explicitUsername;
51432
+ if (explicitUrl) anvilUrl = normalizeAnvilUrl(explicitUrl);
51433
+ else if (!anvilUrl) if (fallbackUrl) anvilUrl = fallbackUrl;
51220
51434
  else {
51221
- const isBundled = !open_dirname || '/' === open_dirname;
51222
- let exeLocalXdgOpen = false;
51223
- try {
51224
- await external_node_fs_promises_namespaceObject.access(localXdgOpenPath, external_node_fs_promises_namespaceObject.constants.X_OK);
51225
- exeLocalXdgOpen = true;
51226
- } catch {}
51227
- const useSystemXdgOpen = external_node_process_.versions.electron ?? ('android' === platform || isBundled || !exeLocalXdgOpen);
51228
- command = useSystemXdgOpen ? 'xdg-open' : localXdgOpenPath;
51435
+ const resolvedFallbackUrl = await resolveUrlForFallback();
51436
+ if (null === resolvedFallbackUrl) {
51437
+ logger_logger.warn("Operation cancelled.");
51438
+ process.exit(0);
51439
+ }
51440
+ anvilUrl = normalizeAnvilUrl(resolvedFallbackUrl);
51229
51441
  }
51230
- if (appArguments.length > 0) cliArguments.push(...appArguments);
51231
- if (!options.wait) {
51232
- childProcessOptions.stdio = 'ignore';
51233
- childProcessOptions.detached = true;
51442
+ anvilUrl = normalizeAnvilUrl(anvilUrl);
51443
+ logger_logger.verbose(chalk_source.green("Using app ID: ") + chalk_source.bold(finalAppId));
51444
+ logger_logger.verbose(chalk_source.cyan("Using Anvil URL: ") + chalk_source.bold(anvilUrl));
51445
+ const resolvedUsername = await resolveUsernameForUrl(anvilUrl, username);
51446
+ if (null === resolvedUsername) {
51447
+ logger_logger.warn("Operation cancelled.");
51448
+ process.exit(0);
51234
51449
  }
51235
- } else {
51236
- command = await powerShellPath();
51237
- cliArguments.push('-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-EncodedCommand');
51238
- if (!is_wsl) childProcessOptions.windowsVerbatimArguments = true;
51239
- const encodedArguments = [
51240
- 'Start'
51241
- ];
51242
- if (options.wait) encodedArguments.push('-Wait');
51243
- if (app) {
51244
- encodedArguments.push(`"\`"${app}\`""`);
51245
- if (options.target) appArguments.push(options.target);
51246
- } else if (options.target) encodedArguments.push(`"${options.target}"`);
51247
- if (appArguments.length > 0) {
51248
- appArguments = appArguments.map((argument)=>`"\`"${argument}\`""`);
51249
- encodedArguments.push('-ArgumentList', appArguments.join(','));
51450
+ username = resolvedUsername;
51451
+ if (shouldPersistUsernameBinding && username && auth_getAccountsForUrl(anvilUrl).length > 1) {
51452
+ await setAppAuthBinding(repoPath, finalAppId, {
51453
+ url: anvilUrl,
51454
+ username
51455
+ });
51456
+ logger_logger.verbose(chalk_source.cyan("Saved app account binding for future non-interactive use."));
51250
51457
  }
51251
- options.target = external_node_buffer_namespaceObject.Buffer.from(encodedArguments.join(' '), 'utf16le').toString('base64');
51458
+ if (username) logger_logger.verbose(chalk_source.cyan("Using account: ") + chalk_source.bold(username));
51459
+ if (!hasTokensForUrl(anvilUrl, username)) {
51460
+ if (username) logger_logger.error(`Not logged in to ${anvilUrl} as ${username}`);
51461
+ else logger_logger.error(`Not logged in to ${anvilUrl}`);
51462
+ logger_logger.verbose(chalk_source.yellow("Please log in first:"));
51463
+ console.log(chalk_source.cyan(` anvil login ${anvilUrl.replace(/^https?:\/\//, "")}`));
51464
+ process.exit(1);
51465
+ }
51466
+ logger_logger.verbose(chalk_source.green("✓ Authentication tokens found"));
51467
+ logger_logger.progress("validate", `Validating app ID: ${finalAppId}`);
51468
+ const appIdValidation = await validateAppId(finalAppId, anvilUrl, username);
51469
+ logger_logger.progressEnd("validate");
51470
+ if (!appIdValidation.valid) {
51471
+ logger_logger.error(`App ID validation failed: ${appIdValidation.error}`);
51472
+ logger_logger.verbose(chalk_source.yellow("This could mean:"));
51473
+ logger_logger.verbose(chalk_source.yellow(" • The app ID doesn't exist on the server"));
51474
+ logger_logger.verbose(chalk_source.yellow(" • You don't have access to this app"));
51475
+ logger_logger.verbose(chalk_source.yellow(" • There's a network or authentication issue"));
51476
+ process.exit(1);
51477
+ }
51478
+ logger_logger.verbose(chalk_source.green(`✔ App ID validated successfully`));
51479
+ if (appIdValidation.app_name) logger_logger.verbose(chalk_source.gray(` App name: ${appIdValidation.app_name}`));
51480
+ if (stagedOnly) logger_logger.info(chalk_source.yellow("▸ Staged-only mode: Only staged changes will be synced"));
51481
+ if (autoMode) logger_logger.info(chalk_source.cyan("▸ Auto mode: Will automatically restart on branch changes and sync when behind"));
51482
+ logger_logger.progress("init", "Initializing watch session...");
51483
+ const session = await api_watch(repoPath, finalAppId, anvilUrl, stagedOnly, username);
51484
+ logger_logger.progressEnd("init");
51485
+ if (openAfterValidation) await openWatchPath(resolveWatchOpenPath(repoPath));
51486
+ logger_logger.verbose(chalk_source.blue("Watching for file changes..."));
51487
+ const started = await checkSyncStatusAndStart(session, {
51488
+ autoMode,
51489
+ repoPath,
51490
+ appId: finalAppId,
51491
+ anvilUrl,
51492
+ stagedOnly,
51493
+ username
51494
+ });
51495
+ if (!started) process.exit(1);
51496
+ } catch (e) {
51497
+ logger_logger.error("Error: " + errors_getErrorMessage(e));
51498
+ process.exit(1);
51252
51499
  }
51253
- if ('darwin' === platform && appArguments.length > 0) cliArguments.push('--args', ...appArguments);
51254
- if (options.target) cliArguments.push(options.target);
51255
- const subprocess = external_node_child_process_.spawn(command, cliArguments, childProcessOptions);
51256
- if (options.wait) return new Promise((resolve, reject)=>{
51257
- subprocess.once('error', reject);
51258
- subprocess.once('close', (exitCode)=>{
51259
- if (!options.allowNonzeroExitCode && exitCode > 0) return void reject(new Error(`Exited with code ${exitCode}`));
51260
- resolve(subprocess);
51500
+ }
51501
+ function registerWatchCommand(program) {
51502
+ const watchCommand = program.command("watch [path]").description("Watch for file changes and sync to Anvil").alias("sync").alias("w").option("-A, --appid <APP_ID>", "Specify app ID directly").option("-f, --first", "Auto-select first detected app ID without confirmation").option("-s, --staged-only", "Only sync staged changes (use git add to stage files)").option("-a, --auto", "Auto mode: restart on branch changes and sync when behind").option("-V, --verbose", "Show detailed output").option("-O, --open", "Open watched path in preferred editor (or default app)").option("-u, --url <ANVIL_URL>", "Specify Anvil server URL (e.g., anvil.works, localhost)").option("-U, --user <USERNAME>", "Specify which user account to use").action(async (path, options)=>{
51503
+ if (void 0 !== options.verbose && logger_logger instanceof CLILogger) logger_logger.setVerbose(options.verbose);
51504
+ await handleWatchCommand({
51505
+ path,
51506
+ appid: options.appid,
51507
+ useFirst: options.first,
51508
+ stagedOnly: options.stagedOnly,
51509
+ autoMode: options.auto,
51510
+ verbose: options.verbose,
51511
+ open: options.open,
51512
+ url: options.url,
51513
+ user: options.user
51261
51514
  });
51262
51515
  });
51263
- subprocess.unref();
51264
- return subprocess;
51265
- };
51266
- const open_open = (target, options)=>{
51267
- if ('string' != typeof target) throw new TypeError('Expected a `target`');
51268
- return baseOpen({
51269
- ...options,
51270
- target
51271
- });
51272
- };
51273
- function detectArchBinary(binary) {
51274
- if ('string' == typeof binary || Array.isArray(binary)) return binary;
51275
- const { [arch]: archBinary } = binary;
51276
- if (!archBinary) throw new Error(`${arch} is not supported`);
51277
- return archBinary;
51278
- }
51279
- function detectPlatformBinary({ [platform]: platformBinary }, { wsl }) {
51280
- if (wsl && is_wsl) return detectArchBinary(wsl);
51281
- if (!platformBinary) throw new Error(`${platform} is not supported`);
51282
- return detectArchBinary(platformBinary);
51516
+ return watchCommand;
51283
51517
  }
51284
- const open_apps = {};
51285
- defineLazyProperty(open_apps, 'chrome', ()=>detectPlatformBinary({
51286
- darwin: 'google chrome',
51287
- win32: 'chrome',
51288
- linux: [
51289
- 'google-chrome',
51290
- 'google-chrome-stable',
51291
- 'chromium'
51292
- ]
51293
- }, {
51294
- wsl: {
51295
- ia32: '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe',
51296
- x64: [
51297
- '/mnt/c/Program Files/Google/Chrome/Application/chrome.exe',
51298
- '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe'
51299
- ]
51300
- }
51301
- }));
51302
- defineLazyProperty(open_apps, 'brave', ()=>detectPlatformBinary({
51303
- darwin: 'brave browser',
51304
- win32: 'brave',
51305
- linux: [
51306
- 'brave-browser',
51307
- 'brave'
51308
- ]
51309
- }, {
51310
- wsl: {
51311
- ia32: '/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe',
51312
- x64: [
51313
- '/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe',
51314
- '/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe'
51315
- ]
51316
- }
51317
- }));
51318
- defineLazyProperty(open_apps, 'firefox', ()=>detectPlatformBinary({
51319
- darwin: 'firefox',
51320
- win32: String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,
51321
- linux: 'firefox'
51322
- }, {
51323
- wsl: '/mnt/c/Program Files/Mozilla Firefox/firefox.exe'
51324
- }));
51325
- defineLazyProperty(open_apps, 'edge', ()=>detectPlatformBinary({
51326
- darwin: 'microsoft edge',
51327
- win32: 'msedge',
51328
- linux: [
51329
- 'microsoft-edge',
51330
- 'microsoft-edge-dev'
51331
- ]
51332
- }, {
51333
- wsl: '/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe'
51334
- }));
51335
- defineLazyProperty(open_apps, 'browser', ()=>'browser');
51336
- defineLazyProperty(open_apps, 'browserPrivate', ()=>'browserPrivate');
51337
- const node_modules_open = open_open;
51338
51518
  var external_http_ = __webpack_require__("http");
51339
51519
  const successPage = `<!DOCTYPE html>
51340
51520
  <html lang="en">
@@ -51599,9 +51779,451 @@ var __webpack_exports__ = {};
51599
51779
  scope: tokenData.scope
51600
51780
  });
51601
51781
  }
51782
+ const CHECKOUT_ERROR_VALUE = "__ERROR__";
51783
+ function isAbortLikeError(error) {
51784
+ if (error instanceof Error && "AbortError" === error.name) return true;
51785
+ if ("object" == typeof error && null !== error) {
51786
+ const maybeType = error.type;
51787
+ const maybeMessage = error.message;
51788
+ if ("network_error" === maybeType && "string" == typeof maybeMessage && maybeMessage.toLowerCase().includes("abort")) return true;
51789
+ }
51790
+ const message = errors_getErrorMessage(error).toLowerCase();
51791
+ return "aborted" === message || message.includes("aborted");
51792
+ }
51793
+ function normalizePickerQuery(term) {
51794
+ return (term || "").trim();
51795
+ }
51796
+ function getCheckoutPickerDelayMs(anvilUrl) {
51797
+ const raw = process.env.ANVIL_CHECKOUT_PICKER_DELAY_MS;
51798
+ const fromEnv = null == raw ? NaN : Number(raw);
51799
+ if (null != raw && Number.isFinite(fromEnv) && fromEnv >= 0) return fromEnv;
51800
+ return /localhost|127\.0\.0\.1/.test(anvilUrl) ? 250 : 0;
51801
+ }
51802
+ function getCheckoutPickerPaginationDelayMs(anvilUrl) {
51803
+ const raw = process.env.ANVIL_CHECKOUT_PICKER_PAGINATION_DELAY_MS;
51804
+ const fromEnv = null == raw ? NaN : Number(raw);
51805
+ if (null != raw && Number.isFinite(fromEnv) && fromEnv >= 0) return fromEnv;
51806
+ return /localhost|127\.0\.0\.1/.test(anvilUrl) ? 1000 : 0;
51807
+ }
51808
+ function getCheckoutPickerMaxRows() {
51809
+ const raw = process.env.ANVIL_CHECKOUT_PICKER_MAX_ROWS;
51810
+ const fromEnv = null == raw ? NaN : Number(raw);
51811
+ if (null != raw && Number.isFinite(fromEnv) && fromEnv >= 4) return Math.floor(fromEnv);
51812
+ return 12;
51813
+ }
51814
+ function formatLastEdited(lastEdited) {
51815
+ if (!lastEdited) return;
51816
+ return chalk_source.gray(`Last edited: ${lastEdited}`);
51817
+ }
51818
+ function formatAppPickerLabel(app) {
51819
+ const appName = app.app_name || "Unnamed App";
51820
+ return `${chalk_source.cyan(appName)} ${chalk_source.gray(`(${app.app_id})`)}`;
51821
+ }
51822
+ async function waitForDebounce(ms, signal) {
51823
+ if (ms <= 0) return;
51824
+ if (signal.aborted) throw new Error("aborted");
51825
+ await new Promise((resolve, reject)=>{
51826
+ const timer = setTimeout(()=>{
51827
+ cleanup();
51828
+ resolve();
51829
+ }, ms);
51830
+ const onAbort = ()=>{
51831
+ cleanup();
51832
+ reject(new Error("aborted"));
51833
+ };
51834
+ const cleanup = ()=>{
51835
+ clearTimeout(timer);
51836
+ signal.removeEventListener("abort", onAbort);
51837
+ };
51838
+ signal.addEventListener("abort", onAbort, {
51839
+ once: true
51840
+ });
51841
+ });
51842
+ }
51843
+ function createCheckoutPickerSource(anvilUrl, username, deps, options = {}) {
51844
+ const pageSize = options.pageSize ?? 20;
51845
+ const debounceMs = options.debounceMs ?? 200;
51846
+ const staleTimeMs = options.staleTimeMs ?? 30000;
51847
+ const maxAutoPagesPerCall = options.maxAutoPagesPerCall ?? 8;
51848
+ const initialQuery = normalizePickerQuery(options.initialQuery);
51849
+ const simulatedDelayMs = getCheckoutPickerDelayMs(anvilUrl);
51850
+ const paginationDelayMs = getCheckoutPickerPaginationDelayMs(anvilUrl);
51851
+ const cache = new Map();
51852
+ const getState = (query)=>{
51853
+ const existing = cache.get(query);
51854
+ if (existing) return existing;
51855
+ const created = {
51856
+ items: [],
51857
+ nextCursor: null,
51858
+ exhausted: false,
51859
+ lastError: null,
51860
+ updatedAt: 0,
51861
+ inFlight: null
51862
+ };
51863
+ cache.set(query, created);
51864
+ return created;
51865
+ };
51866
+ const fetchPage = async (query, signal, options = {})=>{
51867
+ const state = getState(query);
51868
+ if (state.inFlight) return void await state.inFlight;
51869
+ state.inFlight = (async ()=>{
51870
+ try {
51871
+ if (simulatedDelayMs > 0) await new Promise((resolve)=>setTimeout(resolve, simulatedDelayMs));
51872
+ if (options.cursor && paginationDelayMs > 0) await new Promise((resolve)=>setTimeout(resolve, paginationDelayMs));
51873
+ const response = await deps.listAppsForCheckout({
51874
+ anvilUrl,
51875
+ username,
51876
+ limit: pageSize,
51877
+ cursor: options.cursor,
51878
+ q: query || void 0,
51879
+ signal
51880
+ });
51881
+ if (options.reset) state.items = [
51882
+ ...response.apps
51883
+ ];
51884
+ else state.items.push(...response.apps);
51885
+ state.nextCursor = response.next_cursor;
51886
+ state.exhausted = !response.next_cursor;
51887
+ state.lastError = null;
51888
+ state.updatedAt = Date.now();
51889
+ } catch (e) {
51890
+ if (!signal.aborted && !isAbortLikeError(e)) {
51891
+ const message = errors_getErrorMessage(e);
51892
+ state.lastError = message;
51893
+ state.updatedAt = Date.now();
51894
+ }
51895
+ } finally{
51896
+ state.inFlight = null;
51897
+ }
51898
+ })();
51899
+ await state.inFlight;
51900
+ };
51901
+ const ensureLoaded = async (query, signal)=>{
51902
+ const state = getState(query);
51903
+ const isStale = Date.now() - state.updatedAt > staleTimeMs;
51904
+ if (0 === state.items.length) {
51905
+ await waitForDebounce(debounceMs, signal);
51906
+ await fetchPage(query, signal, {
51907
+ cursor: void 0,
51908
+ reset: false
51909
+ });
51910
+ } else if (state.lastError) {
51911
+ await waitForDebounce(debounceMs, signal);
51912
+ await fetchPage(query, signal, {
51913
+ cursor: void 0,
51914
+ reset: true
51915
+ });
51916
+ } else if (isStale) {
51917
+ await waitForDebounce(debounceMs, signal);
51918
+ await fetchPage(query, signal, {
51919
+ cursor: void 0,
51920
+ reset: true
51921
+ });
51922
+ }
51923
+ let pagesLoaded = 0;
51924
+ while(state.nextCursor && pagesLoaded < maxAutoPagesPerCall){
51925
+ if (signal.aborted) throw new Error("aborted");
51926
+ const cursor = state.nextCursor;
51927
+ await fetchPage(query, signal, {
51928
+ cursor,
51929
+ reset: false
51930
+ });
51931
+ pagesLoaded += 1;
51932
+ }
51933
+ };
51934
+ return {
51935
+ source: async (term, { signal })=>{
51936
+ const query = normalizePickerQuery(term) || initialQuery;
51937
+ if (signal.aborted) return [];
51938
+ await ensureLoaded(query, signal);
51939
+ const state = getState(query);
51940
+ if (state.lastError) return [
51941
+ {
51942
+ name: `${chalk_source.yellow("Fetch failed")} ${chalk_source.gray(`(${state.lastError})`)}`,
51943
+ value: CHECKOUT_ERROR_VALUE,
51944
+ disabled: true
51945
+ }
51946
+ ];
51947
+ const summary = state.nextCursor ? chalk_source.gray(`Loaded ${state.items.length} apps • more available`) : chalk_source.gray(`Loaded ${state.items.length} apps`);
51948
+ const choices = state.items.map((app)=>({
51949
+ name: formatAppPickerLabel(app),
51950
+ value: app.app_id,
51951
+ description: formatLastEdited(app.last_edited)
51952
+ }));
51953
+ choices.unshift({
51954
+ name: summary,
51955
+ value: CHECKOUT_ERROR_VALUE,
51956
+ disabled: true
51957
+ });
51958
+ return choices;
51959
+ }
51960
+ };
51961
+ }
51962
+ const INLINE_SPINNER_FRAMES = [
51963
+ "⠋",
51964
+ "⠙",
51965
+ "⠹",
51966
+ "⠸",
51967
+ "⠼",
51968
+ "⠴",
51969
+ "⠦",
51970
+ "⠧",
51971
+ "⠇",
51972
+ "⠏"
51973
+ ];
51974
+ async function runCustomCheckoutPicker(anvilUrl, username, deps, initialQuery) {
51975
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return null;
51976
+ const pageSize = 20;
51977
+ const staleTimeMs = 30000;
51978
+ const debounceMs = 180;
51979
+ const simulatedDelayMs = getCheckoutPickerDelayMs(anvilUrl);
51980
+ const paginationDelayMs = getCheckoutPickerPaginationDelayMs(anvilUrl);
51981
+ const maxVisibleRows = getCheckoutPickerMaxRows();
51982
+ const cache = new Map();
51983
+ let query = normalizePickerQuery(initialQuery);
51984
+ let selectedIndex = 0;
51985
+ let scrollOffset = 0;
51986
+ let loading = false;
51987
+ let loadingMore = false;
51988
+ let done = false;
51989
+ let pendingTimer = null;
51990
+ let loadingRenderTimer = null;
51991
+ let activeController = null;
51992
+ let resolveResult = null;
51993
+ const ensureState = (key)=>{
51994
+ const existing = cache.get(key);
51995
+ if (existing) return existing;
51996
+ const created = {
51997
+ items: [],
51998
+ nextCursor: null,
51999
+ lastError: null,
52000
+ updatedAt: 0
52001
+ };
52002
+ cache.set(key, created);
52003
+ return created;
52004
+ };
52005
+ const currentState = ()=>ensureState(query);
52006
+ const clearScreen = ()=>{
52007
+ process.stdout.write("\x1b[2J\x1b[H");
52008
+ };
52009
+ const visibleRows = ()=>{
52010
+ const rows = process.stdout.rows || 24;
52011
+ return Math.min(maxVisibleRows, Math.max(6, rows - 9));
52012
+ };
52013
+ const syncViewport = ()=>{
52014
+ const items = currentState().items;
52015
+ if (selectedIndex >= items.length) selectedIndex = Math.max(0, items.length - 1);
52016
+ const height = visibleRows();
52017
+ if (selectedIndex < scrollOffset) scrollOffset = selectedIndex;
52018
+ else if (selectedIndex >= scrollOffset + height) scrollOffset = Math.max(0, selectedIndex - height + 1);
52019
+ };
52020
+ const render = ()=>{
52021
+ clearScreen();
52022
+ const state = currentState();
52023
+ const items = state.items;
52024
+ const activeQuery = query || chalk_source.gray("(all apps)");
52025
+ const spinnerFrame = INLINE_SPINNER_FRAMES[Math.floor(Date.now() / 120) % INLINE_SPINNER_FRAMES.length];
52026
+ const status = loading ? loadingMore ? chalk_source.yellow(`${spinnerFrame} Loading more apps... (${items.length} loaded)`) : chalk_source.yellow(`${spinnerFrame} Loading apps...`) : state.nextCursor ? chalk_source.gray(`Loaded ${items.length} apps • more available`) : chalk_source.gray(`Loaded ${items.length} apps`);
52027
+ process.stdout.write(`${chalk_source.bold("Search apps by name or app ID")}\n`);
52028
+ process.stdout.write(`${chalk_source.cyan("Query:")} ${activeQuery}\n`);
52029
+ process.stdout.write(`${status}\n`);
52030
+ if (state.lastError) process.stdout.write(`${chalk_source.red(`Fetch failed: ${state.lastError}`)}\n`);
52031
+ const height = visibleRows();
52032
+ const start = scrollOffset;
52033
+ const end = Math.min(items.length, start + height);
52034
+ if (0 !== items.length || loading || state.lastError) for(let i = start; i < end; i += 1){
52035
+ const item = items[i];
52036
+ const label = formatAppPickerLabel(item);
52037
+ const line = i === selectedIndex ? `${chalk_source.green("❯")} ${label}` : ` ${label}`;
52038
+ process.stdout.write(`${line}\n`);
52039
+ }
52040
+ else process.stdout.write(`${chalk_source.gray("No matches. Keep typing.")}\n`);
52041
+ process.stdout.write(`\n${chalk_source.gray("↑/↓ navigate • type to search • enter select • esc/ctrl+c cancel")}\n`);
52042
+ };
52043
+ const fetchPage = async (key, cursor)=>{
52044
+ const state = ensureState(key);
52045
+ const controller = new AbortController();
52046
+ if (!cursor && activeController) activeController.abort();
52047
+ activeController = controller;
52048
+ loading = true;
52049
+ loadingMore = !!cursor;
52050
+ startLoadingAnimation();
52051
+ render();
52052
+ try {
52053
+ if (simulatedDelayMs > 0) await new Promise((resolve)=>setTimeout(resolve, simulatedDelayMs));
52054
+ if (cursor && paginationDelayMs > 0) await new Promise((resolve)=>setTimeout(resolve, paginationDelayMs));
52055
+ const response = await deps.listAppsForCheckout({
52056
+ anvilUrl,
52057
+ username,
52058
+ limit: pageSize,
52059
+ cursor,
52060
+ q: key || void 0,
52061
+ signal: controller.signal
52062
+ });
52063
+ const merged = cursor ? state.items.concat(response.apps) : response.apps;
52064
+ cache.set(key, {
52065
+ items: merged,
52066
+ nextCursor: response.next_cursor,
52067
+ lastError: null,
52068
+ updatedAt: Date.now()
52069
+ });
52070
+ if (key === query) syncViewport();
52071
+ } catch (e) {
52072
+ if (!controller.signal.aborted && !isAbortLikeError(e)) {
52073
+ const message = errors_getErrorMessage(e);
52074
+ cache.set(key, {
52075
+ ...state,
52076
+ lastError: message,
52077
+ updatedAt: Date.now()
52078
+ });
52079
+ }
52080
+ } finally{
52081
+ if (activeController === controller) activeController = null;
52082
+ loading = false;
52083
+ loadingMore = false;
52084
+ stopLoadingAnimation();
52085
+ render();
52086
+ }
52087
+ };
52088
+ const ensureFresh = async (key)=>{
52089
+ const state = ensureState(key);
52090
+ const stale = Date.now() - state.updatedAt > staleTimeMs;
52091
+ if (0 === state.items.length || stale || state.lastError) await fetchPage(key);
52092
+ else render();
52093
+ };
52094
+ const maybeLoadMore = async ()=>{
52095
+ const state = currentState();
52096
+ if (!state.nextCursor || loading) return;
52097
+ if (selectedIndex >= Math.max(0, state.items.length - 2)) await fetchPage(query, state.nextCursor || void 0);
52098
+ };
52099
+ const scheduleQueryFetch = ()=>{
52100
+ if (pendingTimer) clearTimeout(pendingTimer);
52101
+ pendingTimer = setTimeout(()=>{
52102
+ pendingTimer = null;
52103
+ ensureFresh(query);
52104
+ }, debounceMs);
52105
+ };
52106
+ const startLoadingAnimation = ()=>{
52107
+ if (loadingRenderTimer) return;
52108
+ loadingRenderTimer = setInterval(()=>{
52109
+ if (!done && loading) render();
52110
+ }, 120);
52111
+ };
52112
+ const stopLoadingAnimation = ()=>{
52113
+ if (!loadingRenderTimer) return;
52114
+ clearInterval(loadingRenderTimer);
52115
+ loadingRenderTimer = null;
52116
+ };
52117
+ const finish = (value)=>{
52118
+ done = true;
52119
+ if (pendingTimer) {
52120
+ clearTimeout(pendingTimer);
52121
+ pendingTimer = null;
52122
+ }
52123
+ if (activeController) {
52124
+ activeController.abort();
52125
+ activeController = null;
52126
+ }
52127
+ stopLoadingAnimation();
52128
+ process.stdin.removeListener("keypress", onKeypress);
52129
+ if (process.stdin.isTTY && process.stdin.setRawMode) process.stdin.setRawMode(false);
52130
+ process.stdin.pause();
52131
+ clearScreen();
52132
+ if (resolveResult) resolveResult(value);
52133
+ };
52134
+ const onKeypress = (_str, key)=>{
52135
+ if (done) return;
52136
+ if (key.ctrl && "c" === key.name) return void finish(null);
52137
+ if ("escape" === key.name) return void finish(null);
52138
+ if ("return" === key.name) {
52139
+ const state = currentState();
52140
+ const selected = state.items[selectedIndex];
52141
+ finish(selected ? selected.app_id : null);
52142
+ return;
52143
+ }
52144
+ if ("up" === key.name) {
52145
+ if (selectedIndex > 0) {
52146
+ selectedIndex -= 1;
52147
+ syncViewport();
52148
+ render();
52149
+ }
52150
+ return;
52151
+ }
52152
+ if ("down" === key.name) {
52153
+ const maxIndex = Math.max(0, currentState().items.length - 1);
52154
+ if (selectedIndex < maxIndex) {
52155
+ selectedIndex += 1;
52156
+ syncViewport();
52157
+ render();
52158
+ maybeLoadMore();
52159
+ }
52160
+ return;
52161
+ }
52162
+ if ("backspace" === key.name || "delete" === key.name) {
52163
+ if (query.length > 0) {
52164
+ query = query.slice(0, -1);
52165
+ selectedIndex = 0;
52166
+ scrollOffset = 0;
52167
+ render();
52168
+ scheduleQueryFetch();
52169
+ }
52170
+ return;
52171
+ }
52172
+ if (!key.ctrl && !key.meta && key.sequence && 1 === key.sequence.length && key.sequence >= " ") {
52173
+ query += key.sequence;
52174
+ selectedIndex = 0;
52175
+ scrollOffset = 0;
52176
+ render();
52177
+ scheduleQueryFetch();
52178
+ }
52179
+ };
52180
+ logger_logger.pause();
52181
+ try {
52182
+ external_readline_default().emitKeypressEvents(process.stdin);
52183
+ if (process.stdin.isTTY && process.stdin.setRawMode) process.stdin.setRawMode(true);
52184
+ process.stdin.resume();
52185
+ process.stdin.on("keypress", onKeypress);
52186
+ await ensureFresh(query);
52187
+ return await new Promise((resolve)=>{
52188
+ resolveResult = resolve;
52189
+ });
52190
+ } finally{
52191
+ logger_logger.resume();
52192
+ }
52193
+ }
52194
+ async function selectAppForCheckout(anvilUrl, username, deps, initialQuery) {
52195
+ if (!isTestMode() && "1" !== process.env.ANVIL_CHECKOUT_LEGACY_PICKER) return runCustomCheckoutPicker(anvilUrl, username, deps, initialQuery);
52196
+ const picker = createCheckoutPickerSource(anvilUrl, username, deps, {
52197
+ initialQuery
52198
+ });
52199
+ try {
52200
+ const answer = await logger_logger.prompt([
52201
+ {
52202
+ type: "search",
52203
+ name: "value",
52204
+ message: "Search apps by name or app ID",
52205
+ source: picker.source,
52206
+ pageSize: 12,
52207
+ loop: false,
52208
+ instructions: {
52209
+ navigation: "↑↓ navigate • type to filter",
52210
+ pager: "enter select • ctrl+c cancel"
52211
+ }
52212
+ }
52213
+ ]);
52214
+ const selected = String(answer.value || "");
52215
+ if (!selected || selected === CHECKOUT_ERROR_VALUE) return null;
52216
+ return selected;
52217
+ } catch (e) {
52218
+ const err = e;
52219
+ if ("ExitPromptError" === err.name || String(err.message || "").includes("User force closed")) return null;
52220
+ throw e;
52221
+ }
52222
+ }
51602
52223
  const defaultCheckoutDeps = {
51603
52224
  getValidAuthToken: auth_getValidAuthToken,
51604
52225
  validateAppId: validateAppId,
52226
+ listAppsForCheckout: listAppsForCheckout,
51605
52227
  runInteractiveLoginFlow: runInteractiveLoginFlow,
51606
52228
  clone: async (repoUrl, destinationPath, options)=>{
51607
52229
  const cloneArgs = [];
@@ -51615,6 +52237,9 @@ var __webpack_exports__ = {};
51615
52237
  },
51616
52238
  hardenCheckoutGitAuth: hardenCheckoutGitAuth
51617
52239
  };
52240
+ function isInteractiveSession() {
52241
+ return !!(process.stdin.isTTY && process.stdout.isTTY);
52242
+ }
51618
52243
  function parseCheckoutInput(input) {
51619
52244
  const trimmed = input.trim();
51620
52245
  if (!trimmed) throw new Error("Input is required.");
@@ -51737,10 +52362,25 @@ var __webpack_exports__ = {};
51737
52362
  return logger_logger.select(`Multiple accounts found for ${anvilUrl}. Which account should be used for checkout?`, choices, accounts[0]);
51738
52363
  }
51739
52364
  async function executeCheckout(options, deps = defaultCheckoutDeps) {
51740
- const parsed = parseCheckoutInput(options.input);
51741
- const anvilUrl = await resolveCheckoutUrl(options.url, parsed.detectedUrl);
52365
+ let checkoutInput = options.input?.trim();
52366
+ let preselectedAnvilUrl;
52367
+ let preselectedUsername;
52368
+ if (!checkoutInput) {
52369
+ if (!isInteractiveSession()) throw new Error("`anvil checkout` without arguments requires an interactive TTY. Pass APP_ID or URL explicitly in non-interactive mode.");
52370
+ const selectedUrl = await resolveCheckoutUrl(options.url);
52371
+ if (!selectedUrl) return void logger_logger.info("Checkout cancelled.");
52372
+ preselectedAnvilUrl = selectedUrl;
52373
+ const selectedUsername = await resolveCheckoutUsername(selectedUrl, options.user);
52374
+ if (null === selectedUsername) return void logger_logger.info("Checkout cancelled.");
52375
+ preselectedUsername = selectedUsername || void 0;
52376
+ const selectedInput = await selectAppForCheckout(selectedUrl, preselectedUsername, deps, options.query);
52377
+ if (!selectedInput) return void logger_logger.info("Checkout cancelled.");
52378
+ checkoutInput = selectedInput;
52379
+ }
52380
+ const parsed = parseCheckoutInput(checkoutInput);
52381
+ const anvilUrl = preselectedAnvilUrl || await resolveCheckoutUrl(options.url, parsed.detectedUrl);
51742
52382
  if (!anvilUrl) return void logger_logger.info("Checkout cancelled.");
51743
- const resolvedUsername = await resolveCheckoutUsername(anvilUrl, options.user);
52383
+ const resolvedUsername = preselectedUsername || await resolveCheckoutUsername(anvilUrl, options.user);
51744
52384
  if (null === resolvedUsername) return void logger_logger.info("Checkout cancelled.");
51745
52385
  const authToken = await ensureCheckoutAuthToken(anvilUrl, resolvedUsername, deps);
51746
52386
  let checkoutUsername = resolvedUsername;
@@ -51782,30 +52422,18 @@ var __webpack_exports__ = {};
51782
52422
  logger_logger.progressEnd("checkout", `Checked out ${parsed.appId} into ${destinationDisplay}`);
51783
52423
  const preferredEditor = String(getConfig("preferredEditor") || "").trim();
51784
52424
  const preferredEditorCommand = preferredEditor ? getPreferredEditorCommand(preferredEditor) : "";
51785
- if (options.open) if (preferredEditorCommand) await openInPreferredEditor(preferredEditorCommand, destinationPath);
51786
- else {
51787
- await node_modules_open(destinationPath);
52425
+ if (options.open) {
52426
+ await checkout_openPathInEditorOrDefault(destinationPath, preferredEditorCommand);
51788
52427
  logger_logger.info(chalk_source.gray(`Opened ${destinationDisplay}`));
51789
52428
  }
51790
- if (preferredEditorCommand && !options.open) logger_logger.info(chalk_source.cyan(`Next: ${preferredEditorCommand} ${destinationDisplay}`));
52429
+ if (preferredEditorCommand && !options.open) if (isCommandAvailable(preferredEditorCommand)) logger_logger.info(chalk_source.cyan(`Next: ${preferredEditorCommand} ${destinationDisplay}`));
52430
+ else logger_logger.warn(`Preferred editor command '${preferredEditorCommand}' was not found in PATH. Run 'anvil configure' to choose an installed editor.`);
51791
52431
  }
51792
- async function openInPreferredEditor(editorCommand, destinationPath) {
51793
- await new Promise((resolve, reject)=>{
51794
- const child = (0, external_child_process_.spawn)(editorCommand, [
51795
- destinationPath
51796
- ], {
51797
- shell: true,
51798
- stdio: "inherit"
51799
- });
51800
- child.on("error", reject);
51801
- child.on("exit", (code)=>{
51802
- if (0 === code || null === code) resolve();
51803
- else reject(new Error(`Editor command '${editorCommand}' exited with code ${code}`));
51804
- });
51805
- });
52432
+ async function checkout_openPathInEditorOrDefault(destinationPath, preferredEditorCommand, deps) {
52433
+ await openPathInEditorOrDefault(destinationPath, preferredEditorCommand, deps);
51806
52434
  }
51807
52435
  function registerCheckoutCommand(program) {
51808
- const checkoutCommand = program.command("checkout <input> [directory]").description("Check out an Anvil app locally from editor URL, git URL, or app ID").alias("co").option("-O, --open", "Open destination after checkout").option("-b, --branch <BRANCH>", "Checkout a specific branch").option("--depth <N>", "Create a shallow clone with history truncated to N commits", (value)=>parseInt(value, 10)).option("--single-branch", "Clone only one branch").option("--origin <NAME>", "Use a custom remote name instead of origin").option("-q, --quiet", "Suppress git clone progress output").option("--verbose", "Enable verbose git clone output").option("-u, --url <ANVIL_URL>", "Specify Anvil server URL").option("-U, --user <USERNAME>", "Specify which user account to use").option("-f, --force", "Override safety checks for destination path").action(async (input, directory, options)=>{
52436
+ const checkoutCommand = program.command("checkout [input] [directory]").description("Check out an Anvil app locally from editor URL, git URL, app ID, or interactive selection").alias("co").option("-O, --open", "Open destination after checkout").option("-b, --branch <BRANCH>", "Checkout a specific branch").option("--depth <N>", "Create a shallow clone with history truncated to N commits", (value)=>parseInt(value, 10)).option("--single-branch", "Clone only one branch").option("--origin <NAME>", "Use a custom remote name instead of origin").option("--quiet", "Suppress git clone progress output").option("--verbose", "Enable verbose git clone output").option("-u, --url <ANVIL_URL>", "Specify Anvil server URL").option("-U, --user <USERNAME>", "Specify which user account to use").option("-f, --force", "Override safety checks for destination path").option("-Q, --query <QUERY>", "Initial search query for interactive checkout picker").action(async (input, directory, options)=>{
51809
52437
  try {
51810
52438
  if ("number" == typeof options?.depth && (!Number.isFinite(options.depth) || options.depth <= 0)) throw new Error("--depth must be a positive integer");
51811
52439
  await executeCheckout({
@@ -51820,16 +52448,17 @@ var __webpack_exports__ = {};
51820
52448
  verbose: options?.verbose,
51821
52449
  url: options?.url,
51822
52450
  user: options?.user,
51823
- force: options?.force
52451
+ force: options?.force,
52452
+ query: options?.query
51824
52453
  }, defaultCheckoutDeps);
51825
52454
  } catch (e) {
51826
52455
  logger_logger.error("Error: " + errors_getErrorMessage(e));
51827
52456
  process.exit(1);
51828
52457
  }
51829
52458
  });
51830
- checkoutCommand.addHelpText("after", "\n" + chalk_source.bold("Examples:") + "\n anvil checkout http://localhost:3000/build/apps/APPID/code/assets/theme.css\n anvil checkout https://anvil.works/git/APPID.git\n anvil checkout APPID --url anvil.works\n anvil checkout APPID --branch master --depth 1 --single-branch\n anvil checkout APPID -O # open editor/file browser after checkout\n anvil checkout APPID my-local-folder --force\n");
52459
+ checkoutCommand.addHelpText("after", "\n" + chalk_source.bold("Examples:") + '\n anvil checkout http://localhost:3000/build/apps/APPID/code/assets/theme.css\n anvil checkout https://anvil.works/git/APPID.git\n anvil checkout APPID --url anvil.works\n anvil checkout APPID --branch master --depth 1 --single-branch\n anvil checkout APPID -O # open editor/file browser after checkout\n anvil checkout APPID my-local-folder --force\n anvil checkout # interactive search/select app list\n anvil checkout -Q "dashboard" # preseed picker search\n');
51831
52460
  }
51832
- const defaultDeps = {
52461
+ const gitCredential_defaultDeps = {
51833
52462
  getValidAuthToken: auth_getValidAuthToken,
51834
52463
  getAccountsForUrl: auth_getAccountsForUrl,
51835
52464
  getAppAuthBinding: getAppAuthBinding,
@@ -51848,7 +52477,7 @@ var __webpack_exports__ = {};
51848
52477
  }
51849
52478
  return out;
51850
52479
  }
51851
- async function buildGitCredentialResponse(operation, request, deps = defaultDeps) {
52480
+ async function buildGitCredentialResponse(operation, request, deps = gitCredential_defaultDeps) {
51852
52481
  if ("get" !== operation) return null;
51853
52482
  if (!request.protocol || !request.host) return null;
51854
52483
  const appId = parseAppIdFromGitPath(request.path);
@@ -51880,7 +52509,7 @@ var __webpack_exports__ = {};
51880
52509
  for (const [k, v] of Object.entries(response))lines.push(`${k}=${v}`);
51881
52510
  process.stdout.write(lines.join("\n") + "\n\n");
51882
52511
  }
51883
- async function executeGitCredentialOperation(operation, deps = defaultDeps) {
52512
+ async function executeGitCredentialOperation(operation, deps = gitCredential_defaultDeps) {
51884
52513
  const raw = await readStdin();
51885
52514
  const request = parseGitCredentialRequest(raw);
51886
52515
  const response = await buildGitCredentialResponse(operation, request, deps);
@@ -52111,14 +52740,13 @@ var __webpack_exports__ = {};
52111
52740
  console.log();
52112
52741
  });
52113
52742
  }
52114
- const onboard_defaultDeps = {
52743
+ const configure_defaultDeps = {
52115
52744
  getLatestVersion: getLatestVersion,
52116
52745
  getAccountsForUrl: auth_getAccountsForUrl,
52117
- runInteractiveLoginFlow: runInteractiveLoginFlow,
52118
- executeCheckout: executeCheckout
52746
+ runInteractiveLoginFlow: runInteractiveLoginFlow
52119
52747
  };
52120
52748
  const DEFAULT_ANVIL_SERVER_URL_PROMPT = "Default Anvil server URL";
52121
- async function getOnboardVersionStatus(currentVersion, latestVersionGetter = getLatestVersion) {
52749
+ async function getConfigureVersionStatus(currentVersion, latestVersionGetter = getLatestVersion) {
52122
52750
  const latestVersion = await latestVersionGetter();
52123
52751
  if (!latestVersion) return {
52124
52752
  currentVersion,
@@ -52141,25 +52769,25 @@ var __webpack_exports__ = {};
52141
52769
  if (1 === accounts.length) return accounts[0];
52142
52770
  return `${accounts.length} accounts`;
52143
52771
  }
52144
- function isInteractiveSession() {
52772
+ function configure_isInteractiveSession() {
52145
52773
  return !!(process.stdin.isTTY && process.stdout.isTTY);
52146
52774
  }
52147
- async function runOnboardFlow(version, deps = onboard_defaultDeps) {
52148
- if (!isInteractiveSession()) throw new Error("`anvil onboard` is interactive and requires a TTY terminal.");
52775
+ async function runConfigureFlow(version, deps = configure_defaultDeps) {
52776
+ if (!configure_isInteractiveSession()) throw new Error("`anvil configure` is interactive and requires a TTY terminal.");
52149
52777
  console.log();
52150
- logger_logger.info(chalk_source.cyan.bold("Anvil Onboarding"));
52778
+ logger_logger.info(chalk_source.cyan.bold("Anvil Configuration"));
52151
52779
  console.log();
52152
- logger_logger.progress("onboard-version", "Checking CLI version...");
52153
- const versionStatus = await getOnboardVersionStatus(version, deps.getLatestVersion);
52154
- logger_logger.progressEnd("onboard-version");
52780
+ logger_logger.progress("configure-version", "Checking CLI version...");
52781
+ const versionStatus = await getConfigureVersionStatus(version, deps.getLatestVersion);
52782
+ logger_logger.progressEnd("configure-version");
52155
52783
  logger_logger.info(chalk_source.cyan("CLI version: ") + chalk_source.bold(versionStatus.currentVersion));
52156
52784
  if (versionStatus.latestVersion) {
52157
52785
  logger_logger.info(chalk_source.cyan("Latest version: ") + chalk_source.bold(versionStatus.latestVersion));
52158
52786
  if (versionStatus.isOutdated) {
52159
52787
  logger_logger.warn("Your anvil-cli is out of date.");
52160
52788
  logger_logger.info(chalk_source.gray("Run `anvil update` to see update commands."));
52161
- const continueOnOldVersion = await logger_logger.confirm("Continue onboarding anyway?", true);
52162
- if (!continueOnOldVersion) return void logger_logger.info("Onboarding cancelled.");
52789
+ const continueOnOldVersion = await logger_logger.confirm("Continue configuration anyway?", true);
52790
+ if (!continueOnOldVersion) return void logger_logger.info("Configuration cancelled.");
52163
52791
  }
52164
52792
  } else logger_logger.warn("Could not determine latest published version.");
52165
52793
  console.log();
@@ -52177,16 +52805,23 @@ var __webpack_exports__ = {};
52177
52805
  setConfig("anvilUrl", configuredAnvilUrl);
52178
52806
  logger_logger.success(`Set default Anvil server URL (anvilUrl) = ${configuredAnvilUrl}`);
52179
52807
  const currentEditor = String(getConfig("preferredEditor") || "").trim().toLowerCase();
52808
+ const installedEditors = getInstalledPreferredEditors();
52809
+ if (0 === installedEditors.length) logger_logger.warn("No supported editor command was found in PATH. Leaving preferredEditor unset is recommended.");
52810
+ else if (installedEditors.length < preferredEditors.length) {
52811
+ const unavailableCount = preferredEditors.length - installedEditors.length;
52812
+ logger_logger.verbose(chalk_source.gray(`${unavailableCount} supported editor(s) are not installed and were hidden.`));
52813
+ }
52180
52814
  const editorChoices = [
52181
52815
  {
52182
52816
  name: "None",
52183
52817
  value: ""
52184
52818
  },
52185
- ...preferredEditors.map((editor)=>({
52819
+ ...installedEditors.map((editor)=>({
52186
52820
  name: editor,
52187
52821
  value: editor
52188
52822
  }))
52189
52823
  ];
52824
+ if (currentEditor && !editorChoices.some((choice)=>choice.value === currentEditor)) logger_logger.warn(`Configured preferredEditor '${currentEditor}' is not currently available in PATH.`);
52190
52825
  const selectedEditor = await logger_logger.select("Preferred editor", editorChoices, editorChoices.some((choice)=>choice.value === currentEditor) ? currentEditor : "");
52191
52826
  setConfig("preferredEditor", selectedEditor);
52192
52827
  if (selectedEditor) logger_logger.success(`Set preferredEditor = ${selectedEditor}`);
@@ -52206,35 +52841,14 @@ var __webpack_exports__ = {};
52206
52841
  } else logger_logger.warn("Skipping login.");
52207
52842
  }
52208
52843
  console.log();
52209
- const shouldCheckout = await logger_logger.confirm("Check out an app now?", true);
52210
- if (shouldCheckout) {
52211
- const checkoutAnswer = await logger_logger.prompt([
52212
- {
52213
- type: "input",
52214
- name: "input",
52215
- message: "App ID or app URL to checkout"
52216
- }
52217
- ]);
52218
- const checkoutInput = checkoutAnswer.input.trim();
52219
- if (checkoutInput) {
52220
- const preferredEditorCommand = getPreferredEditorCommand(selectedEditor);
52221
- let openAfterCheckout = false;
52222
- if (preferredEditorCommand) openAfterCheckout = await logger_logger.confirm(`Open checked out app in ${preferredEditorCommand} after checkout?`, true);
52223
- else logger_logger.info("No preferred editor is configured, so open-in-editor is skipped.");
52224
- await deps.executeCheckout({
52225
- input: checkoutInput,
52226
- url: configuredAnvilUrl,
52227
- open: openAfterCheckout
52228
- });
52229
- } else logger_logger.info("No app provided; skipping checkout.");
52230
- } else logger_logger.info("Skipping checkout.");
52231
- console.log();
52232
- logger_logger.success("Onboarding complete.");
52844
+ logger_logger.info(chalk_source.cyan("Next: anvil checkout"));
52845
+ logger_logger.info(chalk_source.gray("Run `anvil checkout` with no arguments to search and select your app."));
52846
+ logger_logger.success("Configuration complete.");
52233
52847
  }
52234
- function registerOnboardCommand(program, version) {
52235
- program.command("onboard").description("Guided setup for configuration, login, and optional app checkout").action(async ()=>{
52848
+ function registerConfigureCommand(program, version) {
52849
+ program.command("configure").description("Guided setup for configuration and login").action(async ()=>{
52236
52850
  try {
52237
- await runOnboardFlow(version, onboard_defaultDeps);
52851
+ await runConfigureFlow(version, configure_defaultDeps);
52238
52852
  } catch (e) {
52239
52853
  logger_logger.error("Error: " + errors_getErrorMessage(e));
52240
52854
  process.exit(1);
@@ -52288,11 +52902,11 @@ var __webpack_exports__ = {};
52288
52902
  console.log(chalk_source.cyan("To update, run one of the following commands:"));
52289
52903
  console.log();
52290
52904
  if ("win32" === process.platform) {
52291
- console.log(chalk_source.gray(" PowerShell: ") + chalk_source.white("irm https://anvil.works/install/anvil-cli/install.ps1 | iex"));
52292
- console.log(chalk_source.gray(" CMD: ") + chalk_source.white("curl -fsSL https://anvil.works/install/anvil-cli/install.cmd -o install.cmd && install.cmd && del install.cmd"));
52293
- } else console.log(chalk_source.gray(" macOS/Linux: ") + chalk_source.white("curl -fsSL https://anvil.works/install/anvil-cli/install.sh | sh"));
52294
- console.log(chalk_source.gray(" npm: ") + chalk_source.white("npm update -g @anvil-works/anvil-cli"));
52295
- console.log(chalk_source.gray(" pnpm: ") + chalk_source.white("pnpm update -g @anvil-works/anvil-cli"));
52905
+ console.log(chalk_source.gray(" PowerShell: ") + chalk_source.white("try { irm https://anvil.works/install-cli.ps1 | iex } catch { npm install -g @anvil-works/anvil-cli@latest }"));
52906
+ console.log(chalk_source.gray(" CMD: ") + chalk_source.white("curl -fsSL https://anvil.works/install-cli.cmd -o install.cmd && install.cmd && del install.cmd || npm install -g @anvil-works/anvil-cli@latest"));
52907
+ } else console.log(chalk_source.gray(" macOS/Linux: ") + chalk_source.white("curl -fsSL https://anvil.works/install-cli.sh | sh || npm install -g @anvil-works/anvil-cli@latest"));
52908
+ console.log(chalk_source.gray(" npm: ") + chalk_source.white("npm install -g @anvil-works/anvil-cli@latest"));
52909
+ console.log(chalk_source.gray(" pnpm: ") + chalk_source.white("pnpm add -g @anvil-works/anvil-cli@latest"));
52296
52910
  console.log();
52297
52911
  } catch (e) {
52298
52912
  logger_logger.error("Error: " + e.message);
@@ -52317,7 +52931,7 @@ var __webpack_exports__ = {};
52317
52931
  registerLogoutCommand(cli_program);
52318
52932
  registerConfigCommand(cli_program);
52319
52933
  registerVersionCommand(cli_program, VERSION);
52320
- registerOnboardCommand(cli_program, VERSION);
52934
+ registerConfigureCommand(cli_program, VERSION);
52321
52935
  cli_program.command("update").description("Update anvil to the latest version").alias("u").action(async ()=>{
52322
52936
  await handleUpdateCommand();
52323
52937
  });