@rethinkingstudio/clawpilot 2.0.0-beta.0 → 2.0.0
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/dist/commands/pair.js +3 -5
- package/dist/commands/pair.js.map +1 -1
- package/dist/platform/service-manager.js +9 -13
- package/dist/platform/service-manager.js.map +1 -1
- package/package.json +7 -1
- package/rethinkingstudio-clawpilot-1.1.17.tgz +0 -0
- package/src/commands/assistant-send.ts +0 -91
- package/src/commands/install.ts +0 -131
- package/src/commands/local-handlers.ts +0 -533
- package/src/commands/openclaw-cli.ts +0 -354
- package/src/commands/pair.ts +0 -128
- package/src/commands/provider-config.ts +0 -275
- package/src/commands/provider-handlers.ts +0 -184
- package/src/commands/provider-registry.ts +0 -138
- package/src/commands/run.ts +0 -34
- package/src/commands/send.ts +0 -42
- package/src/commands/session-key.ts +0 -77
- package/src/commands/set-token.ts +0 -45
- package/src/commands/status.ts +0 -157
- package/src/commands/upload.ts +0 -141
- package/src/config/config.ts +0 -137
- package/src/generated/build-config.ts +0 -1
- package/src/i18n/index.ts +0 -185
- package/src/index.ts +0 -166
- package/src/media/assistant-attachments.ts +0 -205
- package/src/media/oss-uploader.ts +0 -306
- package/src/platform/service-manager.ts +0 -919
- package/src/relay/gateway-client.ts +0 -359
- package/src/relay/reconnect.ts +0 -37
- package/src/relay/relay-manager.ts +0 -328
- package/test-chat.mjs +0 -64
- package/test-direct.mjs +0 -171
- package/tsconfig.json +0 -16
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { execOpenclaw } from "./openclaw-cli.js";
|
|
2
|
-
|
|
3
|
-
type StatusSession = {
|
|
4
|
-
agentId?: string;
|
|
5
|
-
key?: string;
|
|
6
|
-
kind?: string;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
type StatusPayload = {
|
|
10
|
-
sessions?: {
|
|
11
|
-
recent?: StatusSession[];
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
function isDirectMainSession(session: StatusSession | undefined): session is StatusSession & { key: string } {
|
|
16
|
-
return Boolean(
|
|
17
|
-
session?.key &&
|
|
18
|
-
session.kind === "direct" &&
|
|
19
|
-
!session.key.includes(":cron:")
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function extractJsonPayload(raw: string): string {
|
|
24
|
-
const trimmed = raw.trim();
|
|
25
|
-
if (!trimmed) {
|
|
26
|
-
throw new Error("openclaw status returned empty output");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
JSON.parse(trimmed);
|
|
31
|
-
return trimmed;
|
|
32
|
-
} catch {
|
|
33
|
-
// Fall through and try to extract the trailing JSON payload from noisy output.
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const firstObjectIndex = trimmed.indexOf("{");
|
|
37
|
-
const firstArrayIndex = trimmed.indexOf("[");
|
|
38
|
-
const startCandidates = [firstObjectIndex, firstArrayIndex].filter(index => index >= 0);
|
|
39
|
-
const startIndex = startCandidates.length > 0 ? Math.min(...startCandidates) : -1;
|
|
40
|
-
if (startIndex < 0) {
|
|
41
|
-
throw new Error(`openclaw status did not return JSON: ${trimmed}`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
for (let index = startIndex; index < trimmed.length; index += 1) {
|
|
45
|
-
const ch = trimmed[index];
|
|
46
|
-
if (ch !== "{" && ch !== "[") continue;
|
|
47
|
-
const candidate = trimmed.slice(index);
|
|
48
|
-
try {
|
|
49
|
-
JSON.parse(candidate);
|
|
50
|
-
return candidate;
|
|
51
|
-
} catch {
|
|
52
|
-
// Keep scanning for the real JSON start.
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
throw new Error(`openclaw status returned invalid JSON: ${trimmed}`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function parseStatus(): StatusPayload {
|
|
60
|
-
const raw = execOpenclaw("status --json").toString("utf8").trim();
|
|
61
|
-
return JSON.parse(extractJsonPayload(raw)) as StatusPayload;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function resolveSessionKeyFromStatus(): string {
|
|
65
|
-
const status = parseStatus();
|
|
66
|
-
const sessions = status.sessions;
|
|
67
|
-
if (!sessions) {
|
|
68
|
-
throw new Error("openclaw status did not include sessions");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const fallback = sessions.recent?.find(isDirectMainSession);
|
|
72
|
-
if (fallback?.key) {
|
|
73
|
-
return fallback.key;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
throw new Error("Could not resolve a recent direct session key from openclaw status");
|
|
77
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import readline from "readline";
|
|
2
|
-
import { readConfig, writeConfig } from "../config/config.js";
|
|
3
|
-
import { t } from "../i18n/index.js";
|
|
4
|
-
|
|
5
|
-
export async function setTokenCommand(): Promise<void> {
|
|
6
|
-
// Require pairing to be done first
|
|
7
|
-
let config;
|
|
8
|
-
try {
|
|
9
|
-
config = readConfig();
|
|
10
|
-
} catch {
|
|
11
|
-
console.error(t("setToken.noPairing"));
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Tell the user where to find the token
|
|
16
|
-
console.log(t("setToken.whereToFind"));
|
|
17
|
-
console.log(t("setToken.option1"));
|
|
18
|
-
console.log(t("setToken.option2"));
|
|
19
|
-
console.log(t("setToken.option2cmd"));
|
|
20
|
-
console.log(t("setToken.option3"));
|
|
21
|
-
|
|
22
|
-
// Prompt for the token
|
|
23
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
24
|
-
const token = await new Promise<string>((resolve) => {
|
|
25
|
-
rl.question(t("setToken.prompt"), (answer) => {
|
|
26
|
-
rl.close();
|
|
27
|
-
resolve(answer.trim());
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
// Save
|
|
32
|
-
if (token) {
|
|
33
|
-
config.gatewayToken = token;
|
|
34
|
-
delete config.gatewayPassword;
|
|
35
|
-
writeConfig(config);
|
|
36
|
-
console.log(t("setToken.saved"));
|
|
37
|
-
} else {
|
|
38
|
-
delete config.gatewayToken;
|
|
39
|
-
delete config.gatewayPassword;
|
|
40
|
-
writeConfig(config);
|
|
41
|
-
console.log(t("setToken.cleared"));
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
console.log(t("setToken.restart"));
|
|
45
|
-
}
|
package/src/commands/status.ts
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "fs";
|
|
2
|
-
import { configExists, readConfig, readGatewayUrl } from "../config/config.js";
|
|
3
|
-
import { t } from "../i18n/index.js";
|
|
4
|
-
import { getServiceStatus } from "../platform/service-manager.js";
|
|
5
|
-
|
|
6
|
-
type HealthState = {
|
|
7
|
-
kind: "ok" | "warn" | "error" | "unknown";
|
|
8
|
-
detail?: string;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export function statusCommand(): void {
|
|
12
|
-
console.log(t("status.title"));
|
|
13
|
-
|
|
14
|
-
if (!configExists()) {
|
|
15
|
-
console.log(t("status.notPaired"));
|
|
16
|
-
} else {
|
|
17
|
-
try {
|
|
18
|
-
const config = readConfig();
|
|
19
|
-
console.log(t("status.paired"));
|
|
20
|
-
console.log(t("status.displayName", config.displayName));
|
|
21
|
-
console.log(t("status.gatewayId", config.gatewayId));
|
|
22
|
-
console.log(t("status.relayServer", config.relayServerUrl));
|
|
23
|
-
} catch {
|
|
24
|
-
console.log(t("status.configCorrupted"));
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
console.log(t("status.gateway", readGatewayUrl()));
|
|
29
|
-
|
|
30
|
-
const service = getServiceStatus();
|
|
31
|
-
if (service.platform === "unsupported") {
|
|
32
|
-
console.log(t("status.serviceUnsupported", process.platform));
|
|
33
|
-
console.log("");
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
console.log(t("status.servicePlatform", service.manager));
|
|
38
|
-
|
|
39
|
-
if (!service.installed) {
|
|
40
|
-
console.log(t("status.serviceNotInstalled"));
|
|
41
|
-
} else if (service.running) {
|
|
42
|
-
console.log(t("status.serviceRunning", service.manager));
|
|
43
|
-
console.log(t("status.serviceLog", service.logPath));
|
|
44
|
-
const health = readHealth(service.logPath);
|
|
45
|
-
console.log(formatHealthLine("status.relayHealth", health.relay));
|
|
46
|
-
console.log(formatHealthLine("status.gatewayHealth", health.gateway));
|
|
47
|
-
} else {
|
|
48
|
-
console.log(t("status.serviceNotRunning"));
|
|
49
|
-
if (service.servicePath) {
|
|
50
|
-
console.log(t("status.serviceFile", service.servicePath));
|
|
51
|
-
}
|
|
52
|
-
if (service.startHint) {
|
|
53
|
-
console.log(t("status.serviceStart", service.startHint));
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (service.autoStartHint) {
|
|
57
|
-
console.log(t("status.serviceAutoStartHint", service.autoStartHint));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
console.log("");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function readHealth(logPath: string): { relay: HealthState; gateway: HealthState } {
|
|
64
|
-
if (!existsSync(logPath)) {
|
|
65
|
-
return {
|
|
66
|
-
relay: { kind: "unknown", detail: "log missing" },
|
|
67
|
-
gateway: { kind: "unknown", detail: "log missing" },
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const lines = readTailLines(logPath, 400);
|
|
72
|
-
return {
|
|
73
|
-
relay: parseRelayHealth(lines),
|
|
74
|
-
gateway: parseGatewayHealth(lines),
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function readTailLines(path: string, maxLines: number): string[] {
|
|
79
|
-
const raw = readFileSync(path, "utf-8");
|
|
80
|
-
const lines = raw.split(/\r?\n/).filter(Boolean);
|
|
81
|
-
return lines.slice(-maxLines);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function parseRelayHealth(lines: string[]): HealthState {
|
|
85
|
-
const connectedIndex = findLastIndex(lines, (line) => line.includes("Relay connected."));
|
|
86
|
-
const disconnectedIndex = findLastIndex(
|
|
87
|
-
lines,
|
|
88
|
-
(line) => line.includes("Relay disconnected.") || line.includes("Relay connection closed:")
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
if (connectedIndex === -1 && disconnectedIndex === -1) {
|
|
92
|
-
return { kind: "unknown", detail: "no relay events yet" };
|
|
93
|
-
}
|
|
94
|
-
if (connectedIndex > disconnectedIndex) {
|
|
95
|
-
return { kind: "ok", detail: "connected" };
|
|
96
|
-
}
|
|
97
|
-
const line = disconnectedIndex >= 0 ? lines[disconnectedIndex] : "";
|
|
98
|
-
const detail = line.includes("Relay connection closed:")
|
|
99
|
-
? line.replace(/^.*Relay connection closed:\s*/, "").trim()
|
|
100
|
-
: "disconnected";
|
|
101
|
-
return { kind: "error", detail };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function parseGatewayHealth(lines: string[]): HealthState {
|
|
105
|
-
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
106
|
-
const line = lines[i];
|
|
107
|
-
if (line.includes("Gateway connected.")) {
|
|
108
|
-
return { kind: "ok", detail: "connected" };
|
|
109
|
-
}
|
|
110
|
-
if (line.includes("Gateway disconnected:")) {
|
|
111
|
-
return {
|
|
112
|
-
kind: classifyGatewayDetail(line),
|
|
113
|
-
detail: line.replace(/^.*Gateway disconnected:\s*/, "").trim(),
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
if (line.includes("[gateway-client] connect failed:")) {
|
|
117
|
-
return {
|
|
118
|
-
kind: "error",
|
|
119
|
-
detail: line.replace(/^.*connect failed:\s*/, "").trim(),
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
if (line.includes("[gateway-client] ws error:")) {
|
|
123
|
-
return {
|
|
124
|
-
kind: "error",
|
|
125
|
-
detail: line.replace(/^.*ws error:\s*/, "").trim(),
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return { kind: "unknown", detail: "no gateway events yet" };
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function classifyGatewayDetail(line: string): HealthState["kind"] {
|
|
133
|
-
const detail = line.toLowerCase();
|
|
134
|
-
if (detail.includes("unauthorized") || detail.includes("mismatch") || detail.includes("not allowed")) {
|
|
135
|
-
return "error";
|
|
136
|
-
}
|
|
137
|
-
if (detail.includes("tick timeout") || detail.includes("service restart")) {
|
|
138
|
-
return "warn";
|
|
139
|
-
}
|
|
140
|
-
return "warn";
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function findLastIndex(lines: string[], predicate: (line: string) => boolean): number {
|
|
144
|
-
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
145
|
-
if (predicate(lines[i])) return i;
|
|
146
|
-
}
|
|
147
|
-
return -1;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function formatHealthLine(key: string, state: HealthState): string {
|
|
151
|
-
const icon =
|
|
152
|
-
state.kind === "ok" ? "✓"
|
|
153
|
-
: state.kind === "warn" ? "⚠"
|
|
154
|
-
: state.kind === "error" ? "✗"
|
|
155
|
-
: "-";
|
|
156
|
-
return t(key, icon, state.detail ?? "");
|
|
157
|
-
}
|
package/src/commands/upload.ts
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { readFile, stat } from "fs/promises";
|
|
2
|
-
import { basename, extname, resolve } from "path";
|
|
3
|
-
import { readConfig } from "../config/config.js";
|
|
4
|
-
import { t } from "../i18n/index.js";
|
|
5
|
-
import { uploadMedia, type StsCredentials } from "../media/oss-uploader.js";
|
|
6
|
-
|
|
7
|
-
export interface UploadCommandOptions {
|
|
8
|
-
json?: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface UploadedFile {
|
|
12
|
-
path: string;
|
|
13
|
-
fileName: string;
|
|
14
|
-
mimeType: string;
|
|
15
|
-
url: string;
|
|
16
|
-
thumbnailUrl: string | null;
|
|
17
|
-
size: number;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function mimeTypeFromPath(path: string): string {
|
|
21
|
-
switch (extname(path).toLowerCase()) {
|
|
22
|
-
case ".jpg":
|
|
23
|
-
case ".jpeg":
|
|
24
|
-
return "image/jpeg";
|
|
25
|
-
case ".png":
|
|
26
|
-
return "image/png";
|
|
27
|
-
case ".gif":
|
|
28
|
-
return "image/gif";
|
|
29
|
-
case ".webp":
|
|
30
|
-
return "image/webp";
|
|
31
|
-
case ".mp4":
|
|
32
|
-
return "video/mp4";
|
|
33
|
-
case ".mov":
|
|
34
|
-
return "video/quicktime";
|
|
35
|
-
case ".pdf":
|
|
36
|
-
return "application/pdf";
|
|
37
|
-
case ".txt":
|
|
38
|
-
return "text/plain";
|
|
39
|
-
case ".md":
|
|
40
|
-
return "text/markdown";
|
|
41
|
-
case ".json":
|
|
42
|
-
return "application/json";
|
|
43
|
-
case ".csv":
|
|
44
|
-
return "text/csv";
|
|
45
|
-
case ".xls":
|
|
46
|
-
return "application/vnd.ms-excel";
|
|
47
|
-
case ".xlsx":
|
|
48
|
-
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
49
|
-
case ".doc":
|
|
50
|
-
return "application/msword";
|
|
51
|
-
case ".docx":
|
|
52
|
-
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
53
|
-
case ".ppt":
|
|
54
|
-
return "application/vnd.ms-powerpoint";
|
|
55
|
-
case ".pptx":
|
|
56
|
-
return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
|
57
|
-
case ".mp3":
|
|
58
|
-
return "audio/mpeg";
|
|
59
|
-
case ".m4a":
|
|
60
|
-
return "audio/mp4";
|
|
61
|
-
case ".wav":
|
|
62
|
-
return "audio/wav";
|
|
63
|
-
case ".aac":
|
|
64
|
-
return "audio/aac";
|
|
65
|
-
default:
|
|
66
|
-
return "application/octet-stream";
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function fetchSts(
|
|
71
|
-
relayServerUrl: string,
|
|
72
|
-
gatewayId: string,
|
|
73
|
-
relaySecret: string,
|
|
74
|
-
mimeType: string
|
|
75
|
-
): Promise<StsCredentials> {
|
|
76
|
-
const res = await fetch(`${relayServerUrl.replace(/\/$/, "")}/api/media/sts`, {
|
|
77
|
-
method: "POST",
|
|
78
|
-
headers: { "Content-Type": "application/json" },
|
|
79
|
-
body: JSON.stringify({
|
|
80
|
-
gatewayId,
|
|
81
|
-
relaySecret,
|
|
82
|
-
count: 1,
|
|
83
|
-
mimeTypes: [mimeType],
|
|
84
|
-
durationSeconds: mimeType.startsWith("video/") ? 1800 : 900,
|
|
85
|
-
}),
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
if (!res.ok) {
|
|
89
|
-
const text = await res.text().catch(() => "");
|
|
90
|
-
throw new Error(`STS fetch failed: ${res.status} ${text}`);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return res.json() as Promise<StsCredentials>;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export async function uploadFile(filePath: string): Promise<UploadedFile> {
|
|
97
|
-
if (!filePath.trim()) {
|
|
98
|
-
throw new Error(t("upload.pathRequired"));
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const config = readConfig();
|
|
102
|
-
const absolutePath = resolve(filePath);
|
|
103
|
-
const fileStat = await stat(absolutePath).catch(() => null);
|
|
104
|
-
if (!fileStat) {
|
|
105
|
-
throw new Error(t("upload.fileMissing", absolutePath));
|
|
106
|
-
}
|
|
107
|
-
if (!fileStat.isFile()) {
|
|
108
|
-
throw new Error(t("upload.notAFile", absolutePath));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const mimeType = mimeTypeFromPath(absolutePath);
|
|
112
|
-
|
|
113
|
-
const [data, sts] = await Promise.all([
|
|
114
|
-
readFile(absolutePath),
|
|
115
|
-
fetchSts(config.relayServerUrl, config.gatewayId, config.relaySecret, mimeType),
|
|
116
|
-
]);
|
|
117
|
-
|
|
118
|
-
const result = await uploadMedia(sts, data, mimeType, basename(absolutePath));
|
|
119
|
-
return {
|
|
120
|
-
path: absolutePath,
|
|
121
|
-
fileName: basename(absolutePath),
|
|
122
|
-
mimeType,
|
|
123
|
-
url: result.url,
|
|
124
|
-
thumbnailUrl: result.thumbnailUrl ?? null,
|
|
125
|
-
size: result.size,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export async function uploadCommand(filePath: string, options: UploadCommandOptions = {}): Promise<void> {
|
|
130
|
-
const absolutePath = resolve(filePath);
|
|
131
|
-
const mimeType = mimeTypeFromPath(absolutePath);
|
|
132
|
-
if (options.json) {
|
|
133
|
-
console.log(JSON.stringify(await uploadFile(filePath)));
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
console.log(t("upload.starting", basename(absolutePath), mimeType));
|
|
138
|
-
const uploaded = await uploadFile(filePath);
|
|
139
|
-
console.log(t("upload.completed", uploaded.url));
|
|
140
|
-
console.log(uploaded.url);
|
|
141
|
-
}
|
package/src/config/config.ts
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
2
|
-
import { join } from "path";
|
|
3
|
-
import { homedir } from "os";
|
|
4
|
-
import { resolveOpenclawConfigPath } from "../commands/openclaw-cli.js";
|
|
5
|
-
|
|
6
|
-
const CONFIG_DIR = join(homedir(), ".clawai");
|
|
7
|
-
const CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
8
|
-
const LEGACY_OPENCLAW_CONFIG_PATH = join(homedir(), ".openclaw", "openclaw.json");
|
|
9
|
-
|
|
10
|
-
export interface ClawaiConfig {
|
|
11
|
-
relayServerUrl: string;
|
|
12
|
-
gatewayId: string;
|
|
13
|
-
relaySecret: string;
|
|
14
|
-
displayName: string;
|
|
15
|
-
/** Shared token for the local OpenClaw Gateway (gateway.auth.token in openclaw config). */
|
|
16
|
-
gatewayToken?: string;
|
|
17
|
-
/** Password for the local OpenClaw Gateway (used when auth mode is "password"). */
|
|
18
|
-
gatewayPassword?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function configExists(): boolean {
|
|
22
|
-
return existsSync(CONFIG_PATH);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function readConfig(): ClawaiConfig {
|
|
26
|
-
if (!existsSync(CONFIG_PATH)) {
|
|
27
|
-
throw new Error(`Config not found at ${CONFIG_PATH}. Run 'clawai pair' first.`);
|
|
28
|
-
}
|
|
29
|
-
const raw = readFileSync(CONFIG_PATH, "utf-8");
|
|
30
|
-
return JSON.parse(raw) as ClawaiConfig;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function writeConfig(config: ClawaiConfig): void {
|
|
34
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
35
|
-
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function readGatewayUrl(): string {
|
|
39
|
-
try {
|
|
40
|
-
const configPath = resolveOpenclawConfigPath();
|
|
41
|
-
const path = existsSync(configPath) ? configPath : LEGACY_OPENCLAW_CONFIG_PATH;
|
|
42
|
-
const raw = readFileSync(path, "utf-8");
|
|
43
|
-
const json = JSON.parse(raw) as { gateway?: { port?: number } };
|
|
44
|
-
const port = json?.gateway?.port ?? 18789;
|
|
45
|
-
return `ws://localhost:${port}`;
|
|
46
|
-
} catch {
|
|
47
|
-
return "ws://localhost:18789";
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function trimToUndefined(value: unknown): string | undefined {
|
|
52
|
-
if (typeof value !== "string") return undefined;
|
|
53
|
-
const trimmed = value.trim();
|
|
54
|
-
return trimmed ? trimmed : undefined;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function readGatewayAuthEnv(): { token?: string; password?: string } {
|
|
58
|
-
const token =
|
|
59
|
-
trimToUndefined(process.env.OPENCLAW_GATEWAY_TOKEN)
|
|
60
|
-
?? trimToUndefined(process.env.CLAWDBOT_GATEWAY_TOKEN);
|
|
61
|
-
const password =
|
|
62
|
-
trimToUndefined(process.env.OPENCLAW_GATEWAY_PASSWORD)
|
|
63
|
-
?? trimToUndefined(process.env.CLAWDBOT_GATEWAY_PASSWORD);
|
|
64
|
-
return { token, password };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function resolveConfiguredSecret(value: unknown): string | undefined {
|
|
68
|
-
const direct = trimToUndefined(value);
|
|
69
|
-
if (direct) {
|
|
70
|
-
const envTemplate = direct.match(/^\$\{([A-Z0-9_]+)\}$/i);
|
|
71
|
-
if (envTemplate) {
|
|
72
|
-
return trimToUndefined(process.env[envTemplate[1]]);
|
|
73
|
-
}
|
|
74
|
-
return direct;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
78
|
-
return undefined;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const record = value as Record<string, unknown>;
|
|
82
|
-
const envKey = trimToUndefined(record.$env) ?? trimToUndefined(record.env);
|
|
83
|
-
if (envKey) {
|
|
84
|
-
return trimToUndefined(process.env[envKey]);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return undefined;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Reads the gateway token or password. Priority order:
|
|
92
|
-
* 1. ~/.clawai/config.json (gatewayToken / gatewayPassword)
|
|
93
|
-
* 2. Environment variables (OPENCLAW_GATEWAY_TOKEN / OPENCLAW_GATEWAY_PASSWORD)
|
|
94
|
-
* 3. ~/.openclaw/openclaw.json (gateway.token / gateway.auth.token)
|
|
95
|
-
*/
|
|
96
|
-
export function readGatewayAuth(cfg: ClawaiConfig): { token?: string; password?: string } {
|
|
97
|
-
if (cfg.gatewayToken || cfg.gatewayPassword) {
|
|
98
|
-
return {
|
|
99
|
-
token: trimToUndefined(cfg.gatewayToken),
|
|
100
|
-
password: trimToUndefined(cfg.gatewayPassword),
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const envAuth = readGatewayAuthEnv();
|
|
105
|
-
if (envAuth.token || envAuth.password) {
|
|
106
|
-
return envAuth;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Try to read the token from OpenClaw's own config.
|
|
110
|
-
// Accept plaintext, ${ENV_VAR} templates, and simple {$env:"NAME"} refs.
|
|
111
|
-
try {
|
|
112
|
-
const configPath = resolveOpenclawConfigPath();
|
|
113
|
-
const path = existsSync(configPath) ? configPath : LEGACY_OPENCLAW_CONFIG_PATH;
|
|
114
|
-
const raw = readFileSync(path, "utf-8");
|
|
115
|
-
const json = JSON.parse(raw) as {
|
|
116
|
-
gateway?: {
|
|
117
|
-
token?: unknown;
|
|
118
|
-
password?: unknown;
|
|
119
|
-
auth?: {
|
|
120
|
-
token?: unknown;
|
|
121
|
-
password?: unknown;
|
|
122
|
-
};
|
|
123
|
-
};
|
|
124
|
-
};
|
|
125
|
-
const token =
|
|
126
|
-
resolveConfiguredSecret(json?.gateway?.token)
|
|
127
|
-
?? resolveConfiguredSecret(json?.gateway?.auth?.token);
|
|
128
|
-
const password =
|
|
129
|
-
resolveConfiguredSecret(json?.gateway?.password)
|
|
130
|
-
?? resolveConfiguredSecret(json?.gateway?.auth?.password);
|
|
131
|
-
if (token || password) return { token, password };
|
|
132
|
-
} catch {
|
|
133
|
-
// ignore
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return {};
|
|
137
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const DEFAULT_RELAY_SERVER = "https://clawpilot.codeaddict.cn";
|