@mysten-incubation/memwal-mcp 0.0.14-dev.1 → 0.0.14-dev.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +238 -1
- package/dist/auth-required.d.ts +105 -0
- package/dist/auth-required.d.ts.map +1 -1
- package/dist/auth-required.js +48 -14
- package/dist/auth-required.js.map +1 -1
- package/dist/auth.d.ts +179 -18
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +458 -35
- package/dist/auth.js.map +1 -1
- package/dist/auto-save.d.ts +100 -0
- package/dist/auto-save.d.ts.map +1 -0
- package/dist/auto-save.js +290 -0
- package/dist/auto-save.js.map +1 -0
- package/dist/bridge.d.ts +10 -2
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +155 -25
- package/dist/bridge.js.map +1 -1
- package/dist/compatibility.d.ts.map +1 -1
- package/dist/compatibility.js +17 -2
- package/dist/compatibility.js.map +1 -1
- package/dist/consent.d.ts +36 -0
- package/dist/consent.d.ts.map +1 -0
- package/dist/consent.js +129 -0
- package/dist/consent.js.map +1 -0
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +13 -1
- package/dist/crypto.js.map +1 -1
- package/dist/health-probe.d.ts +35 -0
- package/dist/health-probe.d.ts.map +1 -0
- package/dist/health-probe.js +87 -0
- package/dist/health-probe.js.map +1 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +266 -5
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +17 -21
- package/dist/instructions.d.ts.map +1 -1
- package/dist/instructions.js +74 -12
- package/dist/instructions.js.map +1 -1
- package/dist/login.d.ts +15 -1
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +109 -9
- package/dist/login.js.map +1 -1
- package/dist/memory-policy.d.ts +38 -0
- package/dist/memory-policy.d.ts.map +1 -0
- package/dist/memory-policy.js +80 -0
- package/dist/memory-policy.js.map +1 -0
- package/dist/messages.d.ts +10 -3
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +16 -5
- package/dist/messages.js.map +1 -1
- package/dist/recovery.d.ts +1 -0
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +15 -13
- package/dist/recovery.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/** Env var that pins automatic saving on or off for one MCP server process. */
|
|
2
|
+
export declare const AUTO_SAVE_ENV = "MEMWAL_AUTO_SAVE";
|
|
3
|
+
/** Where the answer is stored. Outside every repository, on purpose. */
|
|
4
|
+
export declare function settingsPath(): string;
|
|
5
|
+
/** Where the resolved state is published for the plugin hooks to read. */
|
|
6
|
+
export declare function hookStatePath(): string;
|
|
7
|
+
/**
|
|
8
|
+
* Parse a human-written boolean. Returns null for "not set" and for anything
|
|
9
|
+
* unparseable — an unreadable value must not be read as an answer.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseBooleanSetting(raw: string | undefined | null): boolean | null;
|
|
12
|
+
/** `on`/`off` are answers; `unset` means nobody has been asked yet. */
|
|
13
|
+
export type AutoSaveState = "on" | "off" | "unset";
|
|
14
|
+
export type AutoSaveSource =
|
|
15
|
+
/** `MEMWAL_AUTO_SAVE` in this process's environment. */
|
|
16
|
+
"env"
|
|
17
|
+
/** An answer written to settings.json. */
|
|
18
|
+
| "settings"
|
|
19
|
+
/** Unset, on an install that predates the consent prompt — keeps saving. */
|
|
20
|
+
| "legacy"
|
|
21
|
+
/** Unset, on an install created after it — saves nothing until answered. */
|
|
22
|
+
| "unanswered";
|
|
23
|
+
export interface AutoSaveStatus {
|
|
24
|
+
/** Whether this process may save without being asked. */
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
/** The stored answer, or `unset`. */
|
|
27
|
+
state: AutoSaveState;
|
|
28
|
+
source: AutoSaveSource;
|
|
29
|
+
/** True while the question is still owed a human answer. */
|
|
30
|
+
pendingConsent: boolean;
|
|
31
|
+
/** Where an answer lives (or would be written). */
|
|
32
|
+
path: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the current state: environment, then the stored answer, then the
|
|
36
|
+
* unset rules above.
|
|
37
|
+
*/
|
|
38
|
+
export declare function autoSaveStatus(): AutoSaveStatus;
|
|
39
|
+
/**
|
|
40
|
+
* True when this process may save without being asked.
|
|
41
|
+
*
|
|
42
|
+
* Read at call time, never cached: the answer can be written between calls in
|
|
43
|
+
* the same process.
|
|
44
|
+
*/
|
|
45
|
+
export declare function isAutoSaveEnabled(): boolean;
|
|
46
|
+
/** True while a human still owes the consent question an answer. */
|
|
47
|
+
export declare function isConsentPending(): boolean;
|
|
48
|
+
/** The file the plugin hooks read. Contains no secret — only the answer. */
|
|
49
|
+
export interface HookAutoSaveState {
|
|
50
|
+
version: number;
|
|
51
|
+
enabled: boolean;
|
|
52
|
+
state: AutoSaveState;
|
|
53
|
+
source: AutoSaveSource;
|
|
54
|
+
pendingConsent: boolean;
|
|
55
|
+
/** The settings file this was resolved from, for diagnosis only. */
|
|
56
|
+
settingsPath: string;
|
|
57
|
+
updatedAt: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Publish the resolved state where the plugin hooks can read it.
|
|
61
|
+
*
|
|
62
|
+
* This is the whole hook contract: the hooks do no resolution, consult no
|
|
63
|
+
* project directory and parse no credentials file — they read this one file out
|
|
64
|
+
* of the trusted state dir, or they fail safe. Called at every point the answer
|
|
65
|
+
* can change (`setAutoSave`, `markConsentPending`) and once at start-up, so a
|
|
66
|
+
* hook spawned in the same session as an MCP server sees the current answer.
|
|
67
|
+
*
|
|
68
|
+
* Best effort: a read-only or missing home directory must not stop the server
|
|
69
|
+
* from running, and a hook that finds no file already behaves as "off".
|
|
70
|
+
*/
|
|
71
|
+
export declare function publishHookState(): HookAutoSaveState | null;
|
|
72
|
+
/**
|
|
73
|
+
* Record the answer, preserving any other keys already in the file. Clears the
|
|
74
|
+
* pending stamp — the question has been answered and must not be asked again.
|
|
75
|
+
*/
|
|
76
|
+
export declare function setAutoSave(enabled: boolean): {
|
|
77
|
+
path: string;
|
|
78
|
+
enabled: boolean;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Mark this install as created after the consent prompt existed, so an unset
|
|
82
|
+
* state here is read as "never asked" rather than "long-standing user".
|
|
83
|
+
*
|
|
84
|
+
* Called once when a brand-new install first appears — before the signed-out
|
|
85
|
+
* server boots, and immediately after a first interactive login — so that an
|
|
86
|
+
* abandoned or headless sign-in cannot mature into automatic saving nobody
|
|
87
|
+
* agreed to. A no-op once any answer exists.
|
|
88
|
+
*/
|
|
89
|
+
export declare function markConsentPending(): void;
|
|
90
|
+
/** One line for a TTY, naming the state, where it came from, and how to flip it. */
|
|
91
|
+
export declare function autoSaveSummary(): string;
|
|
92
|
+
/**
|
|
93
|
+
* The single line a non-interactive run prints while consent is outstanding.
|
|
94
|
+
*
|
|
95
|
+
* Non-TTY is every MCP client spawn, so this is stderr-only and says what is
|
|
96
|
+
* happening rather than asking anything — there is no human on the other end of
|
|
97
|
+
* this stdin, and a prompt here would hang the server forever.
|
|
98
|
+
*/
|
|
99
|
+
export declare function pendingConsentNotice(): string | null;
|
|
100
|
+
//# sourceMappingURL=auto-save.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-save.d.ts","sourceRoot":"","sources":["../src/auto-save.ts"],"names":[],"mappings":"AA+EA,+EAA+E;AAC/E,eAAO,MAAM,aAAa,qBAAqB,CAAC;AAqBhD,wEAAwE;AACxE,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAWD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,GAAG,IAAI,CAOlF;AA4BD,uEAAuE;AACvE,MAAM,MAAM,aAAa,GAAG,IAAI,GAAG,KAAK,GAAG,OAAO,CAAC;AAEnD,MAAM,MAAM,cAAc;AACtB,wDAAwD;AACtD,KAAK;AACP,0CAA0C;GACxC,UAAU;AACZ,4EAA4E;GAC1E,QAAQ;AACV,4EAA4E;GAC1E,YAAY,CAAC;AAEnB,MAAM,WAAW,cAAc;IAC3B,yDAAyD;IACzD,OAAO,EAAE,OAAO,CAAC;IACjB,qCAAqC;IACrC,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,cAAc,CAAC;IACvB,4DAA4D;IAC5D,cAAc,EAAE,OAAO,CAAC;IACxB,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,cAAc,CAgC/C;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,cAAc,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,IAAI,iBAAiB,GAAG,IAAI,CAwB3D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAOhF;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAOzC;AAED,oFAAoF;AACpF,wBAAgB,eAAe,IAAI,MAAM,CAcxC;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAUpD"}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automatic-save consent (WALM-642).
|
|
3
|
+
*
|
|
4
|
+
* Saving a fact the user explicitly asked for has never needed permission and
|
|
5
|
+
* still does not. What this module governs is the other thing the package does:
|
|
6
|
+
* saving what it judges durable, without being asked. That is what MemWal is
|
|
7
|
+
* for — so it is ON — but a person has to have been told what it means first.
|
|
8
|
+
*
|
|
9
|
+
* ── Three states, not two ───────────────────────────────────────────────────
|
|
10
|
+
* `off` and `on` are answers. `unset` is the absence of one, and it is not the
|
|
11
|
+
* same as `off`:
|
|
12
|
+
*
|
|
13
|
+
* - **Answered** (`autoSave: true|false` in settings.json) — from the login
|
|
14
|
+
* prompt or from `memwal-mcp auto-save on|off`. Nothing re-asks.
|
|
15
|
+
* - **Unset on an install that predates this change** — no settings file at
|
|
16
|
+
* all, but credentials on disk. These users have been auto-saving all
|
|
17
|
+
* along; switching them off would be a regression dressed up as caution, so
|
|
18
|
+
* saving CONTINUES and the question is put to them at their next
|
|
19
|
+
* interactive login.
|
|
20
|
+
* - **Unset on a new install** (`autoSaveConsent: "pending"`, stamped the
|
|
21
|
+
* moment a post-change install first appears) — nobody has been asked, so
|
|
22
|
+
* nothing is saved unprompted until someone answers. Explicit tool calls
|
|
23
|
+
* and recall keep working the whole time.
|
|
24
|
+
*
|
|
25
|
+
* The stamp is what separates the last two. Without it a headless install could
|
|
26
|
+
* sign in through the `memwal_login` tool, look indistinguishable from a
|
|
27
|
+
* long-standing user, and start saving on its own — consent by never having
|
|
28
|
+
* been asked.
|
|
29
|
+
*
|
|
30
|
+
* ── Where the answer lives: the trusted state dir, and nowhere else ─────────
|
|
31
|
+
* The answer is a decision about the HUMAN, not about a credentials directory.
|
|
32
|
+
* It used to be stored beside whichever `credentials.json` won resolution,
|
|
33
|
+
* which had two consequences, both wrong:
|
|
34
|
+
*
|
|
35
|
+
* 1. Approving a project's credentials (WALM-639) moved `settingsPath()`
|
|
36
|
+
* into the repository. A user who had already answered "off" globally got
|
|
37
|
+
* a directory with no answer in it, fell through to the legacy
|
|
38
|
+
* "credentials exist, so keep saving" rule, and had automatic memory
|
|
39
|
+
* switched back ON by an approval that was only ever about where writes
|
|
40
|
+
* go. The recorded "no" was never consulted there.
|
|
41
|
+
* 2. `setAutoSave` / `markConsentPending` then wrote the consent answer INTO
|
|
42
|
+
* the repository, where it could be committed and shipped to everyone
|
|
43
|
+
* else who cloned it.
|
|
44
|
+
*
|
|
45
|
+
* So consent now lives in the trusted state dir — `MEMWAL_CREDS_DIR` when it is
|
|
46
|
+
* set, else `~/.memwal` — the same directory auth.ts keeps the project-approval
|
|
47
|
+
* store in, and for the same reason: a record a repository can write is a
|
|
48
|
+
* repository approving itself. Project scoping of CREDENTIALS is unchanged;
|
|
49
|
+
* project scoping of CONSENT is gone.
|
|
50
|
+
*
|
|
51
|
+
* The legacy probe reads the trusted directory's own `credentials.json` for the
|
|
52
|
+
* same reason. "Has this person used MemWal before" must not be answerable by a
|
|
53
|
+
* file inside a checkout.
|
|
54
|
+
*
|
|
55
|
+
* ── How the hooks see it ───────────────────────────────────────────────────
|
|
56
|
+
* The plugin's hooks are separate processes: a hook is spawned by the client,
|
|
57
|
+
* not by this package, and inherits none of the MCP server's `env` or argv. The
|
|
58
|
+
* hooks used to re-derive the whole resolution in plain ESM, and the two
|
|
59
|
+
* implementations drifted — the hook side kept the pre-WALM-639 presence rule,
|
|
60
|
+
* so a committed `.memwal/credentials.json` (contents never parsed, only
|
|
61
|
+
* `existsSync`) turned automatic memory on for anyone who opened the repo.
|
|
62
|
+
*
|
|
63
|
+
* Re-deriving is the bug, so the hooks no longer do it. This module publishes
|
|
64
|
+
* the RESOLVED state to `auto-save-state.json` in the trusted state dir, and
|
|
65
|
+
* the hooks read only that. A repository cannot write there, the hooks do no
|
|
66
|
+
* resolution of their own, and a hook that cannot read the file fails safe
|
|
67
|
+
* (automatic memory off). See `plugin/scripts/lib/auto-save.mjs`.
|
|
68
|
+
*/
|
|
69
|
+
import { dirname, join } from "node:path";
|
|
70
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from "node:fs";
|
|
71
|
+
import { projectApprovalsPath } from "./auth.js";
|
|
72
|
+
import { log } from "./logger.js";
|
|
73
|
+
/** Env var that pins automatic saving on or off for one MCP server process. */
|
|
74
|
+
export const AUTO_SAVE_ENV = "MEMWAL_AUTO_SAVE";
|
|
75
|
+
const SETTINGS_FILE = "settings.json";
|
|
76
|
+
const CREDS_FILE = "credentials.json";
|
|
77
|
+
/** Resolved state the plugin hooks read. Never written inside a repository. */
|
|
78
|
+
const HOOK_STATE_FILE = "auto-save-state.json";
|
|
79
|
+
/** Bumped if the hook-facing shape ever changes incompatibly. */
|
|
80
|
+
const HOOK_STATE_VERSION = 1;
|
|
81
|
+
/**
|
|
82
|
+
* Where records a repository must not be able to write are kept:
|
|
83
|
+
* `MEMWAL_CREDS_DIR` when set, else `~/.memwal`.
|
|
84
|
+
*
|
|
85
|
+
* Derived from `projectApprovalsPath()` rather than recomputed, so this file
|
|
86
|
+
* and auth.ts cannot drift apart the way the hook copy did. auth.ts owns the
|
|
87
|
+
* definition; this reads it back.
|
|
88
|
+
*/
|
|
89
|
+
function trustedStateDir() {
|
|
90
|
+
return dirname(projectApprovalsPath());
|
|
91
|
+
}
|
|
92
|
+
/** Where the answer is stored. Outside every repository, on purpose. */
|
|
93
|
+
export function settingsPath() {
|
|
94
|
+
return join(trustedStateDir(), SETTINGS_FILE);
|
|
95
|
+
}
|
|
96
|
+
/** Where the resolved state is published for the plugin hooks to read. */
|
|
97
|
+
export function hookStatePath() {
|
|
98
|
+
return join(trustedStateDir(), HOOK_STATE_FILE);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Parse a human-written boolean. Returns null for "not set" and for anything
|
|
102
|
+
* unparseable — an unreadable value must not be read as an answer.
|
|
103
|
+
*/
|
|
104
|
+
export function parseBooleanSetting(raw) {
|
|
105
|
+
if (raw === undefined || raw === null)
|
|
106
|
+
return null;
|
|
107
|
+
const v = raw.trim().toLowerCase();
|
|
108
|
+
if (v === "")
|
|
109
|
+
return null;
|
|
110
|
+
if (["1", "true", "on", "yes", "y", "enable", "enabled"].includes(v))
|
|
111
|
+
return true;
|
|
112
|
+
if (["0", "false", "off", "no", "n", "disable", "disabled"].includes(v))
|
|
113
|
+
return false;
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
function readSettings() {
|
|
117
|
+
const path = settingsPath();
|
|
118
|
+
if (!existsSync(path))
|
|
119
|
+
return {};
|
|
120
|
+
try {
|
|
121
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
122
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// A corrupt settings file is not an answer. Fall through to unset.
|
|
126
|
+
return {};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function writeSettings(next) {
|
|
130
|
+
const path = settingsPath();
|
|
131
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
132
|
+
writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
|
|
133
|
+
// `writeFileSync`'s `mode` follows POSIX `open()`: the kernel applies it
|
|
134
|
+
// when it CREATES the inode and ignores it for one that already exists, so
|
|
135
|
+
// a file left permissive by anything else would keep its old mode forever.
|
|
136
|
+
// Unlike credentials.json (see `writeSecretFile` in auth.ts) there is no
|
|
137
|
+
// secret in flight here, so tightening afterwards is enough — no reader can
|
|
138
|
+
// learn anything from the window, only whether the flag is set.
|
|
139
|
+
chmodSync(path, 0o600);
|
|
140
|
+
return path;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Resolve the current state: environment, then the stored answer, then the
|
|
144
|
+
* unset rules above.
|
|
145
|
+
*/
|
|
146
|
+
export function autoSaveStatus() {
|
|
147
|
+
const path = settingsPath();
|
|
148
|
+
const settings = readSettings();
|
|
149
|
+
const answered = typeof settings.autoSave === "boolean" ? settings.autoSave : null;
|
|
150
|
+
const state = answered === null ? "unset" : answered ? "on" : "off";
|
|
151
|
+
const fromEnv = parseBooleanSetting(process.env[AUTO_SAVE_ENV]);
|
|
152
|
+
if (fromEnv !== null) {
|
|
153
|
+
// Overrides the behaviour, and counts as a deliberate configuration
|
|
154
|
+
// act: someone who set this does not need to be asked as well.
|
|
155
|
+
return { enabled: fromEnv, state, source: "env", pendingConsent: false, path };
|
|
156
|
+
}
|
|
157
|
+
if (answered !== null) {
|
|
158
|
+
return { enabled: answered, state, source: "settings", pendingConsent: false, path };
|
|
159
|
+
}
|
|
160
|
+
// Unset. Which way it falls depends on whether this install was ever in a
|
|
161
|
+
// position to have been asked — see the module comment. The probe is the
|
|
162
|
+
// TRUSTED directory's credentials file, never a project one: "has this
|
|
163
|
+
// person used MemWal before" must not be answerable by a checkout.
|
|
164
|
+
const stamped = settings.autoSaveConsent === "pending";
|
|
165
|
+
const preExisting = !stamped && existsSync(join(trustedStateDir(), CREDS_FILE));
|
|
166
|
+
return {
|
|
167
|
+
enabled: preExisting,
|
|
168
|
+
state: "unset",
|
|
169
|
+
source: preExisting ? "legacy" : "unanswered",
|
|
170
|
+
pendingConsent: true,
|
|
171
|
+
path,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* True when this process may save without being asked.
|
|
176
|
+
*
|
|
177
|
+
* Read at call time, never cached: the answer can be written between calls in
|
|
178
|
+
* the same process.
|
|
179
|
+
*/
|
|
180
|
+
export function isAutoSaveEnabled() {
|
|
181
|
+
return autoSaveStatus().enabled;
|
|
182
|
+
}
|
|
183
|
+
/** True while a human still owes the consent question an answer. */
|
|
184
|
+
export function isConsentPending() {
|
|
185
|
+
return autoSaveStatus().pendingConsent;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Publish the resolved state where the plugin hooks can read it.
|
|
189
|
+
*
|
|
190
|
+
* This is the whole hook contract: the hooks do no resolution, consult no
|
|
191
|
+
* project directory and parse no credentials file — they read this one file out
|
|
192
|
+
* of the trusted state dir, or they fail safe. Called at every point the answer
|
|
193
|
+
* can change (`setAutoSave`, `markConsentPending`) and once at start-up, so a
|
|
194
|
+
* hook spawned in the same session as an MCP server sees the current answer.
|
|
195
|
+
*
|
|
196
|
+
* Best effort: a read-only or missing home directory must not stop the server
|
|
197
|
+
* from running, and a hook that finds no file already behaves as "off".
|
|
198
|
+
*/
|
|
199
|
+
export function publishHookState() {
|
|
200
|
+
const status = autoSaveStatus();
|
|
201
|
+
const payload = {
|
|
202
|
+
version: HOOK_STATE_VERSION,
|
|
203
|
+
enabled: status.enabled,
|
|
204
|
+
state: status.state,
|
|
205
|
+
source: status.source,
|
|
206
|
+
pendingConsent: status.pendingConsent,
|
|
207
|
+
settingsPath: status.path,
|
|
208
|
+
updatedAt: new Date().toISOString(),
|
|
209
|
+
};
|
|
210
|
+
const path = hookStatePath();
|
|
211
|
+
try {
|
|
212
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
213
|
+
writeFileSync(path, `${JSON.stringify(payload, null, 2)}\n`, { mode: 0o600 });
|
|
214
|
+
chmodSync(path, 0o600);
|
|
215
|
+
return payload;
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
log.warn("autosave.publish_failed", {
|
|
219
|
+
path,
|
|
220
|
+
error: err instanceof Error ? err.message : String(err),
|
|
221
|
+
});
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Record the answer, preserving any other keys already in the file. Clears the
|
|
227
|
+
* pending stamp — the question has been answered and must not be asked again.
|
|
228
|
+
*/
|
|
229
|
+
export function setAutoSave(enabled) {
|
|
230
|
+
const next = { ...readSettings(), autoSave: enabled };
|
|
231
|
+
delete next.autoSaveConsent;
|
|
232
|
+
const path = writeSettings(next);
|
|
233
|
+
publishHookState();
|
|
234
|
+
log.info("autosave.set", { enabled, path });
|
|
235
|
+
return { path, enabled };
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Mark this install as created after the consent prompt existed, so an unset
|
|
239
|
+
* state here is read as "never asked" rather than "long-standing user".
|
|
240
|
+
*
|
|
241
|
+
* Called once when a brand-new install first appears — before the signed-out
|
|
242
|
+
* server boots, and immediately after a first interactive login — so that an
|
|
243
|
+
* abandoned or headless sign-in cannot mature into automatic saving nobody
|
|
244
|
+
* agreed to. A no-op once any answer exists.
|
|
245
|
+
*/
|
|
246
|
+
export function markConsentPending() {
|
|
247
|
+
const settings = readSettings();
|
|
248
|
+
if (typeof settings.autoSave === "boolean")
|
|
249
|
+
return;
|
|
250
|
+
if (settings.autoSaveConsent === "pending")
|
|
251
|
+
return;
|
|
252
|
+
writeSettings({ ...settings, autoSaveConsent: "pending" });
|
|
253
|
+
publishHookState();
|
|
254
|
+
log.info("autosave.consent_pending", { path: settingsPath() });
|
|
255
|
+
}
|
|
256
|
+
/** One line for a TTY, naming the state, where it came from, and how to flip it. */
|
|
257
|
+
export function autoSaveSummary() {
|
|
258
|
+
const status = autoSaveStatus();
|
|
259
|
+
const where = status.source === "env"
|
|
260
|
+
? `from ${AUTO_SAVE_ENV}`
|
|
261
|
+
: status.source === "settings"
|
|
262
|
+
? `from ${status.path}`
|
|
263
|
+
: status.source === "legacy"
|
|
264
|
+
? "carried over from before this setting existed"
|
|
265
|
+
: "not answered yet";
|
|
266
|
+
const how = status.enabled
|
|
267
|
+
? "Turn it off with `memwal-mcp auto-save off`."
|
|
268
|
+
: "Turn it on with `memwal-mcp auto-save on`. Facts you explicitly ask to save are stored either way.";
|
|
269
|
+
return `Automatic memory: ${status.enabled ? "ON" : "OFF"} (${where}). ${how}`;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* The single line a non-interactive run prints while consent is outstanding.
|
|
273
|
+
*
|
|
274
|
+
* Non-TTY is every MCP client spawn, so this is stderr-only and says what is
|
|
275
|
+
* happening rather than asking anything — there is no human on the other end of
|
|
276
|
+
* this stdin, and a prompt here would hang the server forever.
|
|
277
|
+
*/
|
|
278
|
+
export function pendingConsentNotice() {
|
|
279
|
+
const status = autoSaveStatus();
|
|
280
|
+
if (!status.pendingConsent)
|
|
281
|
+
return null;
|
|
282
|
+
return status.enabled
|
|
283
|
+
? "Automatic memory is ON, carried over from before this setting existed. " +
|
|
284
|
+
"Run `memwal-mcp auto-save on|off` in a terminal to confirm or change it."
|
|
285
|
+
: "Automatic memory is waiting on your answer, so nothing is being saved " +
|
|
286
|
+
"unprompted yet (facts you explicitly ask to save still are). Run " +
|
|
287
|
+
"`memwal-mcp login` in a terminal to answer, or set it directly with " +
|
|
288
|
+
"`memwal-mcp auto-save on|off`.";
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=auto-save.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-save.js","sourceRoot":"","sources":["../src/auto-save.ts"],"names":[],"mappings":"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"}
|
package/dist/bridge.d.ts
CHANGED
|
@@ -41,7 +41,8 @@ export interface BridgeConfig {
|
|
|
41
41
|
* per-call namespace always wins over the configured default.
|
|
42
42
|
*/
|
|
43
43
|
/**
|
|
44
|
-
* Name the
|
|
44
|
+
* Name the destination this process is bound to in a `memwal_health` result:
|
|
45
|
+
* the relayer it dialled, and the account it signs for.
|
|
45
46
|
*
|
|
46
47
|
* The relayer-side text can only report an origin its deployment published, and
|
|
47
48
|
* stays silent on a self-hosted or local one, where the sidecar knows nothing
|
|
@@ -53,11 +54,18 @@ export interface BridgeConfig {
|
|
|
53
54
|
* Rewrites an existing `relayer=` field rather than appending a second one: when
|
|
54
55
|
* both sides know the origin they describe the same session, and two
|
|
55
56
|
* conflicting fields would be worse than neither.
|
|
57
|
+
*
|
|
58
|
+
* `account=` rides along for the same reason (WALM-639). A project-local
|
|
59
|
+
* credentials file can point this process at a different account than the one
|
|
60
|
+
* the user signed in with, and "which account am I writing to" was otherwise
|
|
61
|
+
* only visible in stderr the MCP host usually hides — so the half of the
|
|
62
|
+
* destination that decides WHOSE memory this is now shows up beside the half
|
|
63
|
+
* that decides where it is stored.
|
|
56
64
|
*/
|
|
57
65
|
export declare function annotateHealthResult(result: {
|
|
58
66
|
content?: unknown;
|
|
59
67
|
isError?: unknown;
|
|
60
|
-
}, relayerUrl: string): void;
|
|
68
|
+
}, relayerUrl: string, accountId?: string): void;
|
|
61
69
|
export declare function applyDefaultNamespace(msg: RpcMessage, namespace?: string): RpcMessage;
|
|
62
70
|
/** Reply for a `tools/call` naming a tool the connected relayer does not
|
|
63
71
|
* serve. Names what IS on offer, because the agent's next move is to pick one
|
package/dist/bridge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAmBnD,OAAO,EAIH,KAAK,gBAAgB,EACxB,MAAM,eAAe,CAAC;AAIvB;;gFAEgF;AAChF,MAAM,WAAW,YAAY;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd;;;8BAG0B;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAeD;;;;;;;;;;GAUG;AACH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAChC,MAAM,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,EAChD,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GACnB,IAAI,CAkBN;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,CAerF;AAwGD;;qEAEqE;AACrE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,CAOzE;AA2OD,UAAU,UAAU;IAChB,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AA0lBD,oEAAoE;AACpE,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAEpE;AA8BD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,SAAS,CAC3B,YAAY,EAAE,iBAAiB,EAC/B,MAAM,EAAE,YAAY;AACpB;;;;yEAIyE;AACzE,YAAY,GAAE,MAAM,EAAO,GAC5B,OAAO,CAAC,IAAI,CAAC,CAosDf"}
|