mirror of
http://192.168.0.88:13333/lywsvip/openclaw-zero-token.git
synced 2026-05-22 15:21:43 +08:00
Major upgrade from e26988a38 to upstream v2026.3.28 (f9b107928).
Key changes:
- Upstream src/, ui/, extensions/ (89 bundled extensions)
- Zero-token web providers preserved in src/zero-token/
- AskOnce plugin restored and registered as CLI command
- Added missing packages: @anthropic-ai/vertex-sdk, @modelcontextprotocol/sdk
- Fixed tsconfig rootDir, skipLibCheck for plugin-sdk DTS build
- Added askonce to bundled plugin metadata and package.json exports
- Fixed AskOnce CLI command registration (missing commands metadata)
- Restored AskOnce adapter imports (correct 5-level relative paths)
- Removed stale migration artifacts from root directory
124 lines
3.3 KiB
TypeScript
124 lines
3.3 KiB
TypeScript
import os from "node:os";
|
|
import path from "node:path";
|
|
import { expect } from "vitest";
|
|
import { WebSocket } from "ws";
|
|
import {
|
|
loadOrCreateDeviceIdentity,
|
|
publicKeyRawBase64UrlFromPem,
|
|
type DeviceIdentity,
|
|
} from "../infra/device-identity.js";
|
|
import {
|
|
approveDevicePairing,
|
|
getPairedDevice,
|
|
requestDevicePairing,
|
|
rotateDeviceToken,
|
|
} from "../infra/device-pairing.js";
|
|
import { trackConnectChallengeNonce } from "./test-helpers.js";
|
|
|
|
export function resolveDeviceIdentityPath(name: string): string {
|
|
const root = process.env.OPENCLAW_STATE_DIR ?? process.env.HOME ?? os.tmpdir();
|
|
return path.join(root, "test-device-identities", `${name}.json`);
|
|
}
|
|
|
|
export function loadDeviceIdentity(name: string): {
|
|
identityPath: string;
|
|
identity: DeviceIdentity;
|
|
publicKey: string;
|
|
} {
|
|
const identityPath = resolveDeviceIdentityPath(name);
|
|
const identity = loadOrCreateDeviceIdentity(identityPath);
|
|
return {
|
|
identityPath,
|
|
identity,
|
|
publicKey: publicKeyRawBase64UrlFromPem(identity.publicKeyPem),
|
|
};
|
|
}
|
|
|
|
export async function pairDeviceIdentity(params: {
|
|
name: string;
|
|
role: "node" | "operator";
|
|
scopes: string[];
|
|
clientId?: string;
|
|
clientMode?: string;
|
|
}): Promise<{
|
|
identityPath: string;
|
|
identity: DeviceIdentity;
|
|
publicKey: string;
|
|
}> {
|
|
const loaded = loadDeviceIdentity(params.name);
|
|
const request = await requestDevicePairing({
|
|
deviceId: loaded.identity.deviceId,
|
|
publicKey: loaded.publicKey,
|
|
role: params.role,
|
|
scopes: params.scopes,
|
|
clientId: params.clientId,
|
|
clientMode: params.clientMode,
|
|
});
|
|
await approveDevicePairing(request.request.requestId, {
|
|
callerScopes: params.scopes,
|
|
});
|
|
return loaded;
|
|
}
|
|
|
|
export async function issueOperatorToken(params: {
|
|
name: string;
|
|
approvedScopes: string[];
|
|
tokenScopes?: string[];
|
|
clientId?: string;
|
|
clientMode?: string;
|
|
}): Promise<{
|
|
deviceId: string;
|
|
identityPath: string;
|
|
token: string;
|
|
}> {
|
|
const paired = await pairDeviceIdentity({
|
|
name: params.name,
|
|
role: "operator",
|
|
scopes: params.approvedScopes,
|
|
clientId: params.clientId,
|
|
clientMode: params.clientMode,
|
|
});
|
|
if (params.tokenScopes) {
|
|
const rotated = await rotateDeviceToken({
|
|
deviceId: paired.identity.deviceId,
|
|
role: "operator",
|
|
scopes: params.tokenScopes,
|
|
});
|
|
expect(rotated.ok).toBe(true);
|
|
const token = rotated.ok ? rotated.entry.token : "";
|
|
expect(token).toBeTruthy();
|
|
return {
|
|
deviceId: paired.identity.deviceId,
|
|
identityPath: paired.identityPath,
|
|
token,
|
|
};
|
|
}
|
|
|
|
const device = await getPairedDevice(paired.identity.deviceId);
|
|
const token = device?.tokens?.operator?.token ?? "";
|
|
expect(token).toBeTruthy();
|
|
expect(device?.approvedScopes).toEqual(params.approvedScopes);
|
|
return {
|
|
deviceId: paired.identity.deviceId,
|
|
identityPath: paired.identityPath,
|
|
token,
|
|
};
|
|
}
|
|
|
|
export async function openTrackedWs(port: number): Promise<WebSocket> {
|
|
const ws = new WebSocket(`ws://127.0.0.1:${port}`);
|
|
trackConnectChallengeNonce(ws);
|
|
await new Promise<void>((resolve, reject) => {
|
|
const timer = setTimeout(() => reject(new Error("timeout waiting for ws open")), 5_000);
|
|
ws.once("open", () => {
|
|
clearTimeout(timer);
|
|
resolve();
|
|
});
|
|
ws.once("error", (error) => {
|
|
clearTimeout(timer);
|
|
reject(error);
|
|
});
|
|
});
|
|
return ws;
|
|
}
|