va-claw 0.1.3 → 0.2.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/README.md +130 -0
- package/README.zh-CN.md +478 -0
- package/dist/va-claw-bundle.mjs +584 -64
- package/docs/releases/v0.1.3.md +20 -0
- 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 +159 -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 +27 -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/memory-protocol.md +48 -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,19 @@ 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`.',
|
|
152
|
+
"",
|
|
153
|
+
"Memory protocol:",
|
|
154
|
+
'- At session start, run `va-claw memory recall "<current topic or task>"` before answering.',
|
|
155
|
+
'- After important work, run `va-claw memory memorize "<key>" "<essence>" --tags <tags> --importance <0.0-1.0>`.',
|
|
156
|
+
"- Use importance `0.8-1.0` for decisions or hard-won knowledge; use `0.5` for routine outputs.",
|
|
157
|
+
"- Do not memorize every message; store only things worth remembering across sessions.",
|
|
139
158
|
"<!-- va-claw:identity:end -->"
|
|
140
159
|
].join("\n");
|
|
141
160
|
}
|
|
@@ -151,7 +170,21 @@ function toCodexSystemPrompt(config) {
|
|
|
151
170
|
"Wake prompt:",
|
|
152
171
|
normalized.wakePrompt,
|
|
153
172
|
"",
|
|
154
|
-
`
|
|
173
|
+
`Wake timeout (ms): ${normalized.wakeTimeoutMs ?? 3e5}`,
|
|
174
|
+
"",
|
|
175
|
+
`Loop interval: ${normalized.loopInterval}`,
|
|
176
|
+
"",
|
|
177
|
+
"Operational protocol:",
|
|
178
|
+
"Use `va-claw protocol` in terminal to get long-running claw state.",
|
|
179
|
+
'Use `va-claw protocol --text` when a user asks in plain language, including Chinese phrases like "va/claw \u5728\u5E72\u4EC0\u4E48".',
|
|
180
|
+
"Use `va-claw claw list` and `va-claw claw set <name> --status <status>` to manage claws.",
|
|
181
|
+
"If the user asks what claws are doing, run `va-claw protocol --text` and summarize only from that output.",
|
|
182
|
+
"",
|
|
183
|
+
"Memory protocol:",
|
|
184
|
+
'At session start, run `va-claw memory recall "<current topic or task>"` before answering.',
|
|
185
|
+
'After important work, run `va-claw memory memorize "<key>" "<essence>" --tags <tags> --importance <0.0-1.0>`.',
|
|
186
|
+
"Use importance `0.8-1.0` for decisions or hard-won knowledge; use `0.5` for routine outputs.",
|
|
187
|
+
"Do not memorize every message; store only things worth remembering across sessions."
|
|
155
188
|
].join("\n");
|
|
156
189
|
}
|
|
157
190
|
|
|
@@ -924,7 +957,10 @@ var E = class {
|
|
|
924
957
|
};
|
|
925
958
|
|
|
926
959
|
// packages/daemon/dist/wake-cycle.js
|
|
927
|
-
import {
|
|
960
|
+
import { spawn } from "node:child_process";
|
|
961
|
+
import { appendFile, mkdir as mkdir3 } from "node:fs/promises";
|
|
962
|
+
import { homedir as homedir4 } from "node:os";
|
|
963
|
+
import { dirname as dirname3, resolve as resolve3 } from "node:path";
|
|
928
964
|
|
|
929
965
|
// packages/memory/dist/default-store.js
|
|
930
966
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -1861,46 +1897,211 @@ function looksLikePath(value) {
|
|
|
1861
1897
|
|
|
1862
1898
|
// packages/daemon/dist/wake-cycle.js
|
|
1863
1899
|
var DEFAULT_WARN2 = (message) => console.warn(message);
|
|
1900
|
+
var DEFAULT_WAKE_TIMEOUT_MS2 = 3e5;
|
|
1901
|
+
var OUTPUT_TAIL_LIMIT = 2e3;
|
|
1902
|
+
var OUTPUT_BUFFER_LIMIT = 10 * 1024 * 1024;
|
|
1903
|
+
var wakeRunning = false;
|
|
1864
1904
|
async function runWakeCycle(config, deps = {}) {
|
|
1865
|
-
|
|
1866
|
-
|
|
1905
|
+
if (wakeRunning) {
|
|
1906
|
+
(deps.warn ?? DEFAULT_WARN2)("[va-claw/daemon] skipping wake: previous wake still running");
|
|
1867
1907
|
return null;
|
|
1868
1908
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1909
|
+
wakeRunning = true;
|
|
1910
|
+
try {
|
|
1911
|
+
return await _runWakeCycleInner(config, deps);
|
|
1912
|
+
} finally {
|
|
1913
|
+
wakeRunning = false;
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
async function _runWakeCycleInner(config, deps = {}) {
|
|
1917
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
1918
|
+
const startedAt = now();
|
|
1919
|
+
let combinedOutput = "";
|
|
1920
|
+
try {
|
|
1921
|
+
const adapter = await (deps.detect ?? detectCliAdapter)({ warn: deps.warn });
|
|
1922
|
+
if (!adapter) {
|
|
1923
|
+
return null;
|
|
1924
|
+
}
|
|
1925
|
+
const prompt = await resolveWakePrompt(config, deps);
|
|
1926
|
+
const timeoutMs = resolveWakeTimeoutMs(config);
|
|
1927
|
+
const wakeArgs = [...adapter.args, prompt];
|
|
1928
|
+
const wakeOptions = {
|
|
1929
|
+
cwd: process.cwd(),
|
|
1930
|
+
env: {
|
|
1931
|
+
...readProcessEnv(),
|
|
1932
|
+
CLAUDECODE: void 0,
|
|
1933
|
+
CLAUDE_CODE_SESSION: void 0
|
|
1934
|
+
}
|
|
1935
|
+
};
|
|
1936
|
+
const result = await (deps.executeWake ?? executeWakeProcess)(adapter.command, wakeArgs, wakeOptions, timeoutMs);
|
|
1937
|
+
combinedOutput = result.combinedOutput;
|
|
1938
|
+
const finishedAt = now();
|
|
1939
|
+
const durationMs = finishedAt.getTime() - startedAt.getTime();
|
|
1940
|
+
if (result.exitCode !== 0) {
|
|
1941
|
+
await writeWakeLogSafe({
|
|
1942
|
+
ts: startedAt.toISOString(),
|
|
1943
|
+
duration_ms: Math.max(0, durationMs),
|
|
1944
|
+
exit_code: result.exitCode,
|
|
1945
|
+
output_tail: tailOutput(result.combinedOutput)
|
|
1946
|
+
}, deps);
|
|
1947
|
+
(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)}`);
|
|
1948
|
+
return null;
|
|
1949
|
+
}
|
|
1950
|
+
await (deps.storeMemory ?? store)(result.stdout, {
|
|
1951
|
+
source: "va-claw-daemon",
|
|
1952
|
+
kind: "wake",
|
|
1953
|
+
cli: adapter.name,
|
|
1954
|
+
identity: config.name,
|
|
1955
|
+
wokeAt: finishedAt.toISOString()
|
|
1956
|
+
});
|
|
1957
|
+
await writeWakeLogSafe({
|
|
1958
|
+
ts: startedAt.toISOString(),
|
|
1959
|
+
duration_ms: Math.max(0, durationMs),
|
|
1960
|
+
exit_code: 0,
|
|
1961
|
+
output_tail: tailOutput(result.combinedOutput)
|
|
1962
|
+
}, deps);
|
|
1963
|
+
return finishedAt;
|
|
1964
|
+
} catch (error) {
|
|
1965
|
+
const failedAt = now();
|
|
1966
|
+
await writeWakeLogSafe({
|
|
1967
|
+
ts: startedAt.toISOString(),
|
|
1968
|
+
duration_ms: Math.max(0, failedAt.getTime() - startedAt.getTime()),
|
|
1969
|
+
exit_code: "crash",
|
|
1970
|
+
output_tail: tailOutput(combinedOutput)
|
|
1971
|
+
}, deps);
|
|
1972
|
+
(deps.warn ?? DEFAULT_WARN2)(`[va-claw/daemon] wake crashed: ${String(error)}`);
|
|
1883
1973
|
return null;
|
|
1884
1974
|
}
|
|
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
1975
|
}
|
|
1895
|
-
function
|
|
1896
|
-
|
|
1897
|
-
|
|
1976
|
+
async function executeWakeProcess(command, args, options, timeoutMs) {
|
|
1977
|
+
let child;
|
|
1978
|
+
try {
|
|
1979
|
+
child = spawn(command, args, {
|
|
1980
|
+
cwd: options.cwd,
|
|
1981
|
+
env: options.env,
|
|
1982
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1983
|
+
});
|
|
1984
|
+
} catch (error) {
|
|
1985
|
+
return {
|
|
1986
|
+
combinedOutput: String(error),
|
|
1987
|
+
exitCode: "spawn_error",
|
|
1988
|
+
stderr: String(error),
|
|
1989
|
+
stdout: ""
|
|
1990
|
+
};
|
|
1898
1991
|
}
|
|
1899
|
-
|
|
1992
|
+
let stdout2 = "";
|
|
1993
|
+
let stderr = "";
|
|
1994
|
+
let combinedOutput = "";
|
|
1995
|
+
let settled = false;
|
|
1996
|
+
let combinedSize = 0;
|
|
1997
|
+
let stdoutSize = 0;
|
|
1998
|
+
let stderrSize = 0;
|
|
1999
|
+
child.stdout.on("data", (chunk) => {
|
|
2000
|
+
const text2 = String(chunk);
|
|
2001
|
+
if (stdoutSize < OUTPUT_BUFFER_LIMIT) {
|
|
2002
|
+
stdout2 += text2;
|
|
2003
|
+
stdoutSize += text2.length;
|
|
2004
|
+
}
|
|
2005
|
+
if (combinedSize < OUTPUT_BUFFER_LIMIT) {
|
|
2006
|
+
combinedOutput += text2;
|
|
2007
|
+
combinedSize += text2.length;
|
|
2008
|
+
}
|
|
2009
|
+
});
|
|
2010
|
+
child.stderr.on("data", (chunk) => {
|
|
2011
|
+
const text2 = String(chunk);
|
|
2012
|
+
if (stderrSize < OUTPUT_BUFFER_LIMIT) {
|
|
2013
|
+
stderr += text2;
|
|
2014
|
+
stderrSize += text2.length;
|
|
2015
|
+
}
|
|
2016
|
+
if (combinedSize < OUTPUT_BUFFER_LIMIT) {
|
|
2017
|
+
combinedOutput += text2;
|
|
2018
|
+
combinedSize += text2.length;
|
|
2019
|
+
}
|
|
2020
|
+
});
|
|
2021
|
+
return await new Promise((resolve5) => {
|
|
2022
|
+
const timer = setTimeout(() => {
|
|
2023
|
+
if (settled) {
|
|
2024
|
+
return;
|
|
2025
|
+
}
|
|
2026
|
+
settled = true;
|
|
2027
|
+
clearTimeout(timer);
|
|
2028
|
+
child.kill("SIGTERM");
|
|
2029
|
+
const killTimer = setTimeout(() => {
|
|
2030
|
+
try {
|
|
2031
|
+
child.kill("SIGKILL");
|
|
2032
|
+
} catch {
|
|
2033
|
+
}
|
|
2034
|
+
}, 5e3);
|
|
2035
|
+
const timeoutResult = {
|
|
2036
|
+
combinedOutput,
|
|
2037
|
+
exitCode: "timeout",
|
|
2038
|
+
stderr,
|
|
2039
|
+
stdout: stdout2
|
|
2040
|
+
};
|
|
2041
|
+
child.once("close", () => {
|
|
2042
|
+
clearTimeout(killTimer);
|
|
2043
|
+
resolve5(timeoutResult);
|
|
2044
|
+
});
|
|
2045
|
+
}, timeoutMs);
|
|
2046
|
+
child.on("error", (error) => {
|
|
2047
|
+
if (settled) {
|
|
2048
|
+
return;
|
|
2049
|
+
}
|
|
2050
|
+
settled = true;
|
|
2051
|
+
clearTimeout(timer);
|
|
2052
|
+
const message = String(error);
|
|
2053
|
+
stderr = stderr === "" ? message : `${stderr}
|
|
2054
|
+
${message}`;
|
|
2055
|
+
combinedOutput += message;
|
|
2056
|
+
resolve5({
|
|
2057
|
+
combinedOutput,
|
|
2058
|
+
exitCode: "spawn_error",
|
|
2059
|
+
stderr,
|
|
2060
|
+
stdout: stdout2
|
|
2061
|
+
});
|
|
2062
|
+
});
|
|
2063
|
+
child.on("close", (code) => {
|
|
2064
|
+
if (settled) {
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
settled = true;
|
|
2068
|
+
clearTimeout(timer);
|
|
2069
|
+
resolve5({
|
|
2070
|
+
combinedOutput,
|
|
2071
|
+
exitCode: typeof code === "number" ? code : 1,
|
|
2072
|
+
stderr,
|
|
2073
|
+
stdout: stdout2
|
|
2074
|
+
});
|
|
2075
|
+
});
|
|
2076
|
+
});
|
|
1900
2077
|
}
|
|
1901
2078
|
function readProcessEnv() {
|
|
1902
2079
|
return process.env ?? {};
|
|
1903
2080
|
}
|
|
2081
|
+
function readFailureOutput(stderr, combinedOutput) {
|
|
2082
|
+
const candidate = stderr.trim() !== "" ? stderr : combinedOutput;
|
|
2083
|
+
const text2 = candidate.trim();
|
|
2084
|
+
return text2 === "" ? "unknown error" : tailOutput(text2, 200);
|
|
2085
|
+
}
|
|
2086
|
+
function resolveWakeTimeoutMs(config) {
|
|
2087
|
+
return typeof config.wakeTimeoutMs === "number" && Number.isFinite(config.wakeTimeoutMs) && config.wakeTimeoutMs > 0 ? config.wakeTimeoutMs : DEFAULT_WAKE_TIMEOUT_MS2;
|
|
2088
|
+
}
|
|
2089
|
+
function tailOutput(text2, limit = OUTPUT_TAIL_LIMIT) {
|
|
2090
|
+
return text2.length <= limit ? text2 : text2.slice(-limit);
|
|
2091
|
+
}
|
|
2092
|
+
async function writeWakeLogSafe(entry, deps) {
|
|
2093
|
+
try {
|
|
2094
|
+
await (deps.writeWakeLog ?? writeWakeLog)(entry);
|
|
2095
|
+
} catch (error) {
|
|
2096
|
+
(deps.warn ?? DEFAULT_WARN2)(`[va-claw/daemon] failed to write wake.log: ${String(error)}`);
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
async function writeWakeLog(entry) {
|
|
2100
|
+
const logPath = resolve3(homedir4(), ".va-claw", "wake.log");
|
|
2101
|
+
await mkdir3(dirname3(logPath), { recursive: true });
|
|
2102
|
+
await appendFile(logPath, `${JSON.stringify(entry)}
|
|
2103
|
+
`, "utf8");
|
|
2104
|
+
}
|
|
1904
2105
|
async function resolveWakePrompt(config, deps) {
|
|
1905
2106
|
try {
|
|
1906
2107
|
const skills = await (deps.listSkills ?? listSkills)();
|
|
@@ -1970,29 +2171,29 @@ async function getDaemonStatus() {
|
|
|
1970
2171
|
}
|
|
1971
2172
|
|
|
1972
2173
|
// packages/daemon/dist/service.js
|
|
1973
|
-
import { mkdir as
|
|
1974
|
-
import { dirname as
|
|
1975
|
-
import { spawnSync as
|
|
2174
|
+
import { mkdir as mkdir4, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
|
|
2175
|
+
import { dirname as dirname5 } from "node:path";
|
|
2176
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
1976
2177
|
|
|
1977
2178
|
// packages/daemon/dist/service-files.js
|
|
1978
|
-
import { homedir as
|
|
1979
|
-
import { dirname as
|
|
2179
|
+
import { homedir as homedir5 } from "node:os";
|
|
2180
|
+
import { dirname as dirname4, join as join4, resolve as resolve4 } from "node:path";
|
|
1980
2181
|
import { fileURLToPath } from "node:url";
|
|
1981
2182
|
function createServiceDefinition(type) {
|
|
1982
|
-
const packageRoot =
|
|
1983
|
-
const repoRoot =
|
|
2183
|
+
const packageRoot = resolve4(dirname4(fileURLToPath(import.meta.url)), "..");
|
|
2184
|
+
const repoRoot = resolve4(packageRoot, "../..");
|
|
1984
2185
|
const runnerPath = join4(packageRoot, "dist", "runner.js");
|
|
1985
2186
|
const nodePath = process.execPath;
|
|
1986
2187
|
if (type === "launchd") {
|
|
1987
2188
|
return {
|
|
1988
|
-
path: join4(
|
|
2189
|
+
path: join4(homedir5(), "Library", "LaunchAgents", "com.va-claw.daemon.plist"),
|
|
1989
2190
|
content: renderLaunchdPlist(repoRoot, [nodePath, runnerPath]),
|
|
1990
2191
|
command: "launchctl",
|
|
1991
|
-
args: ["load", join4(
|
|
2192
|
+
args: ["load", join4(homedir5(), "Library", "LaunchAgents", "com.va-claw.daemon.plist")]
|
|
1992
2193
|
};
|
|
1993
2194
|
}
|
|
1994
2195
|
return {
|
|
1995
|
-
path: join4(
|
|
2196
|
+
path: join4(homedir5(), ".config", "systemd", "user", "va-claw.service"),
|
|
1996
2197
|
content: renderSystemdUnit(repoRoot, [nodePath, runnerPath]),
|
|
1997
2198
|
command: "systemctl",
|
|
1998
2199
|
args: ["--user", "enable", "--now", "va-claw.service"]
|
|
@@ -2002,7 +2203,7 @@ function createUninstallCommand(type) {
|
|
|
2002
2203
|
if (type === "launchd") {
|
|
2003
2204
|
return {
|
|
2004
2205
|
command: "launchctl",
|
|
2005
|
-
args: ["unload", join4(
|
|
2206
|
+
args: ["unload", join4(homedir5(), "Library", "LaunchAgents", "com.va-claw.daemon.plist")]
|
|
2006
2207
|
};
|
|
2007
2208
|
}
|
|
2008
2209
|
return {
|
|
@@ -2057,7 +2258,7 @@ function quoteSystemdArg(value) {
|
|
|
2057
2258
|
// packages/daemon/dist/service.js
|
|
2058
2259
|
async function installDaemonService(type) {
|
|
2059
2260
|
const definition = createServiceDefinition(type);
|
|
2060
|
-
await
|
|
2261
|
+
await mkdir4(dirname5(definition.path), { recursive: true });
|
|
2061
2262
|
await writeFile3(definition.path, definition.content, "utf8");
|
|
2062
2263
|
if (type === "systemd") {
|
|
2063
2264
|
runCommand("systemctl", ["--user", "daemon-reload"]);
|
|
@@ -2074,7 +2275,7 @@ async function uninstallDaemonService(type) {
|
|
|
2074
2275
|
}
|
|
2075
2276
|
}
|
|
2076
2277
|
function runCommand(command, args, allowFailure = false) {
|
|
2077
|
-
const result =
|
|
2278
|
+
const result = spawnSync2(command, args, { encoding: "utf8" });
|
|
2078
2279
|
if (allowFailure || result.status === 0) {
|
|
2079
2280
|
return;
|
|
2080
2281
|
}
|
|
@@ -2083,9 +2284,9 @@ function runCommand(command, args, allowFailure = false) {
|
|
|
2083
2284
|
}
|
|
2084
2285
|
|
|
2085
2286
|
// packages/cli/dist/install-files.js
|
|
2086
|
-
import { access as access2, mkdir as
|
|
2087
|
-
import { homedir as
|
|
2088
|
-
import { dirname as
|
|
2287
|
+
import { access as access2, mkdir as mkdir5, readFile as readFile3, writeFile as writeFile4 } from "node:fs/promises";
|
|
2288
|
+
import { homedir as homedir6 } from "node:os";
|
|
2289
|
+
import { dirname as dirname6, join as join5 } from "node:path";
|
|
2089
2290
|
var CLAUDE_MARKERS = {
|
|
2090
2291
|
start: "<!-- va-claw:identity:start -->",
|
|
2091
2292
|
end: "<!-- va-claw:identity:end -->"
|
|
@@ -2094,15 +2295,18 @@ var CODEX_MARKERS = {
|
|
|
2094
2295
|
start: "<!-- va-claw:codex:start -->",
|
|
2095
2296
|
end: "<!-- va-claw:codex:end -->"
|
|
2096
2297
|
};
|
|
2097
|
-
function resolveClaudeMdPath(home =
|
|
2298
|
+
function resolveClaudeMdPath(home = homedir6()) {
|
|
2098
2299
|
return join5(home, ".claude", "CLAUDE.md");
|
|
2099
2300
|
}
|
|
2100
|
-
function resolveCodexInstructionsPath(home =
|
|
2301
|
+
function resolveCodexInstructionsPath(home = homedir6()) {
|
|
2101
2302
|
return join5(home, ".codex", "instructions.md");
|
|
2102
2303
|
}
|
|
2103
|
-
function resolveMemoryDbPath(home =
|
|
2304
|
+
function resolveMemoryDbPath(home = homedir6()) {
|
|
2104
2305
|
return join5(home, ".va-claw", "memory.db");
|
|
2105
2306
|
}
|
|
2307
|
+
function resolveClawRegistryPath(home = homedir6()) {
|
|
2308
|
+
return join5(home, ".va-claw", "claws.json");
|
|
2309
|
+
}
|
|
2106
2310
|
async function fileExists(path) {
|
|
2107
2311
|
try {
|
|
2108
2312
|
await access2(path);
|
|
@@ -2114,7 +2318,7 @@ async function fileExists(path) {
|
|
|
2114
2318
|
async function upsertManagedBlock(path, block, markers) {
|
|
2115
2319
|
const current = (await readOptionalFile(path)).trim();
|
|
2116
2320
|
const next = appendManagedBlock(current, block, markers);
|
|
2117
|
-
await
|
|
2321
|
+
await mkdir5(dirname6(path), { recursive: true });
|
|
2118
2322
|
await writeFile4(path, next, "utf8");
|
|
2119
2323
|
}
|
|
2120
2324
|
async function removeManagedBlock(path, markers) {
|
|
@@ -2123,7 +2327,7 @@ async function removeManagedBlock(path, markers) {
|
|
|
2123
2327
|
return;
|
|
2124
2328
|
}
|
|
2125
2329
|
const next = stripManagedBlock(current, markers).trim();
|
|
2126
|
-
await
|
|
2330
|
+
await mkdir5(dirname6(path), { recursive: true });
|
|
2127
2331
|
await writeFile4(path, next === "" ? "" : `${next}
|
|
2128
2332
|
`, "utf8");
|
|
2129
2333
|
}
|
|
@@ -2165,8 +2369,9 @@ function createDefaultCliDeps() {
|
|
|
2165
2369
|
codexPath: resolveCodexInstructionsPath(),
|
|
2166
2370
|
configPath: DEFAULT_CONFIG_PATH,
|
|
2167
2371
|
memoryDbPath: resolveMemoryDbPath(),
|
|
2372
|
+
clawRegistryPath: resolveClawRegistryPath(),
|
|
2168
2373
|
platform: process.platform,
|
|
2169
|
-
spawnSync:
|
|
2374
|
+
spawnSync: spawnSync3,
|
|
2170
2375
|
stdout: process.stdout,
|
|
2171
2376
|
stderr: process.stderr,
|
|
2172
2377
|
fileExists,
|
|
@@ -2269,6 +2474,25 @@ function formatSkills(skills) {
|
|
|
2269
2474
|
` path: ${skill.path}`
|
|
2270
2475
|
].join("\n")).join("\n\n");
|
|
2271
2476
|
}
|
|
2477
|
+
function formatClawDefinitions(claws) {
|
|
2478
|
+
if (claws.length === 0) {
|
|
2479
|
+
return "No claws registered.";
|
|
2480
|
+
}
|
|
2481
|
+
return claws.map((claw) => {
|
|
2482
|
+
const lines = [
|
|
2483
|
+
`${claw.name} [${claw.status}]`,
|
|
2484
|
+
` goal: ${claw.goal || "(no goal)"}`,
|
|
2485
|
+
` cli: ${claw.cliCommand || "va-claw"}`,
|
|
2486
|
+
` tags: ${claw.tags.join(", ") || "(none)"}`,
|
|
2487
|
+
` note: ${claw.note || "(none)"}`,
|
|
2488
|
+
` updatedAt: ${claw.updatedAt}`
|
|
2489
|
+
];
|
|
2490
|
+
if (claw.lastSeenAt) {
|
|
2491
|
+
lines.push(` lastSeenAt: ${claw.lastSeenAt}`);
|
|
2492
|
+
}
|
|
2493
|
+
return lines.join("\n");
|
|
2494
|
+
}).join("\n\n");
|
|
2495
|
+
}
|
|
2272
2496
|
function writeLine(stream, message) {
|
|
2273
2497
|
stream.write(`${message}
|
|
2274
2498
|
`);
|
|
@@ -2276,7 +2500,7 @@ function writeLine(stream, message) {
|
|
|
2276
2500
|
|
|
2277
2501
|
// packages/cli/dist/wait.js
|
|
2278
2502
|
async function waitForStopSignal(stop) {
|
|
2279
|
-
await new Promise((
|
|
2503
|
+
await new Promise((resolve5, reject) => {
|
|
2280
2504
|
let stopping = false;
|
|
2281
2505
|
const cleanup = () => {
|
|
2282
2506
|
process.off("SIGINT", onSignal);
|
|
@@ -2289,7 +2513,7 @@ async function waitForStopSignal(stop) {
|
|
|
2289
2513
|
stopping = true;
|
|
2290
2514
|
void stop().then(() => {
|
|
2291
2515
|
cleanup();
|
|
2292
|
-
|
|
2516
|
+
resolve5();
|
|
2293
2517
|
}, (error) => {
|
|
2294
2518
|
cleanup();
|
|
2295
2519
|
reject(error);
|
|
@@ -2420,7 +2644,7 @@ function parseMetadata2(raw) {
|
|
|
2420
2644
|
}
|
|
2421
2645
|
|
|
2422
2646
|
// packages/cli/dist/platform.js
|
|
2423
|
-
import { homedir as
|
|
2647
|
+
import { homedir as homedir7 } from "node:os";
|
|
2424
2648
|
import { join as join6 } from "node:path";
|
|
2425
2649
|
function detectServiceType(platform) {
|
|
2426
2650
|
if (platform === "darwin") {
|
|
@@ -2431,27 +2655,163 @@ function detectServiceType(platform) {
|
|
|
2431
2655
|
}
|
|
2432
2656
|
throw new Error(`Unsupported platform for va-claw daemon service: ${platform}`);
|
|
2433
2657
|
}
|
|
2434
|
-
function probeServiceRunning(type,
|
|
2658
|
+
function probeServiceRunning(type, spawnSync4) {
|
|
2435
2659
|
if (type === "launchd") {
|
|
2436
|
-
return
|
|
2660
|
+
return spawnSync4("launchctl", ["list", "com.va-claw.daemon"], { encoding: "utf8" }).status === 0;
|
|
2437
2661
|
}
|
|
2438
|
-
const result =
|
|
2662
|
+
const result = spawnSync4("systemctl", ["--user", "is-active", "va-claw.service"], {
|
|
2439
2663
|
encoding: "utf8"
|
|
2440
2664
|
});
|
|
2441
2665
|
return result.status === 0 && result.stdout.trim() === "active";
|
|
2442
2666
|
}
|
|
2443
|
-
function stopInstalledService(type,
|
|
2667
|
+
function stopInstalledService(type, spawnSync4) {
|
|
2444
2668
|
if (type === "launchd") {
|
|
2445
|
-
|
|
2669
|
+
spawnSync4("launchctl", ["unload", resolveLaunchdPath()], { encoding: "utf8" });
|
|
2446
2670
|
return;
|
|
2447
2671
|
}
|
|
2448
|
-
|
|
2672
|
+
spawnSync4("systemctl", ["--user", "stop", "va-claw.service"], { encoding: "utf8" });
|
|
2449
2673
|
}
|
|
2450
2674
|
function resolveLaunchdPath() {
|
|
2451
|
-
return join6(
|
|
2675
|
+
return join6(homedir7(), "Library", "LaunchAgents", "com.va-claw.daemon.plist");
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
// packages/cli/dist/claw-store.js
|
|
2679
|
+
import { mkdir as mkdir6, readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
|
|
2680
|
+
import { dirname as dirname7 } from "node:path";
|
|
2681
|
+
var DEFAULT_CLAW_STATUS = "idle";
|
|
2682
|
+
var KNOWN_STATUSES = /* @__PURE__ */ new Set(["running", "working", "idle", "waiting", "error", "offline", "stopped"]);
|
|
2683
|
+
var REGISTRY_VERSION = 1;
|
|
2684
|
+
var DEFAULT_NOW = () => /* @__PURE__ */ new Date();
|
|
2685
|
+
function isClawStatus(value) {
|
|
2686
|
+
return KNOWN_STATUSES.has(value ?? "");
|
|
2687
|
+
}
|
|
2688
|
+
function pickString2(value, fallback = "") {
|
|
2689
|
+
return typeof value === "string" ? value.trim() : fallback;
|
|
2690
|
+
}
|
|
2691
|
+
function pickTags(value) {
|
|
2692
|
+
if (!Array.isArray(value)) {
|
|
2693
|
+
return [];
|
|
2694
|
+
}
|
|
2695
|
+
return value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter((entry) => entry.length > 0);
|
|
2696
|
+
}
|
|
2697
|
+
function normalizeStatus(value) {
|
|
2698
|
+
return isClawStatus(typeof value === "string" ? value : void 0) ? value : DEFAULT_CLAW_STATUS;
|
|
2699
|
+
}
|
|
2700
|
+
function validateClawStatus(value) {
|
|
2701
|
+
if (typeof value !== "string") {
|
|
2702
|
+
return null;
|
|
2703
|
+
}
|
|
2704
|
+
const normalized = value.trim().toLowerCase();
|
|
2705
|
+
return isClawStatus(normalized) ? normalized : null;
|
|
2706
|
+
}
|
|
2707
|
+
function normalizeClawDefinition(raw) {
|
|
2708
|
+
if (!raw || typeof raw !== "object") {
|
|
2709
|
+
return null;
|
|
2710
|
+
}
|
|
2711
|
+
const name = pickString2(Reflect.get(raw, "name"));
|
|
2712
|
+
if (name === "") {
|
|
2713
|
+
return null;
|
|
2714
|
+
}
|
|
2715
|
+
const createdAt = pickString2(Reflect.get(raw, "createdAt"), (/* @__PURE__ */ new Date(0)).toISOString());
|
|
2716
|
+
const updatedAt = pickString2(Reflect.get(raw, "updatedAt"), createdAt);
|
|
2717
|
+
return {
|
|
2718
|
+
name,
|
|
2719
|
+
goal: pickString2(Reflect.get(raw, "goal")),
|
|
2720
|
+
status: normalizeStatus(Reflect.get(raw, "status")),
|
|
2721
|
+
cliCommand: pickString2(Reflect.get(raw, "cliCommand"), "va-claw"),
|
|
2722
|
+
note: pickString2(Reflect.get(raw, "note")),
|
|
2723
|
+
tags: pickTags(Reflect.get(raw, "tags")),
|
|
2724
|
+
lastSeenAt: pickString2(Reflect.get(raw, "lastSeenAt"), "").trim() || void 0,
|
|
2725
|
+
createdAt,
|
|
2726
|
+
updatedAt
|
|
2727
|
+
};
|
|
2728
|
+
}
|
|
2729
|
+
function normalizeClawRegistry(raw) {
|
|
2730
|
+
if (!raw || typeof raw !== "object") {
|
|
2731
|
+
return { version: REGISTRY_VERSION, claws: [] };
|
|
2732
|
+
}
|
|
2733
|
+
const version = typeof Reflect.get(raw, "version") === "number" ? Reflect.get(raw, "version") : REGISTRY_VERSION;
|
|
2734
|
+
const rawClaws = Reflect.get(raw, "claws");
|
|
2735
|
+
const claws = Array.isArray(rawClaws) ? rawClaws.map(normalizeClawDefinition).filter(Boolean) : [];
|
|
2736
|
+
return { version: Number(version) > 0 ? Number(version) : REGISTRY_VERSION, claws };
|
|
2737
|
+
}
|
|
2738
|
+
async function listClaws(registryPath) {
|
|
2739
|
+
const registry = await loadClawRegistry(registryPath);
|
|
2740
|
+
return [...registry.claws];
|
|
2741
|
+
}
|
|
2742
|
+
async function registerClaw(registryPath, input, now = DEFAULT_NOW) {
|
|
2743
|
+
const registry = await loadClawRegistry(registryPath);
|
|
2744
|
+
const exists2 = registry.claws.some((entry) => entry.name === input.name);
|
|
2745
|
+
if (exists2) {
|
|
2746
|
+
throw new Error(`Claw already exists: ${input.name}`);
|
|
2747
|
+
}
|
|
2748
|
+
const timestamp = now().toISOString();
|
|
2749
|
+
const claw = {
|
|
2750
|
+
name: input.name,
|
|
2751
|
+
goal: pickString2(input.goal),
|
|
2752
|
+
status: input.status ?? DEFAULT_CLAW_STATUS,
|
|
2753
|
+
cliCommand: pickString2(input.cliCommand, "va-claw"),
|
|
2754
|
+
note: pickString2(input.note),
|
|
2755
|
+
tags: Array.isArray(input.tags) ? input.tags.filter((tag) => tag.trim() !== "") : [],
|
|
2756
|
+
createdAt: timestamp,
|
|
2757
|
+
updatedAt: timestamp
|
|
2758
|
+
};
|
|
2759
|
+
registry.claws = [claw, ...registry.claws];
|
|
2760
|
+
await saveClawRegistry(registryPath, registry);
|
|
2761
|
+
return claw;
|
|
2762
|
+
}
|
|
2763
|
+
async function updateClaw(registryPath, name, updates, now = DEFAULT_NOW) {
|
|
2764
|
+
const registry = await loadClawRegistry(registryPath);
|
|
2765
|
+
const index = registry.claws.findIndex((entry) => entry.name === name);
|
|
2766
|
+
if (index < 0) {
|
|
2767
|
+
return null;
|
|
2768
|
+
}
|
|
2769
|
+
const nowIso = now().toISOString();
|
|
2770
|
+
const current = registry.claws[index];
|
|
2771
|
+
const next = {
|
|
2772
|
+
...current,
|
|
2773
|
+
goal: updates.goal ?? current.goal,
|
|
2774
|
+
status: updates.status ?? current.status,
|
|
2775
|
+
cliCommand: updates.cliCommand ?? current.cliCommand,
|
|
2776
|
+
note: updates.note ?? current.note,
|
|
2777
|
+
tags: Array.isArray(updates.tags) ? updates.tags.filter((tag) => tag.trim() !== "") : current.tags,
|
|
2778
|
+
lastSeenAt: updates.seen ? nowIso : current.lastSeenAt,
|
|
2779
|
+
updatedAt: nowIso
|
|
2780
|
+
};
|
|
2781
|
+
registry.claws[index] = next;
|
|
2782
|
+
await saveClawRegistry(registryPath, registry);
|
|
2783
|
+
return next;
|
|
2784
|
+
}
|
|
2785
|
+
async function removeClaw(registryPath, name) {
|
|
2786
|
+
const registry = await loadClawRegistry(registryPath);
|
|
2787
|
+
const before = registry.claws.length;
|
|
2788
|
+
registry.claws = registry.claws.filter((entry) => entry.name !== name);
|
|
2789
|
+
if (registry.claws.length === before) {
|
|
2790
|
+
return false;
|
|
2791
|
+
}
|
|
2792
|
+
await saveClawRegistry(registryPath, registry);
|
|
2793
|
+
return true;
|
|
2794
|
+
}
|
|
2795
|
+
async function loadClawRegistry(registryPath) {
|
|
2796
|
+
try {
|
|
2797
|
+
const rawText = await readFile5(registryPath, "utf8");
|
|
2798
|
+
return normalizeClawRegistry(JSON.parse(rawText));
|
|
2799
|
+
} catch (error) {
|
|
2800
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
2801
|
+
return { version: REGISTRY_VERSION, claws: [] };
|
|
2802
|
+
}
|
|
2803
|
+
throw error;
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
async function saveClawRegistry(registryPath, registry) {
|
|
2807
|
+
await mkdir6(dirname7(registryPath), { recursive: true });
|
|
2808
|
+
await writeFile5(registryPath, `${JSON.stringify({ ...registry, version: REGISTRY_VERSION }, null, 2)}
|
|
2809
|
+
`, "utf8");
|
|
2452
2810
|
}
|
|
2453
2811
|
|
|
2454
2812
|
// packages/cli/dist/handlers.js
|
|
2813
|
+
var CLAW_FLEET_PROTOCOL_SKILL_URL = "https://raw.githubusercontent.com/Vadaski/va-claw/main/skills/claw-fleet-protocol.md";
|
|
2814
|
+
var MEMORY_SKILL_URL = "https://raw.githubusercontent.com/Vadaski/va-claw/main/skills/memory-protocol.md";
|
|
2455
2815
|
async function runInstall(target, deps) {
|
|
2456
2816
|
const installTarget = normalizeInstallTarget(target);
|
|
2457
2817
|
const config = await deps.fileExists(deps.configPath) ? await deps.loadIdentity() : await deps.runInstallWizard();
|
|
@@ -2468,6 +2828,14 @@ async function runInstall(target, deps) {
|
|
|
2468
2828
|
const serviceType = detectServiceType(deps.platform);
|
|
2469
2829
|
await deps.installDaemonService(serviceType);
|
|
2470
2830
|
summary.push(`Daemon service: ${serviceType}`);
|
|
2831
|
+
const fleetSkillName = await installFleetProtocolSkill(deps);
|
|
2832
|
+
if (fleetSkillName) {
|
|
2833
|
+
summary.push(`Claw fleet protocol skill: ${fleetSkillName}`);
|
|
2834
|
+
}
|
|
2835
|
+
const memorySkillName = await installMemoryProtocolSkill(deps);
|
|
2836
|
+
if (memorySkillName) {
|
|
2837
|
+
summary.push(`Memory protocol skill: ${memorySkillName}`);
|
|
2838
|
+
}
|
|
2471
2839
|
for (const line of summary) {
|
|
2472
2840
|
writeLine(deps.stdout, line);
|
|
2473
2841
|
}
|
|
@@ -2500,6 +2868,150 @@ async function runStatus(deps) {
|
|
|
2500
2868
|
writeLine(deps.stdout, `Last wake: ${lastWakeAt ?? "never"}`);
|
|
2501
2869
|
writeLine(deps.stdout, `Memory entries: ${memoryCount}`);
|
|
2502
2870
|
}
|
|
2871
|
+
async function runClawStatus(deps) {
|
|
2872
|
+
const clawStatus = await buildProtocolReport(deps);
|
|
2873
|
+
writeLine(deps.stdout, formatClawDefinitions(clawStatus.claws));
|
|
2874
|
+
writeLine(deps.stdout, `Daemon running: ${clawStatus.runtime.running ? "yes" : "no"} | service: ${clawStatus.runtime.serviceRunning ? "running" : "stopped"} | wakeCount: ${clawStatus.runtime.wakeCount}`);
|
|
2875
|
+
}
|
|
2876
|
+
async function runClawList(deps) {
|
|
2877
|
+
const claws = await listClaws(deps.clawRegistryPath);
|
|
2878
|
+
writeLine(deps.stdout, formatClawDefinitions(claws));
|
|
2879
|
+
}
|
|
2880
|
+
async function runClawAdd(name, options, deps) {
|
|
2881
|
+
const normalizedName = name.trim();
|
|
2882
|
+
if (normalizedName === "") {
|
|
2883
|
+
throw new Error("Claw name cannot be empty.");
|
|
2884
|
+
}
|
|
2885
|
+
const status = options.status ? validateClawStatus(options.status) : null;
|
|
2886
|
+
if (options.status && status === null) {
|
|
2887
|
+
throw new Error(`Invalid claw status: ${options.status}`);
|
|
2888
|
+
}
|
|
2889
|
+
const claw = await registerClaw(deps.clawRegistryPath, {
|
|
2890
|
+
name: normalizedName,
|
|
2891
|
+
goal: options.goal,
|
|
2892
|
+
status: status ?? void 0,
|
|
2893
|
+
cliCommand: options.cliCommand,
|
|
2894
|
+
note: options.note,
|
|
2895
|
+
tags: options.tags ? splitCommaList(options.tags) : []
|
|
2896
|
+
});
|
|
2897
|
+
writeLine(deps.stdout, formatClawDefinitions([claw]));
|
|
2898
|
+
}
|
|
2899
|
+
async function runClawUpdate(name, options, deps) {
|
|
2900
|
+
const status = options.status ? validateClawStatus(options.status) : null;
|
|
2901
|
+
const normalizedName = name.trim();
|
|
2902
|
+
if (normalizedName === "") {
|
|
2903
|
+
throw new Error("Claw name cannot be empty.");
|
|
2904
|
+
}
|
|
2905
|
+
if (options.status && status === null) {
|
|
2906
|
+
throw new Error(`Invalid claw status: ${options.status}`);
|
|
2907
|
+
}
|
|
2908
|
+
const patch = {
|
|
2909
|
+
goal: options.goal,
|
|
2910
|
+
status: status ?? void 0,
|
|
2911
|
+
cliCommand: options.cliCommand,
|
|
2912
|
+
note: options.note,
|
|
2913
|
+
tags: options.tags ? splitCommaList(options.tags) : void 0,
|
|
2914
|
+
seen: options.seen === "1" || options.seen === "true" || options.seen === "yes"
|
|
2915
|
+
};
|
|
2916
|
+
const claw = await updateClaw(deps.clawRegistryPath, normalizedName, patch);
|
|
2917
|
+
if (!claw) {
|
|
2918
|
+
writeLine(deps.stdout, `Claw not found: ${name}`);
|
|
2919
|
+
return;
|
|
2920
|
+
}
|
|
2921
|
+
writeLine(deps.stdout, formatClawDefinitions([claw]));
|
|
2922
|
+
}
|
|
2923
|
+
async function runClawRemove(name, deps) {
|
|
2924
|
+
const normalizedName = name.trim();
|
|
2925
|
+
if (normalizedName === "") {
|
|
2926
|
+
throw new Error("Claw name cannot be empty.");
|
|
2927
|
+
}
|
|
2928
|
+
const removed = await removeClaw(deps.clawRegistryPath, normalizedName);
|
|
2929
|
+
writeLine(deps.stdout, removed ? `Removed claw: ${normalizedName}` : `Claw not found: ${normalizedName}`);
|
|
2930
|
+
}
|
|
2931
|
+
async function runClawHeartbeat(name, deps) {
|
|
2932
|
+
const normalizedName = name.trim();
|
|
2933
|
+
if (normalizedName === "") {
|
|
2934
|
+
throw new Error("Claw name cannot be empty.");
|
|
2935
|
+
}
|
|
2936
|
+
const claw = await updateClaw(deps.clawRegistryPath, normalizedName, { seen: true, status: "running" });
|
|
2937
|
+
if (!claw) {
|
|
2938
|
+
writeLine(deps.stdout, `Claw not found: ${name}`);
|
|
2939
|
+
return;
|
|
2940
|
+
}
|
|
2941
|
+
writeLine(deps.stdout, formatClawDefinitions([claw]));
|
|
2942
|
+
}
|
|
2943
|
+
async function runProtocol(deps, textMode = false) {
|
|
2944
|
+
const report = await buildProtocolReport(deps);
|
|
2945
|
+
if (textMode) {
|
|
2946
|
+
writeLine(deps.stdout, `protocol: ${report.protocol}`);
|
|
2947
|
+
writeLine(deps.stdout, `timestamp: ${report.timestamp}`);
|
|
2948
|
+
writeLine(deps.stdout, `daemon.running: ${report.runtime.running}`);
|
|
2949
|
+
writeLine(deps.stdout, `daemon.serviceRunning: ${report.runtime.serviceRunning}`);
|
|
2950
|
+
writeLine(deps.stdout, `daemon.discord: ${report.runtime.discord}`);
|
|
2951
|
+
writeLine(deps.stdout, `daemon.wakeCount: ${report.runtime.wakeCount}`);
|
|
2952
|
+
writeLine(deps.stdout, `daemon.lastWakeAt: ${report.runtime.lastWakeAt ?? "never"}`);
|
|
2953
|
+
writeLine(deps.stdout, `memory.entries: ${report.memory.entries}`);
|
|
2954
|
+
writeLine(deps.stdout, `memory.lastWakeAt: ${report.memory.lastWakeAt ?? "never"}`);
|
|
2955
|
+
writeLine(deps.stdout, "");
|
|
2956
|
+
writeLine(deps.stdout, "Claws:");
|
|
2957
|
+
writeLine(deps.stdout, formatClawDefinitions(report.claws));
|
|
2958
|
+
return;
|
|
2959
|
+
}
|
|
2960
|
+
writeLine(deps.stdout, JSON.stringify(report, null, 2));
|
|
2961
|
+
}
|
|
2962
|
+
async function buildProtocolReport(deps) {
|
|
2963
|
+
const runtime = await deps.getDaemonStatus();
|
|
2964
|
+
const serviceType = safeDetectServiceType(deps.platform);
|
|
2965
|
+
const serviceRunning = serviceType ? probeServiceRunning(serviceType, deps.spawnSync) : false;
|
|
2966
|
+
const runtimeLastWakeAt = runtime.lastWakeAt?.toISOString() ?? null;
|
|
2967
|
+
const fallbackLastWakeAt = await findLastWakeAt(deps.memoryDbPath, deps.fileExists);
|
|
2968
|
+
const memoryCount = await countMemoryEntries(deps.memoryDbPath, deps.fileExists);
|
|
2969
|
+
const claws = await listClaws(deps.clawRegistryPath);
|
|
2970
|
+
return {
|
|
2971
|
+
protocol: "va-claw-claw-protocol-1",
|
|
2972
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2973
|
+
runtime: {
|
|
2974
|
+
running: runtime.running,
|
|
2975
|
+
serviceRunning,
|
|
2976
|
+
discord: runtime.discord,
|
|
2977
|
+
wakeCount: runtime.wakeCount,
|
|
2978
|
+
lastWakeAt: runtimeLastWakeAt ?? fallbackLastWakeAt
|
|
2979
|
+
},
|
|
2980
|
+
memory: {
|
|
2981
|
+
entries: memoryCount,
|
|
2982
|
+
lastWakeAt: runtimeLastWakeAt ?? fallbackLastWakeAt
|
|
2983
|
+
},
|
|
2984
|
+
claws: claws.map((claw) => ({
|
|
2985
|
+
name: claw.name,
|
|
2986
|
+
goal: claw.goal,
|
|
2987
|
+
status: claw.status,
|
|
2988
|
+
cliCommand: claw.cliCommand,
|
|
2989
|
+
note: claw.note,
|
|
2990
|
+
tags: claw.tags,
|
|
2991
|
+
lastSeenAt: claw.lastSeenAt,
|
|
2992
|
+
createdAt: claw.createdAt,
|
|
2993
|
+
updatedAt: claw.updatedAt
|
|
2994
|
+
}))
|
|
2995
|
+
};
|
|
2996
|
+
}
|
|
2997
|
+
async function installFleetProtocolSkill(deps) {
|
|
2998
|
+
return installRemoteSkill(deps, CLAW_FLEET_PROTOCOL_SKILL_URL, "claw-fleet-protocol");
|
|
2999
|
+
}
|
|
3000
|
+
async function installMemoryProtocolSkill(deps) {
|
|
3001
|
+
return installRemoteSkill(deps, MEMORY_SKILL_URL, "memory-protocol");
|
|
3002
|
+
}
|
|
3003
|
+
async function installRemoteSkill(deps, url, name) {
|
|
3004
|
+
try {
|
|
3005
|
+
const response = await fetch(url);
|
|
3006
|
+
if (!response.ok) {
|
|
3007
|
+
return null;
|
|
3008
|
+
}
|
|
3009
|
+
const content = await response.text();
|
|
3010
|
+
return await deps.skillInstall(content, name);
|
|
3011
|
+
} catch {
|
|
3012
|
+
return null;
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
2503
3015
|
async function runMemorySearch(query, deps) {
|
|
2504
3016
|
writeLine(deps.stdout, formatMemoryEntries(await deps.memorySearch(query, 10)));
|
|
2505
3017
|
}
|
|
@@ -2769,6 +3281,7 @@ function createCliProgram(deps = createDefaultCliDeps()) {
|
|
|
2769
3281
|
program.command("stop").description("Stop the daemon.").action(async () => runStop(deps));
|
|
2770
3282
|
program.command("status").description("Show daemon and memory status.").action(async () => runStatus(deps));
|
|
2771
3283
|
program.command("uninstall").description("Remove daemon service and injected prompts.").action(async () => runUninstall(deps));
|
|
3284
|
+
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
3285
|
const memory = program.command("memory").description("Memory operations.");
|
|
2773
3286
|
memory.command("search").description("Search memory.").argument("<query>").action(async (query) => runMemorySearch(query, deps));
|
|
2774
3287
|
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 +3311,13 @@ function createCliProgram(deps = createDefaultCliDeps()) {
|
|
|
2798
3311
|
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
3312
|
slack.command("start").description("Start the Slack channel in the foreground.").action(async () => runSlackChannelStart(deps));
|
|
2800
3313
|
slack.command("status").description("Show Slack channel status.").action(async () => runSlackChannelStatus(deps));
|
|
3314
|
+
const claw = program.command("claw").description("Long-running claw operations.");
|
|
3315
|
+
claw.command("status").description("Show claw status and daemon summary.").action(async () => runClawStatus(deps));
|
|
3316
|
+
claw.command("list").description("List all registered claws.").action(async () => runClawList(deps));
|
|
3317
|
+
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));
|
|
3318
|
+
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));
|
|
3319
|
+
claw.command("heartbeat").description("Mark a claw as active and update lastSeenAt.").argument("<name>").action((name) => runClawHeartbeat(name, deps));
|
|
3320
|
+
claw.command("remove").description("Unregister a claw.").argument("<name>").action((name) => runClawRemove(name, deps));
|
|
2801
3321
|
return program;
|
|
2802
3322
|
}
|
|
2803
3323
|
async function runCli(argv = process.argv, deps) {
|