owlmem 0.7.7 → 0.7.9
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/owlmem.mjs +35 -17
- package/package.json +1 -1
package/owlmem.mjs
CHANGED
|
@@ -41,7 +41,7 @@ import { join, dirname, basename, relative, isAbsolute } from "node:path";
|
|
|
41
41
|
import { fileURLToPath } from "node:url";
|
|
42
42
|
import { spawn, spawnSync } from "node:child_process";
|
|
43
43
|
|
|
44
|
-
export const VERSION = "0.7.
|
|
44
|
+
export const VERSION = "0.7.9";
|
|
45
45
|
const BASE = (process.env.OWLMEM_URL || "https://owlmem.com").replace(/\/$/, "");
|
|
46
46
|
const DIR = process.env.OWLMEM_HOME || join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "owlmem");
|
|
47
47
|
const enc = new TextEncoder(), dec = new TextDecoder();
|
|
@@ -803,10 +803,13 @@ function nextSteps(items, out) {
|
|
|
803
803
|
// This computer is identified by its device key (device.json); the pairing is approved in the console, where each AI has an
|
|
804
804
|
// auto-wake switch and the computer can be removed. If OwlMem answers 401 (computer removed, AI disconnected, account gone)
|
|
805
805
|
// the program removes its own autostart entry and exits.
|
|
806
|
+
// The OwlMem tools an unattended wake run may call without asking: answer the task, read its inbox, and send (report the
|
|
807
|
+
// result to its person, hand work on). 0.7.8 added kb_send (站长 10-28).
|
|
808
|
+
export const WAKE_TOOLS = ["kb_task_update", "kb_inbox", "kb_send"];
|
|
806
809
|
export const WAKE_CLIENTS = {
|
|
807
810
|
"claude-code": { name: "Claude Code", bin: "claude",
|
|
808
|
-
// -p runs one prompt and exits; it may only use the
|
|
809
|
-
command: ["claude", "-p", "--allowedTools", "
|
|
811
|
+
// -p runs one prompt and exits; it may only use the OwlMem tools in WAKE_TOOLS, beyond what your own settings allow
|
|
812
|
+
command: ["claude", "-p", "--allowedTools", WAKE_TOOLS.map((n) => "mcp__owlmem__" + n).join(",")], prompt: "stdin" },
|
|
810
813
|
codex: { name: "Codex", bin: "codex", command: ["codex", "exec", "--skip-git-repo-check", "-"], prompt: "stdin" },
|
|
811
814
|
// agy -p takes the prompt as an argument; headless it denies MCP tools unless settings.json allows them (see allowAntigravity)
|
|
812
815
|
antigravity: { name: "Antigravity", bin: "agy", home: [[".gemini", "bin", "agy"]], command: ["agy", "-p"], prompt: "arg" },
|
|
@@ -826,21 +829,21 @@ export function wakeBin(c, home_ = home(), exists = existsSync, path = onPath) {
|
|
|
826
829
|
}
|
|
827
830
|
return null;
|
|
828
831
|
}
|
|
829
|
-
// codex exec runs with approval "never", so an MCP call that needs approval is refused: approve just the
|
|
830
|
-
// tools
|
|
832
|
+
// codex exec runs with approval "never", so an MCP call that needs approval is refused: approve just the OwlMem
|
|
833
|
+
// tools in WAKE_TOOLS, as tool tables under the [mcp_servers.owlmem] that install wrote (append only)
|
|
831
834
|
export function allowCodex(home_ = home()) {
|
|
832
835
|
const f = join(process.env.CODEX_HOME || join(home_, ".codex"), "config.toml");
|
|
833
836
|
let t; try { t = readFileSync(f, "utf8"); } catch { return false; }
|
|
834
837
|
if (!/^\[mcp_servers\.owlmem\]\s*$/m.test(t)) return false;
|
|
835
|
-
const add =
|
|
838
|
+
const add = WAKE_TOOLS.filter((n) => !new RegExp("^\\[mcp_servers\\.owlmem\\.tools\\." + n + "\\]\\s*$", "m").test(t));
|
|
836
839
|
if (!add.length) return false;
|
|
837
|
-
writeFileSync(f, t.replace(/\n*$/, "\n") + "\n# OwlMem auto-wake: unattended codex exec may call these
|
|
840
|
+
writeFileSync(f, t.replace(/\n*$/, "\n") + "\n# OwlMem auto-wake: unattended codex exec may call these OwlMem tools to report back\n" +
|
|
838
841
|
add.map((n) => `[mcp_servers.owlmem.tools.${n}]\napproval_mode = "approve"\n`).join("\n"));
|
|
839
842
|
return true;
|
|
840
843
|
}
|
|
841
|
-
// Antigravity's CLI reads ~/.gemini/antigravity-cli/settings.json: add only the
|
|
844
|
+
// Antigravity's CLI reads ~/.gemini/antigravity-cli/settings.json: add only the rules for WAKE_TOOLS
|
|
842
845
|
export function allowAntigravity(home_ = home()) {
|
|
843
|
-
const f = join(home_, ".gemini", "antigravity-cli", "settings.json"), need =
|
|
846
|
+
const f = join(home_, ".gemini", "antigravity-cli", "settings.json"), need = WAKE_TOOLS.map((n) => "mcp(owlmem/" + n + ")");
|
|
844
847
|
let j = {};
|
|
845
848
|
if (existsSync(f)) { try { j = JSON.parse(readFileSync(f, "utf8")); } catch { throw new Fail("Can't read " + tilde(f) + " (not JSON); fix it or add " + need.join(", ") + " under permissions.allow yourself."); } }
|
|
846
849
|
const allow = Array.isArray(j.permissions?.allow) ? j.permissions.allow : [];
|
|
@@ -852,8 +855,8 @@ export function allowAntigravity(home_ = home()) {
|
|
|
852
855
|
return true;
|
|
853
856
|
}
|
|
854
857
|
// Hermes calls MCP tools without asking unless the server is marked `trust: untrusted` (or an unknown value) under mcp_servers in
|
|
855
|
-
// ~/.hermes/config.yaml; then unattended runs refuse its write tools. Only in that case, add
|
|
856
|
-
//
|
|
858
|
+
// ~/.hermes/config.yaml; then unattended runs refuse its write tools. Only in that case, add WAKE_TOOLS
|
|
859
|
+
// to the top-level command_allowlist as mcp:<server>/<tool> (what Hermes itself writes for "Always"). Adds lines only,
|
|
857
860
|
// never rewrites what's there; a command_allowlist it can't extend safely (not a plain block list) returns "manual".
|
|
858
861
|
// false: nothing to do (no config, no OwlMem server in it, a trusted one, or the entries are already there).
|
|
859
862
|
export function allowHermes(home_ = home(), env = process.env) {
|
|
@@ -879,10 +882,10 @@ export function allowHermes(home_ = home(), env = process.env) {
|
|
|
879
882
|
break;
|
|
880
883
|
}
|
|
881
884
|
if (!server || trust === "full") return false;
|
|
882
|
-
const need =
|
|
885
|
+
const need = WAKE_TOOLS.map((n) => "mcp:" + server + "/" + n);
|
|
883
886
|
const at = lines.findIndex((l) => /^command_allowlist:/.test(l));
|
|
884
887
|
if (at < 0) {
|
|
885
|
-
writeFileSync(f, t.replace(/\n*$/, "\n") + "# OwlMem auto-wake: unattended hermes -z may call these
|
|
888
|
+
writeFileSync(f, t.replace(/\n*$/, "\n") + "# OwlMem auto-wake: unattended hermes -z may call these OwlMem tools to report back\ncommand_allowlist:\n" +
|
|
886
889
|
need.map((r) => " - " + r + "\n").join(""));
|
|
887
890
|
return true;
|
|
888
891
|
}
|
|
@@ -1305,10 +1308,25 @@ function wakeLogger(dir = DIR) {
|
|
|
1305
1308
|
};
|
|
1306
1309
|
}
|
|
1307
1310
|
|
|
1311
|
+
// Computers set up before a tool joined WAKE_TOOLS (0.7.8: kb_send) catch up when the program starts after its self-update:
|
|
1312
|
+
// Claude Code's --allowedTools in wake.json if it is still one we wrote, and the other apps' own settings (append only).
|
|
1313
|
+
// Returns true when wake.json changed.
|
|
1314
|
+
export function wakeTools(cfg, home_ = home(), env = process.env) {
|
|
1315
|
+
const ours = ["mcp__owlmem__kb_task_update,mcp__owlmem__kb_inbox"], now = WAKE_TOOLS.map((n) => "mcp__owlmem__" + n).join(",");
|
|
1316
|
+
const cmd = cfg.clients?.["claude-code"]?.command, i = Array.isArray(cmd) ? cmd.indexOf("--allowedTools") : -1;
|
|
1317
|
+
let changed = false;
|
|
1318
|
+
if (i >= 0 && ours.includes(cmd[i + 1])) { cmd[i + 1] = now; changed = true; }
|
|
1319
|
+
if (cfg.clients?.codex) allowCodex(home_);
|
|
1320
|
+
if (cfg.clients?.antigravity) allowAntigravity(home_);
|
|
1321
|
+
if (cfg.clients?.hermes) allowHermes(home_, env);
|
|
1322
|
+
return changed;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1308
1325
|
async function wakeRun() {
|
|
1309
1326
|
const cfg = loadWake();
|
|
1310
1327
|
if (!cfg) throw new Fail("Auto-wake isn't set up on this computer. Run: npx -y owlmem wake enable");
|
|
1311
1328
|
const log = wakeLogger(), api = wakeApi();
|
|
1329
|
+
try { if (wakeTools(cfg)) { saveWake(DIR, cfg); log("Unattended runs may now also use: " + WAKE_TOOLS.join(", ") + "."); } } catch (e) { log("tools: " + e.message); }
|
|
1312
1330
|
const report = async (j, res) => {
|
|
1313
1331
|
log((isReply(j.task) ? "Reply #" : "Task #") + j.id + ": " + res.outcome + (res.code != null ? " (exit code " + res.code + ")" : "") + (res.detail ? " — " + res.detail : ""));
|
|
1314
1332
|
for (let i = 0; i < 5; i++) {
|
|
@@ -1349,12 +1367,12 @@ async function wakeEnable(args = []) {
|
|
|
1349
1367
|
throw new Fail("No AI command line found on this computer that auto-wake can start (Claude Code: claude, Codex: codex, Antigravity: agy, Hermes: hermes). Install one, or add its command to " + wakeFile() + " under \"clients\"." +
|
|
1350
1368
|
(told ? "\nOwlMem support has been told; you'll hear from us once your AI app is supported." : ""));
|
|
1351
1369
|
}
|
|
1352
|
-
if (clients.codex && allowCodex()) say("Let Codex report back to OwlMem when nobody is watching (
|
|
1353
|
-
if (clients.antigravity && allowAntigravity()) say("Let Antigravity's command line report back to OwlMem when nobody is watching (
|
|
1370
|
+
if (clients.codex && allowCodex()) say("Let Codex report back to OwlMem when nobody is watching (tool approvals in ~/.codex/config.toml).");
|
|
1371
|
+
if (clients.antigravity && allowAntigravity()) say("Let Antigravity's command line report back to OwlMem when nobody is watching (rules in ~/.gemini/antigravity-cli/settings.json).");
|
|
1354
1372
|
if (clients.hermes) {
|
|
1355
1373
|
const h = allowHermes();
|
|
1356
|
-
if (h === true) say("Let Hermes report back to OwlMem when nobody is watching (
|
|
1357
|
-
else if (h === "manual") say("Hermes treats OwlMem as untrusted: add mcp:<server>/kb_task_update and mcp:<server>/
|
|
1374
|
+
if (h === true) say("Let Hermes report back to OwlMem when nobody is watching (entries under command_allowlist in ~/.hermes/config.yaml).");
|
|
1375
|
+
else if (h === "manual") say("Hermes treats OwlMem as untrusted: add mcp:<server>/kb_task_update, mcp:<server>/kb_inbox and mcp:<server>/kb_send to command_allowlist in ~/.hermes/config.yaml, or it can't report back when nobody is watching.");
|
|
1358
1376
|
}
|
|
1359
1377
|
const cfg = { v: 1, ...WAKE_DEFAULTS, ...old, machine: process.env.OWLMEM_DEVICE_NAME || hostname(), clients, ...(max ? { max_parallel: max } : {}), ...(timeout ? { timeout_min: timeout } : {}), boot: boot || !!old.boot };
|
|
1360
1378
|
// OwlMem knows these app names; a command you add yourself to wake.json goes under "gemini" or "custom"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "owlmem",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.9",
|
|
4
4
|
"description": "OwlMem command line: connect your AI apps to OwlMem in one command (npx -y owlmem install), an end-to-end encrypted vault for your AI agents, and a local MCP server.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": { "owlmem": "owlmem.mjs" },
|