@anvil-works/anvil-cli 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +131 -210
  2. package/dist/index.js +64 -58
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -42545,30 +42545,6 @@ var __webpack_exports__ = {};
42545
42545
  if (/^https?:\/\//.test(url)) return url;
42546
42546
  return `https://${url}`;
42547
42547
  }
42548
- async function detectAnvilUrlFromRemotes(repoPath) {
42549
- try {
42550
- const git = esm_default(repoPath);
42551
- const remotes = await git.getRemotes(true);
42552
- for (const remote of remotes){
42553
- const fetchUrl = remote.refs.fetch;
42554
- if (!fetchUrl) continue;
42555
- const httpMatch = fetchUrl.match(/(https?:\/\/[^\/]+)\/git\/[A-Z0-9]+\.git/);
42556
- if (httpMatch) return {
42557
- url: normalizeAnvilUrl(httpMatch[1])
42558
- };
42559
- const sshMatch = fetchUrl.match(/ssh:\/\/([^@]+)@([^:]+):(\d+)\/(?:git\/)?([A-Z0-9]+)\.git/);
42560
- if (sshMatch) {
42561
- const username = decodeURIComponent(sshMatch[1]);
42562
- const hostname = sshMatch[2];
42563
- return {
42564
- url: normalizeAnvilUrl(hostname),
42565
- username
42566
- };
42567
- }
42568
- }
42569
- } catch (_e) {}
42570
- return null;
42571
- }
42572
42548
  function resolveAnvilUrl() {
42573
42549
  const fromConfig = getConfig("anvilUrl");
42574
42550
  if ("string" == typeof fromConfig && fromConfig.trim()) return fromConfig.trim();
@@ -47410,38 +47386,36 @@ var __webpack_exports__ = {};
47410
47386
  return null;
47411
47387
  }
47412
47388
  }
47413
- async function detectAppIdsFromRemotes(repoPath, anvilUrl) {
47414
- const git = esm_default(repoPath);
47415
- const out = [];
47416
- try {
47417
- const remotes = await git.getRemotes(true);
47418
- const url = new URL(anvilUrl);
47419
- const expectedHost = url.hostname;
47420
- for (const remote of remotes){
47421
- const httpMatch = remote.refs.fetch?.match(/(?:http|https):\/\/(?:[^@]+@)?([^:\/]+)(?::\d+)?\/git\/([A-Z0-9]+)\.git/);
47422
- if (httpMatch) {
47423
- const [, host, detectedAppId] = httpMatch;
47424
- if (host === expectedHost) {
47425
- out.push({
47426
- appId: detectedAppId,
47427
- source: "remote",
47428
- description: `Git remote '${remote.name}'`
47429
- });
47430
- continue;
47431
- }
47432
- }
47433
- const sshMatch = remote.refs.fetch?.match(/ssh:\/\/(?:[^@]+@)?([^:\/]+):(\d+)\/([A-Z0-9]+)\.git/);
47434
- if (sshMatch) {
47435
- const [, host, , detectedAppId] = sshMatch;
47436
- if (host === expectedHost) out.push({
47437
- appId: detectedAppId,
47438
- source: "remote",
47439
- description: `Git remote '${remote.name}' (SSH)`
47440
- });
47441
- }
47442
- }
47443
- } catch (_e) {}
47444
- return out;
47389
+ function filterCandidates(candidates, explicitUrl, explicitUsername) {
47390
+ let filtered = candidates;
47391
+ if (explicitUrl) {
47392
+ const normalizedExplicit = normalizeAnvilUrl(explicitUrl);
47393
+ filtered = filtered.filter((c)=>c.detectedUrl && normalizeAnvilUrl(c.detectedUrl) === normalizedExplicit);
47394
+ }
47395
+ if (explicitUsername) filtered = filtered.filter((c)=>!c.detectedUsername || c.detectedUsername === explicitUsername);
47396
+ return filtered;
47397
+ }
47398
+ function formatCandidateLabel(candidate) {
47399
+ const parts = [
47400
+ candidate.appId
47401
+ ];
47402
+ if (candidate.detectedUrl) if (candidate.detectedUsername) parts.push(`(${candidate.detectedUsername} on ${candidate.detectedUrl})`);
47403
+ else parts.push(`(${candidate.detectedUrl})`);
47404
+ parts.push(`- ${candidate.description}`);
47405
+ return parts.join(" ");
47406
+ }
47407
+ function lookupRemoteInfoForAppId(appId, detectedRemotes) {
47408
+ const matches = detectedRemotes.filter((c)=>c.appId === appId);
47409
+ if (0 === matches.length) return {};
47410
+ const withUsername = matches.find((c)=>c.detectedUsername);
47411
+ if (withUsername) return {
47412
+ detectedUrl: withUsername.detectedUrl,
47413
+ detectedUsername: withUsername.detectedUsername
47414
+ };
47415
+ return {
47416
+ detectedUrl: matches[0].detectedUrl,
47417
+ detectedUsername: matches[0].detectedUsername
47418
+ };
47445
47419
  }
47446
47420
  async function detectAppIdsFromAllRemotes(repoPath) {
47447
47421
  const git = esm_default(repoPath);
@@ -47477,11 +47451,13 @@ var __webpack_exports__ = {};
47477
47451
  } catch (_e) {}
47478
47452
  return out;
47479
47453
  }
47480
- async function detectAppIdsByCommitLookup(repoPath, anvilUrl) {
47454
+ async function detectAppIdsByCommitLookup(repoPath, options) {
47455
+ const anvilUrl = options.anvilUrl || resolveAnvilUrl();
47456
+ const username = options.username;
47481
47457
  const git = esm_default(repoPath);
47482
47458
  const out = [];
47483
47459
  try {
47484
- const authToken = await auth_getValidAuthToken(anvilUrl);
47460
+ const authToken = await auth_getValidAuthToken(anvilUrl, username);
47485
47461
  const branchRef = await git.revparse([
47486
47462
  "--abbrev-ref",
47487
47463
  "HEAD"
@@ -47520,16 +47496,6 @@ var __webpack_exports__ = {};
47520
47496
  } catch (_e) {}
47521
47497
  return out;
47522
47498
  }
47523
- async function detectAppIds(repoPath, options = {}) {
47524
- const anvilUrl = options.anvilUrl || resolveAnvilUrl();
47525
- const results = [];
47526
- const includeReverseLookup = options.includeReverseLookup ?? true;
47527
- results.push(...await detectAppIdsFromRemotes(repoPath, anvilUrl));
47528
- if (0 === results.length && includeReverseLookup) results.push(...await detectAppIdsByCommitLookup(repoPath, anvilUrl));
47529
- const seen = new Set();
47530
- const unique = results.filter((c)=>seen.has(c.appId) ? false : (seen.add(c.appId), true));
47531
- return unique;
47532
- }
47533
47499
  class WebSocketClient extends Emitter_Emitter {
47534
47500
  ws = null;
47535
47501
  appId;
@@ -49834,7 +49800,8 @@ var __webpack_exports__ = {};
49834
49800
  authToken,
49835
49801
  currentBranch,
49836
49802
  commitId,
49837
- stagedOnly
49803
+ stagedOnly,
49804
+ username
49838
49805
  });
49839
49806
  session.on("branch-changed", (data)=>{
49840
49807
  logger_logger.debug("Event: branch-changed", data);
@@ -49863,27 +49830,8 @@ var __webpack_exports__ = {};
49863
49830
  session.hasUncommittedChanges = hasUncommittedChanges;
49864
49831
  return session;
49865
49832
  }
49866
- async function resolveAnvilUrlWithPrompt(explicitUrl, repoPath, explicitUsername) {
49867
- let url;
49868
- let username = explicitUsername;
49869
- if (explicitUrl) url = normalizeAnvilUrl(explicitUrl);
49870
- else if (repoPath) {
49871
- const detected = await detectAnvilUrlFromRemotes(repoPath);
49872
- if (detected) {
49873
- url = detected.url;
49874
- if (!username && detected.username) username = detected.username;
49875
- } else url = resolveUrlFromAvailableOrConfig();
49876
- } else url = resolveUrlFromAvailableOrConfig();
49877
- if (!username) {
49878
- const accounts = auth_getAccountsForUrl(url);
49879
- if (1 === accounts.length) username = accounts[0];
49880
- }
49881
- return {
49882
- url,
49883
- username
49884
- };
49885
- }
49886
- function resolveUrlFromAvailableOrConfig() {
49833
+ function resolveUrlForFallback(explicitUrl) {
49834
+ if (explicitUrl) return normalizeAnvilUrl(explicitUrl);
49887
49835
  const availableUrls = getAvailableAnvilUrls();
49888
49836
  if (availableUrls.length > 0) return availableUrls[0];
49889
49837
  const fromConfig = getConfig("anvilUrl");
@@ -49911,11 +49859,16 @@ var __webpack_exports__ = {};
49911
49859
  trimmed ? resolve(trimmed) : resolve(null);
49912
49860
  });
49913
49861
  });
49914
- return manualAppId;
49862
+ if (manualAppId) return {
49863
+ appId: manualAppId,
49864
+ source: "config",
49865
+ description: "Manual entry"
49866
+ };
49867
+ return null;
49915
49868
  }
49916
- const choices = candidates.map((candidate)=>({
49917
- name: `${candidate.appId} (${candidate.description})`,
49918
- value: candidate.appId
49869
+ const choices = candidates.map((candidate, index)=>({
49870
+ name: formatCandidateLabel(candidate),
49871
+ value: index
49919
49872
  }));
49920
49873
  choices.push({
49921
49874
  name: "Cancel",
@@ -49944,9 +49897,14 @@ var __webpack_exports__ = {};
49944
49897
  trimmed ? resolve(trimmed) : resolve(null);
49945
49898
  });
49946
49899
  });
49947
- return manualAppId;
49900
+ if (manualAppId) return {
49901
+ appId: manualAppId,
49902
+ source: "config",
49903
+ description: "Manual entry"
49904
+ };
49905
+ return null;
49948
49906
  }
49949
- return answer.appId;
49907
+ return candidates[answer.appId];
49950
49908
  } catch (error) {
49951
49909
  const errorObj = error;
49952
49910
  if ("ExitPromptError" === errorObj.name || errorObj.message.includes("User force closed")) logger_logger.warn("Operation cancelled by user.");
@@ -50384,137 +50342,100 @@ var __webpack_exports__ = {};
50384
50342
  const validationResult = await validateAnvilApp(repoPath);
50385
50343
  if (validationResult.appName) logger_logger.info(chalk_source.green("Anvil app: ") + chalk_source.bold(validationResult.appName));
50386
50344
  const detectedFromAllRemotes = await detectAppIdsFromAllRemotes(repoPath);
50387
- const availableUrls = getAvailableAnvilUrls();
50388
- const resolved = await resolveAnvilUrlWithPrompt(explicitUrl, repoPath, explicitUsername);
50389
- let anvilUrl = resolved.url;
50390
- let username = resolved.username;
50391
- logger_logger.verbose(chalk_source.cyan("Resolved Anvil URL: ") + chalk_source.bold(anvilUrl));
50392
- if (username) logger_logger.verbose(chalk_source.cyan("Using username: ") + chalk_source.bold(username));
50393
- if (!explicitUrl) {
50394
- const detectedFromRemote = detectedFromAllRemotes.length > 0 && detectedFromAllRemotes[0].detectedUrl ? {
50395
- url: normalizeAnvilUrl(detectedFromAllRemotes[0].detectedUrl),
50396
- username: detectedFromAllRemotes[0].detectedUsername
50397
- } : null;
50398
- if (detectedFromRemote) {
50399
- logger_logger.verbose(chalk_source.cyan("Detected Anvil URL from git remote: ") + chalk_source.bold(detectedFromRemote.url));
50400
- if (!username && detectedFromRemote.username) {
50401
- username = detectedFromRemote.username;
50402
- logger_logger.verbose(chalk_source.cyan("Detected username from git remote: ") + chalk_source.bold(username));
50403
- }
50404
- anvilUrl = detectedFromRemote.url;
50405
- if (hasTokensForUrl(detectedFromRemote.url, username)) logger_logger.verbose(chalk_source.green(" Using detected URL (has authentication tokens)"));
50406
- else logger_logger.verbose(chalk_source.yellow("Using detected URL from remotes (auth will be checked)"));
50407
- } else if (1 === availableUrls.length) anvilUrl = normalizeAnvilUrl(availableUrls[0]);
50408
- else if (availableUrls.length > 1) {
50409
- const choices = availableUrls.map((url)=>({
50410
- name: url,
50411
- value: url
50412
- }));
50413
- choices.push({
50414
- name: "Cancel",
50415
- value: null
50416
- });
50417
- const answer = await logger_logger.select("Multiple Anvil installations found. Which one would you like to use?", choices, availableUrls[0]);
50418
- if (null === answer) {
50419
- logger_logger.warn("Operation cancelled. To login to a new Anvil installation, run:");
50420
- logger_logger.verbose(chalk_source.gray(" anvil login <anvil-url>"));
50421
- process.exit(0);
50345
+ let filteredCandidates = filterCandidates(detectedFromAllRemotes, explicitUrl, explicitUsername);
50346
+ logger_logger.verbose(chalk_source.cyan(`Detected ${detectedFromAllRemotes.length} app ID(s) from git remotes`));
50347
+ if (filteredCandidates.length !== detectedFromAllRemotes.length) logger_logger.verbose(chalk_source.cyan(`After filtering: ${filteredCandidates.length} candidate(s)`));
50348
+ let finalAppId;
50349
+ let anvilUrl;
50350
+ let username = explicitUsername;
50351
+ if (explicitAppId) {
50352
+ finalAppId = explicitAppId;
50353
+ const remoteInfo = lookupRemoteInfoForAppId(explicitAppId, detectedFromAllRemotes);
50354
+ if (remoteInfo.detectedUrl && !explicitUrl) {
50355
+ anvilUrl = normalizeAnvilUrl(remoteInfo.detectedUrl);
50356
+ logger_logger.verbose(chalk_source.cyan("Resolved URL from remote for app ID: ") + chalk_source.bold(anvilUrl));
50357
+ }
50358
+ if (remoteInfo.detectedUsername && !explicitUsername) {
50359
+ username = remoteInfo.detectedUsername;
50360
+ logger_logger.verbose(chalk_source.cyan("Resolved username from remote for app ID: ") + chalk_source.bold(username));
50361
+ }
50362
+ } else {
50363
+ logger_logger.verbose(chalk_source.cyan("No app ID provided, attempting auto-detection..."));
50364
+ if (0 === filteredCandidates.length) {
50365
+ logger_logger.verbose(chalk_source.gray("No app IDs found in git remotes."));
50366
+ const fallbackUrl = resolveUrlForFallback(explicitUrl);
50367
+ const shouldContinue = await logger_logger.confirm(`Search ${fallbackUrl} for matching app IDs? (slower)`, true);
50368
+ if (shouldContinue) {
50369
+ logger_logger.progress("detect", `Searching ${fallbackUrl} ${explicitUsername ? `for ${explicitUsername}` : ''} for matching app IDs...`);
50370
+ const reverseLookupCandidates = await detectAppIdsByCommitLookup(repoPath, {
50371
+ anvilUrl: fallbackUrl,
50372
+ username: explicitUsername,
50373
+ includeRemotes: false
50374
+ });
50375
+ logger_logger.progressEnd("detect");
50376
+ for (const c of reverseLookupCandidates)filteredCandidates.push({
50377
+ ...c,
50378
+ detectedUrl: fallbackUrl
50379
+ });
50422
50380
  }
50423
- anvilUrl = answer;
50381
+ }
50382
+ if (filteredCandidates.length > 0) {
50383
+ for (const c of filteredCandidates)logger_logger.verbose(chalk_source.gray(` Found: ${formatCandidateLabel(c)}`));
50384
+ if (filteredCandidates.length > 1) logger_logger.verbose(chalk_source.yellow(`Found ${filteredCandidates.length} potential app IDs`));
50385
+ }
50386
+ if (useFirst && filteredCandidates.length > 0) {
50387
+ const selected = filteredCandidates[0];
50388
+ finalAppId = selected.appId;
50389
+ if (selected.detectedUrl) anvilUrl = normalizeAnvilUrl(selected.detectedUrl);
50390
+ if (selected.detectedUsername && !username) username = selected.detectedUsername;
50391
+ logger_logger.success("Auto-selected first app ID: " + chalk_source.bold(finalAppId));
50392
+ } else {
50393
+ const selected = await selectAppId(filteredCandidates);
50394
+ if (!selected) {
50395
+ logger_logger.error("No app ID provided. Cannot continue without an app ID.");
50396
+ process.exit(1);
50397
+ }
50398
+ finalAppId = selected.appId;
50399
+ if (selected.detectedUrl) anvilUrl = normalizeAnvilUrl(selected.detectedUrl);
50400
+ if (selected.detectedUsername && !username) username = selected.detectedUsername;
50424
50401
  }
50425
50402
  }
50403
+ if (explicitUrl) anvilUrl = normalizeAnvilUrl(explicitUrl);
50404
+ else if (!anvilUrl) anvilUrl = resolveUrlForFallback();
50405
+ anvilUrl = normalizeAnvilUrl(anvilUrl);
50406
+ logger_logger.verbose(chalk_source.green("Using app ID: ") + chalk_source.bold(finalAppId));
50407
+ logger_logger.verbose(chalk_source.cyan("Using Anvil URL: ") + chalk_source.bold(anvilUrl));
50426
50408
  if (!username) {
50427
50409
  const accounts = auth_getAccountsForUrl(anvilUrl);
50428
50410
  if (1 === accounts.length) {
50429
50411
  username = accounts[0];
50430
- logger_logger.verbose(chalk_source.cyan("Auto-selected username: ") + chalk_source.bold(username));
50412
+ logger_logger.verbose(chalk_source.cyan("Auto-selected account: ") + chalk_source.bold(username));
50431
50413
  } else if (accounts.length > 1) {
50432
- logger_logger.error(`Multiple accounts found for ${anvilUrl}. Please specify which account to use with --user flag.`);
50433
- logger_logger.verbose(chalk_source.gray(`Available accounts: ${accounts.join(", ")}`));
50434
- process.exit(1);
50435
- }
50436
- }
50437
- anvilUrl = normalizeAnvilUrl(anvilUrl);
50438
- logger_logger.verbose(chalk_source.cyan("Using Anvil URL: ") + chalk_source.bold(anvilUrl));
50439
- const detectedUrls = new Set(detectedFromAllRemotes.map((c)=>c.detectedUrl && normalizeAnvilUrl(c.detectedUrl)).filter(Boolean));
50440
- const normalizedSelected = normalizeAnvilUrl(anvilUrl);
50441
- if (explicitUrl && detectedFromAllRemotes.length > 0 && !detectedUrls.has(normalizedSelected)) {
50442
- const detectedUrl = Array.from(detectedUrls)[0];
50443
- const detectedAppIds = detectedFromAllRemotes.filter((c)=>c.detectedUrl && normalizeAnvilUrl(c.detectedUrl) === detectedUrl).map((c)=>c.appId);
50444
- logger_logger.warn(chalk_source.yellow("Git remotes point to ") + chalk_source.bold(detectedUrl) + chalk_source.yellow(", but you specified: ") + chalk_source.bold(normalizedSelected));
50445
- if (detectedAppIds.length > 0) logger_logger.info(chalk_source.gray(" Detected app IDs on remote URL: ") + chalk_source.bold(detectedAppIds.join(", ")));
50446
- logger_logger.info(chalk_source.gray(" To use the remote URL instead, run: ") + chalk_source.cyan(`anvil watch --url ${detectedUrl}`));
50447
- }
50448
- const candidatesForSelectedUrl = detectedFromAllRemotes.filter((c)=>c.detectedUrl && normalizeAnvilUrl(c.detectedUrl) === normalizedSelected);
50449
- if (candidatesForSelectedUrl.length > 0) for (const candidate of candidatesForSelectedUrl){
50450
- const isLoggedInAny = hasTokensForUrl(normalizedSelected);
50451
- const isLoggedInWithUsername = candidate.detectedUsername ? hasTokensForUrl(normalizedSelected, candidate.detectedUsername) : false;
50452
- const loggedInAccounts = auth_getAccountsForUrl(normalizedSelected);
50453
- if (isLoggedInAny) {
50454
- if (candidate.detectedUsername && !isLoggedInWithUsername) {
50455
- logger_logger.warn(chalk_source.yellow("Detected from git remote: ") + chalk_source.bold(`app ID ${candidate.appId}`) + chalk_source.yellow(" on ") + chalk_source.bold(normalizedSelected) + chalk_source.yellow(" with username ") + chalk_source.bold(candidate.detectedUsername) + chalk_source.yellow(", but you're logged in as ") + chalk_source.bold(loggedInAccounts.join(", ")) + chalk_source.yellow("."));
50456
- logger_logger.info(chalk_source.gray(" To log in, run: ") + chalk_source.cyan(`anvil login ${normalizedSelected}`));
50457
- logger_logger.info(chalk_source.gray(" Make sure you're logged in as ") + chalk_source.bold(candidate.detectedUsername) + chalk_source.gray(" in your browser."));
50414
+ const choices = accounts.map((acct)=>({
50415
+ name: acct,
50416
+ value: acct
50417
+ }));
50418
+ choices.push({
50419
+ name: "Cancel",
50420
+ value: null
50421
+ });
50422
+ const selected = await logger_logger.select("Multiple accounts found. Which account owns this app?", choices, accounts[0]);
50423
+ if (null === selected) {
50424
+ logger_logger.warn("Operation cancelled.");
50425
+ process.exit(0);
50458
50426
  }
50459
- } else {
50460
- logger_logger.warn(chalk_source.yellow("Detected from git remote: ") + chalk_source.bold(`app ID ${candidate.appId}`) + chalk_source.yellow(" on ") + chalk_source.bold(normalizedSelected) + chalk_source.yellow(", but you're not logged in to this URL."));
50461
- logger_logger.info(chalk_source.gray(" To log in, run: ") + chalk_source.cyan(`anvil login ${normalizedSelected}`));
50462
- if (candidate.detectedUsername) logger_logger.info(chalk_source.gray(" Make sure you're logged in as ") + chalk_source.bold(candidate.detectedUsername) + chalk_source.gray(" in your browser."));
50427
+ username = selected;
50463
50428
  }
50464
50429
  }
50430
+ if (username) logger_logger.verbose(chalk_source.cyan("Using account: ") + chalk_source.bold(username));
50465
50431
  if (!hasTokensForUrl(anvilUrl, username)) {
50466
50432
  if (username) logger_logger.error(`Not logged in to ${anvilUrl} as ${username}`);
50467
50433
  else logger_logger.error(`Not logged in to ${anvilUrl}`);
50468
50434
  logger_logger.verbose(chalk_source.yellow("Please log in first:"));
50469
- console.log(chalk_source.cyan(` anvil login ${anvilUrl}`));
50435
+ console.log(chalk_source.cyan(` anvil login ${anvilUrl.replace(/^https?:\/\//, "")}`));
50470
50436
  process.exit(1);
50471
50437
  }
50472
- logger_logger.verbose(chalk_source.green("✓ Authentication tokens found for this URL"));
50473
- let finalAppId = explicitAppId;
50474
- if (!finalAppId) {
50475
- logger_logger.verbose(chalk_source.cyan("No app ID provided, attempting auto-detection..."));
50476
- logger_logger.progress("detect", "Auto-detecting app ID...");
50477
- let candidates = await detectAppIds(repoPath, {
50478
- includeReverseLookup: false,
50479
- anvilUrl
50480
- });
50481
- logger_logger.progressEnd("detect");
50482
- if (0 === candidates.length) {
50483
- logger_logger.verbose(chalk_source.gray("Fast lookup found no matching app IDs."));
50484
- const shouldContinue = await logger_logger.confirm(`Search ${anvilUrl} for matching app IDs? (slower)`, true);
50485
- if (shouldContinue) {
50486
- logger_logger.progress("detect", `Searching ${anvilUrl} for matching app IDs...`);
50487
- candidates = await detectAppIds(repoPath, {
50488
- anvilUrl
50489
- });
50490
- logger_logger.progressEnd("detect");
50491
- }
50492
- }
50493
- if (candidates.length > 0) {
50494
- logger_logger.verbose(chalk_source.gray("Checking git remotes:"));
50495
- candidates.filter((c)=>"remote" === c.source).forEach((c)=>{
50496
- logger_logger.verbose(chalk_source.gray(` Found: ${c.description}`));
50497
- logger_logger.verbose(chalk_source.green("Found app ID in remote: ") + chalk_source.bold(c.appId));
50498
- });
50499
- if (candidates.some((c)=>"config" === c.source)) candidates.filter((c)=>"config" === c.source).forEach((c)=>{
50500
- logger_logger.verbose(chalk_source.gray(`Checking ${c.description.split("'")[1]} config...`));
50501
- logger_logger.verbose(chalk_source.green("Found app ID in config: ") + chalk_source.bold(c.appId));
50502
- });
50503
- if (candidates.length > 1) logger_logger.verbose(chalk_source.yellow(`Found ${candidates.length} potential app IDs:`));
50504
- }
50505
- if (useFirst && candidates.length > 0) {
50506
- finalAppId = candidates[0].appId;
50507
- logger_logger.success("Auto-selected first app ID: " + chalk_source.bold(finalAppId));
50508
- } else {
50509
- const selectedAppId = await selectAppId(candidates);
50510
- if (!selectedAppId) {
50511
- logger_logger.error("No app ID provided. Cannot continue without an app ID.");
50512
- process.exit(1);
50513
- }
50514
- finalAppId = selectedAppId;
50515
- }
50516
- }
50517
- logger_logger.verbose(chalk_source.green("Using app ID: ") + chalk_source.bold(finalAppId));
50438
+ logger_logger.verbose(chalk_source.green("✓ Authentication tokens found"));
50518
50439
  logger_logger.progress("validate", `Validating app ID: ${finalAppId}`);
50519
50440
  const appIdValidation = await validateAppId(finalAppId, anvilUrl, username);
50520
50441
  logger_logger.progressEnd("validate");
package/dist/index.js CHANGED
@@ -11980,28 +11980,35 @@ var __webpack_exports__ = {};
11980
11980
  "use strict";
11981
11981
  __webpack_require__.r(__webpack_exports__);
11982
11982
  __webpack_require__.d(__webpack_exports__, {
11983
- syncToLatest: ()=>syncToLatest,
11984
- validateAnvilApp: ()=>validateAnvilApp,
11985
- validateBranchSyncStatus: ()=>validateBranchSyncStatus,
11986
11983
  verifyAuth: ()=>verifyAuth,
11987
- watch: ()=>api_watch,
11988
- detectAppIds: ()=>detectAppIds,
11989
- hasTokensForUrl: ()=>hasTokensForUrl,
11984
+ lookupRemoteInfoForAppId: ()=>lookupRemoteInfoForAppId,
11985
+ validateBranchSyncStatus: ()=>validateBranchSyncStatus,
11986
+ AppIdWithContext: ()=>anvil_api_namespaceObject.AppIdWithContext,
11990
11987
  checkUncommittedChanges: ()=>checkUncommittedChanges,
11991
- getValidAuthToken: ()=>auth_getValidAuthToken,
11992
- login: ()=>login,
11988
+ hasTokensForUrl: ()=>hasTokensForUrl,
11989
+ validateAnvilApp: ()=>validateAnvilApp,
11993
11990
  logout: ()=>logout,
11994
11991
  detectAppIdsFromAllRemotes: ()=>detectAppIdsFromAllRemotes,
11992
+ BranchSyncStatus: ()=>validation_namespaceObject.BranchSyncStatus,
11993
+ formatCandidateLabel: ()=>formatCandidateLabel,
11994
+ syncToLatest: ()=>syncToLatest,
11995
+ filterCandidates: ()=>filterCandidates,
11996
+ getValidAuthToken: ()=>auth_getValidAuthToken,
11997
+ login: ()=>login,
11998
+ watch: ()=>api_watch,
11995
11999
  AppIdCandidate: ()=>anvil_api_namespaceObject.AppIdCandidate,
11996
- BranchSyncStatus: ()=>validation_namespaceObject.BranchSyncStatus
12000
+ detectAppIdsByCommitLookup: ()=>detectAppIdsByCommitLookup
11997
12001
  });
11998
12002
  var anvil_api_namespaceObject = {};
11999
12003
  __webpack_require__.r(anvil_api_namespaceObject);
12000
12004
  __webpack_require__.d(anvil_api_namespaceObject, {
12001
- PH: ()=>detectAppIds,
12005
+ lx: ()=>detectAppIdsByCommitLookup,
12002
12006
  NZ: ()=>detectAppIdsFromAllRemotes,
12007
+ rZ: ()=>filterCandidates,
12008
+ T_: ()=>formatCandidateLabel,
12003
12009
  OI: ()=>getGitFetchUrl,
12004
- $0: ()=>getWebSocketUrl
12010
+ $0: ()=>getWebSocketUrl,
12011
+ Wj: ()=>lookupRemoteInfoForAppId
12005
12012
  });
12006
12013
  var validation_namespaceObject = {};
12007
12014
  __webpack_require__.r(validation_namespaceObject);
@@ -22516,38 +22523,36 @@ var __webpack_exports__ = {};
22516
22523
  function getWebSocketUrl(appId, authToken, anvilUrl = anvil_api_getDefaultAnvilUrl()) {
22517
22524
  return anvilUrl.replace(/^http/, "ws") + `/ide/api/_/apps/${appId}/ws?access_token=${authToken}`;
22518
22525
  }
22519
- async function detectAppIdsFromRemotes(repoPath, anvilUrl) {
22520
- const git = esm_default(repoPath);
22521
- const out = [];
22522
- try {
22523
- const remotes = await git.getRemotes(true);
22524
- const url = new URL(anvilUrl);
22525
- const expectedHost = url.hostname;
22526
- for (const remote of remotes){
22527
- const httpMatch = remote.refs.fetch?.match(/(?:http|https):\/\/(?:[^@]+@)?([^:\/]+)(?::\d+)?\/git\/([A-Z0-9]+)\.git/);
22528
- if (httpMatch) {
22529
- const [, host, detectedAppId] = httpMatch;
22530
- if (host === expectedHost) {
22531
- out.push({
22532
- appId: detectedAppId,
22533
- source: "remote",
22534
- description: `Git remote '${remote.name}'`
22535
- });
22536
- continue;
22537
- }
22538
- }
22539
- const sshMatch = remote.refs.fetch?.match(/ssh:\/\/(?:[^@]+@)?([^:\/]+):(\d+)\/([A-Z0-9]+)\.git/);
22540
- if (sshMatch) {
22541
- const [, host, , detectedAppId] = sshMatch;
22542
- if (host === expectedHost) out.push({
22543
- appId: detectedAppId,
22544
- source: "remote",
22545
- description: `Git remote '${remote.name}' (SSH)`
22546
- });
22547
- }
22548
- }
22549
- } catch (_e) {}
22550
- return out;
22526
+ function filterCandidates(candidates, explicitUrl, explicitUsername) {
22527
+ let filtered = candidates;
22528
+ if (explicitUrl) {
22529
+ const normalizedExplicit = config_normalizeAnvilUrl(explicitUrl);
22530
+ filtered = filtered.filter((c)=>c.detectedUrl && config_normalizeAnvilUrl(c.detectedUrl) === normalizedExplicit);
22531
+ }
22532
+ if (explicitUsername) filtered = filtered.filter((c)=>!c.detectedUsername || c.detectedUsername === explicitUsername);
22533
+ return filtered;
22534
+ }
22535
+ function formatCandidateLabel(candidate) {
22536
+ const parts = [
22537
+ candidate.appId
22538
+ ];
22539
+ if (candidate.detectedUrl) if (candidate.detectedUsername) parts.push(`(${candidate.detectedUsername} on ${candidate.detectedUrl})`);
22540
+ else parts.push(`(${candidate.detectedUrl})`);
22541
+ parts.push(`- ${candidate.description}`);
22542
+ return parts.join(" ");
22543
+ }
22544
+ function lookupRemoteInfoForAppId(appId, detectedRemotes) {
22545
+ const matches = detectedRemotes.filter((c)=>c.appId === appId);
22546
+ if (0 === matches.length) return {};
22547
+ const withUsername = matches.find((c)=>c.detectedUsername);
22548
+ if (withUsername) return {
22549
+ detectedUrl: withUsername.detectedUrl,
22550
+ detectedUsername: withUsername.detectedUsername
22551
+ };
22552
+ return {
22553
+ detectedUrl: matches[0].detectedUrl,
22554
+ detectedUsername: matches[0].detectedUsername
22555
+ };
22551
22556
  }
22552
22557
  async function detectAppIdsFromAllRemotes(repoPath) {
22553
22558
  const git = esm_default(repoPath);
@@ -22583,11 +22588,13 @@ var __webpack_exports__ = {};
22583
22588
  } catch (_e) {}
22584
22589
  return out;
22585
22590
  }
22586
- async function detectAppIdsByCommitLookup(repoPath, anvilUrl) {
22591
+ async function detectAppIdsByCommitLookup(repoPath, options) {
22592
+ const anvilUrl = options.anvilUrl || resolveAnvilUrl();
22593
+ const username = options.username;
22587
22594
  const git = esm_default(repoPath);
22588
22595
  const out = [];
22589
22596
  try {
22590
- const authToken = await auth_getValidAuthToken(anvilUrl);
22597
+ const authToken = await auth_getValidAuthToken(anvilUrl, username);
22591
22598
  const branchRef = await git.revparse([
22592
22599
  "--abbrev-ref",
22593
22600
  "HEAD"
@@ -22626,16 +22633,6 @@ var __webpack_exports__ = {};
22626
22633
  } catch (_e) {}
22627
22634
  return out;
22628
22635
  }
22629
- async function detectAppIds(repoPath, options = {}) {
22630
- const anvilUrl = options.anvilUrl || resolveAnvilUrl();
22631
- const results = [];
22632
- const includeReverseLookup = options.includeReverseLookup ?? true;
22633
- results.push(...await detectAppIdsFromRemotes(repoPath, anvilUrl));
22634
- if (0 === results.length && includeReverseLookup) results.push(...await detectAppIdsByCommitLookup(repoPath, anvilUrl));
22635
- const seen = new Set();
22636
- const unique = results.filter((c)=>seen.has(c.appId) ? false : (seen.add(c.appId), true));
22637
- return unique;
22638
- }
22639
22636
  class WebSocketClient extends Emitter {
22640
22637
  ws = null;
22641
22638
  appId;
@@ -25459,7 +25456,8 @@ var __webpack_exports__ = {};
25459
25456
  authToken,
25460
25457
  currentBranch,
25461
25458
  commitId,
25462
- stagedOnly
25459
+ stagedOnly,
25460
+ username
25463
25461
  });
25464
25462
  session.on("branch-changed", (data)=>{
25465
25463
  logger_logger.debug("Event: branch-changed", data);
@@ -25490,14 +25488,18 @@ var __webpack_exports__ = {};
25490
25488
  }
25491
25489
  })();
25492
25490
  exports.AppIdCandidate = __webpack_exports__.AppIdCandidate;
25491
+ exports.AppIdWithContext = __webpack_exports__.AppIdWithContext;
25493
25492
  exports.BranchSyncStatus = __webpack_exports__.BranchSyncStatus;
25494
25493
  exports.checkUncommittedChanges = __webpack_exports__.checkUncommittedChanges;
25495
- exports.detectAppIds = __webpack_exports__.detectAppIds;
25494
+ exports.detectAppIdsByCommitLookup = __webpack_exports__.detectAppIdsByCommitLookup;
25496
25495
  exports.detectAppIdsFromAllRemotes = __webpack_exports__.detectAppIdsFromAllRemotes;
25496
+ exports.filterCandidates = __webpack_exports__.filterCandidates;
25497
+ exports.formatCandidateLabel = __webpack_exports__.formatCandidateLabel;
25497
25498
  exports.getValidAuthToken = __webpack_exports__.getValidAuthToken;
25498
25499
  exports.hasTokensForUrl = __webpack_exports__.hasTokensForUrl;
25499
25500
  exports.login = __webpack_exports__.login;
25500
25501
  exports.logout = __webpack_exports__.logout;
25502
+ exports.lookupRemoteInfoForAppId = __webpack_exports__.lookupRemoteInfoForAppId;
25501
25503
  exports.syncToLatest = __webpack_exports__.syncToLatest;
25502
25504
  exports.validateAnvilApp = __webpack_exports__.validateAnvilApp;
25503
25505
  exports.validateBranchSyncStatus = __webpack_exports__.validateBranchSyncStatus;
@@ -25505,14 +25507,18 @@ exports.verifyAuth = __webpack_exports__.verifyAuth;
25505
25507
  exports.watch = __webpack_exports__.watch;
25506
25508
  for(var __rspack_i in __webpack_exports__)if (-1 === [
25507
25509
  "AppIdCandidate",
25510
+ "AppIdWithContext",
25508
25511
  "BranchSyncStatus",
25509
25512
  "checkUncommittedChanges",
25510
- "detectAppIds",
25513
+ "detectAppIdsByCommitLookup",
25511
25514
  "detectAppIdsFromAllRemotes",
25515
+ "filterCandidates",
25516
+ "formatCandidateLabel",
25512
25517
  "getValidAuthToken",
25513
25518
  "hasTokensForUrl",
25514
25519
  "login",
25515
25520
  "logout",
25521
+ "lookupRemoteInfoForAppId",
25516
25522
  "syncToLatest",
25517
25523
  "validateAnvilApp",
25518
25524
  "validateBranchSyncStatus",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anvil-works/anvil-cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "CLI tool for developing Anvil apps locally",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",