clawdbot 2026.1.4-1 → 2026.1.5
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/CHANGELOG.md +26 -6
- package/README.md +26 -1
- package/dist/agents/pi-embedded-runner.js +2 -0
- package/dist/agents/pi-embedded-subscribe.js +18 -3
- package/dist/agents/pi-tools.js +45 -6
- package/dist/agents/tools/browser-tool.js +38 -89
- package/dist/agents/tools/cron-tool.js +8 -8
- package/dist/agents/workspace.js +8 -1
- package/dist/auto-reply/command-detection.js +26 -0
- package/dist/auto-reply/reply/agent-runner.js +15 -8
- package/dist/auto-reply/reply/commands.js +36 -25
- package/dist/auto-reply/reply/directive-handling.js +4 -2
- package/dist/auto-reply/reply/directives.js +12 -0
- package/dist/auto-reply/reply/session-updates.js +2 -4
- package/dist/auto-reply/reply.js +26 -4
- package/dist/browser/config.js +22 -4
- package/dist/browser/profiles-service.js +3 -1
- package/dist/browser/profiles.js +14 -3
- package/dist/canvas-host/a2ui/.bundle.hash +2 -0
- package/dist/cli/gateway-cli.js +2 -2
- package/dist/cli/profile.js +81 -0
- package/dist/cli/program.js +10 -1
- package/dist/cli/run-main.js +33 -0
- package/dist/commands/configure.js +5 -0
- package/dist/commands/onboard-providers.js +1 -1
- package/dist/commands/setup.js +4 -1
- package/dist/config/defaults.js +56 -0
- package/dist/config/io.js +47 -6
- package/dist/config/paths.js +2 -2
- package/dist/config/port-defaults.js +32 -0
- package/dist/config/sessions.js +3 -2
- package/dist/config/validation.js +2 -2
- package/dist/config/zod-schema.js +16 -0
- package/dist/discord/monitor.js +75 -266
- package/dist/entry.js +16 -0
- package/dist/gateway/call.js +8 -1
- package/dist/gateway/server-methods/chat.js +1 -1
- package/dist/gateway/server.js +14 -3
- package/dist/index.js +2 -2
- package/dist/infra/control-ui-assets.js +118 -0
- package/dist/infra/dotenv.js +15 -0
- package/dist/infra/shell-env.js +79 -0
- package/dist/infra/system-events.js +50 -23
- package/dist/macos/relay.js +8 -2
- package/dist/telegram/bot.js +24 -1
- package/dist/utils.js +8 -2
- package/dist/web/auto-reply.js +18 -21
- package/dist/web/inbound.js +5 -1
- package/dist/web/qr-image.js +4 -4
- package/dist/web/session.js +2 -3
- package/docs/agent.md +0 -2
- package/docs/assets/markdown.css +4 -1
- package/docs/audio.md +0 -2
- package/docs/clawd.md +0 -2
- package/docs/configuration.md +62 -3
- package/docs/docs.json +9 -1
- package/docs/faq.md +32 -7
- package/docs/gateway.md +28 -0
- package/docs/images.md +0 -2
- package/docs/index.md +2 -4
- package/docs/mac/icon.md +1 -1
- package/docs/nix.md +57 -11
- package/docs/onboarding.md +0 -2
- package/docs/refactor/webagent-session.md +0 -2
- package/docs/research/memory.md +1 -1
- package/docs/skills.md +0 -2
- package/docs/templates/AGENTS.md +2 -2
- package/docs/tools.md +15 -0
- package/docs/whatsapp.md +2 -0
- package/package.json +8 -16
- package/dist/control-ui/assets/index-BFID3yAA.css +0 -1
- package/dist/control-ui/assets/index-CE_axlTS.js +0 -2235
- package/dist/control-ui/assets/index-CE_axlTS.js.map +0 -1
- package/dist/control-ui/index.html +0 -15
- package/dist/daemon/constants.js +0 -10
- package/dist/daemon/launchd.js +0 -276
- package/dist/daemon/legacy.js +0 -63
- package/dist/daemon/program-args.js +0 -76
- package/dist/daemon/schtasks.js +0 -257
- package/dist/daemon/service.js +0 -60
- package/dist/daemon/systemd.js +0 -266
- package/dist/imessage/client.js +0 -165
- package/dist/imessage/index.js +0 -3
- package/dist/imessage/monitor.js +0 -272
- package/dist/imessage/probe.js +0 -26
- package/dist/imessage/send.js +0 -83
- package/dist/imessage/targets.js +0 -176
- package/dist/sessions/send-policy.js +0 -68
- package/dist/signal/client.js +0 -134
- package/dist/signal/daemon.js +0 -69
- package/dist/signal/index.js +0 -3
- package/dist/signal/monitor.js +0 -336
- package/dist/signal/probe.js +0 -46
- package/dist/signal/send.js +0 -91
- package/dist/slack/actions.js +0 -97
- package/dist/slack/index.js +0 -5
- package/dist/slack/monitor.js +0 -1029
- package/dist/slack/probe.js +0 -47
- package/dist/slack/send.js +0 -131
- package/dist/slack/token.js +0 -10
- package/dist/tui/commands.js +0 -74
- package/dist/tui/components/assistant-message.js +0 -16
- package/dist/tui/components/chat-log.js +0 -92
- package/dist/tui/components/custom-editor.js +0 -53
- package/dist/tui/components/selectors.js +0 -8
- package/dist/tui/components/tool-execution.js +0 -111
- package/dist/tui/components/user-message.js +0 -17
- package/dist/tui/gateway-chat.js +0 -140
- package/dist/tui/layout.js +0 -41
- package/dist/tui/message-list.js +0 -57
- package/dist/tui/theme/theme.js +0 -80
- package/dist/tui/theme.js +0 -25
- package/dist/tui/tui.js +0 -708
- package/dist/wizard/clack-prompter.js +0 -56
- package/dist/wizard/onboarding.js +0 -452
- package/dist/wizard/prompts.js +0 -6
- package/dist/wizard/session.js +0 -203
package/dist/daemon/service.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { installLaunchAgent, isLaunchAgentLoaded, readLaunchAgentProgramArguments, restartLaunchAgent, uninstallLaunchAgent, } from "./launchd.js";
|
|
2
|
-
import { installScheduledTask, isScheduledTaskInstalled, readScheduledTaskCommand, restartScheduledTask, uninstallScheduledTask, } from "./schtasks.js";
|
|
3
|
-
import { installSystemdService, isSystemdServiceEnabled, readSystemdServiceExecStart, restartSystemdService, uninstallSystemdService, } from "./systemd.js";
|
|
4
|
-
export function resolveGatewayService() {
|
|
5
|
-
if (process.platform === "darwin") {
|
|
6
|
-
return {
|
|
7
|
-
label: "LaunchAgent",
|
|
8
|
-
loadedText: "loaded",
|
|
9
|
-
notLoadedText: "not loaded",
|
|
10
|
-
install: async (args) => {
|
|
11
|
-
await installLaunchAgent(args);
|
|
12
|
-
},
|
|
13
|
-
uninstall: async (args) => {
|
|
14
|
-
await uninstallLaunchAgent(args);
|
|
15
|
-
},
|
|
16
|
-
restart: async (args) => {
|
|
17
|
-
await restartLaunchAgent(args);
|
|
18
|
-
},
|
|
19
|
-
isLoaded: async () => isLaunchAgentLoaded(),
|
|
20
|
-
readCommand: readLaunchAgentProgramArguments,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
if (process.platform === "linux") {
|
|
24
|
-
return {
|
|
25
|
-
label: "systemd",
|
|
26
|
-
loadedText: "enabled",
|
|
27
|
-
notLoadedText: "disabled",
|
|
28
|
-
install: async (args) => {
|
|
29
|
-
await installSystemdService(args);
|
|
30
|
-
},
|
|
31
|
-
uninstall: async (args) => {
|
|
32
|
-
await uninstallSystemdService(args);
|
|
33
|
-
},
|
|
34
|
-
restart: async (args) => {
|
|
35
|
-
await restartSystemdService(args);
|
|
36
|
-
},
|
|
37
|
-
isLoaded: async () => isSystemdServiceEnabled(),
|
|
38
|
-
readCommand: readSystemdServiceExecStart,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
if (process.platform === "win32") {
|
|
42
|
-
return {
|
|
43
|
-
label: "Scheduled Task",
|
|
44
|
-
loadedText: "registered",
|
|
45
|
-
notLoadedText: "missing",
|
|
46
|
-
install: async (args) => {
|
|
47
|
-
await installScheduledTask(args);
|
|
48
|
-
},
|
|
49
|
-
uninstall: async (args) => {
|
|
50
|
-
await uninstallScheduledTask(args);
|
|
51
|
-
},
|
|
52
|
-
restart: async (args) => {
|
|
53
|
-
await restartScheduledTask(args);
|
|
54
|
-
},
|
|
55
|
-
isLoaded: async () => isScheduledTaskInstalled(),
|
|
56
|
-
readCommand: readScheduledTaskCommand,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
throw new Error(`Gateway service install not supported on ${process.platform}`);
|
|
60
|
-
}
|
package/dist/daemon/systemd.js
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { promisify } from "node:util";
|
|
5
|
-
import { GATEWAY_SYSTEMD_SERVICE_NAME, LEGACY_GATEWAY_SYSTEMD_SERVICE_NAMES, } from "./constants.js";
|
|
6
|
-
const execFileAsync = promisify(execFile);
|
|
7
|
-
function resolveHomeDir(env) {
|
|
8
|
-
const home = env.HOME?.trim() || env.USERPROFILE?.trim();
|
|
9
|
-
if (!home)
|
|
10
|
-
throw new Error("Missing HOME");
|
|
11
|
-
return home;
|
|
12
|
-
}
|
|
13
|
-
function resolveSystemdUnitPathForName(env, name) {
|
|
14
|
-
const home = resolveHomeDir(env);
|
|
15
|
-
return path.join(home, ".config", "systemd", "user", `${name}.service`);
|
|
16
|
-
}
|
|
17
|
-
function resolveSystemdUnitPath(env) {
|
|
18
|
-
return resolveSystemdUnitPathForName(env, GATEWAY_SYSTEMD_SERVICE_NAME);
|
|
19
|
-
}
|
|
20
|
-
function systemdEscapeArg(value) {
|
|
21
|
-
if (!/[\s"\\]/.test(value))
|
|
22
|
-
return value;
|
|
23
|
-
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
24
|
-
}
|
|
25
|
-
function renderEnvLines(env) {
|
|
26
|
-
if (!env)
|
|
27
|
-
return [];
|
|
28
|
-
const entries = Object.entries(env).filter(([, value]) => typeof value === "string" && value.trim());
|
|
29
|
-
if (entries.length === 0)
|
|
30
|
-
return [];
|
|
31
|
-
return entries.map(([key, value]) => `Environment=${systemdEscapeArg(`${key}=${value?.trim() ?? ""}`)}`);
|
|
32
|
-
}
|
|
33
|
-
function buildSystemdUnit({ programArguments, workingDirectory, environment, }) {
|
|
34
|
-
const execStart = programArguments.map(systemdEscapeArg).join(" ");
|
|
35
|
-
const workingDirLine = workingDirectory
|
|
36
|
-
? `WorkingDirectory=${systemdEscapeArg(workingDirectory)}`
|
|
37
|
-
: null;
|
|
38
|
-
const envLines = renderEnvLines(environment);
|
|
39
|
-
return [
|
|
40
|
-
"[Unit]",
|
|
41
|
-
"Description=Clawdbot Gateway",
|
|
42
|
-
"",
|
|
43
|
-
"[Service]",
|
|
44
|
-
`ExecStart=${execStart}`,
|
|
45
|
-
"Restart=always",
|
|
46
|
-
workingDirLine,
|
|
47
|
-
...envLines,
|
|
48
|
-
"",
|
|
49
|
-
"[Install]",
|
|
50
|
-
"WantedBy=default.target",
|
|
51
|
-
"",
|
|
52
|
-
]
|
|
53
|
-
.filter((line) => line !== null)
|
|
54
|
-
.join("\n");
|
|
55
|
-
}
|
|
56
|
-
function parseSystemdExecStart(value) {
|
|
57
|
-
const args = [];
|
|
58
|
-
let current = "";
|
|
59
|
-
let inQuotes = false;
|
|
60
|
-
let escapeNext = false;
|
|
61
|
-
for (const char of value) {
|
|
62
|
-
if (escapeNext) {
|
|
63
|
-
current += char;
|
|
64
|
-
escapeNext = false;
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (char === "\\") {
|
|
68
|
-
escapeNext = true;
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (char === '"') {
|
|
72
|
-
inQuotes = !inQuotes;
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
if (!inQuotes && /\s/.test(char)) {
|
|
76
|
-
if (current) {
|
|
77
|
-
args.push(current);
|
|
78
|
-
current = "";
|
|
79
|
-
}
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
current += char;
|
|
83
|
-
}
|
|
84
|
-
if (current)
|
|
85
|
-
args.push(current);
|
|
86
|
-
return args;
|
|
87
|
-
}
|
|
88
|
-
export async function readSystemdServiceExecStart(env) {
|
|
89
|
-
const unitPath = resolveSystemdUnitPath(env);
|
|
90
|
-
try {
|
|
91
|
-
const content = await fs.readFile(unitPath, "utf8");
|
|
92
|
-
let execStart = "";
|
|
93
|
-
let workingDirectory = "";
|
|
94
|
-
for (const rawLine of content.split("\n")) {
|
|
95
|
-
const line = rawLine.trim();
|
|
96
|
-
if (!line || line.startsWith("#"))
|
|
97
|
-
continue;
|
|
98
|
-
if (line.startsWith("ExecStart=")) {
|
|
99
|
-
execStart = line.slice("ExecStart=".length).trim();
|
|
100
|
-
}
|
|
101
|
-
else if (line.startsWith("WorkingDirectory=")) {
|
|
102
|
-
workingDirectory = line.slice("WorkingDirectory=".length).trim();
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (!execStart)
|
|
106
|
-
return null;
|
|
107
|
-
const programArguments = parseSystemdExecStart(execStart);
|
|
108
|
-
return {
|
|
109
|
-
programArguments,
|
|
110
|
-
...(workingDirectory ? { workingDirectory } : {}),
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
async function execSystemctl(args) {
|
|
118
|
-
try {
|
|
119
|
-
const { stdout, stderr } = await execFileAsync("systemctl", args, {
|
|
120
|
-
encoding: "utf8",
|
|
121
|
-
});
|
|
122
|
-
return {
|
|
123
|
-
stdout: String(stdout ?? ""),
|
|
124
|
-
stderr: String(stderr ?? ""),
|
|
125
|
-
code: 0,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
const e = error;
|
|
130
|
-
return {
|
|
131
|
-
stdout: typeof e.stdout === "string" ? e.stdout : "",
|
|
132
|
-
stderr: typeof e.stderr === "string"
|
|
133
|
-
? e.stderr
|
|
134
|
-
: typeof e.message === "string"
|
|
135
|
-
? e.message
|
|
136
|
-
: "",
|
|
137
|
-
code: typeof e.code === "number" ? e.code : 1,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
async function assertSystemdAvailable() {
|
|
142
|
-
const res = await execSystemctl(["--user", "status"]);
|
|
143
|
-
if (res.code === 0)
|
|
144
|
-
return;
|
|
145
|
-
const detail = res.stderr || res.stdout;
|
|
146
|
-
if (detail.toLowerCase().includes("not found")) {
|
|
147
|
-
throw new Error("systemctl not available; systemd user services are required on Linux.");
|
|
148
|
-
}
|
|
149
|
-
throw new Error(`systemctl --user unavailable: ${detail || "unknown error"}`.trim());
|
|
150
|
-
}
|
|
151
|
-
export async function installSystemdService({ env, stdout, programArguments, workingDirectory, environment, }) {
|
|
152
|
-
await assertSystemdAvailable();
|
|
153
|
-
const unitPath = resolveSystemdUnitPath(env);
|
|
154
|
-
await fs.mkdir(path.dirname(unitPath), { recursive: true });
|
|
155
|
-
const unit = buildSystemdUnit({
|
|
156
|
-
programArguments,
|
|
157
|
-
workingDirectory,
|
|
158
|
-
environment,
|
|
159
|
-
});
|
|
160
|
-
await fs.writeFile(unitPath, unit, "utf8");
|
|
161
|
-
const unitName = `${GATEWAY_SYSTEMD_SERVICE_NAME}.service`;
|
|
162
|
-
const reload = await execSystemctl(["--user", "daemon-reload"]);
|
|
163
|
-
if (reload.code !== 0) {
|
|
164
|
-
throw new Error(`systemctl daemon-reload failed: ${reload.stderr || reload.stdout}`.trim());
|
|
165
|
-
}
|
|
166
|
-
const enable = await execSystemctl(["--user", "enable", unitName]);
|
|
167
|
-
if (enable.code !== 0) {
|
|
168
|
-
throw new Error(`systemctl enable failed: ${enable.stderr || enable.stdout}`.trim());
|
|
169
|
-
}
|
|
170
|
-
const restart = await execSystemctl(["--user", "restart", unitName]);
|
|
171
|
-
if (restart.code !== 0) {
|
|
172
|
-
throw new Error(`systemctl restart failed: ${restart.stderr || restart.stdout}`.trim());
|
|
173
|
-
}
|
|
174
|
-
stdout.write(`Installed systemd service: ${unitPath}\n`);
|
|
175
|
-
return { unitPath };
|
|
176
|
-
}
|
|
177
|
-
export async function uninstallSystemdService({ env, stdout, }) {
|
|
178
|
-
await assertSystemdAvailable();
|
|
179
|
-
const unitName = `${GATEWAY_SYSTEMD_SERVICE_NAME}.service`;
|
|
180
|
-
await execSystemctl(["--user", "disable", "--now", unitName]);
|
|
181
|
-
const unitPath = resolveSystemdUnitPath(env);
|
|
182
|
-
try {
|
|
183
|
-
await fs.unlink(unitPath);
|
|
184
|
-
stdout.write(`Removed systemd service: ${unitPath}\n`);
|
|
185
|
-
}
|
|
186
|
-
catch {
|
|
187
|
-
stdout.write(`Systemd service not found at ${unitPath}\n`);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
export async function restartSystemdService({ stdout, }) {
|
|
191
|
-
await assertSystemdAvailable();
|
|
192
|
-
const unitName = `${GATEWAY_SYSTEMD_SERVICE_NAME}.service`;
|
|
193
|
-
const res = await execSystemctl(["--user", "restart", unitName]);
|
|
194
|
-
if (res.code !== 0) {
|
|
195
|
-
throw new Error(`systemctl restart failed: ${res.stderr || res.stdout}`.trim());
|
|
196
|
-
}
|
|
197
|
-
stdout.write(`Restarted systemd service: ${unitName}\n`);
|
|
198
|
-
}
|
|
199
|
-
export async function isSystemdServiceEnabled() {
|
|
200
|
-
await assertSystemdAvailable();
|
|
201
|
-
const unitName = `${GATEWAY_SYSTEMD_SERVICE_NAME}.service`;
|
|
202
|
-
const res = await execSystemctl(["--user", "is-enabled", unitName]);
|
|
203
|
-
return res.code === 0;
|
|
204
|
-
}
|
|
205
|
-
async function isSystemctlAvailable() {
|
|
206
|
-
const res = await execSystemctl(["--user", "status"]);
|
|
207
|
-
if (res.code === 0)
|
|
208
|
-
return true;
|
|
209
|
-
const detail = `${res.stderr || res.stdout}`.toLowerCase();
|
|
210
|
-
return !detail.includes("not found");
|
|
211
|
-
}
|
|
212
|
-
export async function findLegacySystemdUnits(env) {
|
|
213
|
-
const results = [];
|
|
214
|
-
const systemctlAvailable = await isSystemctlAvailable();
|
|
215
|
-
for (const name of LEGACY_GATEWAY_SYSTEMD_SERVICE_NAMES) {
|
|
216
|
-
const unitPath = resolveSystemdUnitPathForName(env, name);
|
|
217
|
-
let exists = false;
|
|
218
|
-
try {
|
|
219
|
-
await fs.access(unitPath);
|
|
220
|
-
exists = true;
|
|
221
|
-
}
|
|
222
|
-
catch {
|
|
223
|
-
// ignore
|
|
224
|
-
}
|
|
225
|
-
let enabled = false;
|
|
226
|
-
if (systemctlAvailable) {
|
|
227
|
-
const res = await execSystemctl([
|
|
228
|
-
"--user",
|
|
229
|
-
"is-enabled",
|
|
230
|
-
`${name}.service`,
|
|
231
|
-
]);
|
|
232
|
-
enabled = res.code === 0;
|
|
233
|
-
}
|
|
234
|
-
if (exists || enabled) {
|
|
235
|
-
results.push({ name, unitPath, enabled, exists });
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return results;
|
|
239
|
-
}
|
|
240
|
-
export async function uninstallLegacySystemdUnits({ env, stdout, }) {
|
|
241
|
-
const units = await findLegacySystemdUnits(env);
|
|
242
|
-
if (units.length === 0)
|
|
243
|
-
return units;
|
|
244
|
-
const systemctlAvailable = await isSystemctlAvailable();
|
|
245
|
-
for (const unit of units) {
|
|
246
|
-
if (systemctlAvailable) {
|
|
247
|
-
await execSystemctl([
|
|
248
|
-
"--user",
|
|
249
|
-
"disable",
|
|
250
|
-
"--now",
|
|
251
|
-
`${unit.name}.service`,
|
|
252
|
-
]);
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
stdout.write(`systemctl unavailable; removed legacy unit file only: ${unit.name}.service\n`);
|
|
256
|
-
}
|
|
257
|
-
try {
|
|
258
|
-
await fs.unlink(unit.unitPath);
|
|
259
|
-
stdout.write(`Removed legacy systemd service: ${unit.unitPath}\n`);
|
|
260
|
-
}
|
|
261
|
-
catch {
|
|
262
|
-
stdout.write(`Legacy systemd unit not found at ${unit.unitPath}\n`);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
return units;
|
|
266
|
-
}
|
package/dist/imessage/client.js
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { createInterface } from "node:readline";
|
|
3
|
-
import { resolveUserPath } from "../utils.js";
|
|
4
|
-
export class IMessageRpcClient {
|
|
5
|
-
cliPath;
|
|
6
|
-
dbPath;
|
|
7
|
-
runtime;
|
|
8
|
-
onNotification;
|
|
9
|
-
pending = new Map();
|
|
10
|
-
closed;
|
|
11
|
-
closedResolve = null;
|
|
12
|
-
child = null;
|
|
13
|
-
reader = null;
|
|
14
|
-
nextId = 1;
|
|
15
|
-
constructor(opts = {}) {
|
|
16
|
-
this.cliPath = opts.cliPath?.trim() || "imsg";
|
|
17
|
-
this.dbPath = opts.dbPath?.trim()
|
|
18
|
-
? resolveUserPath(opts.dbPath)
|
|
19
|
-
: undefined;
|
|
20
|
-
this.runtime = opts.runtime;
|
|
21
|
-
this.onNotification = opts.onNotification;
|
|
22
|
-
this.closed = new Promise((resolve) => {
|
|
23
|
-
this.closedResolve = resolve;
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
async start() {
|
|
27
|
-
if (this.child)
|
|
28
|
-
return;
|
|
29
|
-
const args = ["rpc"];
|
|
30
|
-
if (this.dbPath) {
|
|
31
|
-
args.push("--db", this.dbPath);
|
|
32
|
-
}
|
|
33
|
-
const child = spawn(this.cliPath, args, {
|
|
34
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
35
|
-
});
|
|
36
|
-
this.child = child;
|
|
37
|
-
this.reader = createInterface({ input: child.stdout });
|
|
38
|
-
this.reader.on("line", (line) => {
|
|
39
|
-
const trimmed = line.trim();
|
|
40
|
-
if (!trimmed)
|
|
41
|
-
return;
|
|
42
|
-
this.handleLine(trimmed);
|
|
43
|
-
});
|
|
44
|
-
child.stderr?.on("data", (chunk) => {
|
|
45
|
-
const lines = chunk.toString().split(/\r?\n/);
|
|
46
|
-
for (const line of lines) {
|
|
47
|
-
if (!line.trim())
|
|
48
|
-
continue;
|
|
49
|
-
this.runtime?.error?.(`imsg rpc: ${line.trim()}`);
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
child.on("error", (err) => {
|
|
53
|
-
this.failAll(err instanceof Error ? err : new Error(String(err)));
|
|
54
|
-
this.closedResolve?.();
|
|
55
|
-
});
|
|
56
|
-
child.on("close", (code, signal) => {
|
|
57
|
-
if (code !== 0 && code !== null) {
|
|
58
|
-
const reason = signal ? `signal ${signal}` : `code ${code}`;
|
|
59
|
-
this.failAll(new Error(`imsg rpc exited (${reason})`));
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
this.failAll(new Error("imsg rpc closed"));
|
|
63
|
-
}
|
|
64
|
-
this.closedResolve?.();
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
async stop() {
|
|
68
|
-
if (!this.child)
|
|
69
|
-
return;
|
|
70
|
-
this.reader?.close();
|
|
71
|
-
this.reader = null;
|
|
72
|
-
this.child.stdin?.end();
|
|
73
|
-
const child = this.child;
|
|
74
|
-
this.child = null;
|
|
75
|
-
await Promise.race([
|
|
76
|
-
this.closed,
|
|
77
|
-
new Promise((resolve) => {
|
|
78
|
-
setTimeout(() => {
|
|
79
|
-
if (!child.killed)
|
|
80
|
-
child.kill("SIGTERM");
|
|
81
|
-
resolve();
|
|
82
|
-
}, 500);
|
|
83
|
-
}),
|
|
84
|
-
]);
|
|
85
|
-
}
|
|
86
|
-
async waitForClose() {
|
|
87
|
-
await this.closed;
|
|
88
|
-
}
|
|
89
|
-
async request(method, params, opts) {
|
|
90
|
-
if (!this.child || !this.child.stdin) {
|
|
91
|
-
throw new Error("imsg rpc not running");
|
|
92
|
-
}
|
|
93
|
-
const id = this.nextId++;
|
|
94
|
-
const payload = {
|
|
95
|
-
jsonrpc: "2.0",
|
|
96
|
-
id,
|
|
97
|
-
method,
|
|
98
|
-
params: params ?? {},
|
|
99
|
-
};
|
|
100
|
-
const line = `${JSON.stringify(payload)}\n`;
|
|
101
|
-
const timeoutMs = opts?.timeoutMs ?? 10_000;
|
|
102
|
-
const response = new Promise((resolve, reject) => {
|
|
103
|
-
const key = String(id);
|
|
104
|
-
const timer = timeoutMs > 0
|
|
105
|
-
? setTimeout(() => {
|
|
106
|
-
this.pending.delete(key);
|
|
107
|
-
reject(new Error(`imsg rpc timeout (${method})`));
|
|
108
|
-
}, timeoutMs)
|
|
109
|
-
: undefined;
|
|
110
|
-
this.pending.set(key, {
|
|
111
|
-
resolve: (value) => resolve(value),
|
|
112
|
-
reject,
|
|
113
|
-
timer,
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
this.child.stdin.write(line);
|
|
117
|
-
return await response;
|
|
118
|
-
}
|
|
119
|
-
handleLine(line) {
|
|
120
|
-
let parsed;
|
|
121
|
-
try {
|
|
122
|
-
parsed = JSON.parse(line);
|
|
123
|
-
}
|
|
124
|
-
catch (err) {
|
|
125
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
126
|
-
this.runtime?.error?.(`imsg rpc: failed to parse ${line}: ${detail}`);
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
if (parsed.id !== undefined && parsed.id !== null) {
|
|
130
|
-
const key = String(parsed.id);
|
|
131
|
-
const pending = this.pending.get(key);
|
|
132
|
-
if (!pending)
|
|
133
|
-
return;
|
|
134
|
-
if (pending.timer)
|
|
135
|
-
clearTimeout(pending.timer);
|
|
136
|
-
this.pending.delete(key);
|
|
137
|
-
if (parsed.error) {
|
|
138
|
-
const msg = parsed.error.message ?? "imsg rpc error";
|
|
139
|
-
pending.reject(new Error(msg));
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
pending.resolve(parsed.result);
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
if (parsed.method) {
|
|
146
|
-
this.onNotification?.({
|
|
147
|
-
method: parsed.method,
|
|
148
|
-
params: parsed.params,
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
failAll(err) {
|
|
153
|
-
for (const [key, pending] of this.pending.entries()) {
|
|
154
|
-
if (pending.timer)
|
|
155
|
-
clearTimeout(pending.timer);
|
|
156
|
-
pending.reject(err);
|
|
157
|
-
this.pending.delete(key);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
export async function createIMessageRpcClient(opts = {}) {
|
|
162
|
-
const client = new IMessageRpcClient(opts);
|
|
163
|
-
await client.start();
|
|
164
|
-
return client;
|
|
165
|
-
}
|
package/dist/imessage/index.js
DELETED