mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 16:28:26 +00:00
Gateway: add SecretRef support for gateway.auth.token with auth-mode guardrails (#35094)
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
loadConfigMock as loadConfig,
|
||||
@@ -5,10 +8,19 @@ import {
|
||||
pickPrimaryTailnetIPv4Mock as pickPrimaryTailnetIPv4,
|
||||
resolveGatewayPortMock as resolveGatewayPort,
|
||||
} from "../gateway/gateway-connection.test-mocks.js";
|
||||
import { captureEnv, withEnv } from "../test-utils/env.js";
|
||||
import { captureEnv, withEnvAsync } from "../test-utils/env.js";
|
||||
|
||||
const { resolveGatewayConnection } = await import("./gateway-chat.js");
|
||||
|
||||
async function fileExists(filePath: string): Promise<boolean> {
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
describe("resolveGatewayConnection", () => {
|
||||
let envSnapshot: ReturnType<typeof captureEnv>;
|
||||
|
||||
@@ -29,10 +41,10 @@ describe("resolveGatewayConnection", () => {
|
||||
envSnapshot.restore();
|
||||
});
|
||||
|
||||
it("throws when url override is missing explicit credentials", () => {
|
||||
it("throws when url override is missing explicit credentials", async () => {
|
||||
loadConfig.mockReturnValue({ gateway: { mode: "local" } });
|
||||
|
||||
expect(() => resolveGatewayConnection({ url: "wss://override.example/ws" })).toThrow(
|
||||
await expect(resolveGatewayConnection({ url: "wss://override.example/ws" })).rejects.toThrow(
|
||||
"explicit credentials",
|
||||
);
|
||||
});
|
||||
@@ -48,10 +60,10 @@ describe("resolveGatewayConnection", () => {
|
||||
auth: { password: "explicit-password" },
|
||||
expected: { token: undefined, password: "explicit-password" },
|
||||
},
|
||||
])("uses explicit $label when url override is set", ({ auth, expected }) => {
|
||||
])("uses explicit $label when url override is set", async ({ auth, expected }) => {
|
||||
loadConfig.mockReturnValue({ gateway: { mode: "local" } });
|
||||
|
||||
const result = resolveGatewayConnection({
|
||||
const result = await resolveGatewayConnection({
|
||||
url: "wss://override.example/ws",
|
||||
...auth,
|
||||
});
|
||||
@@ -73,33 +85,98 @@ describe("resolveGatewayConnection", () => {
|
||||
bind: "lan",
|
||||
setup: () => pickPrimaryLanIPv4.mockReturnValue("192.168.1.42"),
|
||||
},
|
||||
])("uses loopback host when local bind is $label", ({ bind, setup }) => {
|
||||
])("uses loopback host when local bind is $label", async ({ bind, setup }) => {
|
||||
loadConfig.mockReturnValue({ gateway: { mode: "local", bind } });
|
||||
resolveGatewayPort.mockReturnValue(18800);
|
||||
setup();
|
||||
|
||||
const result = resolveGatewayConnection({});
|
||||
const result = await withEnvAsync({ OPENCLAW_GATEWAY_TOKEN: "env-token" }, async () => {
|
||||
return await resolveGatewayConnection({});
|
||||
});
|
||||
|
||||
expect(result.url).toBe("ws://127.0.0.1:18800");
|
||||
});
|
||||
|
||||
it("uses OPENCLAW_GATEWAY_TOKEN for local mode", () => {
|
||||
it("uses OPENCLAW_GATEWAY_TOKEN for local mode", async () => {
|
||||
loadConfig.mockReturnValue({ gateway: { mode: "local" } });
|
||||
|
||||
withEnv({ OPENCLAW_GATEWAY_TOKEN: "env-token" }, () => {
|
||||
const result = resolveGatewayConnection({});
|
||||
await withEnvAsync({ OPENCLAW_GATEWAY_TOKEN: "env-token" }, async () => {
|
||||
const result = await resolveGatewayConnection({});
|
||||
expect(result.token).toBe("env-token");
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to config auth token when env token is missing", () => {
|
||||
it("falls back to config auth token when env token is missing", async () => {
|
||||
loadConfig.mockReturnValue({ gateway: { mode: "local", auth: { token: "config-token" } } });
|
||||
|
||||
const result = resolveGatewayConnection({});
|
||||
const result = await resolveGatewayConnection({});
|
||||
expect(result.token).toBe("config-token");
|
||||
});
|
||||
|
||||
it("prefers OPENCLAW_GATEWAY_PASSWORD over remote password fallback", () => {
|
||||
it("uses local password auth when gateway.auth.mode is unset and password-only is configured", async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: {
|
||||
password: "config-password",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const result = await resolveGatewayConnection({});
|
||||
expect(result.password).toBe("config-password");
|
||||
expect(result.token).toBeUndefined();
|
||||
});
|
||||
|
||||
it("fails when both local token and password are configured but gateway.auth.mode is unset", async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: {
|
||||
token: "config-token",
|
||||
password: "config-password",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await expect(resolveGatewayConnection({})).rejects.toThrow(
|
||||
"gateway.auth.mode is unset. Set gateway.auth.mode to token or password.",
|
||||
);
|
||||
});
|
||||
|
||||
it("resolves env-template config auth token from referenced env var", async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
secrets: {
|
||||
providers: {
|
||||
default: { source: "env" },
|
||||
},
|
||||
},
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: { token: "${CUSTOM_GATEWAY_TOKEN}" },
|
||||
},
|
||||
});
|
||||
|
||||
await withEnvAsync({ CUSTOM_GATEWAY_TOKEN: "custom-token" }, async () => {
|
||||
const result = await resolveGatewayConnection({});
|
||||
expect(result.token).toBe("custom-token");
|
||||
});
|
||||
});
|
||||
|
||||
it("fails with guidance when env-template config auth token is unresolved", async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: { token: "${MISSING_GATEWAY_TOKEN}" },
|
||||
},
|
||||
});
|
||||
|
||||
await expect(resolveGatewayConnection({})).rejects.toThrow(
|
||||
"gateway.auth.token SecretRef is unresolved",
|
||||
);
|
||||
});
|
||||
|
||||
it("prefers OPENCLAW_GATEWAY_PASSWORD over remote password fallback", async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
gateway: {
|
||||
mode: "remote",
|
||||
@@ -107,9 +184,181 @@ describe("resolveGatewayConnection", () => {
|
||||
},
|
||||
});
|
||||
|
||||
withEnv({ OPENCLAW_GATEWAY_PASSWORD: "env-pass" }, () => {
|
||||
const result = resolveGatewayConnection({});
|
||||
await withEnvAsync({ OPENCLAW_GATEWAY_PASSWORD: "env-pass" }, async () => {
|
||||
const result = await resolveGatewayConnection({});
|
||||
expect(result.password).toBe("env-pass");
|
||||
});
|
||||
});
|
||||
|
||||
it.runIf(process.platform !== "win32")(
|
||||
"resolves file-backed SecretRef token for local mode",
|
||||
async () => {
|
||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-tui-file-secret-"));
|
||||
const secretFile = path.join(tempDir, "secrets.json");
|
||||
await fs.writeFile(secretFile, JSON.stringify({ gatewayToken: "file-secret-token" }), "utf8");
|
||||
await fs.chmod(secretFile, 0o600);
|
||||
|
||||
loadConfig.mockReturnValue({
|
||||
secrets: {
|
||||
providers: {
|
||||
fileProvider: {
|
||||
source: "file",
|
||||
path: secretFile,
|
||||
mode: "json",
|
||||
allowInsecurePath: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: {
|
||||
token: { source: "file", provider: "fileProvider", id: "/gatewayToken" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await resolveGatewayConnection({});
|
||||
expect(result.token).toBe("file-secret-token");
|
||||
} finally {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("resolves exec-backed SecretRef token for local mode", async () => {
|
||||
const execProgram = [
|
||||
"process.stdout.write(",
|
||||
"JSON.stringify({ protocolVersion: 1, values: { EXEC_GATEWAY_TOKEN: 'exec-secret-token' } })",
|
||||
");",
|
||||
].join("");
|
||||
|
||||
loadConfig.mockReturnValue({
|
||||
secrets: {
|
||||
providers: {
|
||||
execProvider: {
|
||||
source: "exec",
|
||||
command: process.execPath,
|
||||
args: ["-e", execProgram],
|
||||
allowInsecurePath: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: {
|
||||
token: { source: "exec", provider: "execProvider", id: "EXEC_GATEWAY_TOKEN" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const result = await resolveGatewayConnection({});
|
||||
expect(result.token).toBe("exec-secret-token");
|
||||
});
|
||||
|
||||
it("resolves only token SecretRef when gateway.auth.mode is token", async () => {
|
||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-tui-mode-token-"));
|
||||
const tokenMarker = path.join(tempDir, "token-provider-ran");
|
||||
const passwordMarker = path.join(tempDir, "password-provider-ran");
|
||||
const tokenExecProgram = [
|
||||
"const fs=require('node:fs');",
|
||||
`fs.writeFileSync(${JSON.stringify(tokenMarker)},'1');`,
|
||||
"process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { TOKEN_SECRET: 'token-from-exec' } }));",
|
||||
].join("");
|
||||
const passwordExecProgram = [
|
||||
"const fs=require('node:fs');",
|
||||
`fs.writeFileSync(${JSON.stringify(passwordMarker)},'1');`,
|
||||
"process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { PASSWORD_SECRET: 'password-from-exec' } }));",
|
||||
].join("");
|
||||
|
||||
loadConfig.mockReturnValue({
|
||||
secrets: {
|
||||
providers: {
|
||||
tokenProvider: {
|
||||
source: "exec",
|
||||
command: process.execPath,
|
||||
args: ["-e", tokenExecProgram],
|
||||
allowInsecurePath: true,
|
||||
},
|
||||
passwordProvider: {
|
||||
source: "exec",
|
||||
command: process.execPath,
|
||||
args: ["-e", passwordExecProgram],
|
||||
allowInsecurePath: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: {
|
||||
mode: "token",
|
||||
token: { source: "exec", provider: "tokenProvider", id: "TOKEN_SECRET" },
|
||||
password: { source: "exec", provider: "passwordProvider", id: "PASSWORD_SECRET" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await resolveGatewayConnection({});
|
||||
expect(result.token).toBe("token-from-exec");
|
||||
expect(result.password).toBeUndefined();
|
||||
expect(await fileExists(tokenMarker)).toBe(true);
|
||||
expect(await fileExists(passwordMarker)).toBe(false);
|
||||
} finally {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("resolves only password SecretRef when gateway.auth.mode is password", async () => {
|
||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-tui-mode-password-"));
|
||||
const tokenMarker = path.join(tempDir, "token-provider-ran");
|
||||
const passwordMarker = path.join(tempDir, "password-provider-ran");
|
||||
const tokenExecProgram = [
|
||||
"const fs=require('node:fs');",
|
||||
`fs.writeFileSync(${JSON.stringify(tokenMarker)},'1');`,
|
||||
"process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { TOKEN_SECRET: 'token-from-exec' } }));",
|
||||
].join("");
|
||||
const passwordExecProgram = [
|
||||
"const fs=require('node:fs');",
|
||||
`fs.writeFileSync(${JSON.stringify(passwordMarker)},'1');`,
|
||||
"process.stdout.write(JSON.stringify({ protocolVersion: 1, values: { PASSWORD_SECRET: 'password-from-exec' } }));",
|
||||
].join("");
|
||||
|
||||
loadConfig.mockReturnValue({
|
||||
secrets: {
|
||||
providers: {
|
||||
tokenProvider: {
|
||||
source: "exec",
|
||||
command: process.execPath,
|
||||
args: ["-e", tokenExecProgram],
|
||||
allowInsecurePath: true,
|
||||
},
|
||||
passwordProvider: {
|
||||
source: "exec",
|
||||
command: process.execPath,
|
||||
args: ["-e", passwordExecProgram],
|
||||
allowInsecurePath: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: {
|
||||
mode: "password",
|
||||
token: { source: "exec", provider: "tokenProvider", id: "TOKEN_SECRET" },
|
||||
password: { source: "exec", provider: "passwordProvider", id: "PASSWORD_SECRET" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await resolveGatewayConnection({});
|
||||
expect(result.password).toBe("password-from-exec");
|
||||
expect(result.token).toBeUndefined();
|
||||
expect(await fileExists(tokenMarker)).toBe(false);
|
||||
expect(await fileExists(passwordMarker)).toBe(true);
|
||||
} finally {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { hasConfiguredSecretInput } from "../config/types.secrets.js";
|
||||
import { assertExplicitGatewayAuthModeWhenBothConfigured } from "../gateway/auth-mode-policy.js";
|
||||
import {
|
||||
buildGatewayConnectionDetails,
|
||||
ensureExplicitGatewayAuth,
|
||||
@@ -14,6 +16,7 @@ import {
|
||||
type SessionsPatchResult,
|
||||
type SessionsPatchParams,
|
||||
} from "../gateway/protocol/index.js";
|
||||
import { resolveConfiguredSecretInputString } from "../gateway/resolve-configured-secret-input-string.js";
|
||||
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
|
||||
import { VERSION } from "../version.js";
|
||||
import type { ResponseUsageMode, SessionInfo, SessionScope } from "./tui-types.js";
|
||||
@@ -39,6 +42,30 @@ export type GatewayEvent = {
|
||||
seq?: number;
|
||||
};
|
||||
|
||||
type ResolvedGatewayConnection = {
|
||||
url: string;
|
||||
token?: string;
|
||||
password?: string;
|
||||
};
|
||||
|
||||
function trimToUndefined(value: unknown): string | undefined {
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
return trimmed.length > 0 ? trimmed : undefined;
|
||||
}
|
||||
|
||||
function throwGatewayAuthResolutionError(reason: string): never {
|
||||
throw new Error(
|
||||
[
|
||||
reason,
|
||||
"Fix: set OPENCLAW_GATEWAY_TOKEN/OPENCLAW_GATEWAY_PASSWORD, pass --token/--password,",
|
||||
"or resolve the configured secret provider for this credential.",
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
|
||||
export type GatewaySessionList = {
|
||||
ts: number;
|
||||
path: string;
|
||||
@@ -112,18 +139,17 @@ export class GatewayChatClient {
|
||||
onDisconnected?: (reason: string) => void;
|
||||
onGap?: (info: { expected: number; received: number }) => void;
|
||||
|
||||
constructor(opts: GatewayConnectionOptions) {
|
||||
const resolved = resolveGatewayConnection(opts);
|
||||
this.connection = resolved;
|
||||
constructor(connection: ResolvedGatewayConnection) {
|
||||
this.connection = connection;
|
||||
|
||||
this.readyPromise = new Promise((resolve) => {
|
||||
this.resolveReady = resolve;
|
||||
});
|
||||
|
||||
this.client = new GatewayClient({
|
||||
url: resolved.url,
|
||||
token: resolved.token,
|
||||
password: resolved.password,
|
||||
url: connection.url,
|
||||
token: connection.token,
|
||||
password: connection.password,
|
||||
clientName: GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT,
|
||||
clientDisplayName: "openclaw-tui",
|
||||
clientVersion: VERSION,
|
||||
@@ -158,6 +184,11 @@ export class GatewayChatClient {
|
||||
});
|
||||
}
|
||||
|
||||
static async connect(opts: GatewayConnectionOptions): Promise<GatewayChatClient> {
|
||||
const connection = await resolveGatewayConnection(opts);
|
||||
return new GatewayChatClient(connection);
|
||||
}
|
||||
|
||||
start() {
|
||||
this.client.start();
|
||||
}
|
||||
@@ -234,11 +265,16 @@ export class GatewayChatClient {
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveGatewayConnection(opts: GatewayConnectionOptions) {
|
||||
export async function resolveGatewayConnection(
|
||||
opts: GatewayConnectionOptions,
|
||||
): Promise<ResolvedGatewayConnection> {
|
||||
const config = loadConfig();
|
||||
const env = process.env;
|
||||
const gatewayAuthMode = config.gateway?.auth?.mode;
|
||||
const isRemoteMode = config.gateway?.mode === "remote";
|
||||
const remote = isRemoteMode ? config.gateway?.remote : undefined;
|
||||
const authToken = config.gateway?.auth?.token;
|
||||
const remote = config.gateway?.remote;
|
||||
const envToken = trimToUndefined(env.OPENCLAW_GATEWAY_TOKEN);
|
||||
const envPassword = trimToUndefined(env.OPENCLAW_GATEWAY_PASSWORD);
|
||||
|
||||
const urlOverride =
|
||||
typeof opts.url === "string" && opts.url.trim().length > 0 ? opts.url.trim() : undefined;
|
||||
@@ -254,27 +290,152 @@ export function resolveGatewayConnection(opts: GatewayConnectionOptions) {
|
||||
...(urlOverride ? { url: urlOverride } : {}),
|
||||
}).url;
|
||||
|
||||
const token =
|
||||
explicitAuth.token ||
|
||||
(!urlOverride
|
||||
? isRemoteMode
|
||||
? typeof remote?.token === "string" && remote.token.trim().length > 0
|
||||
? remote.token.trim()
|
||||
: undefined
|
||||
: process.env.OPENCLAW_GATEWAY_TOKEN?.trim() ||
|
||||
(typeof authToken === "string" && authToken.trim().length > 0
|
||||
? authToken.trim()
|
||||
: undefined)
|
||||
: undefined);
|
||||
if (urlOverride) {
|
||||
return {
|
||||
url,
|
||||
token: explicitAuth.token,
|
||||
password: explicitAuth.password,
|
||||
};
|
||||
}
|
||||
|
||||
const password =
|
||||
explicitAuth.password ||
|
||||
(!urlOverride
|
||||
? process.env.OPENCLAW_GATEWAY_PASSWORD?.trim() ||
|
||||
(typeof remote?.password === "string" && remote.password.trim().length > 0
|
||||
? remote.password.trim()
|
||||
: undefined)
|
||||
: undefined);
|
||||
if (isRemoteMode) {
|
||||
const remoteToken = explicitAuth.token
|
||||
? { value: explicitAuth.token }
|
||||
: await resolveConfiguredSecretInputString({
|
||||
value: remote?.token,
|
||||
path: "gateway.remote.token",
|
||||
env,
|
||||
config,
|
||||
});
|
||||
const remotePassword =
|
||||
explicitAuth.password || envPassword
|
||||
? { value: explicitAuth.password ?? envPassword }
|
||||
: await resolveConfiguredSecretInputString({
|
||||
value: remote?.password,
|
||||
path: "gateway.remote.password",
|
||||
env,
|
||||
config,
|
||||
});
|
||||
|
||||
return { url, token, password };
|
||||
const token = explicitAuth.token ?? remoteToken.value;
|
||||
const password = explicitAuth.password ?? envPassword ?? remotePassword.value;
|
||||
if (!token && !password) {
|
||||
throwGatewayAuthResolutionError(
|
||||
remoteToken.unresolvedRefReason ??
|
||||
remotePassword.unresolvedRefReason ??
|
||||
"Missing gateway auth credentials.",
|
||||
);
|
||||
}
|
||||
return { url, token, password };
|
||||
}
|
||||
|
||||
if (gatewayAuthMode === "none" || gatewayAuthMode === "trusted-proxy") {
|
||||
return {
|
||||
url,
|
||||
token: explicitAuth.token ?? envToken,
|
||||
password: explicitAuth.password ?? envPassword,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
assertExplicitGatewayAuthModeWhenBothConfigured(config);
|
||||
} catch (err) {
|
||||
throwGatewayAuthResolutionError(err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
|
||||
const defaults = config.secrets?.defaults;
|
||||
const hasConfiguredToken = hasConfiguredSecretInput(config.gateway?.auth?.token, defaults);
|
||||
const hasConfiguredPassword = hasConfiguredSecretInput(config.gateway?.auth?.password, defaults);
|
||||
if (gatewayAuthMode === "password") {
|
||||
const localPassword =
|
||||
explicitAuth.password || envPassword
|
||||
? { value: explicitAuth.password ?? envPassword }
|
||||
: await resolveConfiguredSecretInputString({
|
||||
value: config.gateway?.auth?.password,
|
||||
path: "gateway.auth.password",
|
||||
env,
|
||||
config,
|
||||
});
|
||||
const password = explicitAuth.password ?? envPassword ?? localPassword.value;
|
||||
if (!password) {
|
||||
throwGatewayAuthResolutionError(
|
||||
localPassword.unresolvedRefReason ?? "Missing gateway auth password.",
|
||||
);
|
||||
}
|
||||
return {
|
||||
url,
|
||||
token: explicitAuth.token ?? envToken,
|
||||
password,
|
||||
};
|
||||
}
|
||||
|
||||
if (gatewayAuthMode === "token") {
|
||||
const localToken =
|
||||
explicitAuth.token || envToken
|
||||
? { value: explicitAuth.token ?? envToken }
|
||||
: await resolveConfiguredSecretInputString({
|
||||
value: config.gateway?.auth?.token,
|
||||
path: "gateway.auth.token",
|
||||
env,
|
||||
config,
|
||||
});
|
||||
const token = explicitAuth.token ?? envToken ?? localToken.value;
|
||||
if (!token) {
|
||||
throwGatewayAuthResolutionError(
|
||||
localToken.unresolvedRefReason ?? "Missing gateway auth token.",
|
||||
);
|
||||
}
|
||||
return {
|
||||
url,
|
||||
token,
|
||||
password: explicitAuth.password ?? envPassword,
|
||||
};
|
||||
}
|
||||
|
||||
const passwordCandidate = explicitAuth.password ?? envPassword;
|
||||
const shouldUsePassword =
|
||||
Boolean(passwordCandidate) || (hasConfiguredPassword && !hasConfiguredToken);
|
||||
|
||||
if (shouldUsePassword) {
|
||||
const localPassword = passwordCandidate
|
||||
? { value: passwordCandidate }
|
||||
: await resolveConfiguredSecretInputString({
|
||||
value: config.gateway?.auth?.password,
|
||||
path: "gateway.auth.password",
|
||||
env,
|
||||
config,
|
||||
});
|
||||
const password = passwordCandidate ?? localPassword.value;
|
||||
if (!password) {
|
||||
throwGatewayAuthResolutionError(
|
||||
localPassword.unresolvedRefReason ?? "Missing gateway auth password.",
|
||||
);
|
||||
}
|
||||
return {
|
||||
url,
|
||||
token: explicitAuth.token ?? envToken,
|
||||
password,
|
||||
};
|
||||
}
|
||||
|
||||
const localToken =
|
||||
explicitAuth.token || envToken
|
||||
? { value: explicitAuth.token ?? envToken }
|
||||
: await resolveConfiguredSecretInputString({
|
||||
value: config.gateway?.auth?.token,
|
||||
path: "gateway.auth.token",
|
||||
env,
|
||||
config,
|
||||
});
|
||||
const token = explicitAuth.token ?? envToken ?? localToken.value;
|
||||
if (!token) {
|
||||
throwGatewayAuthResolutionError(
|
||||
localToken.unresolvedRefReason ?? "Missing gateway auth token.",
|
||||
);
|
||||
}
|
||||
return {
|
||||
url,
|
||||
token,
|
||||
password: explicitAuth.password ?? envPassword,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -471,7 +471,7 @@ export async function runTui(opts: TuiOptions) {
|
||||
localRunIds.clear();
|
||||
};
|
||||
|
||||
const client = new GatewayChatClient({
|
||||
const client = await GatewayChatClient.connect({
|
||||
url: opts.url,
|
||||
token: opts.token,
|
||||
password: opts.password,
|
||||
|
||||
Reference in New Issue
Block a user