Files
windmill/cli/src/core/login.ts
T
hugocasaandClaude Opus 4.8 ba4b368706 fix: prevent variable push from corrupting is_secret variables (#9705)
* fix: prevent variable push from corrupting is_secret variables

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(cli): unit-test looksLikeWorkspaceCiphertext shape detection

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(cli): scope is_secret downgrade to single-file push, not sync push

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(cli): warn when variable push stores a secret value as already-encrypted

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(cli): route workspace-resolution and auth diagnostics to stderr

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(cli): rephrase comments to describe current behavior, not history

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 12:45:36 +02:00

104 lines
2.5 KiB
TypeScript

import { GlobalOptions } from "../types.ts";
import { colors } from "@cliffy/ansi/colors";
import * as getPort from "get-port";
import * as log from "./log.ts";
import * as open from "open";
import { Secret } from "@cliffy/prompt/secret";
import { Select } from "@cliffy/prompt/select";
import * as http from "node:http";
export async function loginInteractive(remote: string) {
let token: string | undefined;
if (!process.stdin.isTTY) {
log.infoStderr("Not a TTY, can't login interactively.");
return undefined;
}
if (
(await Select.prompt({
message: "How do you want to login",
options: [
{
name: "Browser",
value: "b",
},
{
name: "Token",
value: "t",
},
],
})) === "b"
) {
token = await browserLogin(remote);
} else {
token = await Secret.prompt("Enter your token");
}
return token;
}
export async function tryGetLoginInfo(
opts: GlobalOptions
): Promise<string | undefined> {
if (opts.token) {
return opts.token;
}
return undefined;
}
export async function browserLogin(
baseUrl: string
): Promise<string | undefined> {
const env =
process.env["TOKEN_PORT"] != undefined
? parseInt(process.env["TOKEN_PORT"]!)
: undefined;
const port = await getPort.default({ port: env });
if (port == undefined) {
log.infoStderr(colors.red.underline("failed to aquire port"));
return undefined;
}
return new Promise((resolve) => {
const server = http.createServer((req, res) => {
const params = new URL(req.url!, `http://${req.headers.host}`)
.searchParams;
const token = params.get("token");
// Redirect the user to the success page
res.writeHead(302, { Location: `${baseUrl}user/cli-success` });
res.end();
// Close the server after a short delay
setTimeout(() => {
server.close();
}, 10);
// Resolve the promise with the token
resolve(token ?? undefined);
});
const url = `${baseUrl}user/cli?port=${port}`;
log.infoStderr(`Login by going to ${url}`);
try {
open.default(url).catch((error) => {
console.error(
`Failed to open browser, please navigate to ${url}, error: ${error}`
);
});
log.infoStderr("Opened browser for you");
} catch (error) {
console.error(
`Failed to open browser, please navigate to ${url}, error: ${error}`
);
}
// Start the server
server.listen(port, () => {
console.log(`Listening on port ${port}`);
});
});
}