va-claw 0.1.2 → 0.1.4
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/README.md +134 -0
- package/README.zh-CN.md +478 -0
- package/dist/va-claw-bundle.mjs +561 -64
- package/docs/comic.svg +264 -0
- package/docs/releases/v0.1.3.md +20 -0
- package/index.html +269 -8
- package/package.json +1 -1
- package/packages/cli/dist/.tsbuildinfo +1 -1
- package/packages/cli/dist/claw-store.d.ts +38 -0
- package/packages/cli/dist/claw-store.d.ts.map +1 -0
- package/packages/cli/dist/claw-store.js +139 -0
- package/packages/cli/dist/claw-store.js.map +1 -0
- package/packages/cli/dist/deps.d.ts.map +1 -1
- package/packages/cli/dist/deps.js +2 -1
- package/packages/cli/dist/deps.js.map +1 -1
- package/packages/cli/dist/handlers.d.ts +20 -0
- package/packages/cli/dist/handlers.d.ts.map +1 -1
- package/packages/cli/dist/handlers.js +148 -1
- package/packages/cli/dist/handlers.js.map +1 -1
- package/packages/cli/dist/index.d.ts +2 -2
- package/packages/cli/dist/index.d.ts.map +1 -1
- package/packages/cli/dist/index.js +2 -2
- package/packages/cli/dist/index.js.map +1 -1
- package/packages/cli/dist/install-files.d.ts +1 -0
- package/packages/cli/dist/install-files.d.ts.map +1 -1
- package/packages/cli/dist/install-files.js +3 -0
- package/packages/cli/dist/install-files.js.map +1 -1
- package/packages/cli/dist/output.d.ts +2 -0
- package/packages/cli/dist/output.d.ts.map +1 -1
- package/packages/cli/dist/output.js +21 -0
- package/packages/cli/dist/output.js.map +1 -1
- package/packages/cli/dist/program.d.ts.map +1 -1
- package/packages/cli/dist/program.js +40 -1
- package/packages/cli/dist/program.js.map +1 -1
- package/packages/cli/dist/test-helpers.d.ts.map +1 -1
- package/packages/cli/dist/test-helpers.js +1 -0
- package/packages/cli/dist/test-helpers.js.map +1 -1
- package/packages/cli/dist/types.d.ts +1 -0
- package/packages/cli/dist/types.d.ts.map +1 -1
- package/packages/daemon/dist/.tsbuildinfo +1 -1
- package/packages/daemon/dist/wake-cycle.d.ts +19 -2
- package/packages/daemon/dist/wake-cycle.d.ts.map +1 -1
- package/packages/daemon/dist/wake-cycle.js +209 -30
- package/packages/daemon/dist/wake-cycle.js.map +1 -1
- package/packages/identity/dist/.tsbuildinfo +1 -1
- package/packages/identity/dist/defaults.d.ts +1 -0
- package/packages/identity/dist/defaults.d.ts.map +1 -1
- package/packages/identity/dist/defaults.js +6 -0
- package/packages/identity/dist/defaults.js.map +1 -1
- package/packages/identity/dist/render.d.ts.map +1 -1
- package/packages/identity/dist/render.js +15 -0
- package/packages/identity/dist/render.js.map +1 -1
- package/packages/identity/dist/types.d.ts +1 -0
- package/packages/identity/dist/types.d.ts.map +1 -1
- package/skills/claw-fleet-protocol.md +58 -0
- package/skills/install-va-claw.md +4 -0
- package/skills/migrate-to-va-claw.md +141 -0
package/dist/va-claw-bundle.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// packages/cli/dist/deps.js
|
|
4
|
-
import { spawnSync as
|
|
4
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
5
5
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
6
6
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
7
7
|
|
|
@@ -28,11 +28,13 @@ import { dirname } from "node:path";
|
|
|
28
28
|
|
|
29
29
|
// packages/identity/dist/defaults.js
|
|
30
30
|
var DEFAULT_LOOP_INTERVAL = "0 * * * *";
|
|
31
|
+
var DEFAULT_WAKE_TIMEOUT_MS = 3e5;
|
|
31
32
|
var DEFAULT_CONFIG = {
|
|
32
33
|
name: "va-claw",
|
|
33
34
|
persona: "A pragmatic CLI identity that values clarity, rigor, and continuity.",
|
|
34
35
|
systemPrompt: "You are va-claw. Be direct, honest about uncertainty, and keep actions aligned with the saved identity.",
|
|
35
36
|
wakePrompt: "Wake up, load the saved identity, and continue from the most recent remembered state.",
|
|
37
|
+
wakeTimeoutMs: DEFAULT_WAKE_TIMEOUT_MS,
|
|
36
38
|
loopInterval: DEFAULT_LOOP_INTERVAL,
|
|
37
39
|
channels: {
|
|
38
40
|
discord: {
|
|
@@ -58,6 +60,9 @@ function pickString(value, fallback) {
|
|
|
58
60
|
function pickBoolean(value, fallback) {
|
|
59
61
|
return typeof value === "boolean" ? value : fallback;
|
|
60
62
|
}
|
|
63
|
+
function pickNumber(value, fallback) {
|
|
64
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
65
|
+
}
|
|
61
66
|
function getDefaultIdentity() {
|
|
62
67
|
return { ...DEFAULT_CONFIG };
|
|
63
68
|
}
|
|
@@ -73,6 +78,7 @@ function normalizeConfig(input) {
|
|
|
73
78
|
persona: pickString(Reflect.get(data, "persona"), base.persona),
|
|
74
79
|
systemPrompt: pickString(Reflect.get(data, "systemPrompt"), base.systemPrompt),
|
|
75
80
|
wakePrompt: pickString(Reflect.get(data, "wakePrompt"), base.wakePrompt),
|
|
81
|
+
wakeTimeoutMs: pickNumber(Reflect.get(data, "wakeTimeoutMs"), base.wakeTimeoutMs ?? DEFAULT_WAKE_TIMEOUT_MS),
|
|
76
82
|
loopInterval: pickString(Reflect.get(data, "loopInterval"), base.loopInterval),
|
|
77
83
|
channels: {
|
|
78
84
|
discord: {
|
|
@@ -136,6 +142,13 @@ function toClaudeMdSnippet(config) {
|
|
|
136
142
|
normalized.persona,
|
|
137
143
|
"",
|
|
138
144
|
textBlock("System Prompt:", normalized.systemPrompt),
|
|
145
|
+
"",
|
|
146
|
+
"Operational protocol:",
|
|
147
|
+
"- Check long-running claw status with `va-claw protocol`.",
|
|
148
|
+
"- For readable text use `va-claw protocol --text`.",
|
|
149
|
+
"- Manage claws with `va-claw claw list | add | set | remove | heartbeat`.",
|
|
150
|
+
'- If asked "va/claw \u5728\u5E72\u4EC0\u4E48" / "my claws are doing what" / similar fleet questions, call `va-claw protocol --text` and answer from it.',
|
|
151
|
+
'- If the user asks a non-technical phrasing like "\u73B0\u5728\u90FD\u5728\u5E72\u561B", interpret it as a fleet status request and run `va-claw protocol --text`.',
|
|
139
152
|
"<!-- va-claw:identity:end -->"
|
|
140
153
|
].join("\n");
|
|
141
154
|
}
|
|
@@ -151,7 +164,15 @@ function toCodexSystemPrompt(config) {
|
|
|
151
164
|
"Wake prompt:",
|
|
152
165
|
normalized.wakePrompt,
|
|
153
166
|
"",
|
|
154
|
-
`
|
|
167
|
+
`Wake timeout (ms): ${normalized.wakeTimeoutMs ?? 3e5}`,
|
|
168
|
+
"",
|
|
169
|
+
`Loop interval: ${normalized.loopInterval}`,
|
|
170
|
+
"",
|
|
171
|
+
"Operational protocol:",
|
|
172
|
+
"Use `va-claw protocol` in terminal to get long-running claw state.",
|
|
173
|
+
'Use `va-claw protocol --text` when a user asks in plain language, including Chinese phrases like "va/claw \u5728\u5E72\u4EC0\u4E48".',
|
|
174
|
+
"Use `va-claw claw list` and `va-claw claw set <name> --status <status>` to manage claws.",
|
|
175
|
+
"If the user asks what claws are doing, run `va-claw protocol --text` and summarize only from that output."
|
|
155
176
|
].join("\n");
|
|
156
177
|
}
|
|
157
178
|
|
|
@@ -924,7 +945,10 @@ var E = class {
|
|
|
924
945
|
};
|
|
925
946
|
|
|
926
947
|
// packages/daemon/dist/wake-cycle.js
|
|
927
|
-
import {
|
|
948
|
+
import { spawn } from "node:child_process";
|
|
949
|
+
import { appendFile, mkdir as mkdir3 } from "node:fs/promises";
|
|
950
|
+
import { homedir as homedir4 } from "node:os";
|
|
951
|
+
import { dirname as dirname3, resolve as resolve3 } from "node:path";
|
|
928
952
|
|
|
929
953
|
// packages/memory/dist/default-store.js
|
|
930
954
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -1861,46 +1885,211 @@ function looksLikePath(value) {
|
|
|
1861
1885
|
|
|
1862
1886
|
// packages/daemon/dist/wake-cycle.js
|
|
1863
1887
|
var DEFAULT_WARN2 = (message) => console.warn(message);
|
|
1888
|
+
var DEFAULT_WAKE_TIMEOUT_MS2 = 3e5;
|
|
1889
|
+
var OUTPUT_TAIL_LIMIT = 2e3;
|
|
1890
|
+
var OUTPUT_BUFFER_LIMIT = 10 * 1024 * 1024;
|
|
1891
|
+
var wakeRunning = false;
|
|
1864
1892
|
async function runWakeCycle(config, deps = {}) {
|
|
1865
|
-
|
|
1866
|
-
|
|
1893
|
+
if (wakeRunning) {
|
|
1894
|
+
(deps.warn ?? DEFAULT_WARN2)("[va-claw/daemon] skipping wake: previous wake still running");
|
|
1867
1895
|
return null;
|
|
1868
1896
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1897
|
+
wakeRunning = true;
|
|
1898
|
+
try {
|
|
1899
|
+
return await _runWakeCycleInner(config, deps);
|
|
1900
|
+
} finally {
|
|
1901
|
+
wakeRunning = false;
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
async function _runWakeCycleInner(config, deps = {}) {
|
|
1905
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
1906
|
+
const startedAt = now();
|
|
1907
|
+
let combinedOutput = "";
|
|
1908
|
+
try {
|
|
1909
|
+
const adapter = await (deps.detect ?? detectCliAdapter)({ warn: deps.warn });
|
|
1910
|
+
if (!adapter) {
|
|
1911
|
+
return null;
|
|
1912
|
+
}
|
|
1913
|
+
const prompt = await resolveWakePrompt(config, deps);
|
|
1914
|
+
const timeoutMs = resolveWakeTimeoutMs(config);
|
|
1915
|
+
const wakeArgs = [...adapter.args, prompt];
|
|
1916
|
+
const wakeOptions = {
|
|
1917
|
+
cwd: process.cwd(),
|
|
1918
|
+
env: {
|
|
1919
|
+
...readProcessEnv(),
|
|
1920
|
+
CLAUDECODE: void 0,
|
|
1921
|
+
CLAUDE_CODE_SESSION: void 0
|
|
1922
|
+
}
|
|
1923
|
+
};
|
|
1924
|
+
const result = await (deps.executeWake ?? executeWakeProcess)(adapter.command, wakeArgs, wakeOptions, timeoutMs);
|
|
1925
|
+
combinedOutput = result.combinedOutput;
|
|
1926
|
+
const finishedAt = now();
|
|
1927
|
+
const durationMs = finishedAt.getTime() - startedAt.getTime();
|
|
1928
|
+
if (result.exitCode !== 0) {
|
|
1929
|
+
await writeWakeLogSafe({
|
|
1930
|
+
ts: startedAt.toISOString(),
|
|
1931
|
+
duration_ms: Math.max(0, durationMs),
|
|
1932
|
+
exit_code: result.exitCode,
|
|
1933
|
+
output_tail: tailOutput(result.combinedOutput)
|
|
1934
|
+
}, deps);
|
|
1935
|
+
(deps.warn ?? DEFAULT_WARN2)(result.exitCode === "timeout" ? `[va-claw/daemon] ${adapter.name} wake timed out after ${timeoutMs}ms.` : `[va-claw/daemon] ${adapter.name} wake failed: ${readFailureOutput(result.stderr, result.combinedOutput)}`);
|
|
1936
|
+
return null;
|
|
1937
|
+
}
|
|
1938
|
+
await (deps.storeMemory ?? store)(result.stdout, {
|
|
1939
|
+
source: "va-claw-daemon",
|
|
1940
|
+
kind: "wake",
|
|
1941
|
+
cli: adapter.name,
|
|
1942
|
+
identity: config.name,
|
|
1943
|
+
wokeAt: finishedAt.toISOString()
|
|
1944
|
+
});
|
|
1945
|
+
await writeWakeLogSafe({
|
|
1946
|
+
ts: startedAt.toISOString(),
|
|
1947
|
+
duration_ms: Math.max(0, durationMs),
|
|
1948
|
+
exit_code: 0,
|
|
1949
|
+
output_tail: tailOutput(result.combinedOutput)
|
|
1950
|
+
}, deps);
|
|
1951
|
+
return finishedAt;
|
|
1952
|
+
} catch (error) {
|
|
1953
|
+
const failedAt = now();
|
|
1954
|
+
await writeWakeLogSafe({
|
|
1955
|
+
ts: startedAt.toISOString(),
|
|
1956
|
+
duration_ms: Math.max(0, failedAt.getTime() - startedAt.getTime()),
|
|
1957
|
+
exit_code: "crash",
|
|
1958
|
+
output_tail: tailOutput(combinedOutput)
|
|
1959
|
+
}, deps);
|
|
1960
|
+
(deps.warn ?? DEFAULT_WARN2)(`[va-claw/daemon] wake crashed: ${String(error)}`);
|
|
1883
1961
|
return null;
|
|
1884
1962
|
}
|
|
1885
|
-
const wokeAt = (deps.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
1886
|
-
await (deps.storeMemory ?? store)(result.stdout ?? "", {
|
|
1887
|
-
source: "va-claw-daemon",
|
|
1888
|
-
kind: "wake",
|
|
1889
|
-
cli: adapter.name,
|
|
1890
|
-
identity: config.name,
|
|
1891
|
-
wokeAt: wokeAt.toISOString()
|
|
1892
|
-
});
|
|
1893
|
-
return wokeAt;
|
|
1894
1963
|
}
|
|
1895
|
-
function
|
|
1896
|
-
|
|
1897
|
-
|
|
1964
|
+
async function executeWakeProcess(command, args, options, timeoutMs) {
|
|
1965
|
+
let child;
|
|
1966
|
+
try {
|
|
1967
|
+
child = spawn(command, args, {
|
|
1968
|
+
cwd: options.cwd,
|
|
1969
|
+
env: options.env,
|
|
1970
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1971
|
+
});
|
|
1972
|
+
} catch (error) {
|
|
1973
|
+
return {
|
|
1974
|
+
combinedOutput: String(error),
|
|
1975
|
+
exitCode: "spawn_error",
|
|
1976
|
+
stderr: String(error),
|
|
1977
|
+
stdout: ""
|
|
1978
|
+
};
|
|
1898
1979
|
}
|
|
1899
|
-
|
|
1980
|
+
let stdout2 = "";
|
|
1981
|
+
let stderr = "";
|
|
1982
|
+
let combinedOutput = "";
|
|
1983
|
+
let settled = false;
|
|
1984
|
+
let combinedSize = 0;
|
|
1985
|
+
let stdoutSize = 0;
|
|
1986
|
+
let stderrSize = 0;
|
|
1987
|
+
child.stdout.on("data", (chunk) => {
|
|
1988
|
+
const text2 = String(chunk);
|
|
1989
|
+
if (stdoutSize < OUTPUT_BUFFER_LIMIT) {
|
|
1990
|
+
stdout2 += text2;
|
|
1991
|
+
stdoutSize += text2.length;
|
|
1992
|
+
}
|
|
1993
|
+
if (combinedSize < OUTPUT_BUFFER_LIMIT) {
|
|
1994
|
+
combinedOutput += text2;
|
|
1995
|
+
combinedSize += text2.length;
|
|
1996
|
+
}
|
|
1997
|
+
});
|
|
1998
|
+
child.stderr.on("data", (chunk) => {
|
|
1999
|
+
const text2 = String(chunk);
|
|
2000
|
+
if (stderrSize < OUTPUT_BUFFER_LIMIT) {
|
|
2001
|
+
stderr += text2;
|
|
2002
|
+
stderrSize += text2.length;
|
|
2003
|
+
}
|
|
2004
|
+
if (combinedSize < OUTPUT_BUFFER_LIMIT) {
|
|
2005
|
+
combinedOutput += text2;
|
|
2006
|
+
combinedSize += text2.length;
|
|
2007
|
+
}
|
|
2008
|
+
});
|
|
2009
|
+
return await new Promise((resolve5) => {
|
|
2010
|
+
const timer = setTimeout(() => {
|
|
2011
|
+
if (settled) {
|
|
2012
|
+
return;
|
|
2013
|
+
}
|
|
2014
|
+
settled = true;
|
|
2015
|
+
clearTimeout(timer);
|
|
2016
|
+
child.kill("SIGTERM");
|
|
2017
|
+
const killTimer = setTimeout(() => {
|
|
2018
|
+
try {
|
|
2019
|
+
child.kill("SIGKILL");
|
|
2020
|
+
} catch {
|
|
2021
|
+
}
|
|
2022
|
+
}, 5e3);
|
|
2023
|
+
const timeoutResult = {
|
|
2024
|
+
combinedOutput,
|
|
2025
|
+
exitCode: "timeout",
|
|
2026
|
+
stderr,
|
|
2027
|
+
stdout: stdout2
|
|
2028
|
+
};
|
|
2029
|
+
child.once("close", () => {
|
|
2030
|
+
clearTimeout(killTimer);
|
|
2031
|
+
resolve5(timeoutResult);
|
|
2032
|
+
});
|
|
2033
|
+
}, timeoutMs);
|
|
2034
|
+
child.on("error", (error) => {
|
|
2035
|
+
if (settled) {
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
settled = true;
|
|
2039
|
+
clearTimeout(timer);
|
|
2040
|
+
const message = String(error);
|
|
2041
|
+
stderr = stderr === "" ? message : `${stderr}
|
|
2042
|
+
${message}`;
|
|
2043
|
+
combinedOutput += message;
|
|
2044
|
+
resolve5({
|
|
2045
|
+
combinedOutput,
|
|
2046
|
+
exitCode: "spawn_error",
|
|
2047
|
+
stderr,
|
|
2048
|
+
stdout: stdout2
|
|
2049
|
+
});
|
|
2050
|
+
});
|
|
2051
|
+
child.on("close", (code) => {
|
|
2052
|
+
if (settled) {
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
2055
|
+
settled = true;
|
|
2056
|
+
clearTimeout(timer);
|
|
2057
|
+
resolve5({
|
|
2058
|
+
combinedOutput,
|
|
2059
|
+
exitCode: typeof code === "number" ? code : 1,
|
|
2060
|
+
stderr,
|
|
2061
|
+
stdout: stdout2
|
|
2062
|
+
});
|
|
2063
|
+
});
|
|
2064
|
+
});
|
|
1900
2065
|
}
|
|
1901
2066
|
function readProcessEnv() {
|
|
1902
2067
|
return process.env ?? {};
|
|
1903
2068
|
}
|
|
2069
|
+
function readFailureOutput(stderr, combinedOutput) {
|
|
2070
|
+
const candidate = stderr.trim() !== "" ? stderr : combinedOutput;
|
|
2071
|
+
const text2 = candidate.trim();
|
|
2072
|
+
return text2 === "" ? "unknown error" : tailOutput(text2, 200);
|
|
2073
|
+
}
|
|
2074
|
+
function resolveWakeTimeoutMs(config) {
|
|
2075
|
+
return typeof config.wakeTimeoutMs === "number" && Number.isFinite(config.wakeTimeoutMs) && config.wakeTimeoutMs > 0 ? config.wakeTimeoutMs : DEFAULT_WAKE_TIMEOUT_MS2;
|
|
2076
|
+
}
|
|
2077
|
+
function tailOutput(text2, limit = OUTPUT_TAIL_LIMIT) {
|
|
2078
|
+
return text2.length <= limit ? text2 : text2.slice(-limit);
|
|
2079
|
+
}
|
|
2080
|
+
async function writeWakeLogSafe(entry, deps) {
|
|
2081
|
+
try {
|
|
2082
|
+
await (deps.writeWakeLog ?? writeWakeLog)(entry);
|
|
2083
|
+
} catch (error) {
|
|
2084
|
+
(deps.warn ?? DEFAULT_WARN2)(`[va-claw/daemon] failed to write wake.log: ${String(error)}`);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
async function writeWakeLog(entry) {
|
|
2088
|
+
const logPath = resolve3(homedir4(), ".va-claw", "wake.log");
|
|
2089
|
+
await mkdir3(dirname3(logPath), { recursive: true });
|
|
2090
|
+
await appendFile(logPath, `${JSON.stringify(entry)}
|
|
2091
|
+
`, "utf8");
|
|
2092
|
+
}
|
|
1904
2093
|
async function resolveWakePrompt(config, deps) {
|
|
1905
2094
|
try {
|
|
1906
2095
|
const skills = await (deps.listSkills ?? listSkills)();
|
|
@@ -1970,29 +2159,29 @@ async function getDaemonStatus() {
|
|
|
1970
2159
|
}
|
|
1971
2160
|
|
|
1972
2161
|
// packages/daemon/dist/service.js
|
|
1973
|
-
import { mkdir as
|
|
1974
|
-
import { dirname as
|
|
1975
|
-
import { spawnSync as
|
|
2162
|
+
import { mkdir as mkdir4, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
|
|
2163
|
+
import { dirname as dirname5 } from "node:path";
|
|
2164
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
1976
2165
|
|
|
1977
2166
|
// packages/daemon/dist/service-files.js
|
|
1978
|
-
import { homedir as
|
|
1979
|
-
import { dirname as
|
|
2167
|
+
import { homedir as homedir5 } from "node:os";
|
|
2168
|
+
import { dirname as dirname4, join as join4, resolve as resolve4 } from "node:path";
|
|
1980
2169
|
import { fileURLToPath } from "node:url";
|
|
1981
2170
|
function createServiceDefinition(type) {
|
|
1982
|
-
const packageRoot =
|
|
1983
|
-
const repoRoot =
|
|
2171
|
+
const packageRoot = resolve4(dirname4(fileURLToPath(import.meta.url)), "..");
|
|
2172
|
+
const repoRoot = resolve4(packageRoot, "../..");
|
|
1984
2173
|
const runnerPath = join4(packageRoot, "dist", "runner.js");
|
|
1985
2174
|
const nodePath = process.execPath;
|
|
1986
2175
|
if (type === "launchd") {
|
|
1987
2176
|
return {
|
|
1988
|
-
path: join4(
|
|
2177
|
+
path: join4(homedir5(), "Library", "LaunchAgents", "com.va-claw.daemon.plist"),
|
|
1989
2178
|
content: renderLaunchdPlist(repoRoot, [nodePath, runnerPath]),
|
|
1990
2179
|
command: "launchctl",
|
|
1991
|
-
args: ["load", join4(
|
|
2180
|
+
args: ["load", join4(homedir5(), "Library", "LaunchAgents", "com.va-claw.daemon.plist")]
|
|
1992
2181
|
};
|
|
1993
2182
|
}
|
|
1994
2183
|
return {
|
|
1995
|
-
path: join4(
|
|
2184
|
+
path: join4(homedir5(), ".config", "systemd", "user", "va-claw.service"),
|
|
1996
2185
|
content: renderSystemdUnit(repoRoot, [nodePath, runnerPath]),
|
|
1997
2186
|
command: "systemctl",
|
|
1998
2187
|
args: ["--user", "enable", "--now", "va-claw.service"]
|
|
@@ -2002,7 +2191,7 @@ function createUninstallCommand(type) {
|
|
|
2002
2191
|
if (type === "launchd") {
|
|
2003
2192
|
return {
|
|
2004
2193
|
command: "launchctl",
|
|
2005
|
-
args: ["unload", join4(
|
|
2194
|
+
args: ["unload", join4(homedir5(), "Library", "LaunchAgents", "com.va-claw.daemon.plist")]
|
|
2006
2195
|
};
|
|
2007
2196
|
}
|
|
2008
2197
|
return {
|
|
@@ -2057,7 +2246,7 @@ function quoteSystemdArg(value) {
|
|
|
2057
2246
|
// packages/daemon/dist/service.js
|
|
2058
2247
|
async function installDaemonService(type) {
|
|
2059
2248
|
const definition = createServiceDefinition(type);
|
|
2060
|
-
await
|
|
2249
|
+
await mkdir4(dirname5(definition.path), { recursive: true });
|
|
2061
2250
|
await writeFile3(definition.path, definition.content, "utf8");
|
|
2062
2251
|
if (type === "systemd") {
|
|
2063
2252
|
runCommand("systemctl", ["--user", "daemon-reload"]);
|
|
@@ -2074,7 +2263,7 @@ async function uninstallDaemonService(type) {
|
|
|
2074
2263
|
}
|
|
2075
2264
|
}
|
|
2076
2265
|
function runCommand(command, args, allowFailure = false) {
|
|
2077
|
-
const result =
|
|
2266
|
+
const result = spawnSync2(command, args, { encoding: "utf8" });
|
|
2078
2267
|
if (allowFailure || result.status === 0) {
|
|
2079
2268
|
return;
|
|
2080
2269
|
}
|
|
@@ -2083,9 +2272,9 @@ function runCommand(command, args, allowFailure = false) {
|
|
|
2083
2272
|
}
|
|
2084
2273
|
|
|
2085
2274
|
// packages/cli/dist/install-files.js
|
|
2086
|
-
import { access as access2, mkdir as
|
|
2087
|
-
import { homedir as
|
|
2088
|
-
import { dirname as
|
|
2275
|
+
import { access as access2, mkdir as mkdir5, readFile as readFile3, writeFile as writeFile4 } from "node:fs/promises";
|
|
2276
|
+
import { homedir as homedir6 } from "node:os";
|
|
2277
|
+
import { dirname as dirname6, join as join5 } from "node:path";
|
|
2089
2278
|
var CLAUDE_MARKERS = {
|
|
2090
2279
|
start: "<!-- va-claw:identity:start -->",
|
|
2091
2280
|
end: "<!-- va-claw:identity:end -->"
|
|
@@ -2094,15 +2283,18 @@ var CODEX_MARKERS = {
|
|
|
2094
2283
|
start: "<!-- va-claw:codex:start -->",
|
|
2095
2284
|
end: "<!-- va-claw:codex:end -->"
|
|
2096
2285
|
};
|
|
2097
|
-
function resolveClaudeMdPath(home =
|
|
2286
|
+
function resolveClaudeMdPath(home = homedir6()) {
|
|
2098
2287
|
return join5(home, ".claude", "CLAUDE.md");
|
|
2099
2288
|
}
|
|
2100
|
-
function resolveCodexInstructionsPath(home =
|
|
2289
|
+
function resolveCodexInstructionsPath(home = homedir6()) {
|
|
2101
2290
|
return join5(home, ".codex", "instructions.md");
|
|
2102
2291
|
}
|
|
2103
|
-
function resolveMemoryDbPath(home =
|
|
2292
|
+
function resolveMemoryDbPath(home = homedir6()) {
|
|
2104
2293
|
return join5(home, ".va-claw", "memory.db");
|
|
2105
2294
|
}
|
|
2295
|
+
function resolveClawRegistryPath(home = homedir6()) {
|
|
2296
|
+
return join5(home, ".va-claw", "claws.json");
|
|
2297
|
+
}
|
|
2106
2298
|
async function fileExists(path) {
|
|
2107
2299
|
try {
|
|
2108
2300
|
await access2(path);
|
|
@@ -2114,7 +2306,7 @@ async function fileExists(path) {
|
|
|
2114
2306
|
async function upsertManagedBlock(path, block, markers) {
|
|
2115
2307
|
const current = (await readOptionalFile(path)).trim();
|
|
2116
2308
|
const next = appendManagedBlock(current, block, markers);
|
|
2117
|
-
await
|
|
2309
|
+
await mkdir5(dirname6(path), { recursive: true });
|
|
2118
2310
|
await writeFile4(path, next, "utf8");
|
|
2119
2311
|
}
|
|
2120
2312
|
async function removeManagedBlock(path, markers) {
|
|
@@ -2123,7 +2315,7 @@ async function removeManagedBlock(path, markers) {
|
|
|
2123
2315
|
return;
|
|
2124
2316
|
}
|
|
2125
2317
|
const next = stripManagedBlock(current, markers).trim();
|
|
2126
|
-
await
|
|
2318
|
+
await mkdir5(dirname6(path), { recursive: true });
|
|
2127
2319
|
await writeFile4(path, next === "" ? "" : `${next}
|
|
2128
2320
|
`, "utf8");
|
|
2129
2321
|
}
|
|
@@ -2165,8 +2357,9 @@ function createDefaultCliDeps() {
|
|
|
2165
2357
|
codexPath: resolveCodexInstructionsPath(),
|
|
2166
2358
|
configPath: DEFAULT_CONFIG_PATH,
|
|
2167
2359
|
memoryDbPath: resolveMemoryDbPath(),
|
|
2360
|
+
clawRegistryPath: resolveClawRegistryPath(),
|
|
2168
2361
|
platform: process.platform,
|
|
2169
|
-
spawnSync:
|
|
2362
|
+
spawnSync: spawnSync3,
|
|
2170
2363
|
stdout: process.stdout,
|
|
2171
2364
|
stderr: process.stderr,
|
|
2172
2365
|
fileExists,
|
|
@@ -2269,6 +2462,25 @@ function formatSkills(skills) {
|
|
|
2269
2462
|
` path: ${skill.path}`
|
|
2270
2463
|
].join("\n")).join("\n\n");
|
|
2271
2464
|
}
|
|
2465
|
+
function formatClawDefinitions(claws) {
|
|
2466
|
+
if (claws.length === 0) {
|
|
2467
|
+
return "No claws registered.";
|
|
2468
|
+
}
|
|
2469
|
+
return claws.map((claw) => {
|
|
2470
|
+
const lines = [
|
|
2471
|
+
`${claw.name} [${claw.status}]`,
|
|
2472
|
+
` goal: ${claw.goal || "(no goal)"}`,
|
|
2473
|
+
` cli: ${claw.cliCommand || "va-claw"}`,
|
|
2474
|
+
` tags: ${claw.tags.join(", ") || "(none)"}`,
|
|
2475
|
+
` note: ${claw.note || "(none)"}`,
|
|
2476
|
+
` updatedAt: ${claw.updatedAt}`
|
|
2477
|
+
];
|
|
2478
|
+
if (claw.lastSeenAt) {
|
|
2479
|
+
lines.push(` lastSeenAt: ${claw.lastSeenAt}`);
|
|
2480
|
+
}
|
|
2481
|
+
return lines.join("\n");
|
|
2482
|
+
}).join("\n\n");
|
|
2483
|
+
}
|
|
2272
2484
|
function writeLine(stream, message) {
|
|
2273
2485
|
stream.write(`${message}
|
|
2274
2486
|
`);
|
|
@@ -2276,7 +2488,7 @@ function writeLine(stream, message) {
|
|
|
2276
2488
|
|
|
2277
2489
|
// packages/cli/dist/wait.js
|
|
2278
2490
|
async function waitForStopSignal(stop) {
|
|
2279
|
-
await new Promise((
|
|
2491
|
+
await new Promise((resolve5, reject) => {
|
|
2280
2492
|
let stopping = false;
|
|
2281
2493
|
const cleanup = () => {
|
|
2282
2494
|
process.off("SIGINT", onSignal);
|
|
@@ -2289,7 +2501,7 @@ async function waitForStopSignal(stop) {
|
|
|
2289
2501
|
stopping = true;
|
|
2290
2502
|
void stop().then(() => {
|
|
2291
2503
|
cleanup();
|
|
2292
|
-
|
|
2504
|
+
resolve5();
|
|
2293
2505
|
}, (error) => {
|
|
2294
2506
|
cleanup();
|
|
2295
2507
|
reject(error);
|
|
@@ -2420,7 +2632,7 @@ function parseMetadata2(raw) {
|
|
|
2420
2632
|
}
|
|
2421
2633
|
|
|
2422
2634
|
// packages/cli/dist/platform.js
|
|
2423
|
-
import { homedir as
|
|
2635
|
+
import { homedir as homedir7 } from "node:os";
|
|
2424
2636
|
import { join as join6 } from "node:path";
|
|
2425
2637
|
function detectServiceType(platform) {
|
|
2426
2638
|
if (platform === "darwin") {
|
|
@@ -2431,27 +2643,162 @@ function detectServiceType(platform) {
|
|
|
2431
2643
|
}
|
|
2432
2644
|
throw new Error(`Unsupported platform for va-claw daemon service: ${platform}`);
|
|
2433
2645
|
}
|
|
2434
|
-
function probeServiceRunning(type,
|
|
2646
|
+
function probeServiceRunning(type, spawnSync4) {
|
|
2435
2647
|
if (type === "launchd") {
|
|
2436
|
-
return
|
|
2648
|
+
return spawnSync4("launchctl", ["list", "com.va-claw.daemon"], { encoding: "utf8" }).status === 0;
|
|
2437
2649
|
}
|
|
2438
|
-
const result =
|
|
2650
|
+
const result = spawnSync4("systemctl", ["--user", "is-active", "va-claw.service"], {
|
|
2439
2651
|
encoding: "utf8"
|
|
2440
2652
|
});
|
|
2441
2653
|
return result.status === 0 && result.stdout.trim() === "active";
|
|
2442
2654
|
}
|
|
2443
|
-
function stopInstalledService(type,
|
|
2655
|
+
function stopInstalledService(type, spawnSync4) {
|
|
2444
2656
|
if (type === "launchd") {
|
|
2445
|
-
|
|
2657
|
+
spawnSync4("launchctl", ["unload", resolveLaunchdPath()], { encoding: "utf8" });
|
|
2446
2658
|
return;
|
|
2447
2659
|
}
|
|
2448
|
-
|
|
2660
|
+
spawnSync4("systemctl", ["--user", "stop", "va-claw.service"], { encoding: "utf8" });
|
|
2449
2661
|
}
|
|
2450
2662
|
function resolveLaunchdPath() {
|
|
2451
|
-
return join6(
|
|
2663
|
+
return join6(homedir7(), "Library", "LaunchAgents", "com.va-claw.daemon.plist");
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2666
|
+
// packages/cli/dist/claw-store.js
|
|
2667
|
+
import { mkdir as mkdir6, readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
|
|
2668
|
+
import { dirname as dirname7 } from "node:path";
|
|
2669
|
+
var DEFAULT_CLAW_STATUS = "idle";
|
|
2670
|
+
var KNOWN_STATUSES = /* @__PURE__ */ new Set(["running", "working", "idle", "waiting", "error", "offline", "stopped"]);
|
|
2671
|
+
var REGISTRY_VERSION = 1;
|
|
2672
|
+
var DEFAULT_NOW = () => /* @__PURE__ */ new Date();
|
|
2673
|
+
function isClawStatus(value) {
|
|
2674
|
+
return KNOWN_STATUSES.has(value ?? "");
|
|
2675
|
+
}
|
|
2676
|
+
function pickString2(value, fallback = "") {
|
|
2677
|
+
return typeof value === "string" ? value.trim() : fallback;
|
|
2678
|
+
}
|
|
2679
|
+
function pickTags(value) {
|
|
2680
|
+
if (!Array.isArray(value)) {
|
|
2681
|
+
return [];
|
|
2682
|
+
}
|
|
2683
|
+
return value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter((entry) => entry.length > 0);
|
|
2684
|
+
}
|
|
2685
|
+
function normalizeStatus(value) {
|
|
2686
|
+
return isClawStatus(typeof value === "string" ? value : void 0) ? value : DEFAULT_CLAW_STATUS;
|
|
2687
|
+
}
|
|
2688
|
+
function validateClawStatus(value) {
|
|
2689
|
+
if (typeof value !== "string") {
|
|
2690
|
+
return null;
|
|
2691
|
+
}
|
|
2692
|
+
const normalized = value.trim().toLowerCase();
|
|
2693
|
+
return isClawStatus(normalized) ? normalized : null;
|
|
2694
|
+
}
|
|
2695
|
+
function normalizeClawDefinition(raw) {
|
|
2696
|
+
if (!raw || typeof raw !== "object") {
|
|
2697
|
+
return null;
|
|
2698
|
+
}
|
|
2699
|
+
const name = pickString2(Reflect.get(raw, "name"));
|
|
2700
|
+
if (name === "") {
|
|
2701
|
+
return null;
|
|
2702
|
+
}
|
|
2703
|
+
const createdAt = pickString2(Reflect.get(raw, "createdAt"), (/* @__PURE__ */ new Date(0)).toISOString());
|
|
2704
|
+
const updatedAt = pickString2(Reflect.get(raw, "updatedAt"), createdAt);
|
|
2705
|
+
return {
|
|
2706
|
+
name,
|
|
2707
|
+
goal: pickString2(Reflect.get(raw, "goal")),
|
|
2708
|
+
status: normalizeStatus(Reflect.get(raw, "status")),
|
|
2709
|
+
cliCommand: pickString2(Reflect.get(raw, "cliCommand"), "va-claw"),
|
|
2710
|
+
note: pickString2(Reflect.get(raw, "note")),
|
|
2711
|
+
tags: pickTags(Reflect.get(raw, "tags")),
|
|
2712
|
+
lastSeenAt: pickString2(Reflect.get(raw, "lastSeenAt"), "").trim() || void 0,
|
|
2713
|
+
createdAt,
|
|
2714
|
+
updatedAt
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
2717
|
+
function normalizeClawRegistry(raw) {
|
|
2718
|
+
if (!raw || typeof raw !== "object") {
|
|
2719
|
+
return { version: REGISTRY_VERSION, claws: [] };
|
|
2720
|
+
}
|
|
2721
|
+
const version = typeof Reflect.get(raw, "version") === "number" ? Reflect.get(raw, "version") : REGISTRY_VERSION;
|
|
2722
|
+
const rawClaws = Reflect.get(raw, "claws");
|
|
2723
|
+
const claws = Array.isArray(rawClaws) ? rawClaws.map(normalizeClawDefinition).filter(Boolean) : [];
|
|
2724
|
+
return { version: Number(version) > 0 ? Number(version) : REGISTRY_VERSION, claws };
|
|
2725
|
+
}
|
|
2726
|
+
async function listClaws(registryPath) {
|
|
2727
|
+
const registry = await loadClawRegistry(registryPath);
|
|
2728
|
+
return [...registry.claws];
|
|
2729
|
+
}
|
|
2730
|
+
async function registerClaw(registryPath, input, now = DEFAULT_NOW) {
|
|
2731
|
+
const registry = await loadClawRegistry(registryPath);
|
|
2732
|
+
const exists2 = registry.claws.some((entry) => entry.name === input.name);
|
|
2733
|
+
if (exists2) {
|
|
2734
|
+
throw new Error(`Claw already exists: ${input.name}`);
|
|
2735
|
+
}
|
|
2736
|
+
const timestamp = now().toISOString();
|
|
2737
|
+
const claw = {
|
|
2738
|
+
name: input.name,
|
|
2739
|
+
goal: pickString2(input.goal),
|
|
2740
|
+
status: input.status ?? DEFAULT_CLAW_STATUS,
|
|
2741
|
+
cliCommand: pickString2(input.cliCommand, "va-claw"),
|
|
2742
|
+
note: pickString2(input.note),
|
|
2743
|
+
tags: Array.isArray(input.tags) ? input.tags.filter((tag) => tag.trim() !== "") : [],
|
|
2744
|
+
createdAt: timestamp,
|
|
2745
|
+
updatedAt: timestamp
|
|
2746
|
+
};
|
|
2747
|
+
registry.claws = [claw, ...registry.claws];
|
|
2748
|
+
await saveClawRegistry(registryPath, registry);
|
|
2749
|
+
return claw;
|
|
2750
|
+
}
|
|
2751
|
+
async function updateClaw(registryPath, name, updates, now = DEFAULT_NOW) {
|
|
2752
|
+
const registry = await loadClawRegistry(registryPath);
|
|
2753
|
+
const index = registry.claws.findIndex((entry) => entry.name === name);
|
|
2754
|
+
if (index < 0) {
|
|
2755
|
+
return null;
|
|
2756
|
+
}
|
|
2757
|
+
const nowIso = now().toISOString();
|
|
2758
|
+
const current = registry.claws[index];
|
|
2759
|
+
const next = {
|
|
2760
|
+
...current,
|
|
2761
|
+
goal: updates.goal ?? current.goal,
|
|
2762
|
+
status: updates.status ?? current.status,
|
|
2763
|
+
cliCommand: updates.cliCommand ?? current.cliCommand,
|
|
2764
|
+
note: updates.note ?? current.note,
|
|
2765
|
+
tags: Array.isArray(updates.tags) ? updates.tags.filter((tag) => tag.trim() !== "") : current.tags,
|
|
2766
|
+
lastSeenAt: updates.seen ? nowIso : current.lastSeenAt,
|
|
2767
|
+
updatedAt: nowIso
|
|
2768
|
+
};
|
|
2769
|
+
registry.claws[index] = next;
|
|
2770
|
+
await saveClawRegistry(registryPath, registry);
|
|
2771
|
+
return next;
|
|
2772
|
+
}
|
|
2773
|
+
async function removeClaw(registryPath, name) {
|
|
2774
|
+
const registry = await loadClawRegistry(registryPath);
|
|
2775
|
+
const before = registry.claws.length;
|
|
2776
|
+
registry.claws = registry.claws.filter((entry) => entry.name !== name);
|
|
2777
|
+
if (registry.claws.length === before) {
|
|
2778
|
+
return false;
|
|
2779
|
+
}
|
|
2780
|
+
await saveClawRegistry(registryPath, registry);
|
|
2781
|
+
return true;
|
|
2782
|
+
}
|
|
2783
|
+
async function loadClawRegistry(registryPath) {
|
|
2784
|
+
try {
|
|
2785
|
+
const rawText = await readFile5(registryPath, "utf8");
|
|
2786
|
+
return normalizeClawRegistry(JSON.parse(rawText));
|
|
2787
|
+
} catch (error) {
|
|
2788
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
2789
|
+
return { version: REGISTRY_VERSION, claws: [] };
|
|
2790
|
+
}
|
|
2791
|
+
throw error;
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
async function saveClawRegistry(registryPath, registry) {
|
|
2795
|
+
await mkdir6(dirname7(registryPath), { recursive: true });
|
|
2796
|
+
await writeFile5(registryPath, `${JSON.stringify({ ...registry, version: REGISTRY_VERSION }, null, 2)}
|
|
2797
|
+
`, "utf8");
|
|
2452
2798
|
}
|
|
2453
2799
|
|
|
2454
2800
|
// packages/cli/dist/handlers.js
|
|
2801
|
+
var CLAW_FLEET_PROTOCOL_SKILL_URL = "https://raw.githubusercontent.com/Vadaski/va-claw/main/skills/claw-fleet-protocol.md";
|
|
2455
2802
|
async function runInstall(target, deps) {
|
|
2456
2803
|
const installTarget = normalizeInstallTarget(target);
|
|
2457
2804
|
const config = await deps.fileExists(deps.configPath) ? await deps.loadIdentity() : await deps.runInstallWizard();
|
|
@@ -2468,6 +2815,10 @@ async function runInstall(target, deps) {
|
|
|
2468
2815
|
const serviceType = detectServiceType(deps.platform);
|
|
2469
2816
|
await deps.installDaemonService(serviceType);
|
|
2470
2817
|
summary.push(`Daemon service: ${serviceType}`);
|
|
2818
|
+
const fleetSkillName = await installFleetProtocolSkill(deps);
|
|
2819
|
+
if (fleetSkillName) {
|
|
2820
|
+
summary.push(`Claw fleet protocol skill: ${fleetSkillName}`);
|
|
2821
|
+
}
|
|
2471
2822
|
for (const line of summary) {
|
|
2472
2823
|
writeLine(deps.stdout, line);
|
|
2473
2824
|
}
|
|
@@ -2500,6 +2851,144 @@ async function runStatus(deps) {
|
|
|
2500
2851
|
writeLine(deps.stdout, `Last wake: ${lastWakeAt ?? "never"}`);
|
|
2501
2852
|
writeLine(deps.stdout, `Memory entries: ${memoryCount}`);
|
|
2502
2853
|
}
|
|
2854
|
+
async function runClawStatus(deps) {
|
|
2855
|
+
const clawStatus = await buildProtocolReport(deps);
|
|
2856
|
+
writeLine(deps.stdout, formatClawDefinitions(clawStatus.claws));
|
|
2857
|
+
writeLine(deps.stdout, `Daemon running: ${clawStatus.runtime.running ? "yes" : "no"} | service: ${clawStatus.runtime.serviceRunning ? "running" : "stopped"} | wakeCount: ${clawStatus.runtime.wakeCount}`);
|
|
2858
|
+
}
|
|
2859
|
+
async function runClawList(deps) {
|
|
2860
|
+
const claws = await listClaws(deps.clawRegistryPath);
|
|
2861
|
+
writeLine(deps.stdout, formatClawDefinitions(claws));
|
|
2862
|
+
}
|
|
2863
|
+
async function runClawAdd(name, options, deps) {
|
|
2864
|
+
const normalizedName = name.trim();
|
|
2865
|
+
if (normalizedName === "") {
|
|
2866
|
+
throw new Error("Claw name cannot be empty.");
|
|
2867
|
+
}
|
|
2868
|
+
const status = options.status ? validateClawStatus(options.status) : null;
|
|
2869
|
+
if (options.status && status === null) {
|
|
2870
|
+
throw new Error(`Invalid claw status: ${options.status}`);
|
|
2871
|
+
}
|
|
2872
|
+
const claw = await registerClaw(deps.clawRegistryPath, {
|
|
2873
|
+
name: normalizedName,
|
|
2874
|
+
goal: options.goal,
|
|
2875
|
+
status: status ?? void 0,
|
|
2876
|
+
cliCommand: options.cliCommand,
|
|
2877
|
+
note: options.note,
|
|
2878
|
+
tags: options.tags ? splitCommaList(options.tags) : []
|
|
2879
|
+
});
|
|
2880
|
+
writeLine(deps.stdout, formatClawDefinitions([claw]));
|
|
2881
|
+
}
|
|
2882
|
+
async function runClawUpdate(name, options, deps) {
|
|
2883
|
+
const status = options.status ? validateClawStatus(options.status) : null;
|
|
2884
|
+
const normalizedName = name.trim();
|
|
2885
|
+
if (normalizedName === "") {
|
|
2886
|
+
throw new Error("Claw name cannot be empty.");
|
|
2887
|
+
}
|
|
2888
|
+
if (options.status && status === null) {
|
|
2889
|
+
throw new Error(`Invalid claw status: ${options.status}`);
|
|
2890
|
+
}
|
|
2891
|
+
const patch = {
|
|
2892
|
+
goal: options.goal,
|
|
2893
|
+
status: status ?? void 0,
|
|
2894
|
+
cliCommand: options.cliCommand,
|
|
2895
|
+
note: options.note,
|
|
2896
|
+
tags: options.tags ? splitCommaList(options.tags) : void 0,
|
|
2897
|
+
seen: options.seen === "1" || options.seen === "true" || options.seen === "yes"
|
|
2898
|
+
};
|
|
2899
|
+
const claw = await updateClaw(deps.clawRegistryPath, normalizedName, patch);
|
|
2900
|
+
if (!claw) {
|
|
2901
|
+
writeLine(deps.stdout, `Claw not found: ${name}`);
|
|
2902
|
+
return;
|
|
2903
|
+
}
|
|
2904
|
+
writeLine(deps.stdout, formatClawDefinitions([claw]));
|
|
2905
|
+
}
|
|
2906
|
+
async function runClawRemove(name, deps) {
|
|
2907
|
+
const normalizedName = name.trim();
|
|
2908
|
+
if (normalizedName === "") {
|
|
2909
|
+
throw new Error("Claw name cannot be empty.");
|
|
2910
|
+
}
|
|
2911
|
+
const removed = await removeClaw(deps.clawRegistryPath, normalizedName);
|
|
2912
|
+
writeLine(deps.stdout, removed ? `Removed claw: ${normalizedName}` : `Claw not found: ${normalizedName}`);
|
|
2913
|
+
}
|
|
2914
|
+
async function runClawHeartbeat(name, deps) {
|
|
2915
|
+
const normalizedName = name.trim();
|
|
2916
|
+
if (normalizedName === "") {
|
|
2917
|
+
throw new Error("Claw name cannot be empty.");
|
|
2918
|
+
}
|
|
2919
|
+
const claw = await updateClaw(deps.clawRegistryPath, normalizedName, { seen: true, status: "running" });
|
|
2920
|
+
if (!claw) {
|
|
2921
|
+
writeLine(deps.stdout, `Claw not found: ${name}`);
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2924
|
+
writeLine(deps.stdout, formatClawDefinitions([claw]));
|
|
2925
|
+
}
|
|
2926
|
+
async function runProtocol(deps, textMode = false) {
|
|
2927
|
+
const report = await buildProtocolReport(deps);
|
|
2928
|
+
if (textMode) {
|
|
2929
|
+
writeLine(deps.stdout, `protocol: ${report.protocol}`);
|
|
2930
|
+
writeLine(deps.stdout, `timestamp: ${report.timestamp}`);
|
|
2931
|
+
writeLine(deps.stdout, `daemon.running: ${report.runtime.running}`);
|
|
2932
|
+
writeLine(deps.stdout, `daemon.serviceRunning: ${report.runtime.serviceRunning}`);
|
|
2933
|
+
writeLine(deps.stdout, `daemon.discord: ${report.runtime.discord}`);
|
|
2934
|
+
writeLine(deps.stdout, `daemon.wakeCount: ${report.runtime.wakeCount}`);
|
|
2935
|
+
writeLine(deps.stdout, `daemon.lastWakeAt: ${report.runtime.lastWakeAt ?? "never"}`);
|
|
2936
|
+
writeLine(deps.stdout, `memory.entries: ${report.memory.entries}`);
|
|
2937
|
+
writeLine(deps.stdout, `memory.lastWakeAt: ${report.memory.lastWakeAt ?? "never"}`);
|
|
2938
|
+
writeLine(deps.stdout, "");
|
|
2939
|
+
writeLine(deps.stdout, "Claws:");
|
|
2940
|
+
writeLine(deps.stdout, formatClawDefinitions(report.claws));
|
|
2941
|
+
return;
|
|
2942
|
+
}
|
|
2943
|
+
writeLine(deps.stdout, JSON.stringify(report, null, 2));
|
|
2944
|
+
}
|
|
2945
|
+
async function buildProtocolReport(deps) {
|
|
2946
|
+
const runtime = await deps.getDaemonStatus();
|
|
2947
|
+
const serviceType = safeDetectServiceType(deps.platform);
|
|
2948
|
+
const serviceRunning = serviceType ? probeServiceRunning(serviceType, deps.spawnSync) : false;
|
|
2949
|
+
const runtimeLastWakeAt = runtime.lastWakeAt?.toISOString() ?? null;
|
|
2950
|
+
const fallbackLastWakeAt = await findLastWakeAt(deps.memoryDbPath, deps.fileExists);
|
|
2951
|
+
const memoryCount = await countMemoryEntries(deps.memoryDbPath, deps.fileExists);
|
|
2952
|
+
const claws = await listClaws(deps.clawRegistryPath);
|
|
2953
|
+
return {
|
|
2954
|
+
protocol: "va-claw-claw-protocol-1",
|
|
2955
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2956
|
+
runtime: {
|
|
2957
|
+
running: runtime.running,
|
|
2958
|
+
serviceRunning,
|
|
2959
|
+
discord: runtime.discord,
|
|
2960
|
+
wakeCount: runtime.wakeCount,
|
|
2961
|
+
lastWakeAt: runtimeLastWakeAt ?? fallbackLastWakeAt
|
|
2962
|
+
},
|
|
2963
|
+
memory: {
|
|
2964
|
+
entries: memoryCount,
|
|
2965
|
+
lastWakeAt: runtimeLastWakeAt ?? fallbackLastWakeAt
|
|
2966
|
+
},
|
|
2967
|
+
claws: claws.map((claw) => ({
|
|
2968
|
+
name: claw.name,
|
|
2969
|
+
goal: claw.goal,
|
|
2970
|
+
status: claw.status,
|
|
2971
|
+
cliCommand: claw.cliCommand,
|
|
2972
|
+
note: claw.note,
|
|
2973
|
+
tags: claw.tags,
|
|
2974
|
+
lastSeenAt: claw.lastSeenAt,
|
|
2975
|
+
createdAt: claw.createdAt,
|
|
2976
|
+
updatedAt: claw.updatedAt
|
|
2977
|
+
}))
|
|
2978
|
+
};
|
|
2979
|
+
}
|
|
2980
|
+
async function installFleetProtocolSkill(deps) {
|
|
2981
|
+
try {
|
|
2982
|
+
const response = await fetch(CLAW_FLEET_PROTOCOL_SKILL_URL);
|
|
2983
|
+
if (!response.ok) {
|
|
2984
|
+
return null;
|
|
2985
|
+
}
|
|
2986
|
+
const content = await response.text();
|
|
2987
|
+
return await deps.skillInstall(content, "claw-fleet-protocol");
|
|
2988
|
+
} catch {
|
|
2989
|
+
return null;
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2503
2992
|
async function runMemorySearch(query, deps) {
|
|
2504
2993
|
writeLine(deps.stdout, formatMemoryEntries(await deps.memorySearch(query, 10)));
|
|
2505
2994
|
}
|
|
@@ -2769,6 +3258,7 @@ function createCliProgram(deps = createDefaultCliDeps()) {
|
|
|
2769
3258
|
program.command("stop").description("Stop the daemon.").action(async () => runStop(deps));
|
|
2770
3259
|
program.command("status").description("Show daemon and memory status.").action(async () => runStatus(deps));
|
|
2771
3260
|
program.command("uninstall").description("Remove daemon service and injected prompts.").action(async () => runUninstall(deps));
|
|
3261
|
+
program.command("protocol").description("Emit a machine-readable protocol summary for agent-facing integrations.").option("--text", "Print a human-readable protocol summary instead of JSON.").action(async (options) => runProtocol(deps, Boolean(options.text)));
|
|
2772
3262
|
const memory = program.command("memory").description("Memory operations.");
|
|
2773
3263
|
memory.command("search").description("Search memory.").argument("<query>").action(async (query) => runMemorySearch(query, deps));
|
|
2774
3264
|
memory.command("memorize").description("Store or update a memory entry by key.").argument("<key>").argument("<essence>").option("--tags <tags>", "Comma-separated tags.").option("--details <details>", "Details text.").option("--importance <importance>", "Importance from 0 to 1.").action((key, essence, options) => runMemoryMemorize(key, essence, options, deps));
|
|
@@ -2798,6 +3288,13 @@ function createCliProgram(deps = createDefaultCliDeps()) {
|
|
|
2798
3288
|
slack.command("setup").description("Configure Slack bot credentials.").option("--bot-token <token>", "Slack bot token").option("--app-token <token>", "Slack app token").option("--cli-command <command>", "CLI command to invoke for each message").action(async (options) => runSlackChannelSetup(options.botToken, options.appToken, options.cliCommand, deps));
|
|
2799
3289
|
slack.command("start").description("Start the Slack channel in the foreground.").action(async () => runSlackChannelStart(deps));
|
|
2800
3290
|
slack.command("status").description("Show Slack channel status.").action(async () => runSlackChannelStatus(deps));
|
|
3291
|
+
const claw = program.command("claw").description("Long-running claw operations.");
|
|
3292
|
+
claw.command("status").description("Show claw status and daemon summary.").action(async () => runClawStatus(deps));
|
|
3293
|
+
claw.command("list").description("List all registered claws.").action(async () => runClawList(deps));
|
|
3294
|
+
claw.command("add").description("Register a long-running claw.").argument("<name>").option("--goal <goal>", "Describe what this claw is responsible for.").option("--status <status>", "running | working | idle | waiting | error | offline | stopped.").option("--cli-command <command>", "Command to execute this claw's actions (default: va-claw).").option("--note <note>", "Single-line note for this claw.").option("--tags <tags>", "Comma-separated tags.").action((name, options) => runClawAdd(name, options, deps));
|
|
3295
|
+
claw.command("set").description("Update a claw's state.").argument("<name>").option("--goal <goal>", "New goal for this claw.").option("--status <status>", "running | working | idle | waiting | error | offline | stopped.").option("--cli-command <command>", "Update command used for this claw.").option("--note <note>", "Update note.").option("--tags <tags>", "Replace tags, comma-separated.").option("--seen", "Mark claw as alive now.").action((name, options) => runClawUpdate(name, { ...options, seen: options.seen ? "true" : void 0 }, deps));
|
|
3296
|
+
claw.command("heartbeat").description("Mark a claw as active and update lastSeenAt.").argument("<name>").action((name) => runClawHeartbeat(name, deps));
|
|
3297
|
+
claw.command("remove").description("Unregister a claw.").argument("<name>").action((name) => runClawRemove(name, deps));
|
|
2801
3298
|
return program;
|
|
2802
3299
|
}
|
|
2803
3300
|
async function runCli(argv = process.argv, deps) {
|