@anvil-works/anvil-cli 0.5.9 → 0.5.11

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 (2) hide show
  1. package/dist/cli.js +308 -111
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -42538,6 +42538,13 @@ var __webpack_exports__ = {};
42538
42538
  function getConfig(key) {
42539
42539
  return config_config.get(key);
42540
42540
  }
42541
+ function deleteConfig(key) {
42542
+ if (key in configDefaults) config_config.set(key, configDefaults[key]);
42543
+ else {
42544
+ const store = config_config.store;
42545
+ if (store && "object" == typeof store && key in store) config_config.delete(key);
42546
+ }
42547
+ }
42541
42548
  function resetConfig() {
42542
42549
  for (const [key, value] of Object.entries(configDefaults))config_config.set(key, value);
42543
42550
  const currentStore = config_config.store;
@@ -42670,7 +42677,7 @@ var __webpack_exports__ = {};
42670
42677
  ...config
42671
42678
  };
42672
42679
  }
42673
- function globalConfig_getGlobalOutputConfig() {
42680
+ function getGlobalOutputConfig() {
42674
42681
  return globalOutputConfig;
42675
42682
  }
42676
42683
  const logger_timestamp = ()=>new Date().toISOString();
@@ -42691,10 +42698,20 @@ var __webpack_exports__ = {};
42691
42698
  function writeJson(entry) {
42692
42699
  console.log(JSON.stringify(entry));
42693
42700
  }
42701
+ function logJsonResult(success, options) {
42702
+ if (getGlobalOutputConfig().jsonMode) writeJson({
42703
+ type: "result",
42704
+ success,
42705
+ message: options?.message,
42706
+ data: options?.data,
42707
+ error: options?.error,
42708
+ timestamp: logger_timestamp()
42709
+ });
42710
+ }
42694
42711
  class DefaultLogger {
42695
42712
  paused = false;
42696
42713
  get jsonMode() {
42697
- return globalConfig_getGlobalOutputConfig().jsonMode;
42714
+ return getGlobalOutputConfig().jsonMode;
42698
42715
  }
42699
42716
  log(...args) {
42700
42717
  if (this.paused) return;
@@ -42796,7 +42813,7 @@ var __webpack_exports__ = {};
42796
42813
  this.sessionVerbose = options.verbose ?? false;
42797
42814
  }
42798
42815
  get jsonMode() {
42799
- return globalConfig_getGlobalOutputConfig().jsonMode;
42816
+ return getGlobalOutputConfig().jsonMode;
42800
42817
  }
42801
42818
  isVerbose() {
42802
42819
  return this.sessionVerbose || getConfig("verbose");
@@ -50593,7 +50610,7 @@ var __webpack_exports__ = {};
50593
50610
  const execFile = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_.execFile);
50594
50611
  const open_dirname = external_node_path_.dirname((0, external_node_url_namespaceObject.fileURLToPath)(__rslib_import_meta_url__));
50595
50612
  const localXdgOpenPath = external_node_path_.join(open_dirname, 'xdg-open');
50596
- const { platform, arch } = external_node_process_;
50613
+ const { platform: open_platform, arch } = external_node_process_;
50597
50614
  async function getWindowsDefaultBrowserFromWsl() {
50598
50615
  const powershellPath = await powerShellPath();
50599
50616
  const rawCommand = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
@@ -50686,13 +50703,13 @@ var __webpack_exports__ = {};
50686
50703
  let command;
50687
50704
  const cliArguments = [];
50688
50705
  const childProcessOptions = {};
50689
- if ('darwin' === platform) {
50706
+ if ('darwin' === open_platform) {
50690
50707
  command = 'open';
50691
50708
  if (options.wait) cliArguments.push('--wait-apps');
50692
50709
  if (options.background) cliArguments.push('--background');
50693
50710
  if (options.newInstance) cliArguments.push('--new');
50694
50711
  if (app) cliArguments.push('-a', app);
50695
- } else if ('win32' !== platform && (!is_wsl || isInsideContainer() || app)) {
50712
+ } else if ('win32' !== open_platform && (!is_wsl || isInsideContainer() || app)) {
50696
50713
  if (app) command = app;
50697
50714
  else {
50698
50715
  const isBundled = !open_dirname || '/' === open_dirname;
@@ -50701,7 +50718,7 @@ var __webpack_exports__ = {};
50701
50718
  await external_node_fs_promises_namespaceObject.access(localXdgOpenPath, external_node_fs_promises_namespaceObject.constants.X_OK);
50702
50719
  exeLocalXdgOpen = true;
50703
50720
  } catch {}
50704
- const useSystemXdgOpen = external_node_process_.versions.electron ?? ('android' === platform || isBundled || !exeLocalXdgOpen);
50721
+ const useSystemXdgOpen = external_node_process_.versions.electron ?? ('android' === open_platform || isBundled || !exeLocalXdgOpen);
50705
50722
  command = useSystemXdgOpen ? 'xdg-open' : localXdgOpenPath;
50706
50723
  }
50707
50724
  if (appArguments.length > 0) cliArguments.push(...appArguments);
@@ -50727,7 +50744,7 @@ var __webpack_exports__ = {};
50727
50744
  }
50728
50745
  options.target = external_node_buffer_namespaceObject.Buffer.from(encodedArguments.join(' '), 'utf16le').toString('base64');
50729
50746
  }
50730
- if ('darwin' === platform && appArguments.length > 0) cliArguments.push('--args', ...appArguments);
50747
+ if ('darwin' === open_platform && appArguments.length > 0) cliArguments.push('--args', ...appArguments);
50731
50748
  if (options.target) cliArguments.push(options.target);
50732
50749
  const subprocess = external_node_child_process_.spawn(command, cliArguments, childProcessOptions);
50733
50750
  if (options.wait) return new Promise((resolve, reject)=>{
@@ -50753,9 +50770,9 @@ var __webpack_exports__ = {};
50753
50770
  if (!archBinary) throw new Error(`${arch} is not supported`);
50754
50771
  return archBinary;
50755
50772
  }
50756
- function detectPlatformBinary({ [platform]: platformBinary }, { wsl }) {
50773
+ function detectPlatformBinary({ [open_platform]: platformBinary }, { wsl }) {
50757
50774
  if (wsl && is_wsl) return detectArchBinary(wsl);
50758
- if (!platformBinary) throw new Error(`${platform} is not supported`);
50775
+ if (!platformBinary) throw new Error(`${open_platform} is not supported`);
50759
50776
  return detectArchBinary(platformBinary);
50760
50777
  }
50761
50778
  const open_apps = {};
@@ -50882,6 +50899,11 @@ var __webpack_exports__ = {};
50882
50899
  if ("win32" === process.platform) return tokens.map(quoteForCmdShell).join(" ");
50883
50900
  return tokens.map(quoteForPosixShell).join(" ");
50884
50901
  }
50902
+ function formatFallbackEditorLabel(preferredEditorCommand) {
50903
+ const trimmed = preferredEditorCommand.trim();
50904
+ if (!trimmed) return "your editor";
50905
+ return trimmed;
50906
+ }
50885
50907
  async function openPathInEditorOrDefault(targetPath, preferredEditorCommand, deps = defaultDeps) {
50886
50908
  if (preferredEditorCommand) if (deps.isCommandAvailable(preferredEditorCommand)) try {
50887
50909
  const tokens = parseCommandTokens(preferredEditorCommand);
@@ -50892,9 +50914,9 @@ var __webpack_exports__ = {};
50892
50914
  ]));
50893
50915
  return;
50894
50916
  } catch (error) {
50895
- logger_logger.warn(`Failed to open preferred editor '${preferredEditorCommand}': ${errors_getErrorMessage(error)}. Falling back to the system default app.`);
50917
+ logger_logger.warn(`Failed to open ${formatFallbackEditorLabel(preferredEditorCommand)} automatically: ${errors_getErrorMessage(error)}.`);
50896
50918
  }
50897
- else logger_logger.warn(`Preferred editor command '${preferredEditorCommand}' was not found in PATH. Falling back to the system default app.`);
50919
+ else logger_logger.info(`Open ${formatFallbackEditorLabel(preferredEditorCommand)} to edit your app.`);
50898
50920
  await deps.openSystem(targetPath);
50899
50921
  }
50900
50922
  function decideFallbackUrl(explicitUrl, availableUrls = getAvailableAnvilUrls()) {
@@ -50976,6 +50998,16 @@ var __webpack_exports__ = {};
50976
50998
  const selectedUrl = await logger_logger.select("Multiple logged-in Anvil URLs found. Which one would you like to use?", choices, decision.urls[0]);
50977
50999
  return selectedUrl;
50978
51000
  }
51001
+ const defaultConfigureWatchGitAuthDeps = {
51002
+ configureCredentialHelperForUrl: configureCredentialHelperForUrl,
51003
+ setAppAuthBinding: setAppAuthBinding
51004
+ };
51005
+ const defaultSyncStartDeps = {
51006
+ pushToAnvil,
51007
+ recreateSessionAndValidate,
51008
+ recheckSyncStatus,
51009
+ startWatchingWithEventHandlers
51010
+ };
50979
51011
  function resolveWatchOpenPath(repoPath) {
50980
51012
  return external_path_default().resolve(repoPath);
50981
51013
  }
@@ -50985,6 +51017,17 @@ var __webpack_exports__ = {};
50985
51017
  await openPathInEditorOrDefault(targetPath, preferredEditorCommand, deps);
50986
51018
  logger_logger.info(chalk_source.gray(`Opened ${targetPath}`));
50987
51019
  }
51020
+ async function configureWatchGitAuth(options, deps = defaultConfigureWatchGitAuthDeps) {
51021
+ await deps.configureCredentialHelperForUrl(options.repoPath, options.anvilUrl);
51022
+ await deps.setAppAuthBinding(options.repoPath, options.appId, {
51023
+ url: options.anvilUrl,
51024
+ username: options.username
51025
+ });
51026
+ }
51027
+ function isStaleRemotePushError(message) {
51028
+ const normalized = message.toLowerCase();
51029
+ return normalized.includes("fetch first") || normalized.includes("non-fast-forward") || normalized.includes("failed to push some refs") || normalized.includes("remote contains work that you do") || normalized.includes("[rejected]");
51030
+ }
50988
51031
  async function selectAppId(candidates) {
50989
51032
  if (0 === candidates.length) {
50990
51033
  logger_logger.warn("Could not auto-detect app ID from repository.");
@@ -51071,11 +51114,19 @@ var __webpack_exports__ = {};
51071
51114
  logger_logger.progress("push", "Pushing to Anvil...");
51072
51115
  await git.push(pushUrl, refSpec, options.force ?? false);
51073
51116
  logger_logger.progressEnd("push", "Pushed to Anvil");
51074
- return true;
51117
+ return {
51118
+ success: true,
51119
+ staleRemote: false
51120
+ };
51075
51121
  } catch (e) {
51122
+ const errorMessage = errors_getErrorMessage(e);
51076
51123
  logger_logger.progressEnd("push", "Push failed");
51077
- logger_logger.error(`Failed to push: ${errors_getErrorMessage(e)}`);
51078
- return false;
51124
+ logger_logger.error(`Failed to push: ${errorMessage}`);
51125
+ return {
51126
+ success: false,
51127
+ staleRemote: isStaleRemotePushError(errorMessage),
51128
+ errorMessage
51129
+ };
51079
51130
  }
51080
51131
  }
51081
51132
  async function fetchAndRebaseFromAnvil(options) {
@@ -51201,7 +51252,7 @@ var __webpack_exports__ = {};
51201
51252
  return false;
51202
51253
  }
51203
51254
  }
51204
- async function checkSyncStatusAndStart(session, options) {
51255
+ async function checkSyncStatusAndStart(session, options, deps = defaultSyncStartDeps) {
51205
51256
  const syncStatus = session.syncStatus;
51206
51257
  const branchName = session.getBranchName() || "master";
51207
51258
  const hasUncommitted = session.hasUncommittedChanges;
@@ -51214,8 +51265,8 @@ var __webpack_exports__ = {};
51214
51265
  else {
51215
51266
  shouldPush = await logger_logger.confirm("Would you like to push this branch to Anvil?", true);
51216
51267
  if (shouldPush) {
51217
- const changed = await recheckSyncStatus(stateCategory, branchName, options);
51218
- if (changed) return await checkSyncStatusAndStart(changed, options);
51268
+ const changed = await deps.recheckSyncStatus(stateCategory, branchName, options);
51269
+ if (changed) return await checkSyncStatusAndStart(changed, options, deps);
51219
51270
  }
51220
51271
  }
51221
51272
  if (!shouldPush) {
@@ -51224,15 +51275,21 @@ var __webpack_exports__ = {};
51224
51275
  return false;
51225
51276
  }
51226
51277
  session.cleanup();
51227
- const pushed = await pushToAnvil({
51278
+ const pushed = await deps.pushToAnvil({
51228
51279
  repoPath: options.repoPath,
51229
51280
  appId: options.appId,
51230
51281
  anvilUrl: options.anvilUrl,
51231
51282
  branchName,
51232
51283
  username: options.username
51233
51284
  });
51234
- if (!pushed) return false;
51235
- return await recreateSessionAndValidate(options);
51285
+ if (!pushed.success) {
51286
+ if (pushed.staleRemote) {
51287
+ logger_logger.warn("Branch appeared on Anvil while pushing. Re-checking sync status...");
51288
+ return await deps.recreateSessionAndValidate(options);
51289
+ }
51290
+ return false;
51291
+ }
51292
+ return await deps.recreateSessionAndValidate(options);
51236
51293
  }
51237
51294
  if (syncStatus?.behind || syncStatus?.ahead) if (syncStatus.ahead && !syncStatus.behind) {
51238
51295
  logger_logger.warn(`Your local repository is ${syncStatus.ahead} commit(s) ahead of Anvil.`);
@@ -51252,8 +51309,8 @@ var __webpack_exports__ = {};
51252
51309
  ], "push");
51253
51310
  shouldPush = "push" === action;
51254
51311
  if (shouldPush) {
51255
- const changed = await recheckSyncStatus(stateCategory, branchName, options);
51256
- if (changed) return await checkSyncStatusAndStart(changed, options);
51312
+ const changed = await deps.recheckSyncStatus(stateCategory, branchName, options);
51313
+ if (changed) return await checkSyncStatusAndStart(changed, options, deps);
51257
51314
  }
51258
51315
  }
51259
51316
  if (!shouldPush) {
@@ -51262,15 +51319,15 @@ var __webpack_exports__ = {};
51262
51319
  return false;
51263
51320
  }
51264
51321
  session.cleanup();
51265
- const pushed = await pushToAnvil({
51322
+ const pushed = await deps.pushToAnvil({
51266
51323
  repoPath: options.repoPath,
51267
51324
  appId: options.appId,
51268
51325
  anvilUrl: options.anvilUrl,
51269
51326
  branchName,
51270
51327
  username: options.username
51271
51328
  });
51272
- if (!pushed) return false;
51273
- return await recreateSessionAndValidate(options);
51329
+ if (!pushed.success) return false;
51330
+ return await deps.recreateSessionAndValidate(options);
51274
51331
  } else if (syncStatus.diverged) {
51275
51332
  logger_logger.warn("Your local repository has diverged from Anvil.");
51276
51333
  logger_logger.info(chalk_source.gray(` You are ${syncStatus.ahead} commit(s) ahead and ${syncStatus.behind} commit(s) behind.`));
@@ -51293,7 +51350,7 @@ var __webpack_exports__ = {};
51293
51350
  return false;
51294
51351
  }
51295
51352
  if (!result.success) return false;
51296
- return await recreateSessionAndValidate(options);
51353
+ return await deps.recreateSessionAndValidate(options);
51297
51354
  }
51298
51355
  const action = await logger_logger.select("How would you like to resolve this?", [
51299
51356
  {
@@ -51317,8 +51374,8 @@ var __webpack_exports__ = {};
51317
51374
  logger_logger.warn("Watch cancelled.");
51318
51375
  return false;
51319
51376
  }
51320
- const changed = await recheckSyncStatus(stateCategory, branchName, options);
51321
- if (changed) return await checkSyncStatusAndStart(changed, options);
51377
+ const changed = await deps.recheckSyncStatus(stateCategory, branchName, options);
51378
+ if (changed) return await checkSyncStatusAndStart(changed, options, deps);
51322
51379
  if ("rebase" === action) {
51323
51380
  const result = await fetchAndRebaseFromAnvil({
51324
51381
  repoPath: options.repoPath,
@@ -51335,7 +51392,7 @@ var __webpack_exports__ = {};
51335
51392
  return false;
51336
51393
  }
51337
51394
  if (!result.success) return false;
51338
- return await recreateSessionAndValidate(options);
51395
+ return await deps.recreateSessionAndValidate(options);
51339
51396
  }
51340
51397
  if ("reset" === action) {
51341
51398
  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?`;
@@ -51352,7 +51409,7 @@ var __webpack_exports__ = {};
51352
51409
  username: options.username
51353
51410
  });
51354
51411
  if (!reset) return false;
51355
- return await recreateSessionAndValidate(options);
51412
+ return await deps.recreateSessionAndValidate(options);
51356
51413
  }
51357
51414
  if ("push" === action) {
51358
51415
  const confirmed = await logger_logger.confirm("This will overwrite Anvil's version. Are you sure?", false);
@@ -51360,7 +51417,7 @@ var __webpack_exports__ = {};
51360
51417
  logger_logger.warn("Watch cancelled.");
51361
51418
  return false;
51362
51419
  }
51363
- const pushed = await pushToAnvil({
51420
+ const pushed = await deps.pushToAnvil({
51364
51421
  repoPath: options.repoPath,
51365
51422
  appId: options.appId,
51366
51423
  anvilUrl: options.anvilUrl,
@@ -51368,8 +51425,8 @@ var __webpack_exports__ = {};
51368
51425
  username: options.username,
51369
51426
  force: true
51370
51427
  });
51371
- if (!pushed) return false;
51372
- return await recreateSessionAndValidate(options);
51428
+ if (!pushed.success) return false;
51429
+ return await deps.recreateSessionAndValidate(options);
51373
51430
  }
51374
51431
  } else {
51375
51432
  logger_logger.warn(`Your local repository is ${syncStatus.behind} commit(s) behind Anvil.`);
@@ -51380,8 +51437,8 @@ var __webpack_exports__ = {};
51380
51437
  else {
51381
51438
  shouldSync = await logger_logger.confirm("Would you like to sync to the latest version from Anvil now?", true);
51382
51439
  if (shouldSync) {
51383
- const changed = await recheckSyncStatus(stateCategory, branchName, options);
51384
- if (changed) return await checkSyncStatusAndStart(changed, options);
51440
+ const changed = await deps.recheckSyncStatus(stateCategory, branchName, options);
51441
+ if (changed) return await checkSyncStatusAndStart(changed, options, deps);
51385
51442
  }
51386
51443
  }
51387
51444
  if (!shouldSync) {
@@ -51406,9 +51463,9 @@ var __webpack_exports__ = {};
51406
51463
  logger_logger.error(`Failed to sync: ${errors_getErrorMessage(e)}`);
51407
51464
  process.exit(1);
51408
51465
  }
51409
- return await recreateSessionAndValidate(options);
51466
+ return await deps.recreateSessionAndValidate(options);
51410
51467
  }
51411
- await startWatchingWithEventHandlers(session, options);
51468
+ await deps.startWatchingWithEventHandlers(session, options);
51412
51469
  return true;
51413
51470
  }
51414
51471
  async function startWatchingWithEventHandlers(session, options) {
@@ -51613,10 +51670,16 @@ var __webpack_exports__ = {};
51613
51670
  if (username) logger_logger.error(`Not logged in to ${anvilUrl} as ${username}`);
51614
51671
  else logger_logger.error(`Not logged in to ${anvilUrl}`);
51615
51672
  logger_logger.verbose(chalk_source.yellow("Please log in first:"));
51616
- console.log(chalk_source.cyan(` anvil login ${anvilUrl.replace(/^https?:\/\//, "")}`));
51673
+ logger_logger.info(chalk_source.cyan(` anvil login ${anvilUrl.replace(/^https?:\/\//, "")}`));
51617
51674
  process.exit(1);
51618
51675
  }
51619
51676
  logger_logger.verbose(chalk_source.green("✓ Authentication tokens found"));
51677
+ await configureWatchGitAuth({
51678
+ repoPath,
51679
+ appId: finalAppId,
51680
+ anvilUrl,
51681
+ username
51682
+ });
51620
51683
  logger_logger.progress("validate", `Validating app ID: ${finalAppId}`);
51621
51684
  const appIdValidation = await validateAppId(finalAppId, anvilUrl, username);
51622
51685
  logger_logger.progressEnd("validate");
@@ -52803,6 +52866,54 @@ var __webpack_exports__ = {};
52803
52866
  await deps.logout();
52804
52867
  deps.resetConfig();
52805
52868
  }
52869
+ function performConfigDelete(key) {
52870
+ const allConfig = getAllConfig();
52871
+ const existed = Object.prototype.hasOwnProperty.call(allConfig, key);
52872
+ deleteConfig(key);
52873
+ return {
52874
+ existed
52875
+ };
52876
+ }
52877
+ function getConfigListData() {
52878
+ const allConfig = getAllConfig();
52879
+ const resolvedUrl = resolveAnvilUrl();
52880
+ const defaultUrl = isDevMode() ? "http://localhost:3000" : "https://anvil.works";
52881
+ const authTokens = allConfig.authTokens;
52882
+ const urls = authTokens && "object" == typeof authTokens ? Object.keys(authTokens).sort() : [];
52883
+ const accounts = [];
52884
+ for (const url of urls){
52885
+ const urlData = authTokens?.[url];
52886
+ if (!(!urlData || "object" != typeof urlData || Array.isArray(urlData))) for (const username of Object.keys(urlData).sort()){
52887
+ const accountData = urlData[username];
52888
+ const hasConfigTokens = !!(accountData?.authToken || accountData?.refreshToken);
52889
+ const hasAnyTokens = hasTokensForUrl(url, username);
52890
+ accounts.push({
52891
+ url,
52892
+ username,
52893
+ status: hasConfigTokens ? "config" : hasAnyTokens ? "keychain" : "none"
52894
+ });
52895
+ }
52896
+ }
52897
+ const skipKeys = new Set([
52898
+ "anvilUrl",
52899
+ "authTokens"
52900
+ ]);
52901
+ const values = {};
52902
+ for (const [key, value] of Object.entries(allConfig))if (!skipKeys.has(key)) values[key] = value;
52903
+ return {
52904
+ anvilUrl: {
52905
+ value: resolvedUrl,
52906
+ defaultValue: defaultUrl,
52907
+ isDefault: resolvedUrl === defaultUrl
52908
+ },
52909
+ authTokens: {
52910
+ totalUrls: urls.length,
52911
+ totalAccounts: accounts.filter((account)=>"none" !== account.status).length,
52912
+ accounts
52913
+ },
52914
+ values
52915
+ };
52916
+ }
52806
52917
  function registerConfigCommand(program) {
52807
52918
  const configCommand = program.command("config").description("Manage configuration").alias("c");
52808
52919
  configCommand.command("get <key>").description("Get a configuration value").action(async (key)=>{
@@ -52829,49 +52940,35 @@ var __webpack_exports__ = {};
52829
52940
  process.exit(1);
52830
52941
  }
52831
52942
  });
52943
+ configCommand.command("delete <key>").alias("unset").description("Delete a configuration value or reset it to its default").action(async (key)=>{
52944
+ try {
52945
+ const result = performConfigDelete(key);
52946
+ if (result.existed) logger_logger.success(`Deleted ${key}`);
52947
+ else logger_logger.warn(`Key '${key}' was not set; applied default/empty value if applicable.`);
52948
+ } catch (error) {
52949
+ logger_logger.error(error instanceof Error ? error.message : String(error));
52950
+ process.exit(1);
52951
+ }
52952
+ });
52832
52953
  configCommand.command("list").description("List all configuration values").action(async ()=>{
52833
52954
  try {
52834
- const allConfig = getAllConfig();
52955
+ const configData = getConfigListData();
52956
+ if (getGlobalOutputConfig().jsonMode) return void logJsonResult(true, {
52957
+ data: configData
52958
+ });
52835
52959
  console.log(chalk_source.bold("\nConfiguration:"));
52836
52960
  console.log(chalk_source.gray("─".repeat(50)));
52837
- const resolvedUrl = resolveAnvilUrl();
52838
- const defaultUrl = isDevMode() ? "http://localhost:3000" : "https://anvil.works";
52839
- const defaultHint = resolvedUrl === defaultUrl ? "" : chalk_source.gray(` (default: ${defaultUrl})`);
52840
- console.log(chalk_source.cyan("anvilUrl") + ` = ${resolvedUrl}${defaultHint}`);
52841
- const authTokens = allConfig.authTokens;
52842
- if (authTokens && "object" == typeof authTokens) {
52843
- const urls = Object.keys(authTokens).sort();
52844
- if (urls.length > 0) {
52845
- let totalAccounts = 0;
52846
- for (const url of urls){
52847
- const urlData = authTokens[url];
52848
- if (urlData && "object" == typeof urlData && !Array.isArray(urlData)) {
52849
- const usernames = Object.keys(urlData);
52850
- totalAccounts += usernames.filter((username)=>hasTokensForUrl(url, username)).length;
52851
- }
52852
- }
52853
- console.log(chalk_source.cyan("authTokens") + ` = ${chalk_source.gray(`${totalAccounts} logged-in account(s) across ${urls.length} URL(s)`)}`);
52854
- for (const url of urls){
52855
- const urlData = authTokens[url];
52856
- if (urlData && "object" == typeof urlData && !Array.isArray(urlData)) {
52857
- const usernames = Object.keys(urlData).sort();
52858
- if (usernames.length > 0) for (const username of usernames){
52859
- const accountData = urlData[username];
52860
- const hasConfigTokens = !!(accountData?.authToken || accountData?.refreshToken);
52861
- const hasAnyTokens = hasTokensForUrl(url, username);
52862
- const status = hasConfigTokens ? chalk_source.green("✓ logged in (config)") : hasAnyTokens ? chalk_source.green("✓ logged in (keychain)") : chalk_source.gray("(no tokens)");
52863
- console.log(chalk_source.gray(` ${url}: ${username} ${status}`));
52864
- }
52865
- }
52866
- }
52867
- } else console.log(chalk_source.cyan("authTokens") + ` = ${chalk_source.gray("(no accounts)")}`);
52961
+ const defaultHint = configData.anvilUrl.isDefault ? "" : chalk_source.gray(` (default: ${configData.anvilUrl.defaultValue})`);
52962
+ console.log(chalk_source.cyan("anvilUrl") + ` = ${configData.anvilUrl.value}${defaultHint}`);
52963
+ if (configData.authTokens.totalUrls > 0) {
52964
+ console.log(chalk_source.cyan("authTokens") + ` = ${chalk_source.gray(`${configData.authTokens.totalAccounts} logged-in account(s) across ${configData.authTokens.totalUrls} URL(s)`)}`);
52965
+ for (const account of configData.authTokens.accounts){
52966
+ const status = "config" === account.status ? chalk_source.green("✓ logged in (config)") : "keychain" === account.status ? chalk_source.green("✓ logged in (keychain)") : chalk_source.gray("(no tokens)");
52967
+ console.log(chalk_source.gray(` ${account.url}: ${account.username} ${status}`));
52968
+ }
52868
52969
  } else console.log(chalk_source.cyan("authTokens") + ` = ${chalk_source.gray("(none)")}`);
52869
- const skipKeys = new Set([
52870
- "anvilUrl",
52871
- "authTokens"
52872
- ]);
52873
- for (const [k, v] of Object.entries(allConfig))if (!skipKeys.has(k)) if (null === v) console.log(chalk_source.cyan(k) + ` = ${chalk_source.gray("null")}`);
52874
- else console.log(chalk_source.cyan(k) + ` = ${v}`);
52970
+ for (const [key, value] of Object.entries(configData.values))if (null === value) console.log(chalk_source.cyan(key) + ` = ${chalk_source.gray("null")}`);
52971
+ else console.log(chalk_source.cyan(key) + ` = ${value}`);
52875
52972
  console.log(chalk_source.gray("─".repeat(50)));
52876
52973
  console.log();
52877
52974
  } catch (e) {
@@ -52889,11 +52986,23 @@ var __webpack_exports__ = {};
52889
52986
  }
52890
52987
  });
52891
52988
  }
52989
+ function getVersionInfo(version) {
52990
+ return {
52991
+ version,
52992
+ nodeVersion: process.version,
52993
+ platform: process.platform,
52994
+ arch: process.arch
52995
+ };
52996
+ }
52892
52997
  function registerVersionCommand(program, version) {
52893
52998
  program.command("version").description("Show version information").action(()=>{
52894
- console.log(chalk_source.cyan.bold(`\n⚙ anvil ${version}`));
52895
- console.log(chalk_source.gray(` Node.js ${process.version}`));
52896
- console.log(chalk_source.gray(` Platform: ${process.platform} ${process.arch}`));
52999
+ const info = getVersionInfo(version);
53000
+ if (getGlobalOutputConfig().jsonMode) return void logJsonResult(true, {
53001
+ data: info
53002
+ });
53003
+ console.log(chalk_source.cyan.bold(`\n⚙ anvil ${info.version}`));
53004
+ console.log(chalk_source.gray(` Node.js ${info.nodeVersion}`));
53005
+ console.log(chalk_source.gray(` Platform: ${info.platform} ${info.arch}`));
52897
53006
  console.log();
52898
53007
  });
52899
53008
  }
@@ -52903,6 +53012,68 @@ var __webpack_exports__ = {};
52903
53012
  runInteractiveLoginFlow: runInteractiveLoginFlow
52904
53013
  };
52905
53014
  const DEFAULT_ANVIL_SERVER_URL_PROMPT = "Default Anvil server URL";
53015
+ const CUSTOM_EDITOR_CHOICE = "__custom_editor_command__";
53016
+ function formatCustomEditorChoice(currentEditor) {
53017
+ if (!currentEditor) return "Enter custom editor command";
53018
+ return `Custom editor command (${currentEditor})`;
53019
+ }
53020
+ async function promptForPreferredEditor(currentEditor, installedEditors, ui) {
53021
+ if (0 === installedEditors.length) {
53022
+ ui.warn("No supported editor command was found in PATH.");
53023
+ ui.info(chalk_source.gray("If your editor is installed, its shell command may not be available in this terminal."));
53024
+ const action = await ui.select("Preferred editor", [
53025
+ {
53026
+ name: "None",
53027
+ value: ""
53028
+ },
53029
+ {
53030
+ name: formatCustomEditorChoice(currentEditor),
53031
+ value: CUSTOM_EDITOR_CHOICE
53032
+ }
53033
+ ], currentEditor ? CUSTOM_EDITOR_CHOICE : "");
53034
+ if (action !== CUSTOM_EDITOR_CHOICE) return action;
53035
+ const customDefault = currentEditor && !preferredEditors.includes(currentEditor) ? currentEditor : "";
53036
+ const answer = await ui.prompt([
53037
+ {
53038
+ type: "input",
53039
+ name: "preferredEditorCommand",
53040
+ message: "Custom editor command",
53041
+ default: customDefault
53042
+ }
53043
+ ]);
53044
+ return answer.preferredEditorCommand.trim();
53045
+ }
53046
+ if (installedEditors.length < preferredEditors.length) {
53047
+ const unavailableCount = preferredEditors.length - installedEditors.length;
53048
+ ui.verbose(chalk_source.gray(`${unavailableCount} supported editor(s) are not installed and were hidden.`));
53049
+ }
53050
+ const editorChoices = [
53051
+ {
53052
+ name: "None",
53053
+ value: ""
53054
+ },
53055
+ ...installedEditors.map((editor)=>({
53056
+ name: editor,
53057
+ value: editor
53058
+ })),
53059
+ {
53060
+ name: formatCustomEditorChoice(currentEditor),
53061
+ value: CUSTOM_EDITOR_CHOICE
53062
+ }
53063
+ ];
53064
+ const defaultValue = editorChoices.some((choice)=>choice.value === currentEditor) ? currentEditor : currentEditor ? CUSTOM_EDITOR_CHOICE : "";
53065
+ const selectedEditor = await ui.select("Preferred editor", editorChoices, defaultValue);
53066
+ if (selectedEditor !== CUSTOM_EDITOR_CHOICE) return selectedEditor;
53067
+ const answer = await ui.prompt([
53068
+ {
53069
+ type: "input",
53070
+ name: "preferredEditorCommand",
53071
+ message: "Custom editor command",
53072
+ default: currentEditor && !installedEditors.includes(currentEditor) ? currentEditor : ""
53073
+ }
53074
+ ]);
53075
+ return answer.preferredEditorCommand.trim();
53076
+ }
52906
53077
  async function getConfigureVersionStatus(currentVersion, latestVersionGetter = getLatestVersion) {
52907
53078
  const latestVersion = await latestVersionGetter();
52908
53079
  if (!latestVersion) return {
@@ -52961,25 +53132,11 @@ var __webpack_exports__ = {};
52961
53132
  const configuredAnvilUrl = parseConfigSetValue("anvilUrl", urlAnswer.anvilUrl);
52962
53133
  setConfig("anvilUrl", configuredAnvilUrl);
52963
53134
  logger_logger.success(`Set default Anvil server URL (anvilUrl) = ${configuredAnvilUrl}`);
52964
- const currentEditor = String(getConfig("preferredEditor") || "").trim().toLowerCase();
53135
+ const currentEditor = String(getConfig("preferredEditor") || "").trim();
53136
+ const currentEditorNormalized = currentEditor.toLowerCase();
52965
53137
  const installedEditors = getInstalledPreferredEditors();
52966
- if (0 === installedEditors.length) logger_logger.warn("No supported editor command was found in PATH. Leaving preferredEditor unset is recommended.");
52967
- else if (installedEditors.length < preferredEditors.length) {
52968
- const unavailableCount = preferredEditors.length - installedEditors.length;
52969
- logger_logger.verbose(chalk_source.gray(`${unavailableCount} supported editor(s) are not installed and were hidden.`));
52970
- }
52971
- const editorChoices = [
52972
- {
52973
- name: "None",
52974
- value: ""
52975
- },
52976
- ...installedEditors.map((editor)=>({
52977
- name: editor,
52978
- value: editor
52979
- }))
52980
- ];
52981
- if (currentEditor && !editorChoices.some((choice)=>choice.value === currentEditor)) logger_logger.warn(`Configured preferredEditor '${currentEditor}' is not currently available in PATH.`);
52982
- const selectedEditor = await logger_logger.select("Preferred editor", editorChoices, editorChoices.some((choice)=>choice.value === currentEditor) ? currentEditor : "");
53138
+ if (currentEditor && installedEditors.length > 0 && !installedEditors.includes(currentEditorNormalized)) logger_logger.warn(`Configured preferredEditor '${currentEditor}' is not currently available in PATH.`);
53139
+ const selectedEditor = await promptForPreferredEditor(currentEditor, installedEditors, logger_logger);
52983
53140
  setConfig("preferredEditor", selectedEditor);
52984
53141
  if (selectedEditor) logger_logger.success(`Set preferredEditor = ${selectedEditor}`);
52985
53142
  else logger_logger.info("Left preferredEditor unset.");
@@ -53025,11 +53182,23 @@ var __webpack_exports__ = {};
53025
53182
  getLatestVersion().then((latestVersion)=>{
53026
53183
  if (latestVersion && semver_default().valid(VERSION) && semver_default().valid(latestVersion) && semver_default().lt(VERSION, latestVersion)) {
53027
53184
  logger_logger.warn("A new version of anvil is available!");
53028
- console.log(chalk_source.gray(` Current: ${VERSION} → Latest: ${latestVersion}`) + chalk_source.cyan("\n Run 'anvil update' to see update commands."));
53029
- console.log();
53185
+ logger_logger.info(chalk_source.gray(` Current: ${VERSION} → Latest: ${latestVersion}`) + chalk_source.cyan("\n Run 'anvil update' to see update commands."));
53030
53186
  }
53031
53187
  }).catch(()=>{});
53032
53188
  }
53189
+ function getUpdateInstructions(platform) {
53190
+ if ("win32" === platform) return [
53191
+ "try { irm https://anvil.works/install-cli.ps1 | iex } catch { npm install -g @anvil-works/anvil-cli@latest }",
53192
+ "curl -fsSL https://anvil.works/install-cli.cmd -o install.cmd && install.cmd && del install.cmd || npm install -g @anvil-works/anvil-cli@latest",
53193
+ "npm install -g @anvil-works/anvil-cli@latest",
53194
+ "pnpm add -g @anvil-works/anvil-cli@latest"
53195
+ ];
53196
+ return [
53197
+ "curl -fsSL https://anvil.works/install-cli.sh | sh || npm install -g @anvil-works/anvil-cli@latest",
53198
+ "npm install -g @anvil-works/anvil-cli@latest",
53199
+ "pnpm add -g @anvil-works/anvil-cli@latest"
53200
+ ];
53201
+ }
53033
53202
  async function handleUpdateCommand() {
53034
53203
  try {
53035
53204
  logger_logger.progress("update", "Checking for updates...");
@@ -53044,26 +53213,54 @@ var __webpack_exports__ = {};
53044
53213
  process.exit(1);
53045
53214
  }
53046
53215
  if (semver_default().eq(VERSION, latestVersion)) {
53047
- logger_logger.success(`You're already using the latest version: ${VERSION}`);
53048
- console.log();
53216
+ if (getGlobalOutputConfig().jsonMode) logJsonResult(true, {
53217
+ data: {
53218
+ currentVersion: VERSION,
53219
+ latestVersion,
53220
+ status: "up-to-date"
53221
+ }
53222
+ });
53223
+ else {
53224
+ logger_logger.success(`You're already using the latest version: ${VERSION}`);
53225
+ console.log();
53226
+ }
53049
53227
  return;
53050
53228
  }
53051
53229
  if (semver_default().gt(VERSION, latestVersion)) {
53052
- logger_logger.warn(`You're using version ${VERSION}, which is newer than the latest published version ${latestVersion}.`);
53053
- console.log(chalk_source.gray(" This might be a development build."));
53054
- console.log();
53230
+ if (getGlobalOutputConfig().jsonMode) logJsonResult(true, {
53231
+ data: {
53232
+ currentVersion: VERSION,
53233
+ latestVersion,
53234
+ status: "ahead",
53235
+ note: "This might be a development build."
53236
+ }
53237
+ });
53238
+ else {
53239
+ logger_logger.warn(`You're using version ${VERSION}, which is newer than the latest published version ${latestVersion}.`);
53240
+ console.log(chalk_source.gray(" This might be a development build."));
53241
+ console.log();
53242
+ }
53055
53243
  return;
53056
53244
  }
53245
+ const instructions = getUpdateInstructions(process.platform);
53246
+ if (getGlobalOutputConfig().jsonMode) return void logJsonResult(true, {
53247
+ data: {
53248
+ currentVersion: VERSION,
53249
+ latestVersion,
53250
+ status: "update-available",
53251
+ instructions
53252
+ }
53253
+ });
53057
53254
  logger_logger.warn(`↑ Update available: ${VERSION} → ${latestVersion}`);
53058
53255
  console.log();
53059
53256
  console.log(chalk_source.cyan("To update, run one of the following commands:"));
53060
53257
  console.log();
53061
53258
  if ("win32" === process.platform) {
53062
- 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 }"));
53063
- 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"));
53064
- } 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"));
53065
- console.log(chalk_source.gray(" npm: ") + chalk_source.white("npm install -g @anvil-works/anvil-cli@latest"));
53066
- console.log(chalk_source.gray(" pnpm: ") + chalk_source.white("pnpm add -g @anvil-works/anvil-cli@latest"));
53259
+ console.log(chalk_source.gray(" PowerShell: ") + chalk_source.white(instructions[0]));
53260
+ console.log(chalk_source.gray(" CMD: ") + chalk_source.white(instructions[1]));
53261
+ } else console.log(chalk_source.gray(" macOS/Linux: ") + chalk_source.white(instructions[0]));
53262
+ console.log(chalk_source.gray(" npm: ") + chalk_source.white(instructions[instructions.length - 2]));
53263
+ console.log(chalk_source.gray(" pnpm: ") + chalk_source.white(instructions[instructions.length - 1]));
53067
53264
  console.log();
53068
53265
  } catch (e) {
53069
53266
  logger_logger.error("Error: " + e.message);
@@ -53071,7 +53268,7 @@ var __webpack_exports__ = {};
53071
53268
  }
53072
53269
  }
53073
53270
  function addGlobalOptionsHelp(command) {
53074
- if ("help" !== command.name()) command.addHelpText("after", "\n" + chalk_source.bold("Global Options:") + "\n -V, --verbose Show detailed output\n");
53271
+ if ("help" !== command.name()) command.addHelpText("after", "\n" + chalk_source.bold("Global Options:") + "\n --json Output in JSON format (NDJSON) for scripting/LLM consumption\n -V, --verbose Show detailed output\n");
53075
53272
  command.commands.forEach((subcommand)=>addGlobalOptionsHelp(subcommand));
53076
53273
  }
53077
53274
  function buildProgram() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anvil-works/anvil-cli",
3
- "version": "0.5.9",
3
+ "version": "0.5.11",
4
4
  "description": "CLI tool for developing Anvil apps locally",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",