@everme/claude-code 0.1.0 → 0.3.1
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/.claude-plugin/marketplace.json +1 -1
- package/{plugin.json → .claude-plugin/plugin.json} +8 -2
- package/README.md +4 -4
- package/commands/everme-help.md +1 -1
- package/hooks/scripts/inject-memories.js +14 -2
- package/hooks/scripts/lib/config.js +1 -1
- package/hooks/scripts/lib/transcript.js +77 -8
- package/hooks/scripts/mcp-server.js +26 -5
- package/hooks/scripts/store-memories.js +10 -1
- package/install.sh +1 -1
- package/package.json +18 -8
- package/hooks/scripts/lib/source-key.js +0 -54
- /package/{.claude-plugin/.mcp.json → .mcp.json} +0 -0
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "everme",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "Automatic memory recall for Claude Code through the EverMe gateway. Saves and recalls per-session context using your EverMe account credentials.",
|
|
13
|
-
"version": "0.1
|
|
13
|
+
"version": "0.3.1",
|
|
14
14
|
"homepage": "https://everme.evermind.ai",
|
|
15
15
|
"license": "Apache-2.0"
|
|
16
16
|
}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "everme",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "EverMe — automatic memory recall for Claude Code. Recalls relevant context from past sessions before each prompt and saves new turns through the EverMe gateway.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "EverMind AI",
|
|
7
7
|
"url": "https://everme.evermind.ai"
|
|
8
8
|
},
|
|
9
9
|
"homepage": "https://everme.evermind.ai",
|
|
10
|
-
"keywords": [
|
|
10
|
+
"keywords": [
|
|
11
|
+
"memory",
|
|
12
|
+
"context",
|
|
13
|
+
"recall",
|
|
14
|
+
"persistence",
|
|
15
|
+
"everme"
|
|
16
|
+
],
|
|
11
17
|
"license": "Apache-2.0"
|
|
12
18
|
}
|
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ The installer:
|
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
45
|
export EVERME_API_KEY="emk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # from EverMe Web UI
|
|
46
|
-
export EVERME_API_BASE="http://localhost:8080" # optional — defaults to everme.evermind.ai
|
|
46
|
+
export EVERME_API_BASE="http://localhost:8080" # optional — defaults to api.everme.evermind.ai
|
|
47
47
|
claude plugin install /path/to/everme/plugins/claude-code
|
|
48
48
|
```
|
|
49
49
|
|
|
@@ -54,7 +54,7 @@ claude plugin install /path/to/everme/plugins/claude-code
|
|
|
54
54
|
| `EVERME_API_KEY` | Account-level emk. Supports recall-only mode. |
|
|
55
55
|
| `EVERME_AGENT_TOKEN` | Per-machine evt — written by `evercli plugin install claude-code`. Required for realtime writes and wins over emk when both are set. |
|
|
56
56
|
| `EVERME_AGENT_ID` | Required with `EVERME_AGENT_TOKEN` for realtime writes; also pins recall to a specific cloud agent. |
|
|
57
|
-
| `EVERME_API_BASE` | Gateway host. Defaults to `https://everme.evermind.ai`. Set to `http://localhost:8080` for local dev. |
|
|
57
|
+
| `EVERME_API_BASE` | Gateway host. Defaults to `https://api.everme.evermind.ai`. Set to `http://localhost:8080` for local dev. |
|
|
58
58
|
| `EVERME_DEBUG` | `1` to print hook traces to stderr (with token redaction). |
|
|
59
59
|
|
|
60
60
|
## Verifying the install
|
|
@@ -76,9 +76,9 @@ In a new Claude Code session:
|
|
|
76
76
|
## Files
|
|
77
77
|
|
|
78
78
|
```
|
|
79
|
-
plugin.json
|
|
80
|
-
.claude-plugin/.mcp.json MCP server registration
|
|
79
|
+
.claude-plugin/plugin.json plugin metadata
|
|
81
80
|
.claude-plugin/marketplace.json marketplace listing
|
|
81
|
+
.mcp.json MCP server registration
|
|
82
82
|
hooks/hooks.json SessionStart / UserPromptSubmit / Stop / SessionEnd wiring
|
|
83
83
|
hooks/scripts/inject-memories.js UserPromptSubmit handler
|
|
84
84
|
hooks/scripts/store-memories.js Stop handler
|
package/commands/everme-help.md
CHANGED
|
@@ -11,7 +11,7 @@ EverMe for Claude Code
|
|
|
11
11
|
─────────────────────
|
|
12
12
|
Auth: EVERME_API_KEY (account emk_*) — recall-only mode
|
|
13
13
|
EVERME_AGENT_TOKEN + EVERME_AGENT_ID — required for realtime writes
|
|
14
|
-
Gateway: EVERME_API_BASE — defaults to https://everme.evermind.ai
|
|
14
|
+
Gateway: EVERME_API_BASE — defaults to https://api.everme.evermind.ai
|
|
15
15
|
|
|
16
16
|
Hooks:
|
|
17
17
|
SessionStart → loads recent context from past sessions
|
|
@@ -48,6 +48,7 @@ async function main() {
|
|
|
48
48
|
// where there is no prompt, but per-turn inject goes through search.
|
|
49
49
|
let block = "";
|
|
50
50
|
let count = 0;
|
|
51
|
+
let degraded = null;
|
|
51
52
|
try {
|
|
52
53
|
const res = await searchMemories(prompt, { topK: TOP_K });
|
|
53
54
|
// EverOS frequently returns score=null on episodic hits (decoded as
|
|
@@ -77,11 +78,22 @@ async function main() {
|
|
|
77
78
|
const inner = buildMemoryPrompt(bundle, { wrapInCodeBlock: false });
|
|
78
79
|
if (inner) block = `<everme_recall>\n${inner}\n</everme_recall>`;
|
|
79
80
|
} catch (err) {
|
|
80
|
-
|
|
81
|
+
const reason = redactError(err?.message || String(err));
|
|
82
|
+
degraded = reason;
|
|
83
|
+
debug("inject", "search failed:", reason);
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
if (!block || count === 0) {
|
|
84
|
-
|
|
87
|
+
if (degraded) {
|
|
88
|
+
// Visible single-line WARN — the hook is by design non-blocking,
|
|
89
|
+
// but a fully silent failure means the user thinks EverMe is
|
|
90
|
+
// running while every recall is dropped. Surface it once per
|
|
91
|
+
// hook invocation so persistent issues (expired token, backend
|
|
92
|
+
// unreachable) bubble up without forcing EVERME_DEBUG=1.
|
|
93
|
+
process.stderr.write(`EverMe inject hook degraded: ${degraded}\n`);
|
|
94
|
+
} else {
|
|
95
|
+
debug("inject", "no memories above threshold");
|
|
96
|
+
}
|
|
85
97
|
return process.exit(0);
|
|
86
98
|
}
|
|
87
99
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* users may legitimately want to override these from a shell or from
|
|
15
15
|
* Claude Code's mcp .env block, and evercli does not own them.
|
|
16
16
|
*
|
|
17
|
-
* Compiled defaults (everme.evermind.ai, no token) sit at the bottom.
|
|
17
|
+
* Compiled defaults (api.everme.evermind.ai, no token) sit at the bottom.
|
|
18
18
|
*
|
|
19
19
|
* Auth modes (mutually exclusive, both wire-compatible):
|
|
20
20
|
* evt — set EVERME_AGENT_TOKEN (per-machine token from evercli)
|
|
@@ -36,7 +36,23 @@ const RETRY_DELAY_MS = 100;
|
|
|
36
36
|
export async function readTranscript(path) {
|
|
37
37
|
if (!path || !existsSync(path)) return [];
|
|
38
38
|
for (let i = 0; i < READ_RETRIES; i++) {
|
|
39
|
-
|
|
39
|
+
// readFile errors must NOT escape this function. The Stop hook
|
|
40
|
+
// installs a process-wide unhandledRejection handler that exits 0
|
|
41
|
+
// silently — an ENOENT here (the transcript file was rotated /
|
|
42
|
+
// removed mid-read) used to fall into that catch-all and the
|
|
43
|
+
// entire Stop write was silently dropped. Treating ENOENT as
|
|
44
|
+
// "transient, retry" and other fs errors as "give up, return []"
|
|
45
|
+
// surfaces the right signal: zero lines means no Stop work to do.
|
|
46
|
+
let raw;
|
|
47
|
+
try {
|
|
48
|
+
raw = await readFile(path, "utf8");
|
|
49
|
+
} catch (err) {
|
|
50
|
+
if (err?.code === "ENOENT" && i < READ_RETRIES - 1) {
|
|
51
|
+
await sleep(RETRY_DELAY_MS);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
40
56
|
const lines = raw.trim().split("\n").filter(Boolean);
|
|
41
57
|
if (lines.length === 0) {
|
|
42
58
|
await sleep(RETRY_DELAY_MS);
|
|
@@ -127,27 +143,80 @@ export function extractAgentMessages(lines) {
|
|
|
127
143
|
continue;
|
|
128
144
|
}
|
|
129
145
|
const timestamp = normalizeTimestamp(ev.timestamp);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
146
|
+
// Real Claude Code transcript schema is nested: each line is
|
|
147
|
+
// { "type":"user"|"assistant", "message":{role, content}, ... }
|
|
148
|
+
// with content.string OR content[]={text|thinking|tool_use|tool_result}.
|
|
149
|
+
// Tool results live INSIDE a user-role envelope (with content[].type=tool_result
|
|
150
|
+
// carrying tool_use_id). The legacy flat-shape fixture (top-level
|
|
151
|
+
// ev.role + ev.content) is accepted as a fallback so existing tests
|
|
152
|
+
// and any external producers keep working.
|
|
153
|
+
const inner = ev.message && typeof ev.message === "object" ? ev.message : null;
|
|
154
|
+
const role = inner?.role ?? ev.role;
|
|
155
|
+
const rawContent = inner?.content ?? ev.content;
|
|
156
|
+
|
|
157
|
+
if (role === AGENT_MEMORY_ROLES.USER) {
|
|
158
|
+
// Split the user envelope: tool_result blocks become role=tool
|
|
159
|
+
// messages (with their own toolCallId), free text becomes a
|
|
160
|
+
// role=user message. A single CC user event can therefore emit
|
|
161
|
+
// multiple EverMe messages.
|
|
162
|
+
const toolResults = extractToolResults(rawContent, timestamp);
|
|
163
|
+
messages.push(...toolResults);
|
|
164
|
+
const text = textFromContent(rawContent);
|
|
165
|
+
if (text) messages.push({ role: AGENT_MEMORY_ROLES.USER, timestamp, content: text });
|
|
133
166
|
continue;
|
|
134
167
|
}
|
|
135
|
-
if (
|
|
136
|
-
const msg = agentAssistantMessage(
|
|
168
|
+
if (role === AGENT_MEMORY_ROLES.ASSISTANT) {
|
|
169
|
+
const msg = agentAssistantMessage(rawContent, timestamp);
|
|
137
170
|
if (msg) messages.push(msg);
|
|
138
171
|
continue;
|
|
139
172
|
}
|
|
140
|
-
|
|
173
|
+
// Legacy flat tool-role fallback: { role:"tool", content, toolCallId }
|
|
174
|
+
if (role === AGENT_MEMORY_ROLES.TOOL || ev.type === "tool_result") {
|
|
141
175
|
const toolCallId = ev.toolCallId || ev.tool_call_id || ev.tool_use_id;
|
|
142
176
|
if (!toolCallId) continue;
|
|
143
177
|
const content =
|
|
144
|
-
typeof
|
|
178
|
+
typeof rawContent === "string" ? rawContent : safeJsonStringify(rawContent);
|
|
145
179
|
messages.push({ role: AGENT_MEMORY_ROLES.TOOL, timestamp, toolCallId, content });
|
|
146
180
|
}
|
|
147
181
|
}
|
|
148
182
|
return messages;
|
|
149
183
|
}
|
|
150
184
|
|
|
185
|
+
// extractToolResults pulls every `tool_result` block out of a CC user-
|
|
186
|
+
// envelope content array and converts each into an EverMe role=tool
|
|
187
|
+
// message. CC encodes tool_result as a content block inside a user
|
|
188
|
+
// envelope (not a separate top-level event), so without this step the
|
|
189
|
+
// entire tool round-trip is lost.
|
|
190
|
+
function extractToolResults(content, timestamp) {
|
|
191
|
+
if (!Array.isArray(content)) return [];
|
|
192
|
+
const out = [];
|
|
193
|
+
for (const b of content) {
|
|
194
|
+
if (!b || typeof b !== "object") continue;
|
|
195
|
+
if (b.type !== "tool_result") continue;
|
|
196
|
+
const toolCallId = b.tool_use_id || b.toolCallId || b.tool_call_id;
|
|
197
|
+
if (!toolCallId) continue;
|
|
198
|
+
let text;
|
|
199
|
+
if (typeof b.content === "string") {
|
|
200
|
+
text = b.content;
|
|
201
|
+
} else if (Array.isArray(b.content)) {
|
|
202
|
+
// tool_result.content can itself be a list of typed blocks (e.g.
|
|
203
|
+
// [{type:"text", text:...}]) — flatten the text-bearing ones.
|
|
204
|
+
text = b.content
|
|
205
|
+
.map((c) => {
|
|
206
|
+
if (typeof c === "string") return c;
|
|
207
|
+
if (c?.type === "text" && typeof c.text === "string") return c.text;
|
|
208
|
+
return "";
|
|
209
|
+
})
|
|
210
|
+
.filter(Boolean)
|
|
211
|
+
.join("\n");
|
|
212
|
+
} else {
|
|
213
|
+
text = safeJsonStringify(b.content);
|
|
214
|
+
}
|
|
215
|
+
out.push({ role: AGENT_MEMORY_ROLES.TOOL, timestamp, toolCallId, content: text });
|
|
216
|
+
}
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
|
|
151
220
|
function agentAssistantMessage(content, timestamp) {
|
|
152
221
|
if (typeof content === "string") {
|
|
153
222
|
return content ? { role: AGENT_MEMORY_ROLES.ASSISTANT, timestamp, content } : null;
|
|
@@ -17,10 +17,25 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { createInterface } from "readline";
|
|
20
|
+
import { createRequire } from "node:module";
|
|
20
21
|
import { searchMemories, getContext, EvermeError } from "./lib/api.js";
|
|
21
22
|
import { isConfigured } from "./lib/config.js";
|
|
22
23
|
import { redactError, debug } from "./lib/redact.js";
|
|
23
24
|
|
|
25
|
+
// Derive serverInfo.version from package.json so the value tracks the
|
|
26
|
+
// plugin release rather than rotting as a hard-coded literal. We sit at
|
|
27
|
+
// hooks/scripts/mcp-server.js, package.json is three levels up.
|
|
28
|
+
const { version: PKG_VERSION } = createRequire(import.meta.url)("../../../package.json");
|
|
29
|
+
|
|
30
|
+
// Protocol versions this hand-rolled server knows about. Clients that
|
|
31
|
+
// announce one of these in `initialize` get their version echoed back
|
|
32
|
+
// (per MCP spec the server SHOULD agree to the client's version when
|
|
33
|
+
// supported); anything else falls back to the newest we support so the
|
|
34
|
+
// session can still establish. Prior code hard-coded "2024-11-05" while
|
|
35
|
+
// Claude Code already negotiates "2025-03-26".
|
|
36
|
+
const SUPPORTED_PROTOCOL_VERSIONS = new Set(["2024-11-05", "2025-03-26"]);
|
|
37
|
+
const LATEST_PROTOCOL_VERSION = "2025-03-26";
|
|
38
|
+
|
|
24
39
|
const TOOLS = [
|
|
25
40
|
{
|
|
26
41
|
name: "everme_search",
|
|
@@ -50,11 +65,17 @@ const TOOLS = [
|
|
|
50
65
|
];
|
|
51
66
|
|
|
52
67
|
const handlers = {
|
|
53
|
-
initialize: () =>
|
|
54
|
-
protocolVersion: "
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
initialize: (params) => {
|
|
69
|
+
const requested = typeof params?.protocolVersion === "string" ? params.protocolVersion : "";
|
|
70
|
+
const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.has(requested)
|
|
71
|
+
? requested
|
|
72
|
+
: LATEST_PROTOCOL_VERSION;
|
|
73
|
+
return {
|
|
74
|
+
protocolVersion,
|
|
75
|
+
capabilities: { tools: { listChanged: false } },
|
|
76
|
+
serverInfo: { name: "everme", version: PKG_VERSION },
|
|
77
|
+
};
|
|
78
|
+
},
|
|
58
79
|
"tools/list": () => ({ tools: TOOLS }),
|
|
59
80
|
"tools/call": async (params) => {
|
|
60
81
|
const name = params?.name;
|
|
@@ -68,11 +68,20 @@ async function main() {
|
|
|
68
68
|
});
|
|
69
69
|
debug("store", `ok agent-memory status=${res?.status || "unknown"} flushed=${!!res?.flushed} messages=${res?.messageCount || tail.length}`);
|
|
70
70
|
} catch (err) {
|
|
71
|
+
// Visible single-line WARN — Stop hook is non-blocking by design,
|
|
72
|
+
// but silently dropping every turn means a misconfigured machine
|
|
73
|
+
// looks healthy while EverMe never receives a write. Surface
|
|
74
|
+
// it once per Stop so persistent issues (401, network, schema
|
|
75
|
+
// drift) bubble up without forcing EVERME_DEBUG=1.
|
|
76
|
+
let reason;
|
|
71
77
|
if (err instanceof EvermeError) {
|
|
78
|
+
reason = `${err.type}: ${redactError(err.message)}`;
|
|
72
79
|
debug("store", `failed type=${err.type}:`, redactError(err.message));
|
|
73
80
|
} else {
|
|
74
|
-
|
|
81
|
+
reason = redactError(err?.message || String(err));
|
|
82
|
+
debug("store", "unexpected:", reason);
|
|
75
83
|
}
|
|
84
|
+
process.stderr.write(`EverMe store hook degraded: ${reason}\n`);
|
|
76
85
|
}
|
|
77
86
|
process.exit(0);
|
|
78
87
|
}
|
package/install.sh
CHANGED
|
@@ -93,7 +93,7 @@ elif [ -f "$ENV_FILE" ]; then
|
|
|
93
93
|
echo -e "${GREEN}✓${NC} Existing $ENV_FILE detected — leaving credentials as-is"
|
|
94
94
|
fi
|
|
95
95
|
|
|
96
|
-
# Optional API base override (default https://everme.evermind.ai).
|
|
96
|
+
# Optional API base override (default https://api.everme.evermind.ai).
|
|
97
97
|
if [ -n "${EVERME_API_BASE:-}" ]; then
|
|
98
98
|
echo -e "${GREEN}✓${NC} Using EVERME_API_BASE=$EVERME_API_BASE"
|
|
99
99
|
fi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@everme/claude-code",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "EverMe native plugin for Claude Code — automatic memory recall via SessionStart/UserPromptSubmit/Stop/SessionEnd hooks, plus /recall slash + bundled MCP server.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
"node": ">=18.0.0"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"test": "node --test tests/redact.test.js tests/config.test.js tests/transcript.test.js"
|
|
11
|
+
"test": "node --test tests/redact.test.js tests/config.test.js tests/transcript.test.js tests/hooks.test.js"
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
|
-
"plugin.json",
|
|
15
14
|
".claude-plugin/",
|
|
15
|
+
".mcp.json",
|
|
16
16
|
"hooks/",
|
|
17
17
|
"commands/",
|
|
18
18
|
"skills/",
|
|
@@ -21,17 +21,27 @@
|
|
|
21
21
|
"README.md"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@everme/agent-sdk": "^0.1
|
|
24
|
+
"@everme/agent-sdk": "^0.3.1"
|
|
25
25
|
},
|
|
26
|
-
"keywords": [
|
|
27
|
-
|
|
26
|
+
"keywords": [
|
|
27
|
+
"evermind",
|
|
28
|
+
"everme",
|
|
29
|
+
"claude-code",
|
|
30
|
+
"claude",
|
|
31
|
+
"anthropic",
|
|
32
|
+
"memory",
|
|
33
|
+
"ai",
|
|
34
|
+
"agent",
|
|
35
|
+
"mcp"
|
|
36
|
+
],
|
|
37
|
+
"homepage": "https://everme.evermind.ai",
|
|
28
38
|
"repository": {
|
|
29
39
|
"type": "git",
|
|
30
|
-
"url": "git+https://github.com/
|
|
40
|
+
"url": "git+https://github.com/EverMind-AI/EverMe-CLI.git",
|
|
31
41
|
"directory": "plugins/claude-code"
|
|
32
42
|
},
|
|
33
43
|
"bugs": {
|
|
34
|
-
"url": "https://github.com/
|
|
44
|
+
"url": "https://github.com/EverMind-AI/EverMe-CLI/issues"
|
|
35
45
|
},
|
|
36
46
|
"publishConfig": {
|
|
37
47
|
"access": "public",
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stable per-machine source key for documentKey derivation.
|
|
3
|
-
*
|
|
4
|
-
* The hooks pass this into `buildDocumentKey(sourceKey, logicalPath)`
|
|
5
|
-
* to anchor a version chain. In evt mode the agentId (`agt_…`) is
|
|
6
|
-
* unique per machine + user + platform, so it works as the source key
|
|
7
|
-
* unmodified. In emk mode the user has no agentId, so the previous
|
|
8
|
-
* code fell back to the literal `"agt_claude_code"` — which means
|
|
9
|
-
* every user on every machine sharing the same EverMe account would
|
|
10
|
-
* write into the SAME version chain, overwriting each other's runtime
|
|
11
|
-
* docs.
|
|
12
|
-
*
|
|
13
|
-
* This module computes a stable replacement: `claude-code:<host>:<user>`,
|
|
14
|
-
* SHA256-truncated to a 24-hex prefix. Same machine + user → same
|
|
15
|
-
* key across processes and reboots; two machines under one account →
|
|
16
|
-
* two distinct chains.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import { createHash } from "node:crypto";
|
|
20
|
-
import { hostname, userInfo } from "node:os";
|
|
21
|
-
|
|
22
|
-
let cached = null;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Returns the source key the runtime hooks should pass into
|
|
26
|
-
* buildDocumentKey. Honours `cfg.agentId` (evt mode) when present —
|
|
27
|
-
* agentId is already the canonical per-machine fingerprint that
|
|
28
|
-
* evercli writes — and falls back to a hashed host+user string when
|
|
29
|
-
* the user is on emk auth.
|
|
30
|
-
*/
|
|
31
|
-
export function getSourceKey(cfg) {
|
|
32
|
-
if (cfg?.agentId) return cfg.agentId;
|
|
33
|
-
if (cached) return cached;
|
|
34
|
-
let host = "unknown-host";
|
|
35
|
-
let user = "unknown-user";
|
|
36
|
-
try {
|
|
37
|
-
host = hostname() || host;
|
|
38
|
-
} catch {
|
|
39
|
-
/* fall through */
|
|
40
|
-
}
|
|
41
|
-
try {
|
|
42
|
-
user = userInfo().username || user;
|
|
43
|
-
} catch {
|
|
44
|
-
/* fall through */
|
|
45
|
-
}
|
|
46
|
-
const sum = createHash("sha256").update(`claude-code:${host}:${user}`).digest("hex");
|
|
47
|
-
cached = "agt_emk_" + sum.slice(0, 24);
|
|
48
|
-
return cached;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Test seam — wipes the per-process cache. */
|
|
52
|
-
export function _reset() {
|
|
53
|
-
cached = null;
|
|
54
|
-
}
|
|
File without changes
|