@hirey/hi-mcp-server 0.1.23 → 0.1.26

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/state.d.ts CHANGED
@@ -38,6 +38,8 @@ export type HiAgentPersistedState = {
38
38
  };
39
39
  export declare const DEFAULT_HI_MCP_PROFILE = "default";
40
40
  export declare function normalizeStateProfile(raw: unknown): string;
41
+ export declare function deriveSubjectProfileSuffix(subjectId: unknown): string;
42
+ export declare function composeSubjectScopedProfile(baseProfile: unknown, subjectId: unknown): string;
41
43
  export declare function resolveDefaultStateDir(profileRaw: unknown): string;
42
44
  export declare function resolveCanonicalOpenClawStateDir(profileRaw?: unknown): string;
43
45
  export type OpenClawStateDirValidation = {
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
package/dist/state.js CHANGED
@@ -1,3 +1,4 @@
1
+ import crypto from 'node:crypto';
1
2
  import fs from 'node:fs/promises';
2
3
  import os from 'node:os';
3
4
  import path from 'node:path';
@@ -17,6 +18,30 @@ export const DEFAULT_HI_MCP_PROFILE = 'default';
17
18
  export function normalizeStateProfile(raw) {
18
19
  return String(raw || '').trim() || DEFAULT_HI_MCP_PROFILE;
19
20
  }
21
+ // In OAuth multi-tenant HTTP mode every /mcp request carries a verified bearer
22
+ // whose `sub` claim is a stable per-installation Hi subject (e.g. `sub_aB12…`).
23
+ // We derive a deterministic profile suffix from that subject so each OAuth
24
+ // caller gets its own state file under the same stateDir — without leaking
25
+ // the raw `sub` (which is a secret-ish identifier) into the filesystem.
26
+ //
27
+ // Background: before 2026-05-20 every OAuth subject hitting the same hi-mcp
28
+ // pod read and wrote `<stateDir>/<profile>.json`, so subject A's
29
+ // `hi_agent_install` would overwrite the disk identity, then subject B's
30
+ // `hi_agent_status` would return A's `client_id`/`client_secret`. That cross-
31
+ // tenant leak is fixed by routing per-subject state through this helper.
32
+ export function deriveSubjectProfileSuffix(subjectId) {
33
+ const raw = String(subjectId || '').trim();
34
+ if (!raw)
35
+ return '';
36
+ return crypto.createHash('sha256').update(raw).digest('hex').slice(0, 16);
37
+ }
38
+ export function composeSubjectScopedProfile(baseProfile, subjectId) {
39
+ const profile = normalizeStateProfile(baseProfile);
40
+ const suffix = deriveSubjectProfileSuffix(subjectId);
41
+ if (!suffix)
42
+ return profile;
43
+ return `${profile}__s_${suffix}`;
44
+ }
20
45
  function normalizeStateDirPath(raw) {
21
46
  const text = typeof raw === 'string' ? String(raw).trim() : '';
22
47
  return text ? path.resolve(text) : '';
@@ -179,13 +204,63 @@ export async function readState(args) {
179
204
  catch (error) {
180
205
  if (error?.code === 'ENOENT')
181
206
  return buildDefaultState(args.profile);
207
+ if (error instanceof SyntaxError) {
208
+ // File exists but isn't valid JSON — a legacy torn/truncated write (from
209
+ // before atomicWriteFile) or external corruption. Don't hard-fail every
210
+ // tool call; preserve the bad file for forensics and fall back to a fresh
211
+ // default so the agent can re-provision instead of bricking.
212
+ const corruptPath = `${filePath}.corrupt-${process.pid}-${crypto.randomBytes(4).toString('hex')}`;
213
+ try {
214
+ await fs.rename(filePath, corruptPath);
215
+ }
216
+ catch { }
217
+ try {
218
+ console.error(`[hi-mcp] state file was corrupt; quarantined to ${corruptPath} and reset to default`);
219
+ }
220
+ catch { }
221
+ return buildDefaultState(args.profile);
222
+ }
223
+ throw error;
224
+ }
225
+ }
226
+ // Crash-safe atomic file write. A plain fs.writeFile truncates the target up
227
+ // front and streams bytes in — a SIGKILL / OOM / power loss mid-write leaves a
228
+ // 0-byte or half-written file. For the credential/identity file that means the
229
+ // agent's client_id/client_secret silently vanish and the user is forced to
230
+ // re-login (the "codex creds mysteriously disappeared" class of bug). Instead:
231
+ // write to a unique temp file, fsync its contents to disk, then atomically
232
+ // rename it over the target. A concurrent reader always sees either the old
233
+ // complete file or the new complete file — never a torn one. mode 0o600 because
234
+ // the payload carries client_secret.
235
+ async function atomicWriteFile(filePath, data) {
236
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
237
+ const tmpPath = `${filePath}.tmp-${process.pid}-${crypto.randomBytes(6).toString('hex')}`;
238
+ let fh;
239
+ try {
240
+ fh = await fs.open(tmpPath, 'w', 0o600);
241
+ await fh.writeFile(data, 'utf8');
242
+ await fh.sync();
243
+ await fh.close();
244
+ fh = undefined;
245
+ await fs.rename(tmpPath, filePath);
246
+ }
247
+ catch (error) {
248
+ if (fh) {
249
+ try {
250
+ await fh.close();
251
+ }
252
+ catch { }
253
+ }
254
+ try {
255
+ await fs.unlink(tmpPath);
256
+ }
257
+ catch { }
182
258
  throw error;
183
259
  }
184
260
  }
185
261
  export async function writeState(args) {
186
262
  const filePath = resolveStateFile(args);
187
- await fs.mkdir(path.dirname(filePath), { recursive: true });
188
- await fs.writeFile(filePath, `${JSON.stringify(args.state, null, 2)}\n`, 'utf8');
263
+ await atomicWriteFile(filePath, `${JSON.stringify(args.state, null, 2)}\n`);
189
264
  }
190
265
  export async function updateState(args) {
191
266
  const current = await readState(args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hirey/hi-mcp-server",
3
- "version": "0.1.23",
3
+ "version": "0.1.26",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/server.js",