@evomap/evolver-mcp 2.0.0-beta.9 → 2.0.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/dist/antigravityInstaller.d.ts +1 -1
- package/dist/antigravityInstaller.js +17 -29
- package/dist/codexInstaller.d.ts +8 -3
- package/dist/codexInstaller.js +203 -50
- package/dist/cursorRulesInstaller.d.ts +1 -1
- package/dist/cursorRulesInstaller.js +66 -17
- package/dist/envFile.d.ts +2 -1
- package/dist/envFile.js +6 -0
- package/dist/installer.d.ts +15 -24
- package/dist/installer.js +224 -142
- package/dist/installerShared.d.ts +103 -0
- package/dist/installerShared.js +99 -0
- package/dist/jsonMcpInstaller.d.ts +5 -1
- package/dist/jsonMcpInstaller.js +54 -21
- package/dist/kiroInstaller.d.ts +3 -3
- package/dist/opencodeInstaller.d.ts +3 -3
- package/dist/proxyClient.d.ts +12 -0
- package/dist/proxyClient.js +117 -26
- package/dist/sharedFileCommit.d.ts +20 -0
- package/dist/sharedFileCommit.js +256 -0
- package/dist/stdio.js +10 -4
- package/package.json +6 -3
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { McpServerCmd, RuntimeId } from './injection.js';
|
|
2
|
+
/** Minimal gene projection consumed by the Cursor rules renderer. */
|
|
3
|
+
export interface CursorGene {
|
|
4
|
+
id: string;
|
|
5
|
+
category?: string;
|
|
6
|
+
hint?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Default command used by runtimes that support a SessionStart hook. The flag enables session-id capture. */
|
|
9
|
+
export declare const DEFAULT_HOOK_COMMAND = "evolver inject session-start --hook-stdin";
|
|
10
|
+
export declare const DEFAULT_PROMPT_RECALL_HOOK_COMMAND = "evolver inject prompt-recall --hook-stdin";
|
|
11
|
+
export declare const EVOLVER_HOOK_STATUS = "evolver-managed-hook";
|
|
12
|
+
/** Bind custom-command ownership to that exact command without adding undocumented hook-schema fields. */
|
|
13
|
+
export declare function evolverManagedHookStatus(command: string): string;
|
|
14
|
+
/** Remove only Evolver-owned handlers, retaining user handlers that share the same matcher group. */
|
|
15
|
+
export declare function stripEvolverHookEntries(entries: readonly unknown[], trustManagedStatus?: boolean, managedCommands?: ReadonlySet<string>): {
|
|
16
|
+
changed: boolean;
|
|
17
|
+
entries: unknown[];
|
|
18
|
+
};
|
|
19
|
+
/** Runtime configuration scope. Project is the default when omitted. */
|
|
20
|
+
export type InstallScope = 'user' | 'project';
|
|
21
|
+
export interface InstallOptions {
|
|
22
|
+
/** Runtime config root. Runtime-specific user scopes may resolve their own home-anchored paths. */
|
|
23
|
+
configRoot: string;
|
|
24
|
+
/** Claude Code scope. Other runtimes may also use this to select project or user configuration. */
|
|
25
|
+
scope?: InstallScope;
|
|
26
|
+
/** The MCP server launch command registered in the runtime configuration. */
|
|
27
|
+
server: McpServerCmd;
|
|
28
|
+
/** Command the SessionStart hook runs to inject memory. */
|
|
29
|
+
hookCommand?: string;
|
|
30
|
+
/** Command the UserPromptSubmit hook runs. Default is local-only, default-off prompt recall. */
|
|
31
|
+
promptRecallHookCommand?: string;
|
|
32
|
+
/** Reinstall even if an Evolver install is already present. */
|
|
33
|
+
force?: boolean;
|
|
34
|
+
/** Plan and validate without writing config or backup files. */
|
|
35
|
+
dryRun?: boolean;
|
|
36
|
+
/** Cursor only: genes rendered into the managed project rules file. */
|
|
37
|
+
genes?: readonly CursorGene[];
|
|
38
|
+
/** Cursor only: cap on genes rendered into the always-on rules body. */
|
|
39
|
+
maxGenes?: number;
|
|
40
|
+
/** Antigravity only: override the user home used to resolve ~/.gemini config roots. */
|
|
41
|
+
homeDir?: string;
|
|
42
|
+
/** Kiro user scope only: direct replacement for ~/.kiro, matching KIRO_HOME semantics. */
|
|
43
|
+
kiroHome?: string;
|
|
44
|
+
/** OpenCode user scope only: explicit XDG_CONFIG_HOME used to resolve the global config. */
|
|
45
|
+
xdgConfigHome?: string;
|
|
46
|
+
/** OpenCode user scope only: explicit OPENCODE_CONFIG file override. */
|
|
47
|
+
opencodeConfig?: string;
|
|
48
|
+
/** OpenCode user scope only: explicit OPENCODE_CONFIG_DIR override. */
|
|
49
|
+
opencodeConfigDir?: string;
|
|
50
|
+
/** OpenCode only: inline config loaded after project/custom-directory config. */
|
|
51
|
+
opencodeConfigContent?: string;
|
|
52
|
+
/** OpenCode only: mirrors truthy OPENCODE_DISABLE_PROJECT_CONFIG handling. */
|
|
53
|
+
opencodeDisableProjectConfig?: boolean;
|
|
54
|
+
/** OpenCode only: injectable managed-config directory for hermetic tests. */
|
|
55
|
+
opencodeManagedConfigDir?: string;
|
|
56
|
+
/** OpenCode only: injectable macOS managed-preference paths for hermetic tests. */
|
|
57
|
+
opencodeManagedPreferencePaths?: readonly string[];
|
|
58
|
+
/** OpenCode only: injectable platform used to resolve system managed paths. */
|
|
59
|
+
opencodePlatform?: NodeJS.Platform;
|
|
60
|
+
/** OpenCode only: injectable ProgramData used to resolve the Windows managed path. */
|
|
61
|
+
opencodeProgramData?: string;
|
|
62
|
+
/** OpenCode only: injectable username used to resolve macOS managed preferences. */
|
|
63
|
+
opencodeUsername?: string;
|
|
64
|
+
}
|
|
65
|
+
export interface UninstallOptions {
|
|
66
|
+
configRoot: string;
|
|
67
|
+
scope?: InstallScope;
|
|
68
|
+
/** Antigravity only: override the user home used to resolve ~/.gemini config roots. */
|
|
69
|
+
homeDir?: string;
|
|
70
|
+
/** Kiro user scope only: direct replacement for ~/.kiro, matching KIRO_HOME semantics. */
|
|
71
|
+
kiroHome?: string;
|
|
72
|
+
/** Validate and report the uninstall without changing config or backup files. */
|
|
73
|
+
dryRun?: boolean;
|
|
74
|
+
/** OpenCode user scope only: explicit XDG_CONFIG_HOME used to resolve the global config. */
|
|
75
|
+
xdgConfigHome?: string;
|
|
76
|
+
/** OpenCode user scope only: explicit OPENCODE_CONFIG file override. */
|
|
77
|
+
opencodeConfig?: string;
|
|
78
|
+
/** OpenCode user scope only: explicit OPENCODE_CONFIG_DIR override. */
|
|
79
|
+
opencodeConfigDir?: string;
|
|
80
|
+
}
|
|
81
|
+
export interface InstallResult {
|
|
82
|
+
ok: boolean;
|
|
83
|
+
runtime: RuntimeId;
|
|
84
|
+
mode: string;
|
|
85
|
+
/** Absolute paths written (install) or cleaned (uninstall). */
|
|
86
|
+
files: string[];
|
|
87
|
+
alreadyInstalled?: boolean;
|
|
88
|
+
dryRun?: boolean;
|
|
89
|
+
verified?: boolean;
|
|
90
|
+
backups?: string[];
|
|
91
|
+
error?: string;
|
|
92
|
+
}
|
|
93
|
+
export declare class SymlinkRefusedError extends Error {
|
|
94
|
+
constructor(label: string, path: string);
|
|
95
|
+
}
|
|
96
|
+
export declare class UnparseableConfigError extends Error {
|
|
97
|
+
constructor(label: string, path: string, owner?: string);
|
|
98
|
+
}
|
|
99
|
+
export declare class EmptySharedConfigError extends Error {
|
|
100
|
+
constructor(label: string, path: string, owner?: string);
|
|
101
|
+
}
|
|
102
|
+
/** Refuse to read or write through a symlink at an adapter-owned path. */
|
|
103
|
+
export declare function assertNotSymlink(path: string, label: string): void;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { lstatSync } from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
/** Default command used by runtimes that support a SessionStart hook. The flag enables session-id capture. */
|
|
4
|
+
export const DEFAULT_HOOK_COMMAND = 'evolver inject session-start --hook-stdin';
|
|
5
|
+
export const DEFAULT_PROMPT_RECALL_HOOK_COMMAND = 'evolver inject prompt-recall --hook-stdin';
|
|
6
|
+
export const EVOLVER_HOOK_STATUS = 'evolver-managed-hook';
|
|
7
|
+
const LEGACY_EVOLVER_HOOK_STATUS = 'Loading Evolver memory';
|
|
8
|
+
function isObj(value) {
|
|
9
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
const LEGACY_NODE_COMMAND = /^"?node(?:\.exe)?"?\s+/i;
|
|
12
|
+
const LEGACY_EVOLVER_SCRIPT_BASENAME = /(?:^|[\\/'"\s])(?:evolver-session-start|evolver-session-end|evolver-signal-detect|evolver-task-recall|evolver-daemon-start)\.js(?=$|[\\/'"\s])/i;
|
|
13
|
+
const LEGACY_V2_CLI_HOOK_COMMAND = /^"?evolver(?:\.cmd|\.exe)?"?\s+inject\s+(?:session-start|prompt-recall)(?:\s|$)/i;
|
|
14
|
+
function isKnownEvolverHookCommand(command) {
|
|
15
|
+
const trimmed = command.trim();
|
|
16
|
+
return LEGACY_V2_CLI_HOOK_COMMAND.test(trimmed)
|
|
17
|
+
|| (LEGACY_NODE_COMMAND.test(trimmed) && LEGACY_EVOLVER_SCRIPT_BASENAME.test(trimmed));
|
|
18
|
+
}
|
|
19
|
+
/** Bind custom-command ownership to that exact command without adding undocumented hook-schema fields. */
|
|
20
|
+
export function evolverManagedHookStatus(command) {
|
|
21
|
+
if (isKnownEvolverHookCommand(command))
|
|
22
|
+
return EVOLVER_HOOK_STATUS;
|
|
23
|
+
const commandTag = createHash('sha256').update(command, 'utf8').digest('hex').slice(0, 16);
|
|
24
|
+
return `${EVOLVER_HOOK_STATUS} [evolver:${commandTag}]`;
|
|
25
|
+
}
|
|
26
|
+
function legacyEvolverManagedHookStatus(command) {
|
|
27
|
+
const currentStatus = evolverManagedHookStatus(command);
|
|
28
|
+
return `${LEGACY_EVOLVER_HOOK_STATUS}${currentStatus.slice(EVOLVER_HOOK_STATUS.length)}`;
|
|
29
|
+
}
|
|
30
|
+
const NO_MANAGED_HOOK_COMMANDS = new Set();
|
|
31
|
+
const isEvolverHandler = (handler, trustManagedStatus, managedCommands) => {
|
|
32
|
+
if (!isObj(handler) || typeof handler['command'] !== 'string')
|
|
33
|
+
return false;
|
|
34
|
+
const command = handler['command'];
|
|
35
|
+
return isKnownEvolverHookCommand(command)
|
|
36
|
+
|| (trustManagedStatus && (managedCommands.has(command)
|
|
37
|
+
|| handler['statusMessage'] === evolverManagedHookStatus(command)
|
|
38
|
+
|| handler['statusMessage'] === legacyEvolverManagedHookStatus(command)));
|
|
39
|
+
};
|
|
40
|
+
/** Remove only Evolver-owned handlers, retaining user handlers that share the same matcher group. */
|
|
41
|
+
export function stripEvolverHookEntries(entries, trustManagedStatus = false, managedCommands = NO_MANAGED_HOOK_COMMANDS) {
|
|
42
|
+
let changed = false;
|
|
43
|
+
const keptEntries = [];
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
if (!isObj(entry)) {
|
|
46
|
+
keptEntries.push(entry);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const handlers = entry['hooks'];
|
|
50
|
+
if (!Array.isArray(handlers)) {
|
|
51
|
+
if (isEvolverHandler(entry, trustManagedStatus, managedCommands))
|
|
52
|
+
changed = true;
|
|
53
|
+
else
|
|
54
|
+
keptEntries.push(entry);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const keptHandlers = handlers.filter((handler) => !isEvolverHandler(handler, trustManagedStatus, managedCommands));
|
|
58
|
+
if (keptHandlers.length === handlers.length) {
|
|
59
|
+
keptEntries.push(entry);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
changed = true;
|
|
63
|
+
if (keptHandlers.length > 0)
|
|
64
|
+
keptEntries.push({ ...entry, hooks: keptHandlers });
|
|
65
|
+
}
|
|
66
|
+
return { changed, entries: keptEntries };
|
|
67
|
+
}
|
|
68
|
+
export class SymlinkRefusedError extends Error {
|
|
69
|
+
constructor(label, path) {
|
|
70
|
+
super(`[setup-hooks] refusing to operate: ${label} ${path} is a symbolic link — evolver will not follow symlinks for adapter-owned paths (a hostile workspace could redirect writes/unlinks outside the project). Replace it with a real directory/file and rerun.`);
|
|
71
|
+
this.name = 'SymlinkRefusedError';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export class UnparseableConfigError extends Error {
|
|
75
|
+
constructor(label, path, owner = 'Claude Code') {
|
|
76
|
+
super(`[setup-hooks] refusing to overwrite ${label} (${path}): the file exists and is non-empty but is not valid JSON. This is ${owner}'s own shared config; merging into it would replace the whole file and could wipe its contents. Fix or remove the corrupt file, then rerun.`);
|
|
77
|
+
this.name = 'UnparseableConfigError';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export class EmptySharedConfigError extends Error {
|
|
81
|
+
constructor(label, path, owner = 'Claude Code') {
|
|
82
|
+
super(`[setup-hooks] refusing to overwrite ${label} (${path}): the file exists but is empty or contains only whitespace. ${owner} may be in the middle of a truncating write, and treating it as fresh config could wipe shared config data. Fix the empty file or retry after ${owner} finishes writing it.`);
|
|
83
|
+
this.name = 'EmptySharedConfigError';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** Refuse to read or write through a symlink at an adapter-owned path. */
|
|
87
|
+
export function assertNotSymlink(path, label) {
|
|
88
|
+
let stat;
|
|
89
|
+
try {
|
|
90
|
+
stat = lstatSync(path);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
if (error.code === 'ENOENT')
|
|
94
|
+
return;
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
if (stat.isSymbolicLink())
|
|
98
|
+
throw new SymlinkRefusedError(label, path);
|
|
99
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { InjectionPlan, McpServerCmd, RuntimeId } from './injection.js';
|
|
2
|
-
import { type InstallOptions, type InstallResult, type UninstallOptions } from './
|
|
2
|
+
import { type InstallOptions, type InstallResult, type UninstallOptions } from './installerShared.js';
|
|
3
3
|
type BeforeReplaceHook = (path: string) => void;
|
|
4
4
|
export declare function _setJsonMcpBeforeReplaceHookForTest(hook?: BeforeReplaceHook): void;
|
|
5
5
|
export declare function _setJsonMcpAfterReplaceHookForTest(hook?: BeforeReplaceHook): void;
|
|
6
|
+
export declare function _setJsonMcpAfterSharedFileValidateHookForTest(hook?: BeforeReplaceHook): void;
|
|
6
7
|
export declare function _setJsonMcpBeforeBackupRemoveHookForTest(hook?: BeforeReplaceHook): void;
|
|
8
|
+
export declare function _setJsonMcpBeforeSharedFileCommitHookForTest(hook?: BeforeReplaceHook): void;
|
|
7
9
|
export interface JsonMcpRuntimeSpec {
|
|
8
10
|
runtime: 'opencode' | 'kiro';
|
|
9
11
|
configPath(opts: JsonMcpPathOptions): string;
|
|
@@ -70,6 +72,8 @@ export declare class McpConfigChangedError extends Error {
|
|
|
70
72
|
export declare class McpServerValidationError extends Error {
|
|
71
73
|
constructor(message: string);
|
|
72
74
|
}
|
|
75
|
+
declare function retrySharedConfigTransaction<T>(operation: () => T): T;
|
|
76
|
+
export declare const _retrySharedConfigTransactionForTest: typeof retrySharedConfigTransaction;
|
|
73
77
|
export declare function installJsonMcpRuntime(spec: JsonMcpRuntimeSpec, plan: InjectionPlan, opts: InstallOptions): InstallResult;
|
|
74
78
|
export declare function uninstallJsonMcpRuntime(spec: JsonMcpRuntimeSpec, runtime: RuntimeId, opts: UninstallOptions): InstallResult;
|
|
75
79
|
export {};
|
package/dist/jsonMcpInstaller.js
CHANGED
|
@@ -1,24 +1,34 @@
|
|
|
1
|
-
import { chmodSync, existsSync, linkSync, lstatSync, mkdirSync, readFileSync,
|
|
1
|
+
import { chmodSync, existsSync, linkSync, lstatSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
2
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
5
|
-
import { EmptySharedConfigError, SymlinkRefusedError, UnparseableConfigError, } from './
|
|
5
|
+
import { EmptySharedConfigError, SymlinkRefusedError, UnparseableConfigError, } from './installerShared.js';
|
|
6
|
+
import { commitSharedFile, SharedFileConflictError } from './sharedFileCommit.js';
|
|
6
7
|
const CONFIG_MODE = 0o600;
|
|
7
8
|
const DIR_MODE = 0o700;
|
|
8
9
|
const BACKUP_VERSION = 1;
|
|
9
10
|
const ENV_FILE_KEY = 'EVOLVER_ENV_FILE';
|
|
11
|
+
const SHARED_CONFIG_WRITE_RETRIES = 5;
|
|
10
12
|
let beforeReplaceHookForTest;
|
|
11
13
|
let afterReplaceHookForTest;
|
|
14
|
+
let afterSharedFileValidateHookForTest;
|
|
12
15
|
let beforeBackupRemoveHookForTest;
|
|
16
|
+
let beforeSharedFileCommitHookForTest;
|
|
13
17
|
export function _setJsonMcpBeforeReplaceHookForTest(hook) {
|
|
14
18
|
beforeReplaceHookForTest = hook;
|
|
15
19
|
}
|
|
16
20
|
export function _setJsonMcpAfterReplaceHookForTest(hook) {
|
|
17
21
|
afterReplaceHookForTest = hook;
|
|
18
22
|
}
|
|
23
|
+
export function _setJsonMcpAfterSharedFileValidateHookForTest(hook) {
|
|
24
|
+
afterSharedFileValidateHookForTest = hook;
|
|
25
|
+
}
|
|
19
26
|
export function _setJsonMcpBeforeBackupRemoveHookForTest(hook) {
|
|
20
27
|
beforeBackupRemoveHookForTest = hook;
|
|
21
28
|
}
|
|
29
|
+
export function _setJsonMcpBeforeSharedFileCommitHookForTest(hook) {
|
|
30
|
+
beforeSharedFileCommitHookForTest = hook;
|
|
31
|
+
}
|
|
22
32
|
function removeBackup(path) {
|
|
23
33
|
beforeBackupRemoveHookForTest?.(path);
|
|
24
34
|
rmSync(path);
|
|
@@ -361,25 +371,20 @@ function assertConfigTopologyUnchanged(runtime, safeRoot, snapshot) {
|
|
|
361
371
|
throw new McpConfigChangedError(runtime, candidate.path);
|
|
362
372
|
}
|
|
363
373
|
}
|
|
364
|
-
function atomicWrite(path, raw, mode, beforeRename) {
|
|
365
|
-
mkdirSync(dirname(path), { recursive: true, mode: DIR_MODE });
|
|
366
|
-
const tempPath = join(dirname(path), `.${randomUUID()}.tmp`);
|
|
367
|
-
try {
|
|
368
|
-
writeFileSync(tempPath, raw, { encoding: 'utf8', mode, flag: 'wx' });
|
|
369
|
-
chmodSync(tempPath, mode);
|
|
370
|
-
beforeRename?.();
|
|
371
|
-
renameSync(tempPath, path);
|
|
372
|
-
}
|
|
373
|
-
finally {
|
|
374
|
-
rmSync(tempPath, { force: true });
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
374
|
function guardedAtomicWrite(runtime, safeRoot, path, raw, mode, expectedRaw, additionalGuard) {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
375
|
+
beforeReplaceHookForTest?.(path);
|
|
376
|
+
assertSafeParents(runtime, safeRoot, path);
|
|
377
|
+
assertUnchanged(runtime, path, expectedRaw);
|
|
378
|
+
additionalGuard?.();
|
|
379
|
+
commitSharedFile({
|
|
380
|
+
path,
|
|
381
|
+
expectedRaw: expectedRaw ?? undefined,
|
|
382
|
+
nextRaw: raw,
|
|
383
|
+
mode,
|
|
384
|
+
...(afterSharedFileValidateHookForTest ? { afterValidateForTest: () => afterSharedFileValidateHookForTest?.(path) } : {}),
|
|
385
|
+
...(beforeSharedFileCommitHookForTest
|
|
386
|
+
? { beforeCommitForTest: () => beforeSharedFileCommitHookForTest?.(path) }
|
|
387
|
+
: {}),
|
|
383
388
|
});
|
|
384
389
|
}
|
|
385
390
|
function guardedRemove(runtime, safeRoot, path, expectedRaw, additionalGuard) {
|
|
@@ -387,7 +392,15 @@ function guardedRemove(runtime, safeRoot, path, expectedRaw, additionalGuard) {
|
|
|
387
392
|
assertSafeParents(runtime, safeRoot, path);
|
|
388
393
|
assertUnchanged(runtime, path, expectedRaw);
|
|
389
394
|
additionalGuard?.();
|
|
390
|
-
|
|
395
|
+
commitSharedFile({
|
|
396
|
+
path,
|
|
397
|
+
expectedRaw: expectedRaw ?? undefined,
|
|
398
|
+
nextRaw: undefined,
|
|
399
|
+
...(afterSharedFileValidateHookForTest ? { afterValidateForTest: () => afterSharedFileValidateHookForTest?.(path) } : {}),
|
|
400
|
+
...(beforeSharedFileCommitHookForTest
|
|
401
|
+
? { beforeCommitForTest: () => beforeSharedFileCommitHookForTest?.(path) }
|
|
402
|
+
: {}),
|
|
403
|
+
});
|
|
391
404
|
}
|
|
392
405
|
function backupPath(configPath) {
|
|
393
406
|
return `${configPath}.evolver-backup.json`;
|
|
@@ -552,7 +565,24 @@ function looksLikeEnvFilePointer(value) {
|
|
|
552
565
|
|| /^[.~$%]/.test(value)
|
|
553
566
|
|| /\.(?:env|dotenv)(?:$|[._-])/i.test(value);
|
|
554
567
|
}
|
|
568
|
+
function retrySharedConfigTransaction(operation) {
|
|
569
|
+
for (let attempt = 1;; attempt += 1) {
|
|
570
|
+
try {
|
|
571
|
+
return operation();
|
|
572
|
+
}
|
|
573
|
+
catch (error) {
|
|
574
|
+
if (!(error instanceof SharedFileConflictError)
|
|
575
|
+
|| error.recoveryPath !== undefined
|
|
576
|
+
|| attempt >= SHARED_CONFIG_WRITE_RETRIES)
|
|
577
|
+
throw error;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
export const _retrySharedConfigTransactionForTest = retrySharedConfigTransaction;
|
|
555
582
|
export function installJsonMcpRuntime(spec, plan, opts) {
|
|
583
|
+
return retrySharedConfigTransaction(() => installJsonMcpRuntimeOnce(spec, plan, opts));
|
|
584
|
+
}
|
|
585
|
+
function installJsonMcpRuntimeOnce(spec, plan, opts) {
|
|
556
586
|
validateServer(opts.server);
|
|
557
587
|
const resolution = resolveInstallRuntimeConfig(spec, opts);
|
|
558
588
|
const { configPath, safeRoot } = resolution;
|
|
@@ -707,6 +737,9 @@ function withoutEvolver(data, containerKey) {
|
|
|
707
737
|
return next;
|
|
708
738
|
}
|
|
709
739
|
export function uninstallJsonMcpRuntime(spec, runtime, opts) {
|
|
740
|
+
return retrySharedConfigTransaction(() => uninstallJsonMcpRuntimeOnce(spec, runtime, opts));
|
|
741
|
+
}
|
|
742
|
+
function uninstallJsonMcpRuntimeOnce(spec, runtime, opts) {
|
|
710
743
|
const resolution = resolveUninstallRuntimeConfig(spec, opts);
|
|
711
744
|
const { configPath, safeRoot } = resolution;
|
|
712
745
|
assertSafeParents(spec.runtime, safeRoot, configPath);
|
package/dist/kiroInstaller.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { InstallOptions } from './
|
|
1
|
+
import type { InstallOptions } from './installerShared.js';
|
|
2
2
|
import { type JsonMcpRuntimeResolution, type JsonMcpRuntimeSpec } from './jsonMcpInstaller.js';
|
|
3
3
|
type KiroPathOptions = Pick<InstallOptions, 'configRoot' | 'scope' | 'homeDir' | 'kiroHome'>;
|
|
4
4
|
/** Resolve only Kiro's base mcp.json contract; custom agent JSON is intentionally out of scope. */
|
|
5
5
|
export declare function resolveKiroConfig(opts: KiroPathOptions): JsonMcpRuntimeResolution;
|
|
6
6
|
export declare const KIRO_SPEC: JsonMcpRuntimeSpec;
|
|
7
7
|
export declare function kiroConfigRoot({ configRoot, scope, homeDir, kiroHome }: Pick<InstallOptions, 'configRoot' | 'scope' | 'homeDir' | 'kiroHome'>): string;
|
|
8
|
-
export declare const installKiro: (plan: import("./injection.js").InjectionPlan, opts: InstallOptions) => import("./
|
|
9
|
-
export declare const uninstallKiro: (runtime: import("./injection.js").RuntimeId, opts: import("./
|
|
8
|
+
export declare const installKiro: (plan: import("./injection.js").InjectionPlan, opts: InstallOptions) => import("./installerShared.js").InstallResult;
|
|
9
|
+
export declare const uninstallKiro: (runtime: import("./injection.js").RuntimeId, opts: import("./installerShared.js").UninstallOptions) => import("./installerShared.js").InstallResult;
|
|
10
10
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { InstallOptions } from './
|
|
1
|
+
import type { InstallOptions } from './installerShared.js';
|
|
2
2
|
import { type JsonMcpRuntimeResolution, type JsonMcpRuntimeSpec } from './jsonMcpInstaller.js';
|
|
3
3
|
type OpenCodePathOptions = Pick<InstallOptions, 'configRoot' | 'scope' | 'homeDir' | 'xdgConfigHome' | 'opencodeConfig' | 'opencodeConfigDir'>;
|
|
4
4
|
type OpenCodeManagedPathOptions = Pick<InstallOptions, 'opencodePlatform' | 'opencodeProgramData' | 'opencodeUsername'>;
|
|
@@ -13,6 +13,6 @@ export declare function resolveOpenCodeManagedPreferencePaths(opts?: OpenCodeMan
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function resolveOpenCodeConfig(opts: OpenCodePathOptions): JsonMcpRuntimeResolution;
|
|
15
15
|
export declare const OPENCODE_SPEC: JsonMcpRuntimeSpec;
|
|
16
|
-
export declare const installOpenCode: (plan: import("./injection.js").InjectionPlan, opts: InstallOptions) => import("./
|
|
17
|
-
export declare const uninstallOpenCode: (runtime: import("./injection.js").RuntimeId, opts: import("./
|
|
16
|
+
export declare const installOpenCode: (plan: import("./injection.js").InjectionPlan, opts: InstallOptions) => import("./installerShared.js").InstallResult;
|
|
17
|
+
export declare const uninstallOpenCode: (runtime: import("./injection.js").RuntimeId, opts: import("./installerShared.js").UninstallOptions) => import("./installerShared.js").InstallResult;
|
|
18
18
|
export {};
|
package/dist/proxyClient.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface ProxyFetch {
|
|
|
13
13
|
export interface EvolverProxyClientOptions {
|
|
14
14
|
baseUrl: string;
|
|
15
15
|
token: string;
|
|
16
|
+
expectedHubMode?: 'public' | 'private';
|
|
16
17
|
fetchFn?: ProxyFetch;
|
|
17
18
|
reloadSettings?: () => EvolverProxyClientOptions | undefined;
|
|
18
19
|
}
|
|
@@ -23,13 +24,16 @@ export interface ProxySearchArgs {
|
|
|
23
24
|
category?: string;
|
|
24
25
|
gene?: string;
|
|
25
26
|
limit?: number;
|
|
27
|
+
expectedHubMode?: 'public' | 'private';
|
|
26
28
|
}
|
|
27
29
|
export interface ProxyFetchArgs {
|
|
28
30
|
assetId?: string;
|
|
29
31
|
assetIds?: string[];
|
|
32
|
+
expectedHubMode?: 'public' | 'private';
|
|
30
33
|
}
|
|
31
34
|
export interface ProxyAssetBundle {
|
|
32
35
|
assets: unknown[];
|
|
36
|
+
expected_hub_mode?: 'public' | 'private';
|
|
33
37
|
}
|
|
34
38
|
export interface ProxyReuseResultArgs {
|
|
35
39
|
assetId: string;
|
|
@@ -40,6 +44,7 @@ export interface ProxyReuseResultArgs {
|
|
|
40
44
|
tokensSaved?: number;
|
|
41
45
|
timeSavedSeconds?: number;
|
|
42
46
|
reason?: string;
|
|
47
|
+
expectedHubMode?: 'public' | 'private';
|
|
43
48
|
}
|
|
44
49
|
export interface ProxyAgentSearchArgs {
|
|
45
50
|
query?: string;
|
|
@@ -59,6 +64,7 @@ export declare class EvolverProxyClient {
|
|
|
59
64
|
private baseUrl;
|
|
60
65
|
private token;
|
|
61
66
|
private readonly fetchFn;
|
|
67
|
+
private readonly expectedHubMode?;
|
|
62
68
|
private readonly reloadSettings;
|
|
63
69
|
constructor(opts: EvolverProxyClientOptions);
|
|
64
70
|
status(opts?: {
|
|
@@ -70,15 +76,21 @@ export declare class EvolverProxyClient {
|
|
|
70
76
|
getAgentProfile(agentId: string, timeoutMs?: number): Promise<unknown>;
|
|
71
77
|
discoverAgentsForTask(args: ProxyAgentDiscoverArgs): Promise<unknown>;
|
|
72
78
|
submitAsset(asset: unknown): Promise<unknown>;
|
|
79
|
+
submitAssetBundle(bundle: ProxyAssetBundle): Promise<unknown>;
|
|
73
80
|
/** Pre-publish dry-run: the hub runs its quality + content-safety gate but stores nothing and charges no credits. */
|
|
74
81
|
validateAsset(asset: unknown): Promise<unknown>;
|
|
75
82
|
validateAssetBundle(bundle: ProxyAssetBundle): Promise<unknown>;
|
|
76
83
|
distillConversation(input: unknown): Promise<unknown>;
|
|
77
84
|
recordReuseResult(args: ProxyReuseResultArgs): Promise<unknown>;
|
|
85
|
+
private modeBoundBody;
|
|
78
86
|
call(method: string, path: string, body?: unknown, opts?: {
|
|
79
87
|
signal?: AbortSignal;
|
|
80
88
|
}): Promise<unknown>;
|
|
89
|
+
private verifyExpectedHubMode;
|
|
90
|
+
private verifyReloadedHubMode;
|
|
91
|
+
private acceptResult;
|
|
81
92
|
private callOnce;
|
|
93
|
+
private connectionSnapshot;
|
|
82
94
|
private reloadFromSettings;
|
|
83
95
|
private proxyError;
|
|
84
96
|
}
|