@anvil-works/anvil-cli 0.5.11 → 0.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/cli.js +225 -54
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -172,6 +172,7 @@ Useful options:
|
|
|
172
172
|
|
|
173
173
|
```bash
|
|
174
174
|
anvil login
|
|
175
|
+
anvil login anvil.company.com
|
|
175
176
|
anvil logout
|
|
176
177
|
anvil config list
|
|
177
178
|
anvil config get <key>
|
|
@@ -260,6 +261,19 @@ If you want to change the default server permanently:
|
|
|
260
261
|
anvil config set anvilUrl https://anvil.company.com
|
|
261
262
|
```
|
|
262
263
|
|
|
264
|
+
When you run `anvil login`, the CLI now supports both local and headless flows:
|
|
265
|
+
|
|
266
|
+
- press `Enter` to open the default browser
|
|
267
|
+
- or copy the printed device-login URL and code to continue in a browser of your choice
|
|
268
|
+
|
|
269
|
+
For `anvil login`, prefer the positional server argument in docs and examples:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
anvil login anvil.company.com
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
`--url` is also supported if needed.
|
|
276
|
+
|
|
263
277
|
## Troubleshooting
|
|
264
278
|
|
|
265
279
|
### `anvil` command not found
|
package/dist/cli.js
CHANGED
|
@@ -51890,34 +51890,134 @@ var __webpack_exports__ = {};
|
|
|
51890
51890
|
</body>
|
|
51891
51891
|
</html>`;
|
|
51892
51892
|
}
|
|
51893
|
+
const CLIENT_ID = "anvil-sync";
|
|
51894
|
+
const SCOPES = "apps:read apps:write user:read";
|
|
51893
51895
|
function oauth_login_base64url(buf) {
|
|
51894
51896
|
return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
51895
51897
|
}
|
|
51896
|
-
|
|
51898
|
+
function sleep(ms) {
|
|
51899
|
+
return new Promise((resolve)=>setTimeout(resolve, ms));
|
|
51900
|
+
}
|
|
51901
|
+
function createBrowserLaunchPrompt(authUrl) {
|
|
51902
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return {
|
|
51903
|
+
waitForOpen: Promise.resolve(false),
|
|
51904
|
+
close: ()=>{}
|
|
51905
|
+
};
|
|
51897
51906
|
const rl = external_readline_namespaceObject.createInterface({
|
|
51898
51907
|
input: process.stdin,
|
|
51899
51908
|
output: process.stdout
|
|
51900
51909
|
});
|
|
51901
|
-
|
|
51902
|
-
|
|
51903
|
-
|
|
51904
|
-
|
|
51905
|
-
|
|
51906
|
-
oauthPromise.then((r)=>({
|
|
51907
|
-
oauth: r
|
|
51908
|
-
}))
|
|
51909
|
-
]);
|
|
51910
|
-
if ("enter" === result) {
|
|
51910
|
+
let closed = false;
|
|
51911
|
+
let resolveWait = null;
|
|
51912
|
+
const close = (opened = false)=>{
|
|
51913
|
+
if (closed) return;
|
|
51914
|
+
closed = true;
|
|
51911
51915
|
rl.close();
|
|
51912
|
-
|
|
51913
|
-
|
|
51914
|
-
|
|
51916
|
+
resolveWait?.(opened);
|
|
51917
|
+
resolveWait = null;
|
|
51918
|
+
};
|
|
51919
|
+
const waitForOpen = new Promise((resolve)=>{
|
|
51920
|
+
resolveWait = resolve;
|
|
51921
|
+
rl.on("SIGINT", ()=>{
|
|
51922
|
+
close();
|
|
51923
|
+
process.kill(process.pid, "SIGINT");
|
|
51924
|
+
});
|
|
51925
|
+
rl.question("", async ()=>{
|
|
51926
|
+
try {
|
|
51927
|
+
await node_modules_open(authUrl);
|
|
51928
|
+
logger_logger.info(chalk_source.dim("Opened your browser to continue login."));
|
|
51929
|
+
close(true);
|
|
51930
|
+
} catch {
|
|
51931
|
+
logger_logger.warn("Could not open a browser automatically.");
|
|
51932
|
+
close(false);
|
|
51933
|
+
}
|
|
51934
|
+
});
|
|
51935
|
+
});
|
|
51936
|
+
return {
|
|
51937
|
+
waitForOpen,
|
|
51938
|
+
close
|
|
51939
|
+
};
|
|
51940
|
+
}
|
|
51941
|
+
async function parseOAuthError(response) {
|
|
51942
|
+
const contentType = response.headers.get("content-type") || "";
|
|
51943
|
+
if (contentType.includes("application/json")) {
|
|
51944
|
+
const data = await response.json();
|
|
51945
|
+
return data.error || `HTTP ${response.status}`;
|
|
51946
|
+
}
|
|
51947
|
+
const text = (await response.text()).trim();
|
|
51948
|
+
if (!text) return `HTTP ${response.status}`;
|
|
51949
|
+
try {
|
|
51950
|
+
const parsed = JSON.parse(text);
|
|
51951
|
+
return parsed.error || text;
|
|
51952
|
+
} catch {
|
|
51953
|
+
return text;
|
|
51915
51954
|
}
|
|
51916
|
-
rl.close();
|
|
51917
|
-
process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
51918
|
-
return result.oauth;
|
|
51919
51955
|
}
|
|
51920
|
-
async function
|
|
51956
|
+
async function exchangeAuthorizationCodeForTokens(anvilUrl, redirectUri, code, codeVerifier) {
|
|
51957
|
+
const tokenResponse = await fetch(`${anvilUrl}/oauth/token`, {
|
|
51958
|
+
method: "POST",
|
|
51959
|
+
headers: {
|
|
51960
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
51961
|
+
},
|
|
51962
|
+
body: new URLSearchParams({
|
|
51963
|
+
grant_type: "authorization_code",
|
|
51964
|
+
code,
|
|
51965
|
+
redirect_uri: redirectUri,
|
|
51966
|
+
client_id: CLIENT_ID,
|
|
51967
|
+
code_verifier: codeVerifier
|
|
51968
|
+
})
|
|
51969
|
+
});
|
|
51970
|
+
if (!tokenResponse.ok) throw new Error(`Failed to exchange authorization code for token. ${await parseOAuthError(tokenResponse)}`);
|
|
51971
|
+
return await tokenResponse.json();
|
|
51972
|
+
}
|
|
51973
|
+
async function requestDeviceAuthorization(anvilUrl) {
|
|
51974
|
+
const response = await fetch(`${anvilUrl}/oauth/device_authorization`, {
|
|
51975
|
+
method: "POST",
|
|
51976
|
+
headers: {
|
|
51977
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
51978
|
+
},
|
|
51979
|
+
body: new URLSearchParams({
|
|
51980
|
+
client_id: CLIENT_ID,
|
|
51981
|
+
scope: SCOPES
|
|
51982
|
+
})
|
|
51983
|
+
});
|
|
51984
|
+
if (!response.ok) throw new Error(`Failed to start device login. ${await parseOAuthError(response)}`);
|
|
51985
|
+
return await response.json();
|
|
51986
|
+
}
|
|
51987
|
+
async function pollDeviceAuthorization(anvilUrl, deviceAuth, options) {
|
|
51988
|
+
const expiresAt = Date.now() + 1000 * deviceAuth.expires_in;
|
|
51989
|
+
let intervalMs = 1000 * Math.max(deviceAuth.interval ?? 5, 1);
|
|
51990
|
+
while(Date.now() < expiresAt){
|
|
51991
|
+
if (options?.isCancelled?.()) throw new Error("cancelled");
|
|
51992
|
+
const response = await fetch(`${anvilUrl}/oauth/token`, {
|
|
51993
|
+
method: "POST",
|
|
51994
|
+
headers: {
|
|
51995
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
51996
|
+
},
|
|
51997
|
+
body: new URLSearchParams({
|
|
51998
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
51999
|
+
device_code: deviceAuth.device_code,
|
|
52000
|
+
client_id: CLIENT_ID
|
|
52001
|
+
})
|
|
52002
|
+
});
|
|
52003
|
+
if (response.ok) return await response.json();
|
|
52004
|
+
const error = await parseOAuthError(response);
|
|
52005
|
+
if ("authorization_pending" === error) {
|
|
52006
|
+
await sleep(intervalMs);
|
|
52007
|
+
continue;
|
|
52008
|
+
}
|
|
52009
|
+
if ("slow_down" === error) {
|
|
52010
|
+
intervalMs += 1000;
|
|
52011
|
+
await sleep(intervalMs);
|
|
52012
|
+
continue;
|
|
52013
|
+
}
|
|
52014
|
+
if ("access_denied" === error) throw new Error("Device login was denied.");
|
|
52015
|
+
if ("expired_token" === error || "invalid_grant" === error) break;
|
|
52016
|
+
throw new Error(`Device login failed. ${error}`);
|
|
52017
|
+
}
|
|
52018
|
+
throw new Error("Device login expired before it was approved.");
|
|
52019
|
+
}
|
|
52020
|
+
async function createPkceLoginFlow(anvilUrl) {
|
|
51921
52021
|
const codeVerifier = external_crypto_.randomBytes(48).toString("hex");
|
|
51922
52022
|
const codeChallenge = oauth_login_base64url(external_crypto_.createHash("sha256").update(codeVerifier, "ascii").digest());
|
|
51923
52023
|
const state = external_crypto_.randomBytes(16).toString("hex");
|
|
@@ -51927,10 +52027,9 @@ var __webpack_exports__ = {};
|
|
|
51927
52027
|
if (!address || "string" == typeof address) throw new Error("No address");
|
|
51928
52028
|
const port = address.port;
|
|
51929
52029
|
const redirectUri = `http://127.0.0.1:${port}/oauth-callback`;
|
|
51930
|
-
const SCOPES = "apps:read apps:write user:read";
|
|
51931
52030
|
const authUrl = new URL(`${anvilUrl}/oauth/authorize`);
|
|
51932
52031
|
authUrl.searchParams.set("response_type", "code");
|
|
51933
|
-
authUrl.searchParams.set("client_id",
|
|
52032
|
+
authUrl.searchParams.set("client_id", CLIENT_ID);
|
|
51934
52033
|
authUrl.searchParams.set("redirect_uri", redirectUri);
|
|
51935
52034
|
authUrl.searchParams.set("scope", SCOPES);
|
|
51936
52035
|
authUrl.searchParams.set("state", state);
|
|
@@ -51964,37 +52063,84 @@ var __webpack_exports__ = {};
|
|
|
51964
52063
|
server.close();
|
|
51965
52064
|
});
|
|
51966
52065
|
});
|
|
51967
|
-
|
|
52066
|
+
let closed = false;
|
|
52067
|
+
server.on("close", ()=>{
|
|
52068
|
+
closed = true;
|
|
52069
|
+
});
|
|
52070
|
+
const close = async ()=>{
|
|
52071
|
+
if (closed) return;
|
|
52072
|
+
closed = true;
|
|
52073
|
+
server.closeAllConnections();
|
|
52074
|
+
if (!server.listening) return;
|
|
52075
|
+
await new Promise((resolve)=>server.close(()=>resolve()));
|
|
52076
|
+
};
|
|
52077
|
+
return {
|
|
52078
|
+
authUrl,
|
|
52079
|
+
close,
|
|
52080
|
+
waitForLogin: (async ()=>{
|
|
52081
|
+
const { code, error, recvState } = await codePromise;
|
|
52082
|
+
if (recvState !== state) throw new Error("Invalid state received from OAuth callback");
|
|
52083
|
+
if (error) throw new Error(`Error received from OAuth callback: ${error}`);
|
|
52084
|
+
const tokenData = await exchangeAuthorizationCodeForTokens(anvilUrl, redirectUri, code, codeVerifier);
|
|
52085
|
+
return login(anvilUrl, {
|
|
52086
|
+
access_token: tokenData.access_token,
|
|
52087
|
+
refresh_token: tokenData.refresh_token,
|
|
52088
|
+
expires_in: tokenData.expires_in,
|
|
52089
|
+
scope: tokenData.scope
|
|
52090
|
+
});
|
|
52091
|
+
})()
|
|
52092
|
+
};
|
|
52093
|
+
}
|
|
52094
|
+
function raceLoginAttempts(attempts) {
|
|
52095
|
+
return new Promise((resolve, reject)=>{
|
|
52096
|
+
let remaining = attempts.length;
|
|
52097
|
+
let lastError = null;
|
|
52098
|
+
for (const attempt of attempts)attempt.then(resolve).catch((error)=>{
|
|
52099
|
+
if ("cancelled" === error.message) return;
|
|
52100
|
+
remaining -= 1;
|
|
52101
|
+
lastError = error;
|
|
52102
|
+
if (0 === remaining && lastError) reject(lastError);
|
|
52103
|
+
});
|
|
52104
|
+
});
|
|
52105
|
+
}
|
|
52106
|
+
async function runInteractiveLoginFlow(anvilUrl) {
|
|
52107
|
+
const pkceFlow = await createPkceLoginFlow(anvilUrl);
|
|
52108
|
+
const deviceAuth = await requestDeviceAuthorization(anvilUrl);
|
|
52109
|
+
const browserPrompt = createBrowserLaunchPrompt(pkceFlow.authUrl.toString());
|
|
52110
|
+
let settled = false;
|
|
52111
|
+
let spinnerStarted = false;
|
|
52112
|
+
logger_logger.info(chalk_source.dim(`Logging in to ${anvilUrl}`));
|
|
51968
52113
|
console.log();
|
|
51969
|
-
logger_logger.info(chalk_source.
|
|
52114
|
+
logger_logger.info(chalk_source.dim("Visit:"), `${deviceAuth.verification_uri_complete || deviceAuth.verification_uri}`);
|
|
52115
|
+
logger_logger.info(chalk_source.dim("Device code:"), `${deviceAuth.user_code}`);
|
|
51970
52116
|
console.log();
|
|
51971
|
-
|
|
51972
|
-
|
|
51973
|
-
|
|
51974
|
-
|
|
51975
|
-
|
|
51976
|
-
|
|
51977
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
51978
|
-
},
|
|
51979
|
-
body: new URLSearchParams({
|
|
51980
|
-
grant_type: "authorization_code",
|
|
51981
|
-
code: code,
|
|
51982
|
-
redirect_uri: redirectUri,
|
|
51983
|
-
client_id: "anvil-sync",
|
|
51984
|
-
code_verifier: codeVerifier
|
|
51985
|
-
})
|
|
52117
|
+
logger_logger.info("OR Press ENTER to open a browser...");
|
|
52118
|
+
browserPrompt.waitForOpen.then((opened)=>{
|
|
52119
|
+
if (opened && !settled) {
|
|
52120
|
+
spinnerStarted = true;
|
|
52121
|
+
logger_logger.progress("login", "Waiting for login to complete...");
|
|
52122
|
+
}
|
|
51986
52123
|
});
|
|
51987
|
-
|
|
51988
|
-
const
|
|
51989
|
-
|
|
52124
|
+
try {
|
|
52125
|
+
const result = await raceLoginAttempts([
|
|
52126
|
+
pkceFlow.waitForLogin,
|
|
52127
|
+
pollDeviceAuthorization(anvilUrl, deviceAuth, {
|
|
52128
|
+
isCancelled: ()=>settled
|
|
52129
|
+
}).then(async (tokenData)=>login(anvilUrl, {
|
|
52130
|
+
access_token: tokenData.access_token,
|
|
52131
|
+
refresh_token: tokenData.refresh_token,
|
|
52132
|
+
expires_in: tokenData.expires_in,
|
|
52133
|
+
scope: tokenData.scope
|
|
52134
|
+
}))
|
|
52135
|
+
]);
|
|
52136
|
+
settled = true;
|
|
52137
|
+
return result;
|
|
52138
|
+
} finally{
|
|
52139
|
+
settled = true;
|
|
52140
|
+
browserPrompt.close();
|
|
52141
|
+
if (spinnerStarted) logger_logger.progressEnd("login");
|
|
52142
|
+
await pkceFlow.close();
|
|
51990
52143
|
}
|
|
51991
|
-
const tokenData = await tokenResponse.json();
|
|
51992
|
-
return login(anvilUrl, {
|
|
51993
|
-
access_token: tokenData.access_token,
|
|
51994
|
-
refresh_token: tokenData.refresh_token,
|
|
51995
|
-
expires_in: tokenData.expires_in,
|
|
51996
|
-
scope: tokenData.scope
|
|
51997
|
-
});
|
|
51998
52144
|
}
|
|
51999
52145
|
const CHECKOUT_ERROR_VALUE = "__ERROR__";
|
|
52000
52146
|
function isAbortLikeError(error) {
|
|
@@ -52501,10 +52647,19 @@ var __webpack_exports__ = {};
|
|
|
52501
52647
|
return appId;
|
|
52502
52648
|
}
|
|
52503
52649
|
async function resolveCheckoutUrl(explicitUrl, parsedUrl) {
|
|
52504
|
-
if (explicitUrl)
|
|
52505
|
-
|
|
52650
|
+
if (explicitUrl) {
|
|
52651
|
+
logger_logger.verbose(chalk_source.cyan("Using Anvil URL from --url: ") + chalk_source.bold(normalizeAnvilUrl(explicitUrl)));
|
|
52652
|
+
return normalizeAnvilUrl(explicitUrl);
|
|
52653
|
+
}
|
|
52654
|
+
if (parsedUrl) {
|
|
52655
|
+
logger_logger.verbose(chalk_source.cyan("Using Anvil URL from checkout input: ") + chalk_source.bold(normalizeAnvilUrl(parsedUrl)));
|
|
52656
|
+
return normalizeAnvilUrl(parsedUrl);
|
|
52657
|
+
}
|
|
52506
52658
|
const decision = decideFallbackUrl(void 0);
|
|
52507
|
-
if ("available-multiple" !== decision.source)
|
|
52659
|
+
if ("available-multiple" !== decision.source) {
|
|
52660
|
+
if (decision.url) logger_logger.verbose(chalk_source.cyan("Using configured Anvil URL: ") + chalk_source.bold(decision.url));
|
|
52661
|
+
return decision.url;
|
|
52662
|
+
}
|
|
52508
52663
|
const choices = decision.urls.map((url)=>({
|
|
52509
52664
|
name: url,
|
|
52510
52665
|
value: url
|
|
@@ -52550,6 +52705,7 @@ var __webpack_exports__ = {};
|
|
|
52550
52705
|
}
|
|
52551
52706
|
async function ensureCheckoutAuthToken(anvilUrl, username, deps = defaultCheckoutDeps) {
|
|
52552
52707
|
try {
|
|
52708
|
+
logger_logger.verbose(chalk_source.cyan("Checking auth token for: ") + chalk_source.bold(username ? `${username} @ ${anvilUrl}` : anvilUrl));
|
|
52553
52709
|
return await deps.getValidAuthToken(anvilUrl, username);
|
|
52554
52710
|
} catch (e) {
|
|
52555
52711
|
const interactive = process.stdin.isTTY && process.stdout.isTTY;
|
|
@@ -52596,11 +52752,15 @@ var __webpack_exports__ = {};
|
|
|
52596
52752
|
if (!selectedInput) return void logger_logger.info("Checkout cancelled.");
|
|
52597
52753
|
checkoutInput = selectedInput;
|
|
52598
52754
|
}
|
|
52755
|
+
logger_logger.verbose(chalk_source.cyan("Checkout input: ") + chalk_source.bold(checkoutInput));
|
|
52599
52756
|
const parsed = parseCheckoutInput(checkoutInput);
|
|
52757
|
+
logger_logger.verbose(chalk_source.cyan("Resolved app ID: ") + chalk_source.bold(parsed.appId));
|
|
52600
52758
|
const anvilUrl = preselectedAnvilUrl || await resolveCheckoutUrl(options.url, parsed.detectedUrl);
|
|
52601
52759
|
if (!anvilUrl) return void logger_logger.info("Checkout cancelled.");
|
|
52602
52760
|
const resolvedUsername = preselectedUsername || await resolveCheckoutUsername(anvilUrl, options.user);
|
|
52603
52761
|
if (null === resolvedUsername) return void logger_logger.info("Checkout cancelled.");
|
|
52762
|
+
if (resolvedUsername) logger_logger.verbose(chalk_source.cyan("Using account: ") + chalk_source.bold(resolvedUsername));
|
|
52763
|
+
else logger_logger.verbose(chalk_source.cyan("No account preselected; resolving after auth token lookup."));
|
|
52604
52764
|
const authToken = await ensureCheckoutAuthToken(anvilUrl, resolvedUsername, deps);
|
|
52605
52765
|
let checkoutUsername = resolvedUsername;
|
|
52606
52766
|
if (!checkoutUsername) {
|
|
@@ -52609,13 +52769,22 @@ var __webpack_exports__ = {};
|
|
|
52609
52769
|
if (!inferredUsername) throw new Error(`Could not determine account for ${anvilUrl}. Use --user <USERNAME> so checkout can bind repository credentials.`);
|
|
52610
52770
|
checkoutUsername = inferredUsername;
|
|
52611
52771
|
}
|
|
52772
|
+
logger_logger.verbose(chalk_source.cyan("Checkout account: ") + chalk_source.bold(checkoutUsername));
|
|
52773
|
+
logger_logger.verbose(chalk_source.blue(`Validating app ID ${parsed.appId} with Anvil server...`));
|
|
52612
52774
|
const validation = await deps.validateAppId(parsed.appId, anvilUrl, checkoutUsername);
|
|
52613
52775
|
if (!validation.valid) throw new Error(validation.error || `App '${parsed.appId}' is not accessible on ${anvilUrl}.`);
|
|
52776
|
+
logger_logger.verbose(chalk_source.green(`✔ App ID validated successfully`));
|
|
52777
|
+
if (validation.app_name) logger_logger.verbose(chalk_source.gray(` App name: ${validation.app_name}`));
|
|
52614
52778
|
const destinationDir = options.directory || getDefaultDestinationDirectory(parsed.appId, validation.app_name);
|
|
52615
52779
|
const destinationPath = external_path_default().resolve(process.cwd(), destinationDir);
|
|
52616
52780
|
const destinationDisplay = formatPathForDisplay(destinationPath);
|
|
52781
|
+
logger_logger.verbose(chalk_source.cyan("Resolved destination: ") + chalk_source.bold(destinationDisplay));
|
|
52617
52782
|
await validateCheckoutDestination(destinationPath, options.force);
|
|
52618
52783
|
const cloneUrl = getGitPushUrl(parsed.appId, authToken, anvilUrl);
|
|
52784
|
+
logger_logger.verbose(chalk_source.cyan("Clone source: ") + chalk_source.bold(`${anvilUrl}/git/${parsed.appId}.git`));
|
|
52785
|
+
if (options.branch) logger_logger.verbose(chalk_source.cyan("Requested branch: ") + chalk_source.bold(options.branch));
|
|
52786
|
+
if ("number" == typeof options.depth) logger_logger.verbose(chalk_source.cyan("Clone depth: ") + chalk_source.bold(String(options.depth)));
|
|
52787
|
+
if (options.singleBranch) logger_logger.verbose(chalk_source.cyan("Single-branch clone enabled"));
|
|
52619
52788
|
logger_logger.progress("checkout", `Checking out app ${parsed.appId} from ${anvilUrl}`);
|
|
52620
52789
|
logger_logger.info(chalk_source.gray(` Destination directory: ${destinationDisplay}`));
|
|
52621
52790
|
await deps.clone(cloneUrl, destinationPath, {
|
|
@@ -52635,6 +52804,7 @@ var __webpack_exports__ = {};
|
|
|
52635
52804
|
username: checkoutUsername,
|
|
52636
52805
|
remoteName
|
|
52637
52806
|
});
|
|
52807
|
+
logger_logger.verbose(chalk_source.cyan("Configured Git auth bridge for remote: ") + chalk_source.bold(remoteName));
|
|
52638
52808
|
} catch (e) {
|
|
52639
52809
|
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.`);
|
|
52640
52810
|
}
|
|
@@ -52747,10 +52917,11 @@ var __webpack_exports__ = {};
|
|
|
52747
52917
|
});
|
|
52748
52918
|
}
|
|
52749
52919
|
function registerLoginCommand(program) {
|
|
52750
|
-
const loginCommand = program.command("login [anvil-server-url]").description("Log in to Anvil using OAuth").alias("l").action(async (anvilUrl)=>{
|
|
52920
|
+
const loginCommand = program.command("login [anvil-server-url]").description("Log in to Anvil using OAuth").alias("l").option("-u, --url <ANVIL_URL>", "Specify Anvil server URL").action(async (anvilUrl, options)=>{
|
|
52751
52921
|
try {
|
|
52752
|
-
|
|
52753
|
-
|
|
52922
|
+
const requestedUrl = anvilUrl || options.url;
|
|
52923
|
+
if (requestedUrl) {
|
|
52924
|
+
anvilUrl = normalizeAnvilUrl(requestedUrl.trim());
|
|
52754
52925
|
setConfig("anvilUrl", anvilUrl);
|
|
52755
52926
|
} else anvilUrl = resolveAnvilUrl();
|
|
52756
52927
|
const result = await runInteractiveLoginFlow(anvilUrl);
|
|
@@ -52760,7 +52931,7 @@ var __webpack_exports__ = {};
|
|
|
52760
52931
|
process.exit(1);
|
|
52761
52932
|
}
|
|
52762
52933
|
});
|
|
52763
|
-
loginCommand.addHelpText("after", "\n" + chalk_source.bold("Examples:") + "\n anvil login Log in to default Anvil server\n anvil login anvil.works Log in to anvil.works\n anvil login localhost Log in to local Anvil server\n");
|
|
52934
|
+
loginCommand.addHelpText("after", "\n" + chalk_source.bold("Examples:") + "\n anvil login Log in to default Anvil server\n anvil login anvil.works Log in to anvil.works\n anvil login --url localhost Log in to localhost\n anvil login localhost Log in to local Anvil server\n");
|
|
52764
52935
|
}
|
|
52765
52936
|
function getTotalLoggedInAccounts(urls) {
|
|
52766
52937
|
return urls.reduce((total, url)=>total + auth_getAccountsForUrl(url).length, 0);
|