@cerefox/memory 0.10.0 → 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 +132 -67
- package/dist/server-assets/_shared/ef-meta/index.ts +1 -1
- package/dist/server-assets/_shared/embeddings/index.ts +34 -6
- package/dist/server-assets/_shared/mcp-tools/_utils.ts +38 -2
- package/dist/server-assets/_shared/mcp-tools/metadata-search.ts +3 -2
- package/dist/server-assets/_shared/mcp-tools/search.ts +4 -3
- package/docs/guides/access-paths.md +26 -0
- package/docs/guides/agent-coordination.md +2 -0
- package/docs/guides/cli.md +11 -5
- package/docs/guides/configuration.md +23 -30
- package/docs/guides/connect-agents.md +14 -0
- package/docs/guides/operational-cost.md +24 -2
- package/docs/guides/ops-scripts.md +1 -1
- package/docs/guides/quickstart.md +8 -7
- package/docs/guides/setup-local.md +10 -3
- package/docs/guides/setup-supabase.md +1 -1
- 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
|
|
@@ -24852,20 +24852,31 @@ var init_bundled_docs = __esm(() => {
|
|
|
24852
24852
|
});
|
|
24853
24853
|
|
|
24854
24854
|
// ../../_shared/embeddings/index.ts
|
|
24855
|
+
function openaiEmbeddingConfig() {
|
|
24856
|
+
const env4 = globalThis.process?.env ?? {};
|
|
24857
|
+
const base = env4.CEREFOX_OPENAI_BASE_URL?.replace(/\/+$/, "");
|
|
24858
|
+
const dims = Number.parseInt(env4.CEREFOX_OPENAI_EMBEDDING_DIMENSIONS ?? "", 10);
|
|
24859
|
+
return {
|
|
24860
|
+
url: base ? `${base}/embeddings` : OPENAI_EMBEDDING_URL,
|
|
24861
|
+
model: env4.CEREFOX_OPENAI_EMBEDDING_MODEL || OPENAI_MODEL,
|
|
24862
|
+
dimensions: Number.isNaN(dims) || dims <= 0 ? EMBEDDING_DIMENSIONS : dims
|
|
24863
|
+
};
|
|
24864
|
+
}
|
|
24855
24865
|
async function getEmbedding(text, apiKey) {
|
|
24856
24866
|
let lastError = null;
|
|
24867
|
+
const cfg = openaiEmbeddingConfig();
|
|
24857
24868
|
for (let attempt = 0;attempt < EMBEDDING_MAX_RETRIES; attempt++) {
|
|
24858
24869
|
try {
|
|
24859
|
-
const response = await fetch(
|
|
24870
|
+
const response = await fetch(cfg.url, {
|
|
24860
24871
|
method: "POST",
|
|
24861
24872
|
headers: {
|
|
24862
24873
|
Authorization: `Bearer ${apiKey}`,
|
|
24863
24874
|
"Content-Type": "application/json"
|
|
24864
24875
|
},
|
|
24865
24876
|
body: JSON.stringify({
|
|
24866
|
-
model:
|
|
24877
|
+
model: cfg.model,
|
|
24867
24878
|
input: text,
|
|
24868
|
-
dimensions:
|
|
24879
|
+
dimensions: cfg.dimensions
|
|
24869
24880
|
})
|
|
24870
24881
|
});
|
|
24871
24882
|
if (!response.ok) {
|
|
@@ -24896,18 +24907,19 @@ async function getEmbedding(text, apiKey) {
|
|
|
24896
24907
|
}
|
|
24897
24908
|
async function embedBatchSingleCall(texts, apiKey) {
|
|
24898
24909
|
let lastError = null;
|
|
24910
|
+
const cfg = openaiEmbeddingConfig();
|
|
24899
24911
|
for (let attempt = 0;attempt < EMBEDDING_MAX_RETRIES; attempt++) {
|
|
24900
24912
|
try {
|
|
24901
|
-
const response = await fetch(
|
|
24913
|
+
const response = await fetch(cfg.url, {
|
|
24902
24914
|
method: "POST",
|
|
24903
24915
|
headers: {
|
|
24904
24916
|
Authorization: `Bearer ${apiKey}`,
|
|
24905
24917
|
"Content-Type": "application/json"
|
|
24906
24918
|
},
|
|
24907
24919
|
body: JSON.stringify({
|
|
24908
|
-
model:
|
|
24920
|
+
model: cfg.model,
|
|
24909
24921
|
input: texts,
|
|
24910
|
-
dimensions:
|
|
24922
|
+
dimensions: cfg.dimensions
|
|
24911
24923
|
})
|
|
24912
24924
|
});
|
|
24913
24925
|
if (!response.ok) {
|
|
@@ -53721,6 +53733,20 @@ var require_cli_progress = __commonJS((exports, module) => {
|
|
|
53721
53733
|
});
|
|
53722
53734
|
|
|
53723
53735
|
// ../../_shared/mcp-tools/_utils.ts
|
|
53736
|
+
function getMaxResponseBytes() {
|
|
53737
|
+
const raw = globalThis.process?.env?.CEREFOX_MAX_RESPONSE_BYTES;
|
|
53738
|
+
if (raw === undefined || raw === "")
|
|
53739
|
+
return MAX_RESPONSE_BYTES;
|
|
53740
|
+
const n = Number.parseInt(raw, 10);
|
|
53741
|
+
return Number.isNaN(n) || n <= 0 ? MAX_RESPONSE_BYTES : n;
|
|
53742
|
+
}
|
|
53743
|
+
function getMinSearchScore() {
|
|
53744
|
+
const raw = globalThis.process?.env?.CEREFOX_MIN_SEARCH_SCORE;
|
|
53745
|
+
if (raw === undefined || raw === "")
|
|
53746
|
+
return DEFAULT_MIN_SEARCH_SCORE;
|
|
53747
|
+
const n = Number.parseFloat(raw);
|
|
53748
|
+
return Number.isNaN(n) || n < 0 || n > 1 ? DEFAULT_MIN_SEARCH_SCORE : n;
|
|
53749
|
+
}
|
|
53724
53750
|
function applyByteBudget(rows, maxBytes) {
|
|
53725
53751
|
const accepted = [];
|
|
53726
53752
|
let usedBytes = 0;
|
|
@@ -53748,7 +53774,7 @@ function logUsage(supabase, params) {
|
|
|
53748
53774
|
p_extra: params.extra ?? {}
|
|
53749
53775
|
})).catch(() => {});
|
|
53750
53776
|
}
|
|
53751
|
-
var MAX_RESPONSE_BYTES = 200000;
|
|
53777
|
+
var MAX_RESPONSE_BYTES = 200000, DEFAULT_MIN_SEARCH_SCORE = 0.5;
|
|
53752
53778
|
|
|
53753
53779
|
// ../../_shared/mcp-tools/audit-log.ts
|
|
53754
53780
|
async function handler(supabase, args, ctx) {
|
|
@@ -54531,7 +54557,8 @@ async function handler8(supabase, args, ctx) {
|
|
|
54531
54557
|
if (!projectId)
|
|
54532
54558
|
throw new Error(`Project not found: ${project_name}`);
|
|
54533
54559
|
}
|
|
54534
|
-
const
|
|
54560
|
+
const ceiling = getMaxResponseBytes();
|
|
54561
|
+
const max_bytes = include_content ? Math.min(requested_max_bytes ?? ceiling, ceiling) : null;
|
|
54535
54562
|
const params = {
|
|
54536
54563
|
p_metadata_filter: metadata_filter,
|
|
54537
54564
|
p_project_id: projectId,
|
|
@@ -54624,10 +54651,11 @@ async function handler9(supabase, args, ctx) {
|
|
|
54624
54651
|
const match_count = args.match_count ?? 5;
|
|
54625
54652
|
const mode = args.mode ?? "docs";
|
|
54626
54653
|
const alpha = args.alpha ?? 0.7;
|
|
54627
|
-
const min_score = args.min_score ??
|
|
54654
|
+
const min_score = args.min_score ?? getMinSearchScore();
|
|
54628
54655
|
const metadata_filter = args.metadata_filter ?? null;
|
|
54629
54656
|
const requested_max_bytes = args.max_bytes;
|
|
54630
|
-
const
|
|
54657
|
+
const ceiling = getMaxResponseBytes();
|
|
54658
|
+
const max_bytes = Math.min(requested_max_bytes ?? ceiling, ceiling);
|
|
54631
54659
|
if (metadata_filter !== null && metadata_filter !== undefined && (typeof metadata_filter !== "object" || Array.isArray(metadata_filter))) {
|
|
54632
54660
|
throw new McpInvalidParams("metadata_filter must be a JSON object or null");
|
|
54633
54661
|
}
|
|
@@ -68087,7 +68115,7 @@ function utcStamp() {
|
|
|
68087
68115
|
return d.getUTCFullYear().toString() + pad(d.getUTCMonth() + 1) + pad(d.getUTCDate()) + "T" + pad(d.getUTCHours()) + pad(d.getUTCMinutes()) + pad(d.getUTCSeconds()) + "Z";
|
|
68088
68116
|
}
|
|
68089
68117
|
async function action(options) {
|
|
68090
|
-
const outDir = resolve(expandHome(options.outputDir ?? "~/.cerefox/backups"));
|
|
68118
|
+
const outDir = resolve(expandHome(options.outputDir ?? process.env.CEREFOX_BACKUP_DIR ?? "~/.cerefox/backups"));
|
|
68091
68119
|
if (!existsSync2(outDir))
|
|
68092
68120
|
mkdirSync(outDir, { recursive: true });
|
|
68093
68121
|
const stamp = utcStamp();
|
|
@@ -68133,7 +68161,7 @@ async function action(options) {
|
|
|
68133
68161
|
}
|
|
68134
68162
|
}
|
|
68135
68163
|
function registerBackup(program2) {
|
|
68136
|
-
program2.command("backup").description("Write a JSON snapshot of the knowledge base.").option("-o, --output-dir <dir>", "Snapshot output directory
|
|
68164
|
+
program2.command("backup").description("Write a JSON snapshot of the knowledge base.").option("-o, --output-dir <dir>", "Snapshot output directory (default: CEREFOX_BACKUP_DIR or ~/.cerefox/backups).").option("-l, --label <label>", "Optional suffix added to the filename.").option("--include-versions", "Include archived versions in the snapshot. (v0.5: ignored — current chunks only.)").option("--git", "Commit the snapshot to the output dir as a git checkpoint. (v0.5: ignored.)").action(action);
|
|
68137
68165
|
}
|
|
68138
68166
|
|
|
68139
68167
|
// src/cli/commands/completion.ts
|
|
@@ -68170,26 +68198,26 @@ function collectNodes() {
|
|
|
68170
68198
|
nodes.sort((a, b) => a.path.localeCompare(b.path));
|
|
68171
68199
|
return nodes;
|
|
68172
68200
|
}
|
|
68173
|
-
function bashScript(nodes) {
|
|
68201
|
+
function bashScript(nodes, prog, fn) {
|
|
68174
68202
|
const candCases = nodes.map((n) => ` "${n.path}") echo "${n.candidates.join(" ")}" ;;`).join(`
|
|
68175
68203
|
`);
|
|
68176
68204
|
const pathPatterns = nodes.filter((n) => n.path !== "").map((n) => `"${n.path}"`).join("|");
|
|
68177
68205
|
return `# Cerefox bash completion. Source from ~/.bashrc:
|
|
68178
|
-
# source <(
|
|
68206
|
+
# source <(${prog} completion bash)
|
|
68179
68207
|
#
|
|
68180
|
-
|
|
68208
|
+
_${fn}_candidates() {
|
|
68181
68209
|
case "$1" in
|
|
68182
68210
|
${candCases}
|
|
68183
68211
|
*) echo "--help" ;;
|
|
68184
68212
|
esac
|
|
68185
68213
|
}
|
|
68186
|
-
|
|
68214
|
+
_${fn}_is_path() {
|
|
68187
68215
|
case "$1" in
|
|
68188
68216
|
${pathPatterns}) return 0 ;;
|
|
68189
68217
|
*) return 1 ;;
|
|
68190
68218
|
esac
|
|
68191
68219
|
}
|
|
68192
|
-
|
|
68220
|
+
_${fn}_completion() {
|
|
68193
68221
|
local cur path trial w i
|
|
68194
68222
|
COMPREPLY=()
|
|
68195
68223
|
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
@@ -68199,39 +68227,39 @@ _cerefox_completion() {
|
|
|
68199
68227
|
w="\${COMP_WORDS[$i]}"
|
|
68200
68228
|
case "$w" in -*) break ;; esac
|
|
68201
68229
|
if [ -z "$path" ]; then trial="$w"; else trial="$path $w"; fi
|
|
68202
|
-
if
|
|
68230
|
+
if _${fn}_is_path "$trial"; then
|
|
68203
68231
|
path="$trial"; i=$((i + 1))
|
|
68204
68232
|
else
|
|
68205
68233
|
break
|
|
68206
68234
|
fi
|
|
68207
68235
|
done
|
|
68208
|
-
COMPREPLY=( $(compgen -W "$(
|
|
68236
|
+
COMPREPLY=( $(compgen -W "$(_${fn}_candidates "$path")" -- "$cur") )
|
|
68209
68237
|
return 0
|
|
68210
68238
|
}
|
|
68211
|
-
complete -F
|
|
68239
|
+
complete -F _${fn}_completion ${prog}
|
|
68212
68240
|
`;
|
|
68213
68241
|
}
|
|
68214
|
-
function zshScript(nodes) {
|
|
68242
|
+
function zshScript(nodes, prog, fn) {
|
|
68215
68243
|
const candCases = nodes.map((n) => ` "${n.path}") REPLY="${n.candidates.join(" ")}" ;;`).join(`
|
|
68216
68244
|
`);
|
|
68217
68245
|
const pathPatterns = nodes.filter((n) => n.path !== "").map((n) => `"${n.path}"`).join("|");
|
|
68218
|
-
return `#compdef
|
|
68246
|
+
return `#compdef ${prog}
|
|
68219
68247
|
# Cerefox zsh completion. Save and source from ~/.zshrc:
|
|
68220
|
-
# source <(
|
|
68248
|
+
# source <(${prog} completion zsh)
|
|
68221
68249
|
#
|
|
68222
|
-
|
|
68250
|
+
_${fn}_candidates() {
|
|
68223
68251
|
case "$1" in
|
|
68224
68252
|
${candCases}
|
|
68225
68253
|
*) REPLY="--help" ;;
|
|
68226
68254
|
esac
|
|
68227
68255
|
}
|
|
68228
|
-
|
|
68256
|
+
_${fn}_is_path() {
|
|
68229
68257
|
case "$1" in
|
|
68230
68258
|
${pathPatterns}) return 0 ;;
|
|
68231
68259
|
*) return 1 ;;
|
|
68232
68260
|
esac
|
|
68233
68261
|
}
|
|
68234
|
-
|
|
68262
|
+
_${fn}() {
|
|
68235
68263
|
local path trial w i REPLY
|
|
68236
68264
|
path=""
|
|
68237
68265
|
i=2
|
|
@@ -68239,13 +68267,13 @@ _cerefox() {
|
|
|
68239
68267
|
w="\${words[i]}"
|
|
68240
68268
|
case "$w" in -*) break ;; esac
|
|
68241
68269
|
if [[ -z "$path" ]]; then trial="$w"; else trial="$path $w"; fi
|
|
68242
|
-
if
|
|
68270
|
+
if _${fn}_is_path "$trial"; then
|
|
68243
68271
|
path="$trial"; (( i++ ))
|
|
68244
68272
|
else
|
|
68245
68273
|
break
|
|
68246
68274
|
fi
|
|
68247
68275
|
done
|
|
68248
|
-
|
|
68276
|
+
_${fn}_candidates "$path"
|
|
68249
68277
|
compadd -- \${=REPLY}
|
|
68250
68278
|
}
|
|
68251
68279
|
# Self-bootstrap the completion system if no \`compinit\` has run yet (e.g. this
|
|
@@ -68254,23 +68282,23 @@ _cerefox() {
|
|
|
68254
68282
|
if ! whence compdef >/dev/null 2>&1; then
|
|
68255
68283
|
autoload -Uz compinit && compinit
|
|
68256
68284
|
fi
|
|
68257
|
-
compdef
|
|
68285
|
+
compdef _${fn} ${prog}
|
|
68258
68286
|
`;
|
|
68259
68287
|
}
|
|
68260
|
-
function fishScript(nodes) {
|
|
68288
|
+
function fishScript(nodes, prog, fn) {
|
|
68261
68289
|
const candCases = nodes.map((n) => ` case "${n.path}"
|
|
68262
68290
|
echo "${n.candidates.join(" ")}"`).join(`
|
|
68263
68291
|
`);
|
|
68264
68292
|
const pathList = nodes.filter((n) => n.path !== "").map((n) => `"${n.path}"`).join(" ");
|
|
68265
|
-
return `# Cerefox fish completion. Save to ~/.config/fish/completions
|
|
68266
|
-
function
|
|
68293
|
+
return `# Cerefox fish completion. Save to ~/.config/fish/completions/${prog}.fish
|
|
68294
|
+
function __${fn}_candidates
|
|
68267
68295
|
switch "$argv[1]"
|
|
68268
68296
|
${candCases}
|
|
68269
68297
|
case '*'
|
|
68270
68298
|
echo "--help"
|
|
68271
68299
|
end
|
|
68272
68300
|
end
|
|
68273
|
-
function
|
|
68301
|
+
function __${fn}_is_path
|
|
68274
68302
|
for p in ${pathList}
|
|
68275
68303
|
if test "$argv[1]" = "$p"
|
|
68276
68304
|
return 0
|
|
@@ -68278,7 +68306,7 @@ function __cerefox_is_path
|
|
|
68278
68306
|
end
|
|
68279
68307
|
return 1
|
|
68280
68308
|
end
|
|
68281
|
-
function
|
|
68309
|
+
function __${fn}_complete
|
|
68282
68310
|
set -l tokens (commandline -opc)
|
|
68283
68311
|
set -l path ""
|
|
68284
68312
|
set -l i 2
|
|
@@ -68293,27 +68321,35 @@ function __cerefox_complete
|
|
|
68293
68321
|
else
|
|
68294
68322
|
set trial "$path $w"
|
|
68295
68323
|
end
|
|
68296
|
-
if
|
|
68324
|
+
if __${fn}_is_path "$trial"
|
|
68297
68325
|
set path "$trial"
|
|
68298
68326
|
set i (math $i + 1)
|
|
68299
68327
|
else
|
|
68300
68328
|
break
|
|
68301
68329
|
end
|
|
68302
68330
|
end
|
|
68303
|
-
string split ' ' -- (
|
|
68331
|
+
string split ' ' -- (__${fn}_candidates "$path")
|
|
68304
68332
|
end
|
|
68305
|
-
complete -c
|
|
68333
|
+
complete -c ${prog} -f -a '(__${fn}_complete)'
|
|
68306
68334
|
`;
|
|
68307
68335
|
}
|
|
68336
|
+
function progName() {
|
|
68337
|
+
return buildProgram().name();
|
|
68338
|
+
}
|
|
68339
|
+
function fnId(prog) {
|
|
68340
|
+
return prog.replace(/[^a-zA-Z0-9]/g, "_");
|
|
68341
|
+
}
|
|
68308
68342
|
function scriptFor(shell) {
|
|
68309
68343
|
const nodes = collectNodes();
|
|
68344
|
+
const prog = progName();
|
|
68345
|
+
const fn = fnId(prog);
|
|
68310
68346
|
switch (shell) {
|
|
68311
68347
|
case "bash":
|
|
68312
|
-
return bashScript(nodes);
|
|
68348
|
+
return bashScript(nodes, prog, fn);
|
|
68313
68349
|
case "zsh":
|
|
68314
|
-
return zshScript(nodes);
|
|
68350
|
+
return zshScript(nodes, prog, fn);
|
|
68315
68351
|
case "fish":
|
|
68316
|
-
return fishScript(nodes);
|
|
68352
|
+
return fishScript(nodes, prog, fn);
|
|
68317
68353
|
}
|
|
68318
68354
|
}
|
|
68319
68355
|
function detectShell() {
|
|
@@ -68322,8 +68358,12 @@ function detectShell() {
|
|
|
68322
68358
|
return sh;
|
|
68323
68359
|
return null;
|
|
68324
68360
|
}
|
|
68325
|
-
|
|
68326
|
-
|
|
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
|
+
}
|
|
68327
68367
|
async function installMode(options) {
|
|
68328
68368
|
const shell = options.shell ?? detectShell();
|
|
68329
68369
|
if (!shell) {
|
|
@@ -68333,21 +68373,24 @@ async function installMode(options) {
|
|
|
68333
68373
|
throw userError(`Unsupported --shell "${shell}". Use bash, zsh, or fish.`);
|
|
68334
68374
|
}
|
|
68335
68375
|
const home = homedir3();
|
|
68336
|
-
const
|
|
68376
|
+
const prog = progName();
|
|
68377
|
+
const rcBegin = rcBeginFor(prog);
|
|
68378
|
+
const rcEnd = rcEndFor(prog);
|
|
68379
|
+
const scriptPath = join3(home, `.${prog}-completion.${shell}`);
|
|
68337
68380
|
writeFileSync2(scriptPath, scriptFor(shell), "utf8");
|
|
68338
68381
|
println(c.green(`✓ Wrote completion script: ${scriptPath}`));
|
|
68339
68382
|
if (shell === "fish") {
|
|
68340
|
-
println(c.dim(
|
|
68383
|
+
println(c.dim(` For fish, also copy it into ~/.config/fish/completions/${prog}.fish (or \`source\` it).`));
|
|
68341
68384
|
return;
|
|
68342
68385
|
}
|
|
68343
68386
|
const rcPath = join3(home, shell === "zsh" ? ".zshrc" : ".bashrc");
|
|
68344
68387
|
const sourceLine = `[ -s "${scriptPath}" ] && source "${scriptPath}"`;
|
|
68345
|
-
const block = `${
|
|
68388
|
+
const block = `${rcBegin}
|
|
68346
68389
|
${sourceLine}
|
|
68347
|
-
${
|
|
68390
|
+
${rcEnd}
|
|
68348
68391
|
`;
|
|
68349
68392
|
const existing = existsSync3(rcPath) ? readFileSync3(rcPath, "utf8") : "";
|
|
68350
|
-
if (existing.includes(
|
|
68393
|
+
if (existing.includes(rcBegin)) {
|
|
68351
68394
|
println(c.dim(` ${rcPath} already sources the completion (left as-is).`));
|
|
68352
68395
|
} else {
|
|
68353
68396
|
const interactive = process.stdout.isTTY && !options.yes;
|
|
@@ -69576,6 +69619,12 @@ function defaultCerefoxEntry() {
|
|
|
69576
69619
|
args: ["-y", "--package=@cerefox/memory", "cerefox", "mcp"]
|
|
69577
69620
|
};
|
|
69578
69621
|
}
|
|
69622
|
+
function localCerefoxEntry() {
|
|
69623
|
+
return {
|
|
69624
|
+
command: process.env.CEREFOX_LOCAL_CMD || "cerefox-local",
|
|
69625
|
+
args: ["mcp"]
|
|
69626
|
+
};
|
|
69627
|
+
}
|
|
69579
69628
|
function claudeCodeUserConfigPath() {
|
|
69580
69629
|
return join4(homedir4(), ".claude.json");
|
|
69581
69630
|
}
|
|
@@ -69589,8 +69638,7 @@ function claudeDesktopConfigPath() {
|
|
|
69589
69638
|
}
|
|
69590
69639
|
return join4(home, ".config", "Claude", "claude_desktop_config.json");
|
|
69591
69640
|
}
|
|
69592
|
-
function claudeCodeDelegated() {
|
|
69593
|
-
const entry = defaultCerefoxEntry();
|
|
69641
|
+
function claudeCodeDelegated(entry) {
|
|
69594
69642
|
return {
|
|
69595
69643
|
cmd: "claude",
|
|
69596
69644
|
args: ["mcp", "add", "cerefox", "--scope", "user", "--", entry.command, ...entry.args]
|
|
@@ -69656,7 +69704,7 @@ function writeMcpConfig(writer, opts = {}) {
|
|
|
69656
69704
|
return directWrite(writer, writer.configPath, opts);
|
|
69657
69705
|
}
|
|
69658
69706
|
function directWrite(writer, configPath, opts) {
|
|
69659
|
-
const entry = writer.buildServerEntry();
|
|
69707
|
+
const entry = opts.entry ?? writer.buildServerEntry();
|
|
69660
69708
|
const format = writer.format ?? "json";
|
|
69661
69709
|
if (!opts.dryRun)
|
|
69662
69710
|
mkdirSync2(dirname(configPath), { recursive: true });
|
|
@@ -69697,8 +69745,8 @@ function delegatedWrite(writer, opts) {
|
|
|
69697
69745
|
if (!writer.delegated) {
|
|
69698
69746
|
throw new Error(`${writer.label}: kind=delegated but no delegated() factory`);
|
|
69699
69747
|
}
|
|
69700
|
-
const
|
|
69701
|
-
const
|
|
69748
|
+
const entry = opts.entry ?? writer.buildServerEntry();
|
|
69749
|
+
const { cmd, args } = writer.delegated(entry);
|
|
69702
69750
|
const delegatedCommand = `${cmd} ${args.join(" ")}`;
|
|
69703
69751
|
if (opts.dryRun) {
|
|
69704
69752
|
return {
|
|
@@ -69743,7 +69791,8 @@ function action6(options) {
|
|
|
69743
69791
|
const result = writeMcpConfig(writer, {
|
|
69744
69792
|
customPath: options.configPath,
|
|
69745
69793
|
noBackup: !options.backup,
|
|
69746
|
-
dryRun: options.dryRun
|
|
69794
|
+
dryRun: options.dryRun,
|
|
69795
|
+
entry: options.local ? localCerefoxEntry() : undefined
|
|
69747
69796
|
});
|
|
69748
69797
|
if (options.json) {
|
|
69749
69798
|
printJson(result);
|
|
@@ -69787,7 +69836,7 @@ function restartHint(id) {
|
|
|
69787
69836
|
}
|
|
69788
69837
|
}
|
|
69789
69838
|
function registerConfigureAgent(program2) {
|
|
69790
|
-
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);
|
|
69791
69840
|
}
|
|
69792
69841
|
|
|
69793
69842
|
// src/cli/commands/delete-doc.ts
|
|
@@ -73925,7 +73974,7 @@ import { homedir as homedir5 } from "node:os";
|
|
|
73925
73974
|
import { join as join8 } from "node:path";
|
|
73926
73975
|
|
|
73927
73976
|
// ../../_shared/ef-meta/index.ts
|
|
73928
|
-
var EF_VERSION = "0.
|
|
73977
|
+
var EF_VERSION = "0.10.1";
|
|
73929
73978
|
|
|
73930
73979
|
// src/cli/util/checks.ts
|
|
73931
73980
|
init_config();
|
|
@@ -75049,6 +75098,22 @@ var DEFAULT_PIPELINE_SETTINGS = {
|
|
|
75049
75098
|
versionRetentionHours: 48,
|
|
75050
75099
|
versionCleanupEnabled: true
|
|
75051
75100
|
};
|
|
75101
|
+
function loadPipelineSettings() {
|
|
75102
|
+
const env4 = globalThis.process?.env ?? {};
|
|
75103
|
+
const intMin = (raw, def, min) => {
|
|
75104
|
+
if (raw === undefined || raw === "")
|
|
75105
|
+
return def;
|
|
75106
|
+
const n = Number.parseInt(raw, 10);
|
|
75107
|
+
return Number.isNaN(n) || n < min ? def : n;
|
|
75108
|
+
};
|
|
75109
|
+
const bool = (raw, def) => raw === undefined || raw === "" ? def : !/^(false|0|no|off)$/i.test(raw.trim());
|
|
75110
|
+
return {
|
|
75111
|
+
maxChunkChars: intMin(env4.CEREFOX_MAX_CHUNK_CHARS, DEFAULT_PIPELINE_SETTINGS.maxChunkChars, 1),
|
|
75112
|
+
minChunkChars: intMin(env4.CEREFOX_MIN_CHUNK_CHARS, DEFAULT_PIPELINE_SETTINGS.minChunkChars, 0),
|
|
75113
|
+
versionRetentionHours: intMin(env4.CEREFOX_VERSION_RETENTION_HOURS, DEFAULT_PIPELINE_SETTINGS.versionRetentionHours, 0),
|
|
75114
|
+
versionCleanupEnabled: bool(env4.CEREFOX_VERSION_CLEANUP_ENABLED, DEFAULT_PIPELINE_SETTINGS.versionCleanupEnabled)
|
|
75115
|
+
};
|
|
75116
|
+
}
|
|
75052
75117
|
|
|
75053
75118
|
// src/ingestion/pipeline.ts
|
|
75054
75119
|
class IngestionPipeline {
|
|
@@ -75060,7 +75125,7 @@ class IngestionPipeline {
|
|
|
75060
75125
|
this.db = new IngestionDbBridge(deps.supabase);
|
|
75061
75126
|
this.apiKey = deps.openAiApiKey;
|
|
75062
75127
|
this.embedderModel = deps.embedderModel ?? "text-embedding-3-small";
|
|
75063
|
-
this.settings = { ...
|
|
75128
|
+
this.settings = { ...loadPipelineSettings(), ...deps.settings ?? {} };
|
|
75064
75129
|
}
|
|
75065
75130
|
async ingestText(opts) {
|
|
75066
75131
|
const {
|
|
@@ -76474,8 +76539,8 @@ async function action28(query, options) {
|
|
|
76474
76539
|
}
|
|
76475
76540
|
const matchCount = parsePositiveInt(options.matchCount, "--match-count", 5);
|
|
76476
76541
|
const alpha = parseFloat01(options.alpha, "--alpha", 0.7);
|
|
76477
|
-
const minScore = parseFloat01(options.minScore, "--min-score",
|
|
76478
|
-
const maxBytes = parseNonNegativeInt(options.maxBytes, "--max-bytes",
|
|
76542
|
+
const minScore = parseFloat01(options.minScore, "--min-score", getMinSearchScore());
|
|
76543
|
+
const maxBytes = parseNonNegativeInt(options.maxBytes, "--max-bytes", getMaxResponseBytes());
|
|
76479
76544
|
const mode = options.mode ?? "docs";
|
|
76480
76545
|
if (!["docs", "hybrid", "fts"].includes(mode)) {
|
|
76481
76546
|
throw userError(`--mode "${mode}": expected "docs", "hybrid", or "fts".`);
|
|
@@ -76620,7 +76685,7 @@ async function action28(query, options) {
|
|
|
76620
76685
|
}
|
|
76621
76686
|
}
|
|
76622
76687
|
function registerSearch(program2) {
|
|
76623
|
-
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
|
|
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);
|
|
76624
76689
|
}
|
|
76625
76690
|
|
|
76626
76691
|
// src/cli/commands/self-update.ts
|
|
@@ -80241,7 +80306,7 @@ async function runSearch(ctx, opts) {
|
|
|
80241
80306
|
p_alpha: 0.7,
|
|
80242
80307
|
p_use_upgrade: false,
|
|
80243
80308
|
p_project_id: projectId,
|
|
80244
|
-
p_min_score:
|
|
80309
|
+
p_min_score: getMinSearchScore()
|
|
80245
80310
|
};
|
|
80246
80311
|
if (metadataFilter)
|
|
80247
80312
|
params2.p_metadata_filter = metadataFilter;
|
|
@@ -80256,7 +80321,7 @@ async function runSearch(ctx, opts) {
|
|
|
80256
80321
|
p_match_count: Math.min(count, 5),
|
|
80257
80322
|
p_alpha: 0.7,
|
|
80258
80323
|
p_project_id: projectId,
|
|
80259
|
-
p_min_score:
|
|
80324
|
+
p_min_score: getMinSearchScore()
|
|
80260
80325
|
};
|
|
80261
80326
|
if (metadataFilter)
|
|
80262
80327
|
params.p_metadata_filter = metadataFilter;
|
|
@@ -81939,9 +82004,9 @@ function registerRenameHusks(program2) {
|
|
|
81939
82004
|
}
|
|
81940
82005
|
}
|
|
81941
82006
|
function buildProgram() {
|
|
81942
|
-
const
|
|
81943
|
-
const program2 = new Command(
|
|
81944
|
-
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\`):
|
|
81945
82010
|
` + ` document get · list · edit · delete · restore · ingest · ingest-dir · version {list·archive·unarchive}
|
|
81946
82011
|
` + ` project list · create · edit · delete
|
|
81947
82012
|
` + ` metadata keys · search
|
|
@@ -81964,8 +82029,8 @@ Exit codes:
|
|
|
81964
82029
|
` + ` 1 user error 3 not found (document / version / project)
|
|
81965
82030
|
` + `
|
|
81966
82031
|
Learn more:
|
|
81967
|
-
` + ` ${
|
|
81968
|
-
` + ` ${
|
|
82032
|
+
` + ` ${progName2} guides list # bundled docs (offline)
|
|
82033
|
+
` + ` ${progName2} doctor # diagnose your install
|
|
81969
82034
|
` + ` https://github.com/fstamatelopoulos/cerefox
|
|
81970
82035
|
`);
|
|
81971
82036
|
registerSearch(program2);
|
|
@@ -17,25 +17,52 @@ export const OPENAI_EMBEDDING_URL = "https://api.openai.com/v1/embeddings";
|
|
|
17
17
|
export const OPENAI_MODEL = "text-embedding-3-small";
|
|
18
18
|
export const EMBEDDING_DIMENSIONS = 768;
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the OpenAI embedding endpoint/model/dimensions, applying `.env`
|
|
22
|
+
* overrides over the built-in defaults. These were configurable in the Python
|
|
23
|
+
* runtime; the TS migration hardcoded them.
|
|
24
|
+
*
|
|
25
|
+
* ⚠ Overriding the MODEL or DIMENSIONS is a BREAKING change: query vectors must
|
|
26
|
+
* match the stored vectors and the DB column is `vector(768)`. Changing either
|
|
27
|
+
* requires re-embedding the whole corpus (`cerefox server reindex`) and, for a
|
|
28
|
+
* non-768 model, a schema change. `CEREFOX_OPENAI_BASE_URL` (proxy/gateway) is
|
|
29
|
+
* the only safe one to flip on an existing KB.
|
|
30
|
+
*
|
|
31
|
+
* Runtime-agnostic env read; the Deno Edge Function (no host env) keeps the
|
|
32
|
+
* constants — matching the EF's "model config is a constant" design.
|
|
33
|
+
*/
|
|
34
|
+
export function openaiEmbeddingConfig(): { url: string; model: string; dimensions: number } {
|
|
35
|
+
const env =
|
|
36
|
+
(globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env ?? {};
|
|
37
|
+
const base = env.CEREFOX_OPENAI_BASE_URL?.replace(/\/+$/, "");
|
|
38
|
+
const dims = Number.parseInt(env.CEREFOX_OPENAI_EMBEDDING_DIMENSIONS ?? "", 10);
|
|
39
|
+
return {
|
|
40
|
+
url: base ? `${base}/embeddings` : OPENAI_EMBEDDING_URL,
|
|
41
|
+
model: env.CEREFOX_OPENAI_EMBEDDING_MODEL || OPENAI_MODEL,
|
|
42
|
+
dimensions: Number.isNaN(dims) || dims <= 0 ? EMBEDDING_DIMENSIONS : dims,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
20
46
|
const EMBEDDING_MAX_RETRIES = 3;
|
|
21
47
|
const EMBEDDING_INITIAL_BACKOFF_MS = 500; // 500ms → 1s → 2s
|
|
22
48
|
|
|
23
49
|
/** Embed a single string. Used for the query vector in `cerefox_search`. */
|
|
24
50
|
export async function getEmbedding(text: string, apiKey: string): Promise<number[]> {
|
|
25
51
|
let lastError: Error | null = null;
|
|
52
|
+
const cfg = openaiEmbeddingConfig();
|
|
26
53
|
|
|
27
54
|
for (let attempt = 0; attempt < EMBEDDING_MAX_RETRIES; attempt++) {
|
|
28
55
|
try {
|
|
29
|
-
const response = await fetch(
|
|
56
|
+
const response = await fetch(cfg.url, {
|
|
30
57
|
method: "POST",
|
|
31
58
|
headers: {
|
|
32
59
|
"Authorization": `Bearer ${apiKey}`,
|
|
33
60
|
"Content-Type": "application/json",
|
|
34
61
|
},
|
|
35
62
|
body: JSON.stringify({
|
|
36
|
-
model:
|
|
63
|
+
model: cfg.model,
|
|
37
64
|
input: text,
|
|
38
|
-
dimensions:
|
|
65
|
+
dimensions: cfg.dimensions,
|
|
39
66
|
}),
|
|
40
67
|
});
|
|
41
68
|
|
|
@@ -93,19 +120,20 @@ async function embedBatchSingleCall(
|
|
|
93
120
|
apiKey: string,
|
|
94
121
|
): Promise<number[][]> {
|
|
95
122
|
let lastError: Error | null = null;
|
|
123
|
+
const cfg = openaiEmbeddingConfig();
|
|
96
124
|
|
|
97
125
|
for (let attempt = 0; attempt < EMBEDDING_MAX_RETRIES; attempt++) {
|
|
98
126
|
try {
|
|
99
|
-
const response = await fetch(
|
|
127
|
+
const response = await fetch(cfg.url, {
|
|
100
128
|
method: "POST",
|
|
101
129
|
headers: {
|
|
102
130
|
"Authorization": `Bearer ${apiKey}`,
|
|
103
131
|
"Content-Type": "application/json",
|
|
104
132
|
},
|
|
105
133
|
body: JSON.stringify({
|
|
106
|
-
model:
|
|
134
|
+
model: cfg.model,
|
|
107
135
|
input: texts,
|
|
108
|
-
dimensions:
|
|
136
|
+
dimensions: cfg.dimensions,
|
|
109
137
|
}),
|
|
110
138
|
});
|
|
111
139
|
|
|
@@ -14,10 +14,46 @@
|
|
|
14
14
|
|
|
15
15
|
import type { MCPSupabaseClient } from "./types.ts";
|
|
16
16
|
|
|
17
|
-
/**
|
|
18
|
-
* smaller budgets via `max_bytes`; values above this are capped. */
|
|
17
|
+
/** Built-in default response-size ceiling for MCP/EF results. */
|
|
19
18
|
export const MAX_RESPONSE_BYTES = 200_000;
|
|
20
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Server-enforced response-size ceiling for MCP/Edge-Function results (agents
|
|
22
|
+
* can request smaller via `max_bytes`; larger is capped). Overridable via
|
|
23
|
+
* `CEREFOX_MAX_RESPONSE_BYTES`. Read by the Python runtime; restored after the
|
|
24
|
+
* TS migration. The web UI + CLI are intentionally unlimited and do not use this.
|
|
25
|
+
* Runtime-agnostic env read (Deno EF safely falls back to the default).
|
|
26
|
+
*/
|
|
27
|
+
export function getMaxResponseBytes(): number {
|
|
28
|
+
const raw = (globalThis as { process?: { env?: Record<string, string | undefined> } })
|
|
29
|
+
.process?.env?.CEREFOX_MAX_RESPONSE_BYTES;
|
|
30
|
+
if (raw === undefined || raw === "") return MAX_RESPONSE_BYTES;
|
|
31
|
+
const n = Number.parseInt(raw, 10);
|
|
32
|
+
return Number.isNaN(n) || n <= 0 ? MAX_RESPONSE_BYTES : n;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Built-in default cosine-similarity floor for hybrid/semantic search. */
|
|
36
|
+
export const DEFAULT_MIN_SEARCH_SCORE = 0.5;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the minimum cosine-similarity floor for hybrid/semantic search
|
|
40
|
+
* (vector-only matches below this are dropped; FTS matches always pass).
|
|
41
|
+
* Overridable via the `CEREFOX_MIN_SEARCH_SCORE` env var (0.0–1.0). The Python
|
|
42
|
+
* runtime read this; the TS migration dropped it — restored here as the single
|
|
43
|
+
* default used by the CLI, local/remote MCP, and the web API.
|
|
44
|
+
*
|
|
45
|
+
* Runtime-agnostic env read: works in Node/Bun; in the Deno Edge Function
|
|
46
|
+
* `process` may be absent, so it falls back to the built-in default (the cloud
|
|
47
|
+
* EF path doesn't use the host `.env` anyway).
|
|
48
|
+
*/
|
|
49
|
+
export function getMinSearchScore(): number {
|
|
50
|
+
const raw = (globalThis as { process?: { env?: Record<string, string | undefined> } })
|
|
51
|
+
.process?.env?.CEREFOX_MIN_SEARCH_SCORE;
|
|
52
|
+
if (raw === undefined || raw === "") return DEFAULT_MIN_SEARCH_SCORE;
|
|
53
|
+
const n = Number.parseFloat(raw);
|
|
54
|
+
return Number.isNaN(n) || n < 0 || n > 1 ? DEFAULT_MIN_SEARCH_SCORE : n;
|
|
55
|
+
}
|
|
56
|
+
|
|
21
57
|
export function applyByteBudget(
|
|
22
58
|
rows: unknown[],
|
|
23
59
|
maxBytes: number,
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { MCPSupabaseClient } from "./types.ts";
|
|
9
9
|
|
|
10
|
-
import { applyByteBudget,
|
|
10
|
+
import { applyByteBudget, getMaxResponseBytes, logUsage } from "./_utils.ts";
|
|
11
11
|
import { lookupProjectId } from "./_projects.ts";
|
|
12
12
|
import { McpInvalidParams, type ToolContext, type ToolDefinition } from "./types.ts";
|
|
13
13
|
|
|
@@ -39,8 +39,9 @@ async function handler(
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// Enforce byte ceiling for content mode
|
|
42
|
+
const ceiling = getMaxResponseBytes();
|
|
42
43
|
const max_bytes = include_content
|
|
43
|
-
? Math.min(requested_max_bytes ??
|
|
44
|
+
? Math.min(requested_max_bytes ?? ceiling, ceiling)
|
|
44
45
|
: null;
|
|
45
46
|
|
|
46
47
|
const params: Record<string, unknown> = {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import type { MCPSupabaseClient } from "./types.ts";
|
|
19
19
|
|
|
20
20
|
import { getEmbedding } from "../embeddings/index.ts";
|
|
21
|
-
import { applyByteBudget,
|
|
21
|
+
import { applyByteBudget, getMaxResponseBytes, getMinSearchScore, logUsage } from "./_utils.ts";
|
|
22
22
|
import { lookupProjectId } from "./_projects.ts";
|
|
23
23
|
import { McpInvalidParams, type ToolContext, type ToolDefinition } from "./types.ts";
|
|
24
24
|
|
|
@@ -32,12 +32,13 @@ async function handler(
|
|
|
32
32
|
const match_count = (args.match_count as number | undefined) ?? 5;
|
|
33
33
|
const mode = (args.mode as string | undefined) ?? "docs";
|
|
34
34
|
const alpha = (args.alpha as number | undefined) ?? 0.7;
|
|
35
|
-
const min_score = (args.min_score as number | undefined) ??
|
|
35
|
+
const min_score = (args.min_score as number | undefined) ?? getMinSearchScore();
|
|
36
36
|
const metadata_filter =
|
|
37
37
|
(args.metadata_filter as Record<string, string> | null | undefined) ?? null;
|
|
38
38
|
const requested_max_bytes = args.max_bytes as number | undefined;
|
|
39
39
|
|
|
40
|
-
const
|
|
40
|
+
const ceiling = getMaxResponseBytes();
|
|
41
|
+
const max_bytes = Math.min(requested_max_bytes ?? ceiling, ceiling);
|
|
41
42
|
|
|
42
43
|
if (
|
|
43
44
|
metadata_filter !== null &&
|
|
@@ -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,17 +4,23 @@ 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)).
|
|
16
22
|
|
|
17
|
-
> **v0.9 verb rename**: commands now follow a `resource verb` shape (e.g. `cerefox document get`, `cerefox project list`). The old flat verbs (`get-doc`, `list-docs`, `ingest`, `list-versions`, `config-get`, `deploy-server`, `docs`, …)
|
|
23
|
+
> **v0.9 verb rename**: commands now follow a `resource verb` shape (e.g. `cerefox document get`, `cerefox project list`). The old flat verbs (`get-doc`, `list-docs`, `ingest`, `list-versions`, `config-get`, `deploy-server`, `docs`, …) survive as hidden husks — they still run but print a pointer to the new form and exit non-zero (removed only at v1.0). Use the new forms below.
|
|
18
24
|
|
|
19
25
|
## Commands
|
|
20
26
|
|
|
@@ -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. |
|
|
@@ -141,7 +147,7 @@ cerefox search [OPTIONS] QUERY
|
|
|
141
147
|
```bash
|
|
142
148
|
cerefox search "OAuth design"
|
|
143
149
|
cerefox search "decisions" --metadata-filter '{"type":"decision-log"}' --match-count 5
|
|
144
|
-
cerefox search "what we tried" --mode
|
|
150
|
+
cerefox search "what we tried" --mode hybrid --requestor "claude-code"
|
|
145
151
|
cerefox search "design docs" --only-metadata
|
|
146
152
|
```
|
|
147
153
|
|
|
@@ -24,6 +24,13 @@ CEREFOX_CONFIG_DIR=~/.cerefox-personal cerefox search "…"
|
|
|
24
24
|
|
|
25
25
|
Full rule documented in [`docs/specs/polish-and-distribution-design.md` §7](../specs/polish-and-distribution-design.md).
|
|
26
26
|
|
|
27
|
+
> **Local / self-hosted (World B).** For the Docker backend (`cerefox-local`), do **not**
|
|
28
|
+
> set the Supabase or `CEREFOX_DATABASE_URL` vars below — the container generates and owns
|
|
29
|
+
> them. Put `OPENAI_API_KEY` plus any of the `CEREFOX_*` **tuning** options on this page
|
|
30
|
+
> (search, chunking, retrieval, versioning, embedding base-url/model, caller identity) in
|
|
31
|
+
> `~/.cerefox/local/.env`; the installer + `cerefox-local` forward them into the container.
|
|
32
|
+
> Apply changes with `cerefox-local init`. See [`setup-local.md`](setup-local.md).
|
|
33
|
+
|
|
27
34
|
---
|
|
28
35
|
|
|
29
36
|
## Supabase / Database
|
|
@@ -45,34 +52,27 @@ Full rule documented in [`docs/specs/polish-and-distribution-design.md` §7](../
|
|
|
45
52
|
|
|
46
53
|
Cerefox uses cloud-based embedding APIs. Local models (mpnet, Ollama) are not supported — they require large downloads, fail on some hardware, and add installation complexity.
|
|
47
54
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
> **TS runtime: OpenAI only (today).** The current TypeScript runtime implements the
|
|
56
|
+
> OpenAI embedder. `CEREFOX_EMBEDDER` and the `CEREFOX_FIREWORKS_*` variables are
|
|
57
|
+
> documented (they worked in the retired Python runtime) but are **not yet wired in TS** —
|
|
58
|
+
> they're currently no-ops, tracked for a future release.
|
|
51
59
|
|
|
52
60
|
### OpenAI (default, recommended)
|
|
53
61
|
|
|
54
62
|
| Variable | Default | Description |
|
|
55
63
|
|----------|---------|-------------|
|
|
56
64
|
| `OPENAI_API_KEY` | `""` | OpenAI API key. Also accepted as `CEREFOX_OPENAI_API_KEY`. Get one at [platform.openai.com/api-keys](https://platform.openai.com/api-keys). |
|
|
57
|
-
| `CEREFOX_OPENAI_BASE_URL` | `https://api.openai.com/v1` | API base URL.
|
|
58
|
-
| `CEREFOX_OPENAI_EMBEDDING_MODEL` | `text-embedding-3-small` | OpenAI embedding model. |
|
|
59
|
-
| `CEREFOX_OPENAI_EMBEDDING_DIMENSIONS` | `768` | Output dimensions. Must match the
|
|
60
|
-
|
|
61
|
-
For cost estimates see `docs/guides/operational-cost.md`.
|
|
65
|
+
| `CEREFOX_OPENAI_BASE_URL` | `https://api.openai.com/v1` | API base URL. Safe to override for proxies or OpenAI-compatible gateways. |
|
|
66
|
+
| `CEREFOX_OPENAI_EMBEDDING_MODEL` | `text-embedding-3-small` | OpenAI embedding model. ⚠ see warning below. |
|
|
67
|
+
| `CEREFOX_OPENAI_EMBEDDING_DIMENSIONS` | `768` | Output dimensions. Must match the DB schema (`VECTOR(768)`). ⚠ see warning below. |
|
|
62
68
|
|
|
63
|
-
|
|
69
|
+
> **⚠ Changing the model or dimensions is breaking.** Query vectors must match the stored
|
|
70
|
+
> vectors. After changing `CEREFOX_OPENAI_EMBEDDING_MODEL` you MUST re-embed the whole
|
|
71
|
+
> corpus (`cerefox server reindex`); changing `CEREFOX_OPENAI_EMBEDDING_DIMENSIONS` away
|
|
72
|
+
> from 768 also requires a schema change. `CEREFOX_OPENAI_BASE_URL` is the only one safe to
|
|
73
|
+
> flip on an existing knowledge base.
|
|
64
74
|
|
|
65
|
-
|
|
66
|
-
|----------|---------|-------------|
|
|
67
|
-
| `CEREFOX_FIREWORKS_API_KEY` | `""` | Fireworks AI API key. |
|
|
68
|
-
| `CEREFOX_FIREWORKS_BASE_URL` | `https://api.fireworks.ai/inference/v1` | Fireworks API base URL. |
|
|
69
|
-
| `CEREFOX_FIREWORKS_EMBEDDING_MODEL` | `nomic-ai/nomic-embed-text-v1.5` | Fireworks model. Must natively output 768-dim vectors. |
|
|
70
|
-
|
|
71
|
-
To use Fireworks:
|
|
72
|
-
```env
|
|
73
|
-
CEREFOX_EMBEDDER=fireworks
|
|
74
|
-
CEREFOX_FIREWORKS_API_KEY=fw_...
|
|
75
|
-
```
|
|
75
|
+
For cost estimates see `docs/guides/operational-cost.md`.
|
|
76
76
|
|
|
77
77
|
### Edge Functions (for agents)
|
|
78
78
|
|
|
@@ -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
|
|
@@ -37,7 +37,7 @@ jobs / CI / make targets that invoke them.
|
|
|
37
37
|
|
|
38
38
|
### TS scripts and `.env` resolution
|
|
39
39
|
|
|
40
|
-
`bun scripts/<name>.ts` reads the same `.env` the
|
|
40
|
+
`bun scripts/<name>.ts` reads the same `.env` the `cerefox` CLI does. Precedence:
|
|
41
41
|
|
|
42
42
|
1. `CEREFOX_CONFIG_DIR` env var (explicit override; supports `~`).
|
|
43
43
|
2. `./.env` in the current working directory (dev mode).
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Quickstart -- Zero to First Document
|
|
2
2
|
|
|
3
|
-
Get Cerefox running on your machine via the npm install path
|
|
4
|
-
|
|
5
|
-
prerequisite — provisioning a free one takes a few minutes),
|
|
6
|
-
install and setup below
|
|
3
|
+
Get Cerefox running on your machine via the npm install path (the **cloud /
|
|
4
|
+
Supabase** backend). **No source clone required.** Once you have a Supabase
|
|
5
|
+
project (the one prerequisite — provisioning a free one takes a few minutes),
|
|
6
|
+
the Cerefox install and setup below takes ~15 minutes.
|
|
7
7
|
|
|
8
8
|
> **Upgrading from an earlier version?** See [`upgrading.md`](upgrading.md)
|
|
9
9
|
> for migration steps instead.
|
|
@@ -114,8 +114,9 @@ You should see results from the bundled self-docs.
|
|
|
114
114
|
The path above is for **end users** (no clone). If you want to hack on Cerefox,
|
|
115
115
|
clone the repo, run `bun install`, and use the contributor scripts
|
|
116
116
|
(`bun scripts/db_deploy.ts`, `bun scripts/db_migrate.ts`). `uv` is only needed
|
|
117
|
-
for the legacy Python MCP fallback. See [`
|
|
118
|
-
|
|
117
|
+
for the legacy Python MCP fallback. See [`CONTRIBUTING.md`](../../CONTRIBUTING.md).
|
|
118
|
+
(Want a no-cloud install instead? That's the self-hosted Docker backend —
|
|
119
|
+
[`setup-local.md`](setup-local.md).)
|
|
119
120
|
|
|
120
121
|
---
|
|
121
122
|
|
|
@@ -125,7 +126,7 @@ for the legacy Python MCP fallback. See [`setup-local.md`](setup-local.md) and
|
|
|
125
126
|
`cerefox document ingest-dir ./notes/` (recurses into sub-directories automatically)
|
|
126
127
|
- **Search from the CLI**: `cerefox search "your query"`
|
|
127
128
|
- **Discover all commands**: `cerefox --help`
|
|
128
|
-
- **Run the web UI**: `cerefox web` (TypeScript — Hono backend + React SPA); see [`
|
|
129
|
+
- **Run the web UI**: `cerefox web` (TypeScript — Hono backend + React SPA); see [`cli.md`](cli.md)
|
|
129
130
|
- **Connect more AI clients** (Cursor, Codex, ChatGPT GPT Actions, etc.):
|
|
130
131
|
[`connect-agents.md`](connect-agents.md)
|
|
131
132
|
- **Configuration reference**: [`configuration.md`](configuration.md)
|
|
@@ -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
|
|
@@ -47,7 +47,7 @@ You need three values from Supabase: a URL, an API key, and a direct Postgres co
|
|
|
47
47
|
|
|
48
48
|
See the **[Supabase API keys (2026)](#supabase-api-keys-2026)** section near the end of this guide for the full picture. The short version:
|
|
49
49
|
|
|
50
|
-
- For `CEREFOX_SUPABASE_KEY` (this guide,
|
|
50
|
+
- For `CEREFOX_SUPABASE_KEY` (this guide, the web UI, and the CLI): use the new **secret key** (`sb_secret_…`) from **Project Settings → API Keys → Secret key**. The legacy `service_role` JWT also still works during the transition.
|
|
51
51
|
- For `CEREFOX_SUPABASE_ANON_KEY` (only if you'll use Edge Functions / MCP / GPT Actions; not needed for this guide's deployment step): you must use the **legacy anon JWT** (`eyJ…`). The new `sb_publishable_…` key fails at the Edge Function gateway. See the reference section for why.
|
|
52
52
|
|
|
53
53
|
Either way: keep this key secret — it bypasses Row Level Security and grants full database access.
|
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",
|