@anvil-works/anvil-cli 0.6.3 → 0.7.0-canary.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.
package/dist/cli.js CHANGED
@@ -42250,12 +42250,13 @@ var __webpack_exports__ = {};
42250
42250
  const EMPTY_TOKENS = {
42251
42251
  authToken: null,
42252
42252
  refreshToken: null,
42253
- authTokenExpiresAt: null
42253
+ authTokenExpiresAt: null,
42254
+ clientId: null
42254
42255
  };
42255
42256
  function isAccountTokens(value) {
42256
42257
  if (!value || "object" != typeof value || Array.isArray(value)) return false;
42257
42258
  const obj = value;
42258
- return ("authToken" in obj || "refreshToken" in obj || "authTokenExpiresAt" in obj) && (null === obj.authToken || "string" == typeof obj.authToken) && (null === obj.refreshToken || "string" == typeof obj.refreshToken) && (null === obj.authTokenExpiresAt || "number" == typeof obj.authTokenExpiresAt);
42259
+ return ("authToken" in obj || "refreshToken" in obj || "authTokenExpiresAt" in obj) && (null === obj.authToken || "string" == typeof obj.authToken) && (null === obj.refreshToken || "string" == typeof obj.refreshToken) && (null === obj.authTokenExpiresAt || "number" == typeof obj.authTokenExpiresAt) && (!("clientId" in obj) || null === obj.clientId || "string" == typeof obj.clientId);
42259
42260
  }
42260
42261
  function isUrlTokens(value) {
42261
42262
  if (!value || "object" != typeof value || Array.isArray(value)) return false;
@@ -42319,7 +42320,8 @@ var __webpack_exports__ = {};
42319
42320
  if (keychainOk) store[normalized][username] = {
42320
42321
  authToken: null,
42321
42322
  refreshToken: null,
42322
- authTokenExpiresAt: tokens.authTokenExpiresAt
42323
+ authTokenExpiresAt: tokens.authTokenExpiresAt,
42324
+ clientId: tokens.clientId ?? null
42323
42325
  };
42324
42326
  else store[normalized][username] = tokens;
42325
42327
  setTokenStore(store);
@@ -42600,6 +42602,10 @@ var __webpack_exports__ = {};
42600
42602
  function getGlobalOutputConfig() {
42601
42603
  return globalOutputConfig;
42602
42604
  }
42605
+ function assertCanPrompt(action = "This command", explicitAlternative = "Pass explicit flags instead.") {
42606
+ if (globalOutputConfig.jsonMode) throw new Error(`${action} requires interactive input, but --json disables prompts. ${explicitAlternative}`);
42607
+ if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error(`${action} requires interactive input, but stdin/stdout is not a TTY. ${explicitAlternative}`);
42608
+ }
42603
42609
  const logger_timestamp = ()=>new Date().toISOString();
42604
42610
  const verboseTimestamp = ()=>{
42605
42611
  const now = new Date();
@@ -42706,14 +42712,32 @@ var __webpack_exports__ = {};
42706
42712
  resume() {
42707
42713
  this.paused = false;
42708
42714
  }
42709
- async prompt(questions) {
42715
+ async prompt(questions, options) {
42716
+ assertCanPrompt(options?.action ?? "Prompt", options?.nonInteractiveHint);
42710
42717
  throw new Error("Prompting not supported in DefaultLogger. Use CLILogger for interactive prompts.");
42711
42718
  }
42712
- async confirm(message, defaultValue = true) {
42713
- throw new Error("Prompting not supported in DefaultLogger. Use CLILogger for interactive prompts.");
42719
+ async confirm(message, defaultValue = true, options) {
42720
+ const answer = await this.prompt([
42721
+ {
42722
+ type: "confirm",
42723
+ name: "value",
42724
+ message,
42725
+ default: defaultValue
42726
+ }
42727
+ ], options);
42728
+ return answer.value;
42714
42729
  }
42715
- async select(message, choices, defaultValue) {
42716
- throw new Error("Prompting not supported in DefaultLogger. Use CLILogger for interactive prompts.");
42730
+ async select(message, choices, defaultValue, options) {
42731
+ const answer = await this.prompt([
42732
+ {
42733
+ type: "list",
42734
+ name: "value",
42735
+ message,
42736
+ choices,
42737
+ default: defaultValue
42738
+ }
42739
+ ], options);
42740
+ return answer.value;
42717
42741
  }
42718
42742
  }
42719
42743
  let logger_logger = new DefaultLogger();
@@ -42854,7 +42878,8 @@ var __webpack_exports__ = {};
42854
42878
  setVerbose(value) {
42855
42879
  this.sessionVerbose = value;
42856
42880
  }
42857
- async prompt(questions) {
42881
+ async prompt(questions, options) {
42882
+ assertCanPrompt(options?.action ?? "Prompt", options?.nonInteractiveHint);
42858
42883
  this.pause();
42859
42884
  try {
42860
42885
  return await inquirer_dist_esm.prompt(questions);
@@ -42862,7 +42887,7 @@ var __webpack_exports__ = {};
42862
42887
  this.resume();
42863
42888
  }
42864
42889
  }
42865
- async confirm(message, defaultValue = true) {
42890
+ async confirm(message, defaultValue = true, options) {
42866
42891
  const answer = await this.prompt([
42867
42892
  {
42868
42893
  type: "confirm",
@@ -42870,10 +42895,10 @@ var __webpack_exports__ = {};
42870
42895
  message,
42871
42896
  default: defaultValue
42872
42897
  }
42873
- ]);
42898
+ ], options);
42874
42899
  return answer.value;
42875
42900
  }
42876
- async select(message, choices, defaultValue) {
42901
+ async select(message, choices, defaultValue, options) {
42877
42902
  const answer = await this.prompt([
42878
42903
  {
42879
42904
  type: "list",
@@ -42883,7 +42908,7 @@ var __webpack_exports__ = {};
42883
42908
  default: defaultValue,
42884
42909
  loop: false
42885
42910
  }
42886
- ]);
42911
+ ], options);
42887
42912
  return answer.value;
42888
42913
  }
42889
42914
  }
@@ -47243,6 +47268,7 @@ var __webpack_exports__ = {};
47243
47268
  function getDefaultAnvilUrl() {
47244
47269
  return resolveAnvilUrl();
47245
47270
  }
47271
+ const ANVIL_SYNC_CLIENT_ID = "anvil-sync";
47246
47272
  async function verifyAuth(authToken, anvilUrl = getDefaultAnvilUrl()) {
47247
47273
  try {
47248
47274
  const resp = await fetch(`${anvilUrl}/ide/api/_/user`, {
@@ -47262,7 +47288,7 @@ var __webpack_exports__ = {};
47262
47288
  throw errors_createAuthError.invalid(`Network error: ${e.message}`);
47263
47289
  }
47264
47290
  }
47265
- async function refreshAccessToken(refreshToken, anvilUrl = getDefaultAnvilUrl()) {
47291
+ async function refreshAccessToken(refreshToken, anvilUrl = getDefaultAnvilUrl(), clientId = ANVIL_SYNC_CLIENT_ID) {
47266
47292
  try {
47267
47293
  const tokenResponse = await fetch(`${anvilUrl}/oauth/token`, {
47268
47294
  method: "POST",
@@ -47272,7 +47298,7 @@ var __webpack_exports__ = {};
47272
47298
  body: new URLSearchParams({
47273
47299
  grant_type: "refresh_token",
47274
47300
  refresh_token: refreshToken,
47275
- client_id: "anvil-sync"
47301
+ client_id: clientId
47276
47302
  })
47277
47303
  });
47278
47304
  if (!tokenResponse.ok) {
@@ -47295,7 +47321,8 @@ var __webpack_exports__ = {};
47295
47321
  if (!tokens.authToken && !tokens.refreshToken) throw errors_createAuthError.required("Not logged in. Please log in first.");
47296
47322
  const isExpired = null !== tokens.authTokenExpiresAt && tokens.authTokenExpiresAt <= Math.floor(Date.now() / 1000) + 60;
47297
47323
  if (tokens.refreshToken && isExpired) try {
47298
- const tokenData = await refreshAccessToken(tokens.refreshToken, normalized);
47324
+ const clientId = tokens.clientId ?? ANVIL_SYNC_CLIENT_ID;
47325
+ const tokenData = await refreshAccessToken(tokens.refreshToken, normalized, clientId);
47299
47326
  const newExpiresAt = Math.floor(Date.now() / 1000) + tokenData.expires_in;
47300
47327
  let accountUsername = username;
47301
47328
  if (!accountUsername) {
@@ -47312,7 +47339,8 @@ var __webpack_exports__ = {};
47312
47339
  setAccountTokens(normalized, accountUsername, {
47313
47340
  authToken: tokenData.access_token,
47314
47341
  refreshToken: tokenData.refresh_token,
47315
- authTokenExpiresAt: newExpiresAt
47342
+ authTokenExpiresAt: newExpiresAt,
47343
+ clientId
47316
47344
  });
47317
47345
  return tokenData.access_token;
47318
47346
  } catch (e) {
@@ -47337,7 +47365,8 @@ var __webpack_exports__ = {};
47337
47365
  setAccountTokens(normalized, username, {
47338
47366
  authToken: tokenData.access_token,
47339
47367
  refreshToken: tokenData.refresh_token,
47340
- authTokenExpiresAt: expiresAt
47368
+ authTokenExpiresAt: expiresAt,
47369
+ clientId: ANVIL_SYNC_CLIENT_ID
47341
47370
  });
47342
47371
  return {
47343
47372
  success: true,
@@ -48731,7 +48760,9 @@ var __webpack_exports__ = {};
48731
48760
  };
48732
48761
  return null;
48733
48762
  }
48734
- async function loadFormTemplateData(repoPath, relativePath, stagedOnly, kind, format) {
48763
+ async function loadFormTemplateData(relativePath, templateInfo, context) {
48764
+ const { repoPath, stagedOnly = false } = context;
48765
+ const { kind, format } = templateInfo;
48735
48766
  try {
48736
48767
  const content = await readFileContent(repoPath, relativePath, stagedOnly);
48737
48768
  if ("yaml" === format) {
@@ -48819,7 +48850,8 @@ var __webpack_exports__ = {};
48819
48850
  if (formTemplateInfo) return "forms";
48820
48851
  return null;
48821
48852
  }
48822
- async function validateRename(fromPath, toPath, repoPath, editorYaml) {
48853
+ async function validateRename(fromPath, toPath, context) {
48854
+ const { repoPath, editorYaml } = context;
48823
48855
  const fromEntityType = await getEntityTypeFromPath(repoPath, fromPath, editorYaml);
48824
48856
  const toEntityType = await getEntityTypeFromPath(repoPath, toPath, editorYaml);
48825
48857
  if (null === fromEntityType || null === toEntityType) return {
@@ -48977,7 +49009,8 @@ var __webpack_exports__ = {};
48977
49009
  }
48978
49010
  return filtered;
48979
49011
  }
48980
- async function routeFileChange(repoPath, relativePath, changeType, editorYaml, stagedOnly = false, fromPath) {
49012
+ async function routeFileChange(change, context) {
49013
+ const { relativePath, changeType, fromPath } = change;
48981
49014
  logger_logger.verbose(chalk_source.gray(`Routing ${changeType}: ${relativePath}${fromPath ? ` (from: ${fromPath})` : ""}`));
48982
49015
  if (isGitRelatedFile(relativePath)) return {
48983
49016
  type: "ignore",
@@ -48990,9 +49023,12 @@ var __webpack_exports__ = {};
48990
49023
  type: "ignore",
48991
49024
  reason: "Rename operation missing from path"
48992
49025
  };
48993
- return await handleRename(repoPath, fromPath, relativePath, editorYaml, stagedOnly);
49026
+ return await handleRename({
49027
+ fromPath,
49028
+ toPath: relativePath
49029
+ }, context);
48994
49030
  }
48995
- return await routeSavableChange(repoPath, relativePath, changeType, editorYaml, stagedOnly);
49031
+ return await routeSavableChange(change, context);
48996
49032
  }
48997
49033
  function isGitRelatedFile(relativePath) {
48998
49034
  return ".git" === relativePath || relativePath.startsWith(".git/") || relativePath.endsWith("/.git");
@@ -49011,47 +49047,60 @@ var __webpack_exports__ = {};
49011
49047
  const parts = relativePath.split("/");
49012
49048
  return 2 === parts.length;
49013
49049
  },
49014
- handle: (repoPath, relativePath, changeType, editorYaml, stagedOnly)=>handleScriptsChange(repoPath, relativePath, editorYaml, changeType, stagedOnly)
49050
+ handle: handleScriptsChange
49015
49051
  },
49016
49052
  {
49017
49053
  matches: (relativePath)=>relativePath.startsWith("server_code/") && relativePath.endsWith(".py"),
49018
- handle: (repoPath, relativePath, changeType, editorYaml, stagedOnly)=>handleServerCodeChange(repoPath, relativePath, editorYaml, changeType, stagedOnly)
49054
+ handle: handleServerCodeChange
49019
49055
  },
49020
49056
  {
49021
49057
  matches: (relativePath)=>relativePath.startsWith("client_code/") && relativePath.endsWith(".py"),
49022
- handle: (repoPath, relativePath, changeType, editorYaml, stagedOnly)=>handleClientCodeChange(repoPath, relativePath, editorYaml, changeType, stagedOnly)
49058
+ handle: handleClientCodeChange
49023
49059
  },
49024
49060
  {
49025
49061
  matches: (relativePath)=>null !== detectFormTemplate(relativePath),
49026
- handle: (repoPath, relativePath, changeType, editorYaml, stagedOnly)=>handleFormTemplateChange(repoPath, relativePath, editorYaml, changeType, stagedOnly)
49062
+ handle: handleFormTemplateChange
49027
49063
  },
49028
49064
  {
49029
49065
  matches: (relativePath)=>"theme/parameters.yaml" === relativePath,
49030
- handle: (repoPath, relativePath, changeType, editorYaml, stagedOnly)=>handleThemeParametersChange(repoPath, relativePath, stagedOnly)
49066
+ handle: handleThemeParametersChange
49031
49067
  },
49032
49068
  {
49033
49069
  matches: (relativePath)=>relativePath.startsWith("theme/assets/"),
49034
- handle: (repoPath, relativePath, changeType, editorYaml, stagedOnly)=>handleAssetChange(repoPath, relativePath, changeType, editorYaml, stagedOnly)
49070
+ handle: handleAssetChange
49035
49071
  },
49036
49072
  {
49037
49073
  matches: ()=>true,
49038
- handle: (repoPath, relativePath, changeType, editorYaml, stagedOnly)=>handleExtraFileChange(repoPath, relativePath, stagedOnly)
49074
+ handle: handleExtraFileChange
49039
49075
  }
49040
49076
  ];
49041
- async function handleRename(repoPath, fromPath, toPath, editorYaml, stagedOnly) {
49042
- const validation = await validateRename(fromPath, toPath, repoPath, editorYaml);
49077
+ async function handleRename(rename, context) {
49078
+ const { fromPath, toPath } = rename;
49079
+ const validation = await validateRename(fromPath, toPath, context);
49043
49080
  if (!validation.valid) {
49044
49081
  logger_logger.verbose(chalk_source.yellow(`Rename invalid (${validation.reason}), falling back to unlink + add`));
49045
- const unlinkResult = await routeFileChange(repoPath, fromPath, "unlink", editorYaml, stagedOnly);
49046
- const addResult = await routeFileChange(repoPath, toPath, "add", editorYaml, stagedOnly);
49082
+ const unlinkResult = await routeFileChange({
49083
+ relativePath: fromPath,
49084
+ changeType: "unlink"
49085
+ }, context);
49086
+ const addResult = await routeFileChange({
49087
+ relativePath: toPath,
49088
+ changeType: "add"
49089
+ }, context);
49047
49090
  return [
49048
49091
  unlinkResult,
49049
49092
  addResult
49050
49093
  ].flat();
49051
49094
  }
49052
- return await handleRenameOperation(repoPath, fromPath, toPath, validation.entityType, editorYaml, stagedOnly);
49095
+ return await handleRenameOperation({
49096
+ fromPath,
49097
+ toPath,
49098
+ entityType: validation.entityType
49099
+ }, context);
49053
49100
  }
49054
- async function handleRenameOperation(repoPath, fromPath, toPath, entityType, editorYaml, stagedOnly) {
49101
+ async function handleRenameOperation(rename, context) {
49102
+ const { repoPath, editorYaml, stagedOnly = false } = context;
49103
+ const { fromPath, toPath, entityType } = rename;
49055
49104
  let oldName;
49056
49105
  let newName;
49057
49106
  let prefix = [];
@@ -49060,10 +49109,10 @@ var __webpack_exports__ = {};
49060
49109
  let isPackage = false;
49061
49110
  let actualToPath = toPath;
49062
49111
  if ("scripts" === entityType) {
49063
- const toRenameResult = await handlePathRenaming(repoPath, toPath, "scripts", stagedOnly);
49112
+ const toRenameResult = await handlePathRenaming(toPath, "scripts", context);
49064
49113
  if (toRenameResult.ignoreResult) return toRenameResult.ignoreResult;
49065
49114
  actualToPath = toRenameResult.newRelativePath;
49066
- const fromRenameResult = await handlePathRenaming(repoPath, fromPath, "scripts", stagedOnly);
49115
+ const fromRenameResult = await handlePathRenaming(fromPath, "scripts", context);
49067
49116
  oldName = fromRenameResult.pythonifiedName;
49068
49117
  newName = toRenameResult.pythonifiedName;
49069
49118
  prefix = [
@@ -49075,10 +49124,10 @@ var __webpack_exports__ = {};
49075
49124
  code
49076
49125
  });
49077
49126
  } else if ("server_modules" === entityType) {
49078
- const toRenameResult = await handlePathRenaming(repoPath, toPath, "server_code", stagedOnly);
49127
+ const toRenameResult = await handlePathRenaming(toPath, "server_code", context);
49079
49128
  if (toRenameResult.ignoreResult) return toRenameResult.ignoreResult;
49080
49129
  actualToPath = toRenameResult.newRelativePath;
49081
- const fromRenameResult = await handlePathRenaming(repoPath, fromPath, "server_code", stagedOnly);
49130
+ const fromRenameResult = await handlePathRenaming(fromPath, "server_code", context);
49082
49131
  oldName = fromRenameResult.pythonifiedName;
49083
49132
  newName = toRenameResult.pythonifiedName;
49084
49133
  prefix = [
@@ -49092,10 +49141,10 @@ var __webpack_exports__ = {};
49092
49141
  is_package: isPackage
49093
49142
  });
49094
49143
  } else if ("modules" === entityType) {
49095
- const toRenameResult = await handlePathRenaming(repoPath, toPath, "client_code", stagedOnly);
49144
+ const toRenameResult = await handlePathRenaming(toPath, "client_code", context);
49096
49145
  if (toRenameResult.ignoreResult) return toRenameResult.ignoreResult;
49097
49146
  actualToPath = toRenameResult.newRelativePath;
49098
- const fromRenameResult = await handlePathRenaming(repoPath, fromPath, "client_code", stagedOnly);
49147
+ const fromRenameResult = await handlePathRenaming(fromPath, "client_code", context);
49099
49148
  oldName = fromRenameResult.pythonifiedName;
49100
49149
  newName = toRenameResult.pythonifiedName;
49101
49150
  isPackage = actualToPath.endsWith("__init__.py");
@@ -49113,10 +49162,10 @@ var __webpack_exports__ = {};
49113
49162
  type: "ignore",
49114
49163
  reason: "Form template rename to .yaml file is only supported when entire form is renamed"
49115
49164
  };
49116
- const toRenameResult = await handlePathRenaming(repoPath, toPath, "client_code", stagedOnly);
49165
+ const toRenameResult = await handlePathRenaming(toPath, "client_code", context);
49117
49166
  if (toRenameResult.ignoreResult) return toRenameResult.ignoreResult;
49118
49167
  actualToPath = toRenameResult.newRelativePath;
49119
- const fromRenameResult = await handlePathRenaming(repoPath, fromPath, "client_code", stagedOnly);
49168
+ const fromRenameResult = await handlePathRenaming(fromPath, "client_code", context);
49120
49169
  oldName = fromRenameResult.pythonifiedName;
49121
49170
  const isPackageInit = actualToPath.endsWith("__init__.py");
49122
49171
  const kind = isPackageInit ? "package" : "module";
@@ -49125,7 +49174,10 @@ var __webpack_exports__ = {};
49125
49174
  type: "ignore",
49126
49175
  reason: "Form template not found for rename destination"
49127
49176
  };
49128
- const result = await buildFormTemplateData(repoPath, templateInfo.relativePath, stagedOnly, kind, templateInfo.format);
49177
+ const result = await buildFormTemplateData(templateInfo.relativePath, {
49178
+ kind,
49179
+ format: templateInfo.format
49180
+ }, context);
49129
49181
  if (result.ignoreResult) return result.ignoreResult;
49130
49182
  newName = result.className;
49131
49183
  prefix = [
@@ -49173,7 +49225,9 @@ var __webpack_exports__ = {};
49173
49225
  content: payload
49174
49226
  };
49175
49227
  }
49176
- async function routeSavableChange(repoPath, relativePath, changeType, editorYaml, stagedOnly = false) {
49228
+ async function routeSavableChange(change, context) {
49229
+ const { repoPath, editorYaml, stagedOnly = false } = context;
49230
+ const { relativePath, changeType } = change;
49177
49231
  if ("anvil.yaml" === relativePath) return {
49178
49232
  type: "ignore",
49179
49233
  reason: "anvil.yaml handled specially (multiple save paths)"
@@ -49217,13 +49271,15 @@ var __webpack_exports__ = {};
49217
49271
  };
49218
49272
  }
49219
49273
  if ("unlink" === changeType) return await handleFileDeletion(repoPath, relativePath, editorYaml);
49220
- for (const route of ROUTES)if (route.matches(relativePath)) return await route.handle(repoPath, relativePath, changeType, editorYaml, stagedOnly);
49274
+ for (const route of ROUTES)if (route.matches(relativePath)) return await route.handle(change, context);
49221
49275
  return {
49222
49276
  type: "ignore",
49223
49277
  reason: "Not a recognized Anvil file type"
49224
49278
  };
49225
49279
  }
49226
- async function handleThemeParametersChange(repoPath, relativePath, stagedOnly = false) {
49280
+ async function handleThemeParametersChange(change, context) {
49281
+ const { repoPath, stagedOnly = false } = context;
49282
+ const { relativePath } = change;
49227
49283
  const content = await readFileContent(repoPath, relativePath, stagedOnly);
49228
49284
  const parametersData = jsYaml.load(content);
49229
49285
  return {
@@ -49235,7 +49291,9 @@ var __webpack_exports__ = {};
49235
49291
  content: parametersData
49236
49292
  };
49237
49293
  }
49238
- async function handleExtraFileChange(repoPath, relativePath, stagedOnly = false) {
49294
+ async function handleExtraFileChange(change, context) {
49295
+ const { repoPath, stagedOnly = false } = context;
49296
+ const { relativePath } = change;
49239
49297
  const content = await readBinaryFileContent(repoPath, relativePath, stagedOnly);
49240
49298
  const base64Content = content.toString("base64");
49241
49299
  const pathParts = [
@@ -49248,7 +49306,9 @@ var __webpack_exports__ = {};
49248
49306
  content: base64Content
49249
49307
  };
49250
49308
  }
49251
- async function handleDirectoryRenaming(repoPath, originalParts, pythonifiedParts, baseDir) {
49309
+ async function handleDirectoryRenaming(rename, context) {
49310
+ const { repoPath } = context;
49311
+ const { originalParts, pythonifiedParts, baseDir } = rename;
49252
49312
  const needsRename = originalParts.some((part, idx)=>part !== pythonifiedParts[idx]);
49253
49313
  if (!needsRename) return null;
49254
49314
  const needsDirRename = originalParts.slice(0, -1).some((part, idx)=>part !== pythonifiedParts[idx]);
@@ -49266,7 +49326,9 @@ var __webpack_exports__ = {};
49266
49326
  }
49267
49327
  return null;
49268
49328
  }
49269
- async function renameIfNeeded(repoPath, relativePath, originalNameOrParts, baseDir) {
49329
+ async function renameIfNeeded(rename, context) {
49330
+ const { repoPath } = context;
49331
+ const { relativePath, originalNameOrParts, baseDir } = rename;
49270
49332
  let pythonifiedName;
49271
49333
  let pythonifiedParts;
49272
49334
  let needsRename;
@@ -49304,8 +49366,10 @@ var __webpack_exports__ = {};
49304
49366
  ignoreResult
49305
49367
  };
49306
49368
  }
49307
- async function handleScriptsChange(repoPath, relativePath, editorYaml, changeType, stagedOnly = false) {
49308
- const renameResult = await handlePathRenaming(repoPath, relativePath, "scripts", stagedOnly);
49369
+ async function handleScriptsChange(change, context) {
49370
+ const { repoPath, editorYaml, stagedOnly = false } = context;
49371
+ const { relativePath, changeType } = change;
49372
+ const renameResult = await handlePathRenaming(relativePath, "scripts", context);
49309
49373
  if (renameResult.ignoreResult) return renameResult.ignoreResult;
49310
49374
  const actualPath = renameResult.newRelativePath;
49311
49375
  const content = await readFileContent(repoPath, actualPath, stagedOnly);
@@ -49327,8 +49391,10 @@ var __webpack_exports__ = {};
49327
49391
  ]
49328
49392
  });
49329
49393
  }
49330
- async function handleServerCodeChange(repoPath, relativePath, editorYaml, changeType, stagedOnly = false) {
49331
- const renameResult = await handlePathRenaming(repoPath, relativePath, "server_code", stagedOnly);
49394
+ async function handleServerCodeChange(change, context) {
49395
+ const { repoPath, editorYaml, stagedOnly = false } = context;
49396
+ const { relativePath, changeType } = change;
49397
+ const renameResult = await handlePathRenaming(relativePath, "server_code", context);
49332
49398
  if (renameResult.ignoreResult) return renameResult.ignoreResult;
49333
49399
  const actualPath = renameResult.newRelativePath;
49334
49400
  const isPackage = actualPath.endsWith("__init__.py");
@@ -49353,8 +49419,10 @@ var __webpack_exports__ = {};
49353
49419
  ]
49354
49420
  });
49355
49421
  }
49356
- async function handleClientCodeFormChange(repoPath, relativePath, className, templatePath, format, kind, changeType, editorYaml, stagedOnly) {
49357
- const result = await buildFormTemplateData(repoPath, templatePath, stagedOnly, kind, format);
49422
+ async function handleClientCodeFormChange(options, context) {
49423
+ const { editorYaml } = context;
49424
+ const { templatePath, templateInfo, changeType } = options;
49425
+ const result = await buildFormTemplateData(templatePath, templateInfo, context);
49358
49426
  if (result.ignoreResult) return result.ignoreResult;
49359
49427
  const { formData, className: extractedClassName } = result;
49360
49428
  return handleIdBasedOperation({
@@ -49372,30 +49440,47 @@ var __webpack_exports__ = {};
49372
49440
  ]
49373
49441
  });
49374
49442
  }
49375
- async function handlePathRenaming(repoPath, relativePath, baseDir, stagedOnly) {
49443
+ async function handlePathRenaming(relativePath, baseDir, context) {
49376
49444
  const parts = relativePath.slice(0, -3).split("/");
49377
49445
  const originalParts = parts.slice(1);
49378
49446
  const pythonifiedParts = originalParts.map(pythonifyName);
49379
49447
  const pythonifiedName = extractPythonName(relativePath);
49380
- const dirRenameResult = await handleDirectoryRenaming(repoPath, originalParts, pythonifiedParts, baseDir);
49448
+ const dirRenameResult = await handleDirectoryRenaming({
49449
+ originalParts,
49450
+ pythonifiedParts,
49451
+ baseDir
49452
+ }, context);
49381
49453
  if (dirRenameResult) return {
49382
49454
  pythonifiedName,
49383
49455
  pythonifiedParts,
49384
49456
  newRelativePath: relativePath,
49385
49457
  ignoreResult: dirRenameResult
49386
49458
  };
49387
- const renameResult = await renameIfNeeded(repoPath, relativePath, originalParts, baseDir);
49459
+ const renameResult = await renameIfNeeded({
49460
+ relativePath,
49461
+ originalNameOrParts: originalParts,
49462
+ baseDir
49463
+ }, context);
49388
49464
  return renameResult;
49389
49465
  }
49390
- async function handleClientCodeChange(repoPath, relativePath, editorYaml, changeType, stagedOnly = false) {
49391
- const renameResult = await handlePathRenaming(repoPath, relativePath, "client_code", stagedOnly);
49466
+ async function handleClientCodeChange(change, context) {
49467
+ const { repoPath, editorYaml, stagedOnly = false } = context;
49468
+ const { relativePath, changeType } = change;
49469
+ const renameResult = await handlePathRenaming(relativePath, "client_code", context);
49392
49470
  if (renameResult.ignoreResult) return renameResult.ignoreResult;
49393
49471
  const actualPath = renameResult.newRelativePath;
49394
49472
  const isPackageInit = actualPath.endsWith("__init__.py");
49395
49473
  const kind = isPackageInit ? "package" : "module";
49396
49474
  const className = renameResult.pythonifiedName;
49397
49475
  const templateInfo = findFormTemplateForCodePath(repoPath, actualPath, kind);
49398
- if (templateInfo) return await handleClientCodeFormChange(repoPath, actualPath, className, templateInfo.relativePath, templateInfo.format, kind, changeType, editorYaml, stagedOnly);
49476
+ if (templateInfo) return await handleClientCodeFormChange({
49477
+ templatePath: templateInfo.relativePath,
49478
+ templateInfo: {
49479
+ format: templateInfo.format,
49480
+ kind
49481
+ },
49482
+ changeType
49483
+ }, context);
49399
49484
  const content = await readFileContent(repoPath, actualPath, stagedOnly);
49400
49485
  return handleIdBasedOperation({
49401
49486
  entityType: "modules",
@@ -49430,8 +49515,10 @@ var __webpack_exports__ = {};
49430
49515
  }
49431
49516
  return formData;
49432
49517
  }
49433
- async function buildFormTemplateData(repoPath, relativePath, stagedOnly, kind, format) {
49434
- const loadResult = await loadFormTemplateData(repoPath, relativePath, stagedOnly, kind, format);
49518
+ async function buildFormTemplateData(relativePath, templateInfo, context) {
49519
+ const { repoPath, stagedOnly = false } = context;
49520
+ const { kind } = templateInfo;
49521
+ const loadResult = await loadFormTemplateData(relativePath, templateInfo, context);
49435
49522
  if (false === loadResult.ok) return {
49436
49523
  formData: {},
49437
49524
  className: "",
@@ -49465,7 +49552,9 @@ var __webpack_exports__ = {};
49465
49552
  ignoreResult: null
49466
49553
  };
49467
49554
  }
49468
- async function handleFormTemplateChange(repoPath, relativePath, editorYaml, changeType, stagedOnly) {
49555
+ async function handleFormTemplateChange(change, context) {
49556
+ const { editorYaml } = context;
49557
+ const { relativePath, changeType } = change;
49469
49558
  const templateInfo = detectFormTemplate(relativePath);
49470
49559
  if (!templateInfo) return {
49471
49560
  type: "ignore",
@@ -49473,7 +49562,10 @@ var __webpack_exports__ = {};
49473
49562
  };
49474
49563
  const kind = templateInfo.kind;
49475
49564
  const format = templateInfo.format;
49476
- const result = await buildFormTemplateData(repoPath, relativePath, stagedOnly, kind, format);
49565
+ const result = await buildFormTemplateData(relativePath, {
49566
+ kind,
49567
+ format
49568
+ }, context);
49477
49569
  if (result.ignoreResult) return result.ignoreResult;
49478
49570
  const { formData, className } = result;
49479
49571
  return handleIdBasedOperation({
@@ -49489,7 +49581,9 @@ var __webpack_exports__ = {};
49489
49581
  subPathForCodeUpdate: null
49490
49582
  });
49491
49583
  }
49492
- async function handleAssetChange(repoPath, relativePath, changeType, editorYaml, stagedOnly = false) {
49584
+ async function handleAssetChange(change, context) {
49585
+ const { repoPath, editorYaml, stagedOnly = false } = context;
49586
+ const { relativePath, changeType } = change;
49493
49587
  const assetPath = relativePath.replace(/^theme[\/\\]assets[\/\\]/, "");
49494
49588
  const fileBuffer = await readBinaryFileContent(repoPath, relativePath, stagedOnly);
49495
49589
  const base64Content = fileBuffer.toString("base64");
@@ -49916,7 +50010,15 @@ var __webpack_exports__ = {};
49916
50010
  originalFilePaths.push(change.path);
49917
50011
  continue;
49918
50012
  }
49919
- const routeResult = await routeFileChange(this.config.repoPath, change.path, change.type, this.config.editorYaml, this.config.stagedOnly, change.from);
50013
+ const routeResult = await routeFileChange({
50014
+ relativePath: change.path,
50015
+ changeType: change.type,
50016
+ fromPath: change.from
50017
+ }, {
50018
+ repoPath: this.config.repoPath,
50019
+ editorYaml: this.config.editorYaml,
50020
+ stagedOnly: this.config.stagedOnly
50021
+ });
49920
50022
  const results = Array.isArray(routeResult) ? routeResult : [
49921
50023
  routeResult
49922
50024
  ];
@@ -50103,7 +50205,15 @@ var __webpack_exports__ = {};
50103
50205
  const newSaveArray = [];
50104
50206
  const newFilePaths = [];
50105
50207
  for (const change of changes){
50106
- const routeResult = await routeFileChange(this.config.repoPath, change.path, change.type, this.config.editorYaml, this.config.stagedOnly, change.from);
50208
+ const routeResult = await routeFileChange({
50209
+ relativePath: change.path,
50210
+ changeType: change.type,
50211
+ fromPath: change.from
50212
+ }, {
50213
+ repoPath: this.config.repoPath,
50214
+ editorYaml: this.config.editorYaml,
50215
+ stagedOnly: this.config.stagedOnly
50216
+ });
50107
50217
  const results = Array.isArray(routeResult) ? routeResult : [
50108
50218
  routeResult
50109
50219
  ];
@@ -50637,22 +50747,28 @@ var __webpack_exports__ = {};
50637
50747
  appName
50638
50748
  };
50639
50749
  }
50640
- async function validateBranchSyncStatus(git, branchName, appId, anvilUrl, username) {
50750
+ async function validateBranchSyncStatus(git, branchName, options) {
50751
+ const { appId, anvilUrl, username } = options;
50641
50752
  logger_logger.verbose(chalk_source.blue("Checking branch sync status..."));
50642
50753
  let authToken;
50643
50754
  try {
50644
50755
  authToken = await auth_getValidAuthToken(anvilUrl, username);
50645
50756
  } catch (e) {
50646
- return validateViaAnvilServer(git, branchName, appId, anvilUrl, username);
50757
+ return validateViaAnvilServer(git, branchName, options);
50647
50758
  }
50648
50759
  try {
50649
- return await validateViaGitFetch(git, branchName, appId, anvilUrl, authToken);
50760
+ return await validateViaGitFetch(git, branchName, {
50761
+ appId,
50762
+ anvilUrl,
50763
+ authToken
50764
+ });
50650
50765
  } catch (e) {
50651
50766
  logger_logger.verbose(chalk_source.yellow("Git fetch failed, trying server validation..."));
50652
- return validateViaAnvilServer(git, branchName, appId, anvilUrl, username);
50767
+ return validateViaAnvilServer(git, branchName, options);
50653
50768
  }
50654
50769
  }
50655
- async function validateViaGitFetch(git, branchName, appId, anvilUrl, authToken) {
50770
+ async function validateViaGitFetch(git, branchName, options) {
50771
+ const { appId, anvilUrl, authToken } = options;
50656
50772
  const baseUrl = new URL(anvilUrl);
50657
50773
  const encodedToken = encodeURIComponent(authToken);
50658
50774
  const gitPath = `/git/${appId}.git`;
@@ -50713,7 +50829,8 @@ var __webpack_exports__ = {};
50713
50829
  throw errors_createGitError.fetchFailed(e.message);
50714
50830
  }
50715
50831
  }
50716
- async function validateViaAnvilServer(git, branchName, appId, anvilUrl, username) {
50832
+ async function validateViaAnvilServer(git, branchName, options) {
50833
+ const { appId, anvilUrl, username } = options;
50717
50834
  logger_logger.verbose(chalk_source.blue("Validating sync status with Anvil server..."));
50718
50835
  const authToken = await auth_getValidAuthToken(anvilUrl, username);
50719
50836
  let commitId;
@@ -51378,7 +51495,7 @@ var __webpack_exports__ = {};
51378
51495
  </body>
51379
51496
  </html>`;
51380
51497
  }
51381
- const CLIENT_ID = "anvil-sync";
51498
+ const CLIENT_ID = ANVIL_SYNC_CLIENT_ID;
51382
51499
  const SCOPES = "apps:read apps:write user:read";
51383
51500
  function oauth_login_base64url(buf) {
51384
51501
  return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
@@ -51441,7 +51558,8 @@ var __webpack_exports__ = {};
51441
51558
  return text;
51442
51559
  }
51443
51560
  }
51444
- async function exchangeAuthorizationCodeForTokens(anvilUrl, redirectUri, code, codeVerifier) {
51561
+ async function exchangeAuthorizationCodeForTokens(anvilUrl, options) {
51562
+ const { redirectUri, code, codeVerifier } = options;
51445
51563
  const tokenResponse = await fetch(`${anvilUrl}/oauth/token`, {
51446
51564
  method: "POST",
51447
51565
  headers: {
@@ -51569,7 +51687,11 @@ var __webpack_exports__ = {};
51569
51687
  const { code, error, recvState } = await codePromise;
51570
51688
  if (recvState !== state) throw new Error("Invalid state received from OAuth callback");
51571
51689
  if (error) throw new Error(`Error received from OAuth callback: ${error}`);
51572
- const tokenData = await exchangeAuthorizationCodeForTokens(anvilUrl, redirectUri, code, codeVerifier);
51690
+ const tokenData = await exchangeAuthorizationCodeForTokens(anvilUrl, {
51691
+ redirectUri,
51692
+ code: code,
51693
+ codeVerifier
51694
+ });
51573
51695
  return login(anvilUrl, {
51574
51696
  access_token: tokenData.access_token,
51575
51697
  refresh_token: tokenData.refresh_token,
@@ -52309,7 +52431,6 @@ var __webpack_exports__ = {};
52309
52431
  getValidAuthToken: auth_getValidAuthToken,
52310
52432
  validateAppId: validateAppId,
52311
52433
  listAppsForCheckout: listAppsForCheckout,
52312
- runInteractiveLoginFlow: runInteractiveLoginFlow,
52313
52434
  clone: async (repoUrl, destinationPath, options)=>{
52314
52435
  const cloneArgs = [];
52315
52436
  if (options?.branch) cloneArgs.push("--branch", options.branch);
@@ -52322,9 +52443,6 @@ var __webpack_exports__ = {};
52322
52443
  },
52323
52444
  hardenCheckoutGitAuth: hardenCheckoutGitAuth
52324
52445
  };
52325
- function isInteractiveSession() {
52326
- return !!(process.stdin.isTTY && process.stdout.isTTY);
52327
- }
52328
52446
  function parseCheckoutInput(input) {
52329
52447
  const trimmed = input.trim();
52330
52448
  if (!trimmed) throw new Error("Input is required.");
@@ -52388,7 +52506,17 @@ var __webpack_exports__ = {};
52388
52506
  name: "Cancel",
52389
52507
  value: null
52390
52508
  });
52391
- return logger_logger.select("Multiple logged-in Anvil URLs found. Which one would you like to use?", choices, decision.urls[0]);
52509
+ return logger_logger.select("Multiple logged-in Anvil URLs found. Which one would you like to use?", choices, decision.urls[0], {
52510
+ action: "`anvil checkout` URL selection",
52511
+ nonInteractiveHint: `Use --url <ANVIL_URL>. Available URLs: ${decision.urls.join(", ")}.`
52512
+ });
52513
+ }
52514
+ function resolveCheckoutUrlForApi(explicitUrl, parsedUrl) {
52515
+ if (explicitUrl) return normalizeAnvilUrl(explicitUrl);
52516
+ if (parsedUrl) return normalizeAnvilUrl(parsedUrl);
52517
+ const decision = decideFallbackUrl(void 0);
52518
+ if ("available-multiple" === decision.source) throw new Error(`Multiple logged-in Anvil URLs found: ${decision.urls.join(", ")}. Use --url <ANVIL_URL> to choose one.`);
52519
+ return decision.url;
52392
52520
  }
52393
52521
  async function isPathInsideGitRepo(targetPath) {
52394
52522
  const resolved = external_path_default().resolve(targetPath);
@@ -52428,11 +52556,17 @@ var __webpack_exports__ = {};
52428
52556
  logger_logger.verbose(chalk_source.cyan("Checking auth token for: ") + chalk_source.bold(username ? `${username} @ ${anvilUrl}` : anvilUrl));
52429
52557
  return await deps.getValidAuthToken(anvilUrl, username);
52430
52558
  } catch (e) {
52431
- const interactive = process.stdin.isTTY && process.stdout.isTTY;
52432
- if (!interactive) throw errors_createAuthError.required(`Not logged in to ${anvilUrl}. Run 'anvil login ${anvilUrl}' and retry.`);
52559
+ throw errors_createAuthError.required(`Not logged in to ${anvilUrl}. Run 'anvil login ${anvilUrl}' and retry.`);
52560
+ }
52561
+ }
52562
+ async function ensureCheckoutAuthTokenInteractive(anvilUrl, username, deps = defaultCheckoutDeps) {
52563
+ try {
52564
+ return await ensureCheckoutAuthToken(anvilUrl, username, deps);
52565
+ } catch (e) {
52566
+ assertCanPrompt("`anvil checkout` login prompt", `Run 'anvil login ${anvilUrl}' before checkout.`);
52433
52567
  const shouldLogin = await logger_logger.confirm(`Not logged in to ${anvilUrl}. Log in now?`, true);
52434
- if (!shouldLogin) throw errors_createAuthError.required(`Not logged in to ${anvilUrl}. Run 'anvil login ${anvilUrl}' and retry.`);
52435
- await deps.runInteractiveLoginFlow(anvilUrl);
52568
+ if (!shouldLogin) throw e;
52569
+ await runInteractiveLoginFlow(anvilUrl);
52436
52570
  return deps.getValidAuthToken(anvilUrl, username);
52437
52571
  }
52438
52572
  }
@@ -52444,8 +52578,6 @@ var __webpack_exports__ = {};
52444
52578
  logger_logger.verbose(chalk_source.cyan("Auto-selected account: ") + chalk_source.bold(accounts[0]));
52445
52579
  return accounts[0];
52446
52580
  }
52447
- const interactive = process.stdin.isTTY && process.stdout.isTTY;
52448
- if (!interactive) throw new Error(`Multiple accounts found for ${anvilUrl}. Use --user <USERNAME> to choose one.`);
52449
52581
  const choices = accounts.map((acct)=>({
52450
52582
  name: acct,
52451
52583
  value: acct
@@ -52454,38 +52586,33 @@ var __webpack_exports__ = {};
52454
52586
  name: "Cancel",
52455
52587
  value: null
52456
52588
  });
52457
- return logger_logger.select(`Multiple accounts found for ${anvilUrl}. Which account should be used for checkout?`, choices, accounts[0]);
52589
+ return logger_logger.select(`Multiple accounts found for ${anvilUrl}. Which account should be used for checkout?`, choices, accounts[0], {
52590
+ action: "`anvil checkout` account selection",
52591
+ nonInteractiveHint: `Use --user <USERNAME>. Available accounts: ${accounts.join(", ")}.`
52592
+ });
52593
+ }
52594
+ function resolveCheckoutUsernameForApi(anvilUrl, explicitUsername) {
52595
+ if (explicitUsername) return explicitUsername;
52596
+ const accounts = auth_getAccountsForUrl(anvilUrl);
52597
+ if (0 === accounts.length) return;
52598
+ if (1 === accounts.length) return accounts[0];
52599
+ throw new Error(`Multiple accounts found for ${anvilUrl}: ${accounts.join(", ")}. Use --user <USERNAME> to choose one.`);
52458
52600
  }
52459
52601
  async function executeCheckout(options, deps = defaultCheckoutDeps) {
52460
52602
  let checkoutInput = options.input?.trim();
52461
- let preselectedAnvilUrl;
52462
- let preselectedUsername;
52463
- if (!checkoutInput) {
52464
- if (!isInteractiveSession()) throw new Error("`anvil checkout` without arguments requires an interactive TTY. Pass APP_ID or URL explicitly in non-interactive mode.");
52465
- const selectedUrl = await resolveCheckoutUrl(options.url);
52466
- if (!selectedUrl) return void logger_logger.info("Checkout cancelled.");
52467
- preselectedAnvilUrl = selectedUrl;
52468
- const selectedUsername = await resolveCheckoutUsername(selectedUrl, options.user);
52469
- if (null === selectedUsername) return void logger_logger.info("Checkout cancelled.");
52470
- preselectedUsername = selectedUsername || void 0;
52471
- const selectedInput = await selectAppForCheckout(selectedUrl, preselectedUsername, deps, options.query);
52472
- if (!selectedInput) return void logger_logger.info("Checkout cancelled.");
52473
- checkoutInput = selectedInput;
52474
- }
52603
+ if (!checkoutInput) throw new Error("Checkout input is required. Pass an app ID, editor URL, or /git URL explicitly.");
52475
52604
  logger_logger.verbose(chalk_source.cyan("Checkout input: ") + chalk_source.bold(checkoutInput));
52476
52605
  const parsed = parseCheckoutInput(checkoutInput);
52477
52606
  logger_logger.verbose(chalk_source.cyan("Resolved app ID: ") + chalk_source.bold(parsed.appId));
52478
- const anvilUrl = preselectedAnvilUrl || await resolveCheckoutUrl(options.url, parsed.detectedUrl);
52479
- if (!anvilUrl) return void logger_logger.info("Checkout cancelled.");
52480
- const resolvedUsername = preselectedUsername || await resolveCheckoutUsername(anvilUrl, options.user);
52481
- if (null === resolvedUsername) return void logger_logger.info("Checkout cancelled.");
52607
+ const anvilUrl = resolveCheckoutUrlForApi(options.url, parsed.detectedUrl);
52608
+ if (!anvilUrl) throw new Error("Anvil URL is required. Use --url <ANVIL_URL> or pass an input URL containing the host.");
52609
+ const resolvedUsername = resolveCheckoutUsernameForApi(anvilUrl, options.user);
52482
52610
  if (resolvedUsername) logger_logger.verbose(chalk_source.cyan("Using account: ") + chalk_source.bold(resolvedUsername));
52483
52611
  else logger_logger.verbose(chalk_source.cyan("No account preselected; resolving after auth token lookup."));
52484
52612
  const authToken = await ensureCheckoutAuthToken(anvilUrl, resolvedUsername, deps);
52485
52613
  let checkoutUsername = resolvedUsername;
52486
52614
  if (!checkoutUsername) {
52487
- const inferredUsername = await resolveCheckoutUsername(anvilUrl, void 0);
52488
- if (null === inferredUsername) return void logger_logger.info("Checkout cancelled.");
52615
+ const inferredUsername = resolveCheckoutUsernameForApi(anvilUrl, void 0);
52489
52616
  if (!inferredUsername) throw new Error(`Could not determine account for ${anvilUrl}. Use --user <USERNAME> so checkout can bind repository credentials.`);
52490
52617
  checkoutUsername = inferredUsername;
52491
52618
  }
@@ -52505,8 +52632,6 @@ var __webpack_exports__ = {};
52505
52632
  if (options.branch) logger_logger.verbose(chalk_source.cyan("Requested branch: ") + chalk_source.bold(options.branch));
52506
52633
  if ("number" == typeof options.depth) logger_logger.verbose(chalk_source.cyan("Clone depth: ") + chalk_source.bold(String(options.depth)));
52507
52634
  if (options.singleBranch) logger_logger.verbose(chalk_source.cyan("Single-branch clone enabled"));
52508
- logger_logger.progress("checkout", `Checking out app ${parsed.appId} from ${anvilUrl}`);
52509
- logger_logger.info(chalk_source.gray(` Destination directory: ${destinationDisplay}`));
52510
52635
  await deps.clone(cloneUrl, destinationPath, {
52511
52636
  branch: options.branch,
52512
52637
  depth: options.depth,
@@ -52528,14 +52653,57 @@ var __webpack_exports__ = {};
52528
52653
  } catch (e) {
52529
52654
  throw new Error(`Checkout clone succeeded, but failed to configure repository credentials: ${errors_getErrorMessage(e)}. The repository exists at ${destinationDisplay}, but Git auth bridge setup is incomplete.`);
52530
52655
  }
52531
- logger_logger.progressEnd("checkout", `Checked out ${parsed.appId} into ${destinationDisplay}`);
52656
+ return {
52657
+ appId: parsed.appId,
52658
+ appName: validation.app_name,
52659
+ anvilUrl,
52660
+ destinationPath,
52661
+ destinationDisplay,
52662
+ username: checkoutUsername,
52663
+ remoteName
52664
+ };
52665
+ }
52666
+ async function executeCheckoutInteractive(options, deps = defaultCheckoutDeps) {
52667
+ let checkoutInput = options.input?.trim();
52668
+ let preselectedAnvilUrl;
52669
+ let preselectedUsername;
52670
+ if (!checkoutInput) {
52671
+ assertCanPrompt("`anvil checkout` without arguments", "Pass an app ID, editor URL, or /git URL. Use --url <ANVIL_URL> and --user <USERNAME> if needed.");
52672
+ const selectedUrl = await resolveCheckoutUrl(options.url);
52673
+ if (!selectedUrl) return void logger_logger.info("Checkout cancelled.");
52674
+ preselectedAnvilUrl = selectedUrl;
52675
+ const selectedUsername = await resolveCheckoutUsername(selectedUrl, options.user);
52676
+ if (null === selectedUsername) return void logger_logger.info("Checkout cancelled.");
52677
+ preselectedUsername = selectedUsername || void 0;
52678
+ const selectedInput = await selectAppForCheckout(selectedUrl, preselectedUsername, deps, options.query);
52679
+ if (!selectedInput) return void logger_logger.info("Checkout cancelled.");
52680
+ checkoutInput = selectedInput;
52681
+ }
52682
+ logger_logger.verbose(chalk_source.cyan("Checkout input: ") + chalk_source.bold(checkoutInput));
52683
+ const parsed = parseCheckoutInput(checkoutInput);
52684
+ logger_logger.verbose(chalk_source.cyan("Resolved app ID: ") + chalk_source.bold(parsed.appId));
52685
+ const anvilUrl = preselectedAnvilUrl || await resolveCheckoutUrl(options.url, parsed.detectedUrl);
52686
+ if (!anvilUrl) return void logger_logger.info("Checkout cancelled.");
52687
+ const resolvedUsername = preselectedUsername || await resolveCheckoutUsername(anvilUrl, options.user);
52688
+ if (null === resolvedUsername) return void logger_logger.info("Checkout cancelled.");
52689
+ if (resolvedUsername) logger_logger.verbose(chalk_source.cyan("Using account: ") + chalk_source.bold(resolvedUsername));
52690
+ else logger_logger.verbose(chalk_source.cyan("No account preselected; resolving after auth token lookup."));
52691
+ await ensureCheckoutAuthTokenInteractive(anvilUrl, resolvedUsername, deps);
52692
+ logger_logger.progress("checkout", `Checking out app ${parsed.appId} from ${anvilUrl}`);
52693
+ const result = await executeCheckout({
52694
+ ...options,
52695
+ input: checkoutInput,
52696
+ url: anvilUrl,
52697
+ user: resolvedUsername || options.user
52698
+ }, deps);
52699
+ logger_logger.progressEnd("checkout", `Checked out ${result.appId} into ${result.destinationDisplay}`);
52532
52700
  const preferredEditor = String(getConfig("preferredEditor") || "").trim();
52533
52701
  const preferredEditorCommand = preferredEditor ? getPreferredEditorCommand(preferredEditor) : "";
52534
52702
  if (options.open) {
52535
- await checkout_openPathInEditorOrDefault(destinationPath, preferredEditorCommand);
52536
- logger_logger.info(chalk_source.gray(`Opened ${destinationDisplay}`));
52703
+ await checkout_openPathInEditorOrDefault(result.destinationPath, preferredEditorCommand);
52704
+ logger_logger.info(chalk_source.gray(`Opened ${result.destinationDisplay}`));
52537
52705
  }
52538
- if (preferredEditorCommand && !options.open) if (isCommandAvailable(preferredEditorCommand)) logger_logger.info(chalk_source.cyan(`Next: ${preferredEditorCommand} ${destinationDisplay}`));
52706
+ if (preferredEditorCommand && !options.open) if (isCommandAvailable(preferredEditorCommand)) logger_logger.info(chalk_source.cyan(`Next: ${preferredEditorCommand} ${result.destinationDisplay}`));
52539
52707
  else logger_logger.warn(`Preferred editor command '${preferredEditorCommand}' was not found in PATH. Run 'anvil configure' to choose an installed editor.`);
52540
52708
  }
52541
52709
  async function checkout_openPathInEditorOrDefault(destinationPath, preferredEditorCommand, deps) {
@@ -52546,7 +52714,7 @@ var __webpack_exports__ = {};
52546
52714
  try {
52547
52715
  const globalOptions = command?.optsWithGlobals() || options || {};
52548
52716
  if ("number" == typeof options?.depth && (!Number.isFinite(options.depth) || options.depth <= 0)) throw new Error("--depth must be a positive integer");
52549
- await executeCheckout({
52717
+ await executeCheckoutInteractive({
52550
52718
  input,
52551
52719
  directory,
52552
52720
  open: options?.open,
@@ -52569,7 +52737,8 @@ var __webpack_exports__ = {};
52569
52737
  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');
52570
52738
  }
52571
52739
  const DEFAULT_ANVIL_URL = resolveAnvilUrl();
52572
- async function syncToLatest(repoPath, appId, anvilUrl, authToken, currentBranch, username) {
52740
+ async function syncToLatest(repoPath, appId, options) {
52741
+ const { anvilUrl, authToken, currentBranch, username } = options;
52573
52742
  try {
52574
52743
  const git = esm_default(repoPath);
52575
52744
  const commitId = (await git.revparse([
@@ -52593,7 +52762,8 @@ var __webpack_exports__ = {};
52593
52762
  throw errors_createGitError.commandFailed("sync", e instanceof Error ? e.message : String(e));
52594
52763
  }
52595
52764
  }
52596
- async function api_watch(repoPath, appId, anvilUrl = DEFAULT_ANVIL_URL, stagedOnly = false, username) {
52765
+ async function api_watch(repoPath, appId, options = {}) {
52766
+ const { anvilUrl = DEFAULT_ANVIL_URL, stagedOnly = false, username } = options;
52597
52767
  repoPath = external_path_default().resolve(repoPath);
52598
52768
  const authToken = await auth_getValidAuthToken(anvilUrl, username);
52599
52769
  await verifyAuth(authToken, anvilUrl);
@@ -52620,7 +52790,11 @@ var __webpack_exports__ = {};
52620
52790
  }
52621
52791
  logger_logger.verbose(chalk_source.cyan("Current branch: ") + chalk_source.bold(currentBranch));
52622
52792
  logger_logger.verbose(chalk_source.cyan("Current commit ID: ") + chalk_source.gray(commitId));
52623
- const syncStatus = await validateBranchSyncStatus(git, currentBranch, appId, anvilUrl, username);
52793
+ const syncStatus = await validateBranchSyncStatus(git, currentBranch, {
52794
+ appId,
52795
+ anvilUrl,
52796
+ username
52797
+ });
52624
52798
  let hasUncommittedChanges = false;
52625
52799
  try {
52626
52800
  const status = await git.status();
@@ -52969,7 +53143,11 @@ var __webpack_exports__ = {};
52969
53143
  async function recheckSyncStatus(previousCategory, previousBranch, options) {
52970
53144
  try {
52971
53145
  logger_logger.progress("verify", "Verifying repository status...");
52972
- const freshSession = await api_watch(options.repoPath, options.appId, options.anvilUrl, options.stagedOnly, options.username);
53146
+ const freshSession = await api_watch(options.repoPath, options.appId, {
53147
+ anvilUrl: options.anvilUrl,
53148
+ stagedOnly: options.stagedOnly,
53149
+ username: options.username
53150
+ });
52973
53151
  logger_logger.progressEnd("verify");
52974
53152
  const currentCategory = getSyncStateCategory(freshSession.syncStatus);
52975
53153
  const currentBranch = freshSession.getBranchName() || "master";
@@ -52988,7 +53166,11 @@ var __webpack_exports__ = {};
52988
53166
  async function recreateSessionAndValidate(options) {
52989
53167
  logger_logger.progress("init", "Restarting watch session...");
52990
53168
  try {
52991
- const newSession = await api_watch(options.repoPath, options.appId, options.anvilUrl, options.stagedOnly, options.username);
53169
+ const newSession = await api_watch(options.repoPath, options.appId, {
53170
+ anvilUrl: options.anvilUrl,
53171
+ stagedOnly: options.stagedOnly,
53172
+ username: options.username
53173
+ });
52992
53174
  logger_logger.progressEnd("init");
52993
53175
  return await checkSyncStatusAndStart(newSession, options);
52994
53176
  } catch (e) {
@@ -53201,7 +53383,12 @@ var __webpack_exports__ = {};
53201
53383
  }
53202
53384
  logger_logger.progress("sync-latest", "Syncing to latest version from Anvil...");
53203
53385
  try {
53204
- const newCommitId = await syncToLatest(options.repoPath, options.appId, options.anvilUrl, authToken, branchName, options.username);
53386
+ const newCommitId = await syncToLatest(options.repoPath, options.appId, {
53387
+ anvilUrl: options.anvilUrl,
53388
+ authToken,
53389
+ currentBranch: branchName,
53390
+ username: options.username
53391
+ });
53205
53392
  logger_logger.progressEnd("sync-latest", `Synced to latest version: ${newCommitId.substring(0, 8)}`);
53206
53393
  } catch (e) {
53207
53394
  logger_logger.progressEnd("sync-latest", "Failed");
@@ -53228,7 +53415,11 @@ var __webpack_exports__ = {};
53228
53415
  if (actualBranch !== newBranch) logger_logger.verbose(chalk_source.yellow(`Branch changed again to ${chalk_source.bold(actualBranch)}, using that instead`));
53229
53416
  logger_logger.progress("init", "Initializing watch session...");
53230
53417
  try {
53231
- const newSession = await api_watch(repoPath, appId, anvilUrl, stagedOnly, options.username);
53418
+ const newSession = await api_watch(repoPath, appId, {
53419
+ anvilUrl,
53420
+ stagedOnly,
53421
+ username: options.username
53422
+ });
53232
53423
  logger_logger.progressEnd("init");
53233
53424
  const started = await checkSyncStatusAndStart(newSession, options);
53234
53425
  if (!started) process.exit(0);
@@ -53441,7 +53632,11 @@ var __webpack_exports__ = {};
53441
53632
  if (stagedOnly) logger_logger.info(chalk_source.yellow("▸ Staged-only mode: Only staged changes will be synced"));
53442
53633
  if (autoMode) logger_logger.info(chalk_source.cyan("▸ Auto mode: Will automatically restart on branch changes and sync when behind"));
53443
53634
  logger_logger.progress("init", "Initializing watch session...");
53444
- const session = await api_watch(repoPath, finalAppId, anvilUrl, stagedOnly, username);
53635
+ const session = await api_watch(repoPath, finalAppId, {
53636
+ anvilUrl,
53637
+ stagedOnly,
53638
+ username
53639
+ });
53445
53640
  logger_logger.progressEnd("init");
53446
53641
  if (openAfterValidation) await openWatchPath(resolveWatchOpenPath(repoPath));
53447
53642
  logger_logger.verbose(chalk_source.blue("Watching for file changes..."));
@@ -53897,11 +54092,8 @@ var __webpack_exports__ = {};
53897
54092
  if (1 === accounts.length) return accounts[0];
53898
54093
  return `${accounts.length} accounts`;
53899
54094
  }
53900
- function configure_isInteractiveSession() {
53901
- return !!(process.stdin.isTTY && process.stdout.isTTY);
53902
- }
53903
54095
  async function runConfigureFlow(version, deps = configure_defaultDeps) {
53904
- if (!configure_isInteractiveSession()) throw new Error("`anvil configure` is interactive and requires a TTY terminal.");
54096
+ assertCanPrompt("`anvil configure`", "Use `anvil config set anvilUrl <URL>`, `anvil config set preferredEditor <COMMAND>`, `anvil config set verbose <true|false>`, and `anvil login <URL>` instead.");
53905
54097
  console.log();
53906
54098
  logger_logger.info(chalk_source.cyan.bold("Anvil Configuration"));
53907
54099
  console.log();