Compare commits
No commits in common. "466a4303b247f99eb444a74b014c40b148974e02" and "f7f385321f401c2996e98a81746c00eb55a11d8d" have entirely different histories.
466a4303b2
...
f7f385321f
|
|
@ -108,6 +108,8 @@ export async function refreshAccessToken(
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheFile = getCacheFile(config.authBase, config.clientKey, config.authCacheDir);
|
const cacheFile = getCacheFile(config.authBase, config.clientKey, config.authCacheDir);
|
||||||
|
|
||||||
|
// Remove cache file if exists
|
||||||
deleteCache(cacheFile);
|
deleteCache(cacheFile);
|
||||||
|
|
||||||
return getAccessToken(dryRun, config);
|
return getAccessToken(dryRun, config);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import type { ApiResponse, ClientConfig, EnvConfig, HttpMethod, SessionResponse } from './types.js';
|
import type { ApiResponse, ClientConfig, EnvConfig, HttpMethod, SessionResponse } from './types.js';
|
||||||
import { requestApi } from './http.js';
|
import { requestApi } from './http.js';
|
||||||
import { getCacheFile, readCachedToken, writeCache, deleteCache } from './cache.js';
|
import { getCacheFile, readCachedToken, writeCache, deleteCache } from './cache.js';
|
||||||
import { loadGlobalEnv, reloadGlobalEnv } from './env.js';
|
import { loadGlobalEnv } from './env.js';
|
||||||
|
|
||||||
const SESSION_RETRYABLE_STATUS = new Set([401, 403]);
|
const SESSION_RETRYABLE_STATUS = new Set([401, 403]);
|
||||||
const SESSION_RETRYABLE_BODY_MARKERS = [
|
const SESSION_RETRYABLE_BODY_MARKERS = [
|
||||||
|
|
@ -28,6 +28,7 @@ export interface SkillClientOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildConfig(options: SkillClientOptions): EnvConfig {
|
function buildConfig(options: SkillClientOptions): EnvConfig {
|
||||||
|
loadGlobalEnv();
|
||||||
return {
|
return {
|
||||||
authBase: (options.authBase || process.env.AUTH_BASE || 'https://api-gw-test.yuanwei-lnc.com').replace(/\/$/, ''),
|
authBase: (options.authBase || process.env.AUTH_BASE || 'https://api-gw-test.yuanwei-lnc.com').replace(/\/$/, ''),
|
||||||
clientKey: options.clientKey || process.env.CLIENT_KEY || '',
|
clientKey: options.clientKey || process.env.CLIENT_KEY || '',
|
||||||
|
|
@ -43,20 +44,12 @@ function isRetryable(response: ApiResponse): boolean {
|
||||||
return SESSION_RETRYABLE_BODY_MARKERS.some((m) => body.includes(m));
|
return SESSION_RETRYABLE_BODY_MARKERS.some((m) => body.includes(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
function isKeyError(response: ApiResponse): boolean {
|
|
||||||
const body = (response.body || '').toLowerCase();
|
|
||||||
return body.includes('client key revoked') || body.includes('client key expired');
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SkillClient {
|
export class SkillClient {
|
||||||
private config: EnvConfig;
|
private readonly config: EnvConfig;
|
||||||
private apiBase: string;
|
private readonly apiBase: string;
|
||||||
private readonly dryRun: boolean;
|
private readonly dryRun: boolean;
|
||||||
private readonly options: SkillClientOptions;
|
|
||||||
|
|
||||||
constructor(options: SkillClientOptions = {}) {
|
constructor(options: SkillClientOptions = {}) {
|
||||||
this.options = options;
|
|
||||||
loadGlobalEnv();
|
|
||||||
this.config = buildConfig(options);
|
this.config = buildConfig(options);
|
||||||
this.apiBase = (options.apiBase || process.env.ECOM_BASE || this.config.authBase).replace(/\/$/, '');
|
this.apiBase = (options.apiBase || process.env.ECOM_BASE || this.config.authBase).replace(/\/$/, '');
|
||||||
this.dryRun = options.dryRun ?? false;
|
this.dryRun = options.dryRun ?? false;
|
||||||
|
|
@ -125,7 +118,6 @@ export class SkillClient {
|
||||||
const url = `${this.apiBase}${path}`;
|
const url = `${this.apiBase}${path}`;
|
||||||
const bodyStr = body != null ? JSON.stringify(body) : undefined;
|
const bodyStr = body != null ? JSON.stringify(body) : undefined;
|
||||||
|
|
||||||
// 1. Try with cached or fresh token
|
|
||||||
const token = await this.getToken();
|
const token = await this.getToken();
|
||||||
const first = await requestApi(method, url, token, bodyStr);
|
const first = await requestApi(method, url, token, bodyStr);
|
||||||
|
|
||||||
|
|
@ -133,18 +125,9 @@ export class SkillClient {
|
||||||
return first;
|
return first;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Token rejected — clear cache, fetch new session with same key
|
// Token expired — refresh and retry once
|
||||||
const freshToken = await this.refreshToken();
|
const freshToken = await this.refreshToken();
|
||||||
const second = await requestApi(method, url, freshToken, bodyStr);
|
return requestApi(method, url, freshToken, bodyStr);
|
||||||
|
|
||||||
if (!isRetryable(second)) {
|
|
||||||
return second;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Still failing — maybe CLIENT_KEY changed in .env, reload and retry
|
|
||||||
this.reloadConfig();
|
|
||||||
const reloadedToken = await this.refreshToken();
|
|
||||||
return requestApi(method, url, reloadedToken, bodyStr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getToken(): Promise<string> {
|
private async getToken(): Promise<string> {
|
||||||
|
|
@ -166,23 +149,6 @@ export class SkillClient {
|
||||||
return session.accessToken;
|
return session.accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Re-read ~/.openclaw/.env and rebuild config.
|
|
||||||
* Called when auth keeps failing — the CLIENT_KEY may have been updated on disk.
|
|
||||||
*/
|
|
||||||
private reloadConfig(): void {
|
|
||||||
reloadGlobalEnv();
|
|
||||||
const oldKey = this.config.clientKey;
|
|
||||||
this.config = buildConfig(this.options);
|
|
||||||
this.apiBase = (this.options.apiBase || process.env.ECOM_BASE || this.config.authBase).replace(/\/$/, '');
|
|
||||||
|
|
||||||
if (this.config.clientKey !== oldKey) {
|
|
||||||
// Clear old key's cache too
|
|
||||||
const oldCacheFile = getCacheFile(this.config.authBase, oldKey, this.config.authCacheDir);
|
|
||||||
deleteCache(oldCacheFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async fetchSession(): Promise<SessionResponse> {
|
private async fetchSession(): Promise<SessionResponse> {
|
||||||
const result = await requestApi(
|
const result = await requestApi(
|
||||||
'POST',
|
'POST',
|
||||||
|
|
|
||||||
21
src/env.ts
21
src/env.ts
|
|
@ -4,33 +4,15 @@ import * as os from 'os';
|
||||||
|
|
||||||
const GLOBAL_ENV_PATH = path.join(os.homedir(), '.openclaw', '.env');
|
const GLOBAL_ENV_PATH = path.join(os.homedir(), '.openclaw', '.env');
|
||||||
|
|
||||||
/** Keys that were loaded from .env (not pre-existing in process.env) */
|
|
||||||
const loadedKeys = new Set<string>();
|
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load ~/.openclaw/.env into process.env (once, won't overwrite explicitly set env vars).
|
* Load ~/.openclaw/.env into process.env (once, won't overwrite existing vars).
|
||||||
*/
|
*/
|
||||||
export function loadGlobalEnv(): void {
|
export function loadGlobalEnv(): void {
|
||||||
if (loaded) return;
|
if (loaded) return;
|
||||||
loaded = true;
|
loaded = true;
|
||||||
applyEnvFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Force re-read ~/.openclaw/.env. Overwrites keys that were originally loaded
|
|
||||||
* from .env, but still won't touch keys set externally (e.g. shell export).
|
|
||||||
*/
|
|
||||||
export function reloadGlobalEnv(): void {
|
|
||||||
// Clear values we previously loaded so applyEnvFile can overwrite them
|
|
||||||
for (const key of loadedKeys) {
|
|
||||||
delete process.env[key];
|
|
||||||
}
|
|
||||||
loadedKeys.clear();
|
|
||||||
applyEnvFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyEnvFile(): void {
|
|
||||||
let content: string;
|
let content: string;
|
||||||
try {
|
try {
|
||||||
content = fs.readFileSync(GLOBAL_ENV_PATH, 'utf-8');
|
content = fs.readFileSync(GLOBAL_ENV_PATH, 'utf-8');
|
||||||
|
|
@ -56,7 +38,6 @@ function applyEnvFile(): void {
|
||||||
// don't overwrite explicitly set env vars
|
// don't overwrite explicitly set env vars
|
||||||
if (process.env[key] === undefined) {
|
if (process.env[key] === undefined) {
|
||||||
process.env[key] = value;
|
process.env[key] = value;
|
||||||
loadedKeys.add(key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue