@cerefox/memory 0.10.1 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/cerefox.js +72 -51
- package/docs/guides/access-paths.md +26 -0
- package/docs/guides/agent-coordination.md +2 -0
- package/docs/guides/cli.md +9 -3
- package/docs/guides/configuration.md +3 -10
- package/docs/guides/connect-agents.md +14 -0
- package/docs/guides/operational-cost.md +24 -2
- package/docs/guides/setup-local.md +10 -3
- package/docs/guides/upgrading.md +1 -0
- package/package.json +1 -1
package/dist/bin/cerefox.js
CHANGED
|
@@ -7184,7 +7184,7 @@ var exports_meta = {};
|
|
|
7184
7184
|
__export(exports_meta, {
|
|
7185
7185
|
PKG_VERSION: () => PKG_VERSION
|
|
7186
7186
|
});
|
|
7187
|
-
var PKG_VERSION = "0.10.
|
|
7187
|
+
var PKG_VERSION = "0.10.2";
|
|
7188
7188
|
var init_meta = () => {};
|
|
7189
7189
|
|
|
7190
7190
|
// ../../node_modules/.bun/tslib@2.8.1/node_modules/tslib/tslib.js
|
|
@@ -68198,26 +68198,26 @@ function collectNodes() {
|
|
|
68198
68198
|
nodes.sort((a, b) => a.path.localeCompare(b.path));
|
|
68199
68199
|
return nodes;
|
|
68200
68200
|
}
|
|
68201
|
-
function bashScript(nodes) {
|
|
68201
|
+
function bashScript(nodes, prog, fn) {
|
|
68202
68202
|
const candCases = nodes.map((n) => ` "${n.path}") echo "${n.candidates.join(" ")}" ;;`).join(`
|
|
68203
68203
|
`);
|
|
68204
68204
|
const pathPatterns = nodes.filter((n) => n.path !== "").map((n) => `"${n.path}"`).join("|");
|
|
68205
68205
|
return `# Cerefox bash completion. Source from ~/.bashrc:
|
|
68206
|
-
# source <(
|
|
68206
|
+
# source <(${prog} completion bash)
|
|
68207
68207
|
#
|
|
68208
|
-
|
|
68208
|
+
_${fn}_candidates() {
|
|
68209
68209
|
case "$1" in
|
|
68210
68210
|
${candCases}
|
|
68211
68211
|
*) echo "--help" ;;
|
|
68212
68212
|
esac
|
|
68213
68213
|
}
|
|
68214
|
-
|
|
68214
|
+
_${fn}_is_path() {
|
|
68215
68215
|
case "$1" in
|
|
68216
68216
|
${pathPatterns}) return 0 ;;
|
|
68217
68217
|
*) return 1 ;;
|
|
68218
68218
|
esac
|
|
68219
68219
|
}
|
|
68220
|
-
|
|
68220
|
+
_${fn}_completion() {
|
|
68221
68221
|
local cur path trial w i
|
|
68222
68222
|
COMPREPLY=()
|
|
68223
68223
|
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
@@ -68227,39 +68227,39 @@ _cerefox_completion() {
|
|
|
68227
68227
|
w="\${COMP_WORDS[$i]}"
|
|
68228
68228
|
case "$w" in -*) break ;; esac
|
|
68229
68229
|
if [ -z "$path" ]; then trial="$w"; else trial="$path $w"; fi
|
|
68230
|
-
if
|
|
68230
|
+
if _${fn}_is_path "$trial"; then
|
|
68231
68231
|
path="$trial"; i=$((i + 1))
|
|
68232
68232
|
else
|
|
68233
68233
|
break
|
|
68234
68234
|
fi
|
|
68235
68235
|
done
|
|
68236
|
-
COMPREPLY=( $(compgen -W "$(
|
|
68236
|
+
COMPREPLY=( $(compgen -W "$(_${fn}_candidates "$path")" -- "$cur") )
|
|
68237
68237
|
return 0
|
|
68238
68238
|
}
|
|
68239
|
-
complete -F
|
|
68239
|
+
complete -F _${fn}_completion ${prog}
|
|
68240
68240
|
`;
|
|
68241
68241
|
}
|
|
68242
|
-
function zshScript(nodes) {
|
|
68242
|
+
function zshScript(nodes, prog, fn) {
|
|
68243
68243
|
const candCases = nodes.map((n) => ` "${n.path}") REPLY="${n.candidates.join(" ")}" ;;`).join(`
|
|
68244
68244
|
`);
|
|
68245
68245
|
const pathPatterns = nodes.filter((n) => n.path !== "").map((n) => `"${n.path}"`).join("|");
|
|
68246
|
-
return `#compdef
|
|
68246
|
+
return `#compdef ${prog}
|
|
68247
68247
|
# Cerefox zsh completion. Save and source from ~/.zshrc:
|
|
68248
|
-
# source <(
|
|
68248
|
+
# source <(${prog} completion zsh)
|
|
68249
68249
|
#
|
|
68250
|
-
|
|
68250
|
+
_${fn}_candidates() {
|
|
68251
68251
|
case "$1" in
|
|
68252
68252
|
${candCases}
|
|
68253
68253
|
*) REPLY="--help" ;;
|
|
68254
68254
|
esac
|
|
68255
68255
|
}
|
|
68256
|
-
|
|
68256
|
+
_${fn}_is_path() {
|
|
68257
68257
|
case "$1" in
|
|
68258
68258
|
${pathPatterns}) return 0 ;;
|
|
68259
68259
|
*) return 1 ;;
|
|
68260
68260
|
esac
|
|
68261
68261
|
}
|
|
68262
|
-
|
|
68262
|
+
_${fn}() {
|
|
68263
68263
|
local path trial w i REPLY
|
|
68264
68264
|
path=""
|
|
68265
68265
|
i=2
|
|
@@ -68267,13 +68267,13 @@ _cerefox() {
|
|
|
68267
68267
|
w="\${words[i]}"
|
|
68268
68268
|
case "$w" in -*) break ;; esac
|
|
68269
68269
|
if [[ -z "$path" ]]; then trial="$w"; else trial="$path $w"; fi
|
|
68270
|
-
if
|
|
68270
|
+
if _${fn}_is_path "$trial"; then
|
|
68271
68271
|
path="$trial"; (( i++ ))
|
|
68272
68272
|
else
|
|
68273
68273
|
break
|
|
68274
68274
|
fi
|
|
68275
68275
|
done
|
|
68276
|
-
|
|
68276
|
+
_${fn}_candidates "$path"
|
|
68277
68277
|
compadd -- \${=REPLY}
|
|
68278
68278
|
}
|
|
68279
68279
|
# Self-bootstrap the completion system if no \`compinit\` has run yet (e.g. this
|
|
@@ -68282,23 +68282,23 @@ _cerefox() {
|
|
|
68282
68282
|
if ! whence compdef >/dev/null 2>&1; then
|
|
68283
68283
|
autoload -Uz compinit && compinit
|
|
68284
68284
|
fi
|
|
68285
|
-
compdef
|
|
68285
|
+
compdef _${fn} ${prog}
|
|
68286
68286
|
`;
|
|
68287
68287
|
}
|
|
68288
|
-
function fishScript(nodes) {
|
|
68288
|
+
function fishScript(nodes, prog, fn) {
|
|
68289
68289
|
const candCases = nodes.map((n) => ` case "${n.path}"
|
|
68290
68290
|
echo "${n.candidates.join(" ")}"`).join(`
|
|
68291
68291
|
`);
|
|
68292
68292
|
const pathList = nodes.filter((n) => n.path !== "").map((n) => `"${n.path}"`).join(" ");
|
|
68293
|
-
return `# Cerefox fish completion. Save to ~/.config/fish/completions
|
|
68294
|
-
function
|
|
68293
|
+
return `# Cerefox fish completion. Save to ~/.config/fish/completions/${prog}.fish
|
|
68294
|
+
function __${fn}_candidates
|
|
68295
68295
|
switch "$argv[1]"
|
|
68296
68296
|
${candCases}
|
|
68297
68297
|
case '*'
|
|
68298
68298
|
echo "--help"
|
|
68299
68299
|
end
|
|
68300
68300
|
end
|
|
68301
|
-
function
|
|
68301
|
+
function __${fn}_is_path
|
|
68302
68302
|
for p in ${pathList}
|
|
68303
68303
|
if test "$argv[1]" = "$p"
|
|
68304
68304
|
return 0
|
|
@@ -68306,7 +68306,7 @@ function __cerefox_is_path
|
|
|
68306
68306
|
end
|
|
68307
68307
|
return 1
|
|
68308
68308
|
end
|
|
68309
|
-
function
|
|
68309
|
+
function __${fn}_complete
|
|
68310
68310
|
set -l tokens (commandline -opc)
|
|
68311
68311
|
set -l path ""
|
|
68312
68312
|
set -l i 2
|
|
@@ -68321,27 +68321,35 @@ function __cerefox_complete
|
|
|
68321
68321
|
else
|
|
68322
68322
|
set trial "$path $w"
|
|
68323
68323
|
end
|
|
68324
|
-
if
|
|
68324
|
+
if __${fn}_is_path "$trial"
|
|
68325
68325
|
set path "$trial"
|
|
68326
68326
|
set i (math $i + 1)
|
|
68327
68327
|
else
|
|
68328
68328
|
break
|
|
68329
68329
|
end
|
|
68330
68330
|
end
|
|
68331
|
-
string split ' ' -- (
|
|
68331
|
+
string split ' ' -- (__${fn}_candidates "$path")
|
|
68332
68332
|
end
|
|
68333
|
-
complete -c
|
|
68333
|
+
complete -c ${prog} -f -a '(__${fn}_complete)'
|
|
68334
68334
|
`;
|
|
68335
68335
|
}
|
|
68336
|
+
function progName() {
|
|
68337
|
+
return buildProgram().name();
|
|
68338
|
+
}
|
|
68339
|
+
function fnId(prog) {
|
|
68340
|
+
return prog.replace(/[^a-zA-Z0-9]/g, "_");
|
|
68341
|
+
}
|
|
68336
68342
|
function scriptFor(shell) {
|
|
68337
68343
|
const nodes = collectNodes();
|
|
68344
|
+
const prog = progName();
|
|
68345
|
+
const fn = fnId(prog);
|
|
68338
68346
|
switch (shell) {
|
|
68339
68347
|
case "bash":
|
|
68340
|
-
return bashScript(nodes);
|
|
68348
|
+
return bashScript(nodes, prog, fn);
|
|
68341
68349
|
case "zsh":
|
|
68342
|
-
return zshScript(nodes);
|
|
68350
|
+
return zshScript(nodes, prog, fn);
|
|
68343
68351
|
case "fish":
|
|
68344
|
-
return fishScript(nodes);
|
|
68352
|
+
return fishScript(nodes, prog, fn);
|
|
68345
68353
|
}
|
|
68346
68354
|
}
|
|
68347
68355
|
function detectShell() {
|
|
@@ -68350,8 +68358,12 @@ function detectShell() {
|
|
|
68350
68358
|
return sh;
|
|
68351
68359
|
return null;
|
|
68352
68360
|
}
|
|
68353
|
-
|
|
68354
|
-
|
|
68361
|
+
function rcBeginFor(prog) {
|
|
68362
|
+
return `# >>> ${prog} shell completion (managed by \`${prog} completion install\`) >>>`;
|
|
68363
|
+
}
|
|
68364
|
+
function rcEndFor(prog) {
|
|
68365
|
+
return `# <<< ${prog} shell completion <<<`;
|
|
68366
|
+
}
|
|
68355
68367
|
async function installMode(options) {
|
|
68356
68368
|
const shell = options.shell ?? detectShell();
|
|
68357
68369
|
if (!shell) {
|
|
@@ -68361,21 +68373,24 @@ async function installMode(options) {
|
|
|
68361
68373
|
throw userError(`Unsupported --shell "${shell}". Use bash, zsh, or fish.`);
|
|
68362
68374
|
}
|
|
68363
68375
|
const home = homedir3();
|
|
68364
|
-
const
|
|
68376
|
+
const prog = progName();
|
|
68377
|
+
const rcBegin = rcBeginFor(prog);
|
|
68378
|
+
const rcEnd = rcEndFor(prog);
|
|
68379
|
+
const scriptPath = join3(home, `.${prog}-completion.${shell}`);
|
|
68365
68380
|
writeFileSync2(scriptPath, scriptFor(shell), "utf8");
|
|
68366
68381
|
println(c.green(`✓ Wrote completion script: ${scriptPath}`));
|
|
68367
68382
|
if (shell === "fish") {
|
|
68368
|
-
println(c.dim(
|
|
68383
|
+
println(c.dim(` For fish, also copy it into ~/.config/fish/completions/${prog}.fish (or \`source\` it).`));
|
|
68369
68384
|
return;
|
|
68370
68385
|
}
|
|
68371
68386
|
const rcPath = join3(home, shell === "zsh" ? ".zshrc" : ".bashrc");
|
|
68372
68387
|
const sourceLine = `[ -s "${scriptPath}" ] && source "${scriptPath}"`;
|
|
68373
|
-
const block = `${
|
|
68388
|
+
const block = `${rcBegin}
|
|
68374
68389
|
${sourceLine}
|
|
68375
|
-
${
|
|
68390
|
+
${rcEnd}
|
|
68376
68391
|
`;
|
|
68377
68392
|
const existing = existsSync3(rcPath) ? readFileSync3(rcPath, "utf8") : "";
|
|
68378
|
-
if (existing.includes(
|
|
68393
|
+
if (existing.includes(rcBegin)) {
|
|
68379
68394
|
println(c.dim(` ${rcPath} already sources the completion (left as-is).`));
|
|
68380
68395
|
} else {
|
|
68381
68396
|
const interactive = process.stdout.isTTY && !options.yes;
|
|
@@ -69604,6 +69619,12 @@ function defaultCerefoxEntry() {
|
|
|
69604
69619
|
args: ["-y", "--package=@cerefox/memory", "cerefox", "mcp"]
|
|
69605
69620
|
};
|
|
69606
69621
|
}
|
|
69622
|
+
function localCerefoxEntry() {
|
|
69623
|
+
return {
|
|
69624
|
+
command: process.env.CEREFOX_LOCAL_CMD || "cerefox-local",
|
|
69625
|
+
args: ["mcp"]
|
|
69626
|
+
};
|
|
69627
|
+
}
|
|
69607
69628
|
function claudeCodeUserConfigPath() {
|
|
69608
69629
|
return join4(homedir4(), ".claude.json");
|
|
69609
69630
|
}
|
|
@@ -69617,8 +69638,7 @@ function claudeDesktopConfigPath() {
|
|
|
69617
69638
|
}
|
|
69618
69639
|
return join4(home, ".config", "Claude", "claude_desktop_config.json");
|
|
69619
69640
|
}
|
|
69620
|
-
function claudeCodeDelegated() {
|
|
69621
|
-
const entry = defaultCerefoxEntry();
|
|
69641
|
+
function claudeCodeDelegated(entry) {
|
|
69622
69642
|
return {
|
|
69623
69643
|
cmd: "claude",
|
|
69624
69644
|
args: ["mcp", "add", "cerefox", "--scope", "user", "--", entry.command, ...entry.args]
|
|
@@ -69684,7 +69704,7 @@ function writeMcpConfig(writer, opts = {}) {
|
|
|
69684
69704
|
return directWrite(writer, writer.configPath, opts);
|
|
69685
69705
|
}
|
|
69686
69706
|
function directWrite(writer, configPath, opts) {
|
|
69687
|
-
const entry = writer.buildServerEntry();
|
|
69707
|
+
const entry = opts.entry ?? writer.buildServerEntry();
|
|
69688
69708
|
const format = writer.format ?? "json";
|
|
69689
69709
|
if (!opts.dryRun)
|
|
69690
69710
|
mkdirSync2(dirname(configPath), { recursive: true });
|
|
@@ -69725,8 +69745,8 @@ function delegatedWrite(writer, opts) {
|
|
|
69725
69745
|
if (!writer.delegated) {
|
|
69726
69746
|
throw new Error(`${writer.label}: kind=delegated but no delegated() factory`);
|
|
69727
69747
|
}
|
|
69728
|
-
const
|
|
69729
|
-
const
|
|
69748
|
+
const entry = opts.entry ?? writer.buildServerEntry();
|
|
69749
|
+
const { cmd, args } = writer.delegated(entry);
|
|
69730
69750
|
const delegatedCommand = `${cmd} ${args.join(" ")}`;
|
|
69731
69751
|
if (opts.dryRun) {
|
|
69732
69752
|
return {
|
|
@@ -69771,7 +69791,8 @@ function action6(options) {
|
|
|
69771
69791
|
const result = writeMcpConfig(writer, {
|
|
69772
69792
|
customPath: options.configPath,
|
|
69773
69793
|
noBackup: !options.backup,
|
|
69774
|
-
dryRun: options.dryRun
|
|
69794
|
+
dryRun: options.dryRun,
|
|
69795
|
+
entry: options.local ? localCerefoxEntry() : undefined
|
|
69775
69796
|
});
|
|
69776
69797
|
if (options.json) {
|
|
69777
69798
|
printJson(result);
|
|
@@ -69815,7 +69836,7 @@ function restartHint(id) {
|
|
|
69815
69836
|
}
|
|
69816
69837
|
}
|
|
69817
69838
|
function registerConfigureAgent(program2) {
|
|
69818
|
-
program2.command("configure-agent").description("Write the MCP server config for a supported client.").requiredOption("-t, --tool <client>", "Target client: claude-code, claude-desktop, cursor, codex, gemini.").option("--config-path <path>", "Override the default config-file path.").option("--no-backup", "Skip the .pre-cerefox.bak backup of any existing config.").option("--dry-run", "Print the planned write without modifying any file.").option("--json", "Emit JSON describing the result.").action(action6);
|
|
69839
|
+
program2.command("configure-agent").description("Write the MCP server config for a supported client.").requiredOption("-t, --tool <client>", "Target client: claude-code, claude-desktop, cursor, codex, gemini.").option("--config-path <path>", "Override the default config-file path.").option("--no-backup", "Skip the .pre-cerefox.bak backup of any existing config.").option("--dry-run", "Print the planned write without modifying any file.").option("--json", "Emit JSON describing the result.").option("--local", "Wire the local/self-hosted backend (`cerefox-local mcp`) instead of npx. Used by `cerefox-local configure-agent`.").action(action6);
|
|
69819
69840
|
}
|
|
69820
69841
|
|
|
69821
69842
|
// src/cli/commands/delete-doc.ts
|
|
@@ -76519,7 +76540,7 @@ async function action28(query, options) {
|
|
|
76519
76540
|
const matchCount = parsePositiveInt(options.matchCount, "--match-count", 5);
|
|
76520
76541
|
const alpha = parseFloat01(options.alpha, "--alpha", 0.7);
|
|
76521
76542
|
const minScore = parseFloat01(options.minScore, "--min-score", getMinSearchScore());
|
|
76522
|
-
const maxBytes = parseNonNegativeInt(options.maxBytes, "--max-bytes",
|
|
76543
|
+
const maxBytes = parseNonNegativeInt(options.maxBytes, "--max-bytes", getMaxResponseBytes());
|
|
76523
76544
|
const mode = options.mode ?? "docs";
|
|
76524
76545
|
if (!["docs", "hybrid", "fts"].includes(mode)) {
|
|
76525
76546
|
throw userError(`--mode "${mode}": expected "docs", "hybrid", or "fts".`);
|
|
@@ -76664,7 +76685,7 @@ async function action28(query, options) {
|
|
|
76664
76685
|
}
|
|
76665
76686
|
}
|
|
76666
76687
|
function registerSearch(program2) {
|
|
76667
|
-
program2.command("search").description("Search the knowledge base (hybrid FTS + semantic).").argument("<query>", "Natural-language search query.").option("-c, --match-count <n>", "Maximum number of documents to return.", "5").option("-p, --project-name <name>", "Filter results to a specific project.").option("-f, --metadata-filter <json>", "JSON containment filter; only docs whose metadata contains ALL pairs are returned.").option("--mode <mode>", "Search mode: docs (default), hybrid, fts.", "docs").option("--alpha <float>", "Semantic weight 0..1 (default: 0.7).", "0.7").option("--min-score <float>", "Minimum cosine similarity threshold (default: CEREFOX_MIN_SEARCH_SCORE or 0.5).").option("--max-bytes <n>", "Response size budget in bytes
|
|
76688
|
+
program2.command("search").description("Search the knowledge base (hybrid FTS + semantic).").argument("<query>", "Natural-language search query.").option("-c, --match-count <n>", "Maximum number of documents to return.", "5").option("-p, --project-name <name>", "Filter results to a specific project.").option("-f, --metadata-filter <json>", "JSON containment filter; only docs whose metadata contains ALL pairs are returned.").option("--mode <mode>", "Search mode: docs (default), hybrid, fts.", "docs").option("--alpha <float>", "Semantic weight 0..1 (default: 0.7).", "0.7").option("--min-score <float>", "Minimum cosine similarity threshold (default: CEREFOX_MIN_SEARCH_SCORE or 0.5).").option("--max-bytes <n>", "Response size budget in bytes (default: CEREFOX_MAX_RESPONSE_BYTES or 200000).").option("-r, --requestor <name>", "Agent / user name (recorded in usage log).").option("--json", "Emit machine-readable JSON instead of the default text.").option("--only-metadata", "List matching docs (id, score, chunks, chars, partial/full) WITHOUT their content — like the web UI's collapsed result list. Grab a [id:…] then `cerefox document get <id>`.").action(action28);
|
|
76668
76689
|
}
|
|
76669
76690
|
|
|
76670
76691
|
// src/cli/commands/self-update.ts
|
|
@@ -80300,7 +80321,7 @@ async function runSearch(ctx, opts) {
|
|
|
80300
80321
|
p_match_count: Math.min(count, 5),
|
|
80301
80322
|
p_alpha: 0.7,
|
|
80302
80323
|
p_project_id: projectId,
|
|
80303
|
-
p_min_score:
|
|
80324
|
+
p_min_score: getMinSearchScore()
|
|
80304
80325
|
};
|
|
80305
80326
|
if (metadataFilter)
|
|
80306
80327
|
params.p_metadata_filter = metadataFilter;
|
|
@@ -81983,9 +82004,9 @@ function registerRenameHusks(program2) {
|
|
|
81983
82004
|
}
|
|
81984
82005
|
}
|
|
81985
82006
|
function buildProgram() {
|
|
81986
|
-
const
|
|
81987
|
-
const program2 = new Command(
|
|
81988
|
-
Resource groups (run \`${
|
|
82007
|
+
const progName2 = process.env.CEREFOX_PROG_NAME || "cerefox";
|
|
82008
|
+
const program2 = new Command(progName2).description("Cerefox — user-owned shared memory for AI agents.").version(PKG_VERSION, "-v, --version", `Print the ${progName2} version and exit.`).addOption(new Option("--json", "Emit machine-readable JSON on stdout instead of the default human text. " + "Available on read commands; ignored on commands without a JSON shape.").hideHelp()).showHelpAfterError(`(run \`${progName2} --help\` for usage)`).enablePositionalOptions().addHelpText("after", `
|
|
82009
|
+
Resource groups (run \`${progName2} <group> --help\`):
|
|
81989
82010
|
` + ` document get · list · edit · delete · restore · ingest · ingest-dir · version {list·archive·unarchive}
|
|
81990
82011
|
` + ` project list · create · edit · delete
|
|
81991
82012
|
` + ` metadata keys · search
|
|
@@ -82008,8 +82029,8 @@ Exit codes:
|
|
|
82008
82029
|
` + ` 1 user error 3 not found (document / version / project)
|
|
82009
82030
|
` + `
|
|
82010
82031
|
Learn more:
|
|
82011
|
-
` + ` ${
|
|
82012
|
-
` + ` ${
|
|
82032
|
+
` + ` ${progName2} guides list # bundled docs (offline)
|
|
82033
|
+
` + ` ${progName2} doctor # diagnose your install
|
|
82013
82034
|
` + ` https://github.com/fstamatelopoulos/cerefox
|
|
82014
82035
|
`);
|
|
82015
82036
|
registerSearch(program2);
|
|
@@ -147,6 +147,32 @@ operations.
|
|
|
147
147
|
|
|
148
148
|
---
|
|
149
149
|
|
|
150
|
+
## Local / self-hosted (World B) — a different access model
|
|
151
|
+
|
|
152
|
+
Everything above describes the **cloud / Supabase** deployment. The **local / self-hosted**
|
|
153
|
+
backend ([`setup-local.md`](setup-local.md)) runs Postgres + PostgREST + the Cerefox server
|
|
154
|
+
in one Docker container, and its access model is deliberately simpler:
|
|
155
|
+
|
|
156
|
+
- **No Layer 1 (Edge Functions) and no anon-JWT.** There are no Edge Functions; the
|
|
157
|
+
`cerefox-server` inside the container exposes `/rest/v1` (a reverse-proxy to the in-container
|
|
158
|
+
PostgREST) plus `/app` + `/api/v1`. Remote agents over HTTP are not a goal of World B.
|
|
159
|
+
- **The access token never leaves the container.** db-init self-generates the PostgREST JWT
|
|
160
|
+
secret on boot and mints a `service_role` token into the container's runtime env. The web
|
|
161
|
+
UI (served by the container) and the in-container CLI/MCP read it internally — nothing on
|
|
162
|
+
the host holds it.
|
|
163
|
+
- **Agents use stdio over `docker exec`, not a network credential.** `cerefox-local mcp`
|
|
164
|
+
runs `cerefox mcp` inside the container via `docker exec -i`; the MCP client launches that
|
|
165
|
+
as a local subprocess. No URL, no bearer token in the client config.
|
|
166
|
+
- **The only host-side secret is `OPENAI_API_KEY`** (in `~/.cerefox/local/.env`), used for
|
|
167
|
+
embeddings — the same as every deployment.
|
|
168
|
+
- By default the container publishes on **`127.0.0.1`** (loopback only); set
|
|
169
|
+
`CEREFOX_LOCAL_BIND=0.0.0.0` to expose it on the LAN.
|
|
170
|
+
|
|
171
|
+
So Layers 1–3 below apply to the cloud deployment; the local backend collapses them into a
|
|
172
|
+
single container with an internally-held token.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
150
176
|
## Summary
|
|
151
177
|
|
|
152
178
|
| Caller | Transport | Auth credential | Typical use |
|
|
@@ -23,6 +23,8 @@ Within a single runtime (e.g., Claude Code's agent teams feature, or a LangGraph
|
|
|
23
23
|
|
|
24
24
|
Cerefox sits in a unique position: it is vendor-neutral, protocol-native (MCP + REST), and designed for persistent storage. Any agent that can make an HTTP call can read and write to Cerefox.
|
|
25
25
|
|
|
26
|
+
> **Cross-machine coordination assumes the cloud / Supabase backend** (a shared network endpoint). The default **local / self-hosted (Docker) backend is single-machine** — it binds `127.0.0.1` and agents reach it via `cerefox-local mcp` (stdio). To coordinate agents across machines with a local backend, expose it on your LAN (`CEREFOX_LOCAL_BIND=0.0.0.0`) or use the cloud backend.
|
|
27
|
+
|
|
26
28
|
The coordination model is **asynchronous and knowledge-based**:
|
|
27
29
|
|
|
28
30
|
1. **Agent A writes** a finding, decision, or task breakdown to Cerefox. It does not need to know which agent will consume it.
|
package/docs/guides/cli.md
CHANGED
|
@@ -4,12 +4,18 @@ Comprehensive reference for every `cerefox` subcommand. For tutorials and walkth
|
|
|
4
4
|
|
|
5
5
|
> `--help` is canonical. If anything in this document disagrees with `cerefox <subcommand> --help`, trust `--help` and file an issue against this guide.
|
|
6
6
|
|
|
7
|
+
> **Local / self-hosted (Docker) backend?** Every KB verb below is identical, but you run it
|
|
8
|
+
> as **`cerefox-local <verb>`** (it proxies into the container via `docker exec`); lifecycle
|
|
9
|
+
> is different (`cerefox-local init/start/stop/upgrade/uninstall/status/logs/configure-agent`).
|
|
10
|
+
> A local user sets **only `OPENAI_API_KEY`** — the Supabase/database vars below do **not**
|
|
11
|
+
> apply (the container owns them). See [`setup-local.md`](setup-local.md).
|
|
12
|
+
|
|
7
13
|
## Setup
|
|
8
14
|
|
|
9
|
-
Every command reads configuration from `.env` in the working directory (or environment variables — see [`configuration.md`](configuration.md)). Required at minimum:
|
|
15
|
+
This section is for the **cloud / Supabase** backend. Every command reads configuration from `.env` in the working directory (or environment variables — see [`configuration.md`](configuration.md)). Required at minimum:
|
|
10
16
|
|
|
11
17
|
- `CEREFOX_SUPABASE_URL` and `CEREFOX_SUPABASE_KEY` for any command that talks to Supabase
|
|
12
|
-
- `OPENAI_API_KEY`
|
|
18
|
+
- `OPENAI_API_KEY` for any command that embeds (ingest, search)
|
|
13
19
|
- `CEREFOX_DATABASE_URL` for `cerefox server deploy` and the contributor scripts (`bun scripts/db_*.ts`)
|
|
14
20
|
|
|
15
21
|
The CLI is the TypeScript `@cerefox/memory` package. Invoke any command as plain `cerefox <subcommand>` (installed via the installer or `npm install -g @cerefox/memory` — see [`quickstart.md`](quickstart.md#1-install)).
|
|
@@ -86,7 +92,7 @@ Walks `DIRECTORY` **recursively** (always — there is no recurse toggle) and in
|
|
|
86
92
|
|
|
87
93
|
| Flag | Type | Default | Description |
|
|
88
94
|
|---|---|---|---|
|
|
89
|
-
| `--extensions <list>` (`-e`) | comma list | `.md,.txt` | File extensions to ingest, e.g. `--extensions .md`. |
|
|
95
|
+
| `--extensions <list>` (`-e`) | comma list | `.md,.txt` | File extensions to ingest, e.g. `--extensions .md`. `.docx` is **not** in the default set — opt in with `--extensions .md,.txt,.docx` (converted via mammoth, same as single-file ingest). |
|
|
90
96
|
| `--project-name <name>` (`-p`) | str | _none_ | Project to assign every document to. |
|
|
91
97
|
| `--update-if-exists` (`-u`) | flag | off | Update existing documents by source path / title. |
|
|
92
98
|
| `--metadata <json>` (`-m`) | JSON | `{}` | JSON metadata applied to every file in the run. |
|
|
@@ -250,16 +250,9 @@ OPENAI_API_KEY=sk-...
|
|
|
250
250
|
# All other settings use defaults
|
|
251
251
|
```
|
|
252
252
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
CEREFOX_SUPABASE_URL=https://abcdefghijkl.supabase.co
|
|
257
|
-
CEREFOX_SUPABASE_KEY=eyJhbGciOiJIUzI1NiIs...
|
|
258
|
-
CEREFOX_DATABASE_URL=postgresql://...
|
|
259
|
-
|
|
260
|
-
CEREFOX_EMBEDDER=fireworks
|
|
261
|
-
CEREFOX_FIREWORKS_API_KEY=fw_...
|
|
262
|
-
```
|
|
253
|
+
> **Fireworks is not wired in the TS runtime yet** — `CEREFOX_EMBEDDER=fireworks` /
|
|
254
|
+
> `CEREFOX_FIREWORKS_*` are documented for the retired Python runtime but are currently
|
|
255
|
+
> no-ops (OpenAI is the only embedder implemented today). Tracked for a future release.
|
|
263
256
|
|
|
264
257
|
---
|
|
265
258
|
|
|
@@ -59,6 +59,20 @@ Three top-level paths plus a few special cases:
|
|
|
59
59
|
> available in [`examples/mcp-configs/`](../examples/mcp-configs/). Pick the one for your
|
|
60
60
|
> client, replace the placeholders, and you're connected.
|
|
61
61
|
|
|
62
|
+
### Local / self-hosted (World B)
|
|
63
|
+
|
|
64
|
+
If you run the **Docker backend** ([`setup-local.md`](setup-local.md)) instead of cloud, the
|
|
65
|
+
MCP path is different: the server runs **inside the container**, launched per session over
|
|
66
|
+
`docker exec`. There's no URL or bearer token in the client config — the access token stays
|
|
67
|
+
in the container.
|
|
68
|
+
|
|
69
|
+
- **Easiest:** `cerefox-local configure-agent` wires it up (registers an MCP server named
|
|
70
|
+
`cerefox-local` with Claude Code if the `claude` CLI is present, else prints the snippet).
|
|
71
|
+
- **Manual:** point the client at `command: cerefox-local, args: ["mcp"]` (stdio). That proxies
|
|
72
|
+
to `cerefox mcp` in the container; the same 10 tools, identical behavior to every other path.
|
|
73
|
+
- The cloud paths above (remote Edge Function, GPT Actions) **do not apply** to a local-only
|
|
74
|
+
install — there are no Edge Functions.
|
|
75
|
+
|
|
62
76
|
---
|
|
63
77
|
|
|
64
78
|
## Prerequisites
|
|
@@ -116,12 +116,34 @@ These limits comfortably cover personal-use traffic. Check
|
|
|
116
116
|
|
|
117
117
|
---
|
|
118
118
|
|
|
119
|
+
## Scenario C — Fully local / self-hosted (Docker)
|
|
120
|
+
|
|
121
|
+
The whole backend runs in one Docker container on your machine — Postgres + pgvector +
|
|
122
|
+
the Cerefox server — with **no Supabase and no Edge Functions** ([`setup-local.md`](setup-local.md)).
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
Your machine
|
|
126
|
+
└── Docker container (free)
|
|
127
|
+
└── Postgres + pgvector + cerefox web/MCP
|
|
128
|
+
Embeddings via OpenAI API (pay-per-use)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Typical cost for personal use**: just the **OpenAI embedding spend** (the same pay-per-use
|
|
132
|
+
as every scenario — fractions of a cent per document) plus local compute/electricity. There
|
|
133
|
+
is **no Supabase tier and no Edge-Function invocation limit** to worry about — the binding
|
|
134
|
+
free-tier constraint from Scenarios A/B (500K EF calls/month) simply doesn't exist here, and
|
|
135
|
+
agents using the local MCP server make zero billable cloud calls. The only ongoing cost is
|
|
136
|
+
embeddings (and only when you ingest or run semantic/hybrid search) — see "Controlling
|
|
137
|
+
embedding costs" below.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
119
141
|
## Controlling embedding costs
|
|
120
142
|
|
|
121
143
|
If you want to keep costs as low as possible:
|
|
122
144
|
|
|
123
|
-
- **
|
|
124
|
-
|
|
145
|
+
- **Cheaper embedding models**: a lower-cost OpenAI-compatible provider (e.g. Fireworks AI)
|
|
146
|
+
is on the roadmap — **not yet wired in the TS runtime** (OpenAI only today).
|
|
125
147
|
- **Batch ingest, don't re-ingest**: Cerefox deduplicates by content hash — re-ingesting the
|
|
126
148
|
same file twice costs nothing. Only new or changed content triggers embedding calls.
|
|
127
149
|
- **`cerefox server reindex`**: Re-embeds all existing chunks if you switch embedders. Run this once
|
|
@@ -36,11 +36,13 @@ That's it. No Node, Bun, Postgres, or repo clone needed.
|
|
|
36
36
|
## Step 1 — Install
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
|
|
39
|
+
curl -fsSL https://github.com/fstamatelopoulos/cerefox/releases/latest/download/install-local.sh | sh
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
This pulls the published multi-arch image (`amd64` + `arm64`), starts the container, and
|
|
43
|
-
installs a `cerefox-local` command (symlinked into `~/.local/bin`).
|
|
43
|
+
installs a `cerefox-local` command (symlinked into `~/.local/bin`). To set your OpenAI key
|
|
44
|
+
inline at install instead of via `cerefox-local init` (Step 2), use the command-substitution
|
|
45
|
+
form: `OPENAI_API_KEY=sk-... sh -c "$(curl -fsSL …/install-local.sh)"`. Pick a specific port
|
|
44
46
|
with `PORT=8017 …`.
|
|
45
47
|
|
|
46
48
|
> If the installer warns that `~/.local/bin` isn't on your `PATH`, add it:
|
|
@@ -48,7 +50,12 @@ with `PORT=8017 …`.
|
|
|
48
50
|
> echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc
|
|
49
51
|
> ```
|
|
50
52
|
|
|
51
|
-
The web UI is now at **http://localhost:8000/app/**
|
|
53
|
+
The web UI is now at **http://localhost:8000/app/** — **or the port the installer chose**
|
|
54
|
+
(it auto-steps to 8010/8020/… if 8000 is busy or you also run the cloud `cerefox web`, which
|
|
55
|
+
defaults to 8000). The installer prints the actual URL; `cerefox-local status` shows it too.
|
|
56
|
+
|
|
57
|
+
The installer also wires **shell tab-completion** for `cerefox-local` (best-effort) — run
|
|
58
|
+
`exec $SHELL` or open a new terminal to activate it.
|
|
52
59
|
|
|
53
60
|
**How the credential works:** the container generates its own JWT secret on first boot and
|
|
54
61
|
mints the access token internally — the token never leaves the container. The only secret
|
package/docs/guides/upgrading.md
CHANGED
|
@@ -10,6 +10,7 @@ to re-run.
|
|
|
10
10
|
|---|---|
|
|
11
11
|
| **Installer / npm** (end user, no repo clone) | `cerefox self-update` (or re-run the [installer](quickstart.md#1-install), or `bun/npm update -g @cerefox/memory`). Then `cerefox server deploy` **if the release notes flag a server-side change**. `cerefox doctor` verifies. |
|
|
12
12
|
| **Source checkout** (`git clone`, contributor) | `git pull`, then `cerefox server deploy` (or the lower-level `bun scripts/db_*.ts` + `npx supabase functions deploy`). Rebuild the SPA if you run `cerefox web` from source. |
|
|
13
|
+
| **Local / self-hosted (Docker, World B)** | `cerefox-local upgrade` — pulls the new image and recreates the container (data persists in the volume; OpenAI key + tuning overrides preserved). **No separate `server deploy`/`reindex`**: the CLI, web, PostgREST, and schema all ship together in one versioned image, so they can't drift. See [`setup-local.md`](setup-local.md). |
|
|
13
14
|
|
|
14
15
|
> **On an old pre-installer clone (0.1.x)?** The cleanest upgrade is to stop
|
|
15
16
|
> running from the repo and install the package: follow the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cerefox/memory",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"description": "Cerefox — user-owned shared memory for AI agents. The local TypeScript runtime: stdio MCP server in v0.4; CLI binary added in v0.5; in-process web server in v0.6; ingestion pipeline in v0.7.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/fstamatelopoulos/cerefox",
|