@lnar/cli 0.0.1-dev.1da013d
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 +96 -0
- package/dist/api-client.d.ts +17 -0
- package/dist/api-client.js +35 -0
- package/dist/api-client.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +91 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/daemon.d.ts +5 -0
- package/dist/commands/daemon.js +158 -0
- package/dist/commands/daemon.js.map +1 -0
- package/dist/commands/down.d.ts +1 -0
- package/dist/commands/down.js +12 -0
- package/dist/commands/down.js.map +1 -0
- package/dist/commands/login.d.ts +6 -0
- package/dist/commands/login.js +86 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/scan.d.ts +4 -0
- package/dist/commands/scan.js +36 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +32 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +4 -0
- package/dist/commands/sync.js +96 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/up.d.ts +3 -0
- package/dist/commands/up.js +27 -0
- package/dist/commands/up.js.map +1 -0
- package/dist/config.d.ts +25 -0
- package/dist/config.js +82 -0
- package/dist/config.js.map +1 -0
- package/dist/hash.d.ts +7 -0
- package/dist/hash.js +12 -0
- package/dist/hash.js.map +1 -0
- package/dist/oauth-client.d.ts +32 -0
- package/dist/oauth-client.js +62 -0
- package/dist/oauth-client.js.map +1 -0
- package/dist/pending-client.d.ts +40 -0
- package/dist/pending-client.js +48 -0
- package/dist/pending-client.js.map +1 -0
- package/dist/scanners/_normalize.d.ts +13 -0
- package/dist/scanners/_normalize.js +76 -0
- package/dist/scanners/_normalize.js.map +1 -0
- package/dist/scanners/chatgpt.d.ts +20 -0
- package/dist/scanners/chatgpt.js +49 -0
- package/dist/scanners/chatgpt.js.map +1 -0
- package/dist/scanners/claude-code-plugins.d.ts +2 -0
- package/dist/scanners/claude-code-plugins.js +104 -0
- package/dist/scanners/claude-code-plugins.js.map +1 -0
- package/dist/scanners/claude-code.d.ts +13 -0
- package/dist/scanners/claude-code.js +221 -0
- package/dist/scanners/claude-code.js.map +1 -0
- package/dist/scanners/claude-desktop.d.ts +1 -0
- package/dist/scanners/claude-desktop.js +2 -0
- package/dist/scanners/claude-desktop.js.map +1 -0
- package/dist/scanners/codex.d.ts +5 -0
- package/dist/scanners/codex.js +69 -0
- package/dist/scanners/codex.js.map +1 -0
- package/dist/scanners/cursor.d.ts +9 -0
- package/dist/scanners/cursor.js +25 -0
- package/dist/scanners/cursor.js.map +1 -0
- package/dist/scanners/gemini-cli.d.ts +21 -0
- package/dist/scanners/gemini-cli.js +37 -0
- package/dist/scanners/gemini-cli.js.map +1 -0
- package/dist/scanners/index.d.ts +6 -0
- package/dist/scanners/index.js +16 -0
- package/dist/scanners/index.js.map +1 -0
- package/dist/service/files.d.ts +26 -0
- package/dist/service/files.js +119 -0
- package/dist/service/files.js.map +1 -0
- package/dist/service/install.d.ts +14 -0
- package/dist/service/install.js +231 -0
- package/dist/service/install.js.map +1 -0
- package/dist/types.d.ts +161 -0
- package/dist/types.js +53 -0
- package/dist/types.js.map +1 -0
- package/dist/writers/_fs.d.ts +22 -0
- package/dist/writers/_fs.js +55 -0
- package/dist/writers/_fs.js.map +1 -0
- package/dist/writers/chatgpt.d.ts +8 -0
- package/dist/writers/chatgpt.js +12 -0
- package/dist/writers/chatgpt.js.map +1 -0
- package/dist/writers/claude-code-plugins.d.ts +3 -0
- package/dist/writers/claude-code-plugins.js +74 -0
- package/dist/writers/claude-code-plugins.js.map +1 -0
- package/dist/writers/claude-code.d.ts +4 -0
- package/dist/writers/claude-code.js +128 -0
- package/dist/writers/claude-code.js.map +1 -0
- package/dist/writers/codex.d.ts +4 -0
- package/dist/writers/codex.js +88 -0
- package/dist/writers/codex.js.map +1 -0
- package/dist/writers/cursor.d.ts +4 -0
- package/dist/writers/cursor.js +74 -0
- package/dist/writers/cursor.js.map +1 -0
- package/dist/writers/gemini-cli.d.ts +4 -0
- package/dist/writers/gemini-cli.js +87 -0
- package/dist/writers/gemini-cli.js.map +1 -0
- package/dist/writers/index.d.ts +18 -0
- package/dist/writers/index.js +11 -0
- package/dist/writers/index.js.map +1 -0
- package/dist/writers/registry.d.ts +7 -0
- package/dist/writers/registry.js +31 -0
- package/dist/writers/registry.js.map +1 -0
- package/package.json +38 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 書き込み共通ユーティリティ。
|
|
3
|
+
*
|
|
4
|
+
* すべての writer はここを経由して
|
|
5
|
+
* 1. 既存ファイルを `.bak` にコピー (バックアップ)
|
|
6
|
+
* 2. `.tmp` に新内容を書く
|
|
7
|
+
* 3. rename(2) で atomic に置き換える
|
|
8
|
+
* の手順を踏む。中断されてもユーザー設定は保たれる。
|
|
9
|
+
*/
|
|
10
|
+
export declare const exists: (path: string) => Promise<boolean>;
|
|
11
|
+
export declare const readTextOrEmpty: (path: string) => Promise<string | null>;
|
|
12
|
+
/**
|
|
13
|
+
* 指定パスにテキストを atomic に書き込む。
|
|
14
|
+
* - 親ディレクトリが無ければ作成
|
|
15
|
+
* - 既存ファイルがあれば `.bak` を残す
|
|
16
|
+
* - tmp に書いて rename
|
|
17
|
+
*/
|
|
18
|
+
export declare const atomicWriteText: (path: string, content: string) => Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* JSON ファイルを atomic に書き込む。インデント 2 スペース、末尾改行付き。
|
|
21
|
+
*/
|
|
22
|
+
export declare const atomicWriteJson: (path: string, data: unknown) => Promise<void>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { copyFile, mkdir, readFile, rename, stat, writeFile, } from 'node:fs/promises';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* 書き込み共通ユーティリティ。
|
|
5
|
+
*
|
|
6
|
+
* すべての writer はここを経由して
|
|
7
|
+
* 1. 既存ファイルを `.bak` にコピー (バックアップ)
|
|
8
|
+
* 2. `.tmp` に新内容を書く
|
|
9
|
+
* 3. rename(2) で atomic に置き換える
|
|
10
|
+
* の手順を踏む。中断されてもユーザー設定は保たれる。
|
|
11
|
+
*/
|
|
12
|
+
export const exists = async (path) => {
|
|
13
|
+
try {
|
|
14
|
+
await stat(path);
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
if (err.code === 'ENOENT')
|
|
19
|
+
return false;
|
|
20
|
+
throw err;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
export const readTextOrEmpty = async (path) => {
|
|
24
|
+
try {
|
|
25
|
+
return await readFile(path, 'utf-8');
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
if (err.code === 'ENOENT')
|
|
29
|
+
return null;
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* 指定パスにテキストを atomic に書き込む。
|
|
35
|
+
* - 親ディレクトリが無ければ作成
|
|
36
|
+
* - 既存ファイルがあれば `.bak` を残す
|
|
37
|
+
* - tmp に書いて rename
|
|
38
|
+
*/
|
|
39
|
+
export const atomicWriteText = async (path, content) => {
|
|
40
|
+
await mkdir(dirname(path), { recursive: true });
|
|
41
|
+
if (await exists(path)) {
|
|
42
|
+
await copyFile(path, `${path}.bak`);
|
|
43
|
+
}
|
|
44
|
+
const tmp = `${path}.tmp`;
|
|
45
|
+
await writeFile(tmp, content, 'utf-8');
|
|
46
|
+
await rename(tmp, path);
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* JSON ファイルを atomic に書き込む。インデント 2 スペース、末尾改行付き。
|
|
50
|
+
*/
|
|
51
|
+
export const atomicWriteJson = async (path, data) => {
|
|
52
|
+
const text = `${JSON.stringify(data, null, 2)}\n`;
|
|
53
|
+
await atomicWriteText(path, text);
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=_fs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_fs.js","sourceRoot":"","sources":["../../src/writers/_fs.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,MAAM,EACN,IAAI,EACJ,SAAS,GACV,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,MAAM,GAAG,KAAK,EAAE,IAAY,EAAoB,EAAE;IAC7D,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,IAAI,CAAC,CAAA;QAChB,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAA;QAClE,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,IAAY,EAA0B,EAAE;IAC5E,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QACjE,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAClC,IAAY,EACZ,OAAe,EACA,EAAE;IACjB,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC/C,IAAI,MAAM,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,QAAQ,CAAC,IAAI,EAAE,GAAG,IAAI,MAAM,CAAC,CAAA;IACrC,CAAC;IACD,MAAM,GAAG,GAAG,GAAG,IAAI,MAAM,CAAA;IACzB,MAAM,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACtC,MAAM,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;AACzB,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAClC,IAAY,EACZ,IAAa,EACE,EAAE;IACjB,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAA;IACjD,MAAM,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;AACnC,CAAC,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChatGPT Desktop: connector データは OS Keychain で暗号化されており、
|
|
3
|
+
* 平文の MCP 設定ファイルが存在しない。よって書き込みは未対応 (unsupported)。
|
|
4
|
+
*
|
|
5
|
+
* 将来 OpenAI 側が export / API を公開した場合に実装する。
|
|
6
|
+
*/
|
|
7
|
+
export const chatgptWriter = {
|
|
8
|
+
async apply(_change) {
|
|
9
|
+
return 'unsupported';
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=chatgpt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatgpt.js","sourceRoot":"","sources":["../../src/writers/chatgpt.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAW;IACnC,KAAK,CAAC,KAAK,CAAC,OAAO;QACjB,OAAO,aAAa,CAAA;IACtB,CAAC;CACF,CAAA"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code plugin の install / uninstall / enable / disable を `claude plugin`
|
|
3
|
+
* サブコマンド経由で実行する writer。
|
|
4
|
+
*
|
|
5
|
+
* 設計判断:
|
|
6
|
+
* - 設定ファイルを直接編集しない (installed_plugins.json + enabledPlugins の
|
|
7
|
+
* 整合性は CLI が保つので、絶対に CLI 経由で操作する)。
|
|
8
|
+
* - `--scope user|local|project` を必ず付ける。local / project の場合は
|
|
9
|
+
* project_path を cwd にして実行する (CLI が cwd から project を解決するため)。
|
|
10
|
+
* - 非対話で確実に終わるよう `--yes` を付ける (uninstall がプロンプトを出すケース)。
|
|
11
|
+
*/
|
|
12
|
+
import { exec } from 'node:child_process';
|
|
13
|
+
import { promisify } from 'node:util';
|
|
14
|
+
const execAsync = promisify(exec);
|
|
15
|
+
const PLUGIN_SCOPES = new Set(['user', 'local', 'project']);
|
|
16
|
+
const ensurePluginScope = (change) => {
|
|
17
|
+
if (!PLUGIN_SCOPES.has(change.source_scope)) {
|
|
18
|
+
throw new Error(`plugin action ${change.action} requires source_scope in (user, local, project), got: ${change.source_scope ?? 'null'}`);
|
|
19
|
+
}
|
|
20
|
+
return change.source_scope;
|
|
21
|
+
};
|
|
22
|
+
const ensureProjectPath = (change, scope) => {
|
|
23
|
+
if (scope === 'user') {
|
|
24
|
+
// user scope はマシン全体なので cwd を要しない
|
|
25
|
+
throw new Error('ensureProjectPath called with user scope');
|
|
26
|
+
}
|
|
27
|
+
if (!change.project_path) {
|
|
28
|
+
throw new Error(`plugin action with scope=${scope} requires project_path`);
|
|
29
|
+
}
|
|
30
|
+
return change.project_path;
|
|
31
|
+
};
|
|
32
|
+
const runClaudeCli = async (args, options = {}) => {
|
|
33
|
+
// execFile が安全だが、PATH 探索を効かせたいので shell 経由にする。
|
|
34
|
+
// 引数はホワイトリスト (action / scope / プラグイン名) しか入らないが、
|
|
35
|
+
// 念のため shell-quote する: 単純な英数 + `@:-._/` 以外が来たら拒否。
|
|
36
|
+
for (const a of args) {
|
|
37
|
+
if (!/^[A-Za-z0-9@:_\-./]+$/.test(a)) {
|
|
38
|
+
throw new Error(`refusing to pass unsafe argument to claude CLI: ${JSON.stringify(a)}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const cmd = ['claude', ...args].join(' ');
|
|
42
|
+
await execAsync(cmd, {
|
|
43
|
+
cwd: options.cwd,
|
|
44
|
+
timeout: 120_000,
|
|
45
|
+
// 非対話実行ではホームディレクトリの設定を読みたいので env は引き継ぐ
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
export const applyPluginChange = async (change) => {
|
|
49
|
+
const scope = ensurePluginScope(change);
|
|
50
|
+
const cwd = scope === 'user' ? undefined : ensureProjectPath(change, scope);
|
|
51
|
+
switch (change.action) {
|
|
52
|
+
case 'install_plugin':
|
|
53
|
+
await runClaudeCli(['plugin', 'install', change.target_name, '--scope', scope], { cwd });
|
|
54
|
+
return 'applied';
|
|
55
|
+
case 'uninstall_plugin':
|
|
56
|
+
await runClaudeCli(['plugin', 'uninstall', change.target_name, '--scope', scope, '--yes'], { cwd });
|
|
57
|
+
return 'applied';
|
|
58
|
+
case 'enable_plugin':
|
|
59
|
+
await runClaudeCli(['plugin', 'enable', change.target_name, '--scope', scope], { cwd });
|
|
60
|
+
return 'applied';
|
|
61
|
+
case 'disable_plugin':
|
|
62
|
+
await runClaudeCli(['plugin', 'disable', change.target_name, '--scope', scope], { cwd });
|
|
63
|
+
return 'applied';
|
|
64
|
+
default:
|
|
65
|
+
throw new Error(`applyPluginChange called with non-plugin action: ${change.action}`);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
export const PLUGIN_ACTIONS = new Set([
|
|
69
|
+
'install_plugin',
|
|
70
|
+
'uninstall_plugin',
|
|
71
|
+
'enable_plugin',
|
|
72
|
+
'disable_plugin',
|
|
73
|
+
]);
|
|
74
|
+
//# sourceMappingURL=claude-code-plugins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code-plugins.js","sourceRoot":"","sources":["../../src/writers/claude-code-plugins.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { atomicWriteJson } from './_fs.js';
|
|
5
|
+
import { applyPluginChange, PLUGIN_ACTIONS } from './claude-code-plugins.js';
|
|
6
|
+
/**
|
|
7
|
+
* Claude Code writer.
|
|
8
|
+
*
|
|
9
|
+
* `source_scope` で書き込み先を振り分ける:
|
|
10
|
+
* - source_scope='global' (Claude Code 公式の "user" scope)
|
|
11
|
+
* → ~/.claude.json の top-level `mcpServers` を編集
|
|
12
|
+
* - source_scope='local'
|
|
13
|
+
* → ~/.claude.json の `projects[<project_path>].mcpServers` を編集
|
|
14
|
+
* - source_scope='project' (.mcp.json, チーム共有)
|
|
15
|
+
* → 未対応。team 共有ファイルを CLI が勝手に編集するのは事故の元なので
|
|
16
|
+
* dashboard 側でも Disconnect ボタンを出さない方針。
|
|
17
|
+
* - source_scope='remote' / plugin 由来
|
|
18
|
+
* → 同じく未対応 (claude.ai / plugin lifecycle の管理外)
|
|
19
|
+
*
|
|
20
|
+
* 形式: `{ "<server>": { type, command, args, url, env } }`
|
|
21
|
+
* - env キーは spec.envKeys 由来。値は空文字で埋める (ユーザーが手動補完)。
|
|
22
|
+
*/
|
|
23
|
+
const buildEntryFromSpec = (spec) => {
|
|
24
|
+
const entry = {
|
|
25
|
+
type: spec.transport,
|
|
26
|
+
};
|
|
27
|
+
if (spec.command)
|
|
28
|
+
entry.command = spec.command;
|
|
29
|
+
if (spec.args && spec.args.length > 0)
|
|
30
|
+
entry.args = spec.args;
|
|
31
|
+
if (spec.url)
|
|
32
|
+
entry.url = spec.url;
|
|
33
|
+
if (spec.envKeys && spec.envKeys.length > 0) {
|
|
34
|
+
const env = {};
|
|
35
|
+
for (const key of spec.envKeys) {
|
|
36
|
+
env[key] = '';
|
|
37
|
+
}
|
|
38
|
+
entry.env = env;
|
|
39
|
+
}
|
|
40
|
+
return entry;
|
|
41
|
+
};
|
|
42
|
+
const globalConfigPath = (home = homedir()) => join(home, '.claude.json');
|
|
43
|
+
const loadGlobalConfig = async (path) => {
|
|
44
|
+
try {
|
|
45
|
+
const text = await readFile(path, 'utf-8');
|
|
46
|
+
return JSON.parse(text);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
if (err.code === 'ENOENT')
|
|
50
|
+
return {};
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* 操作対象の mcpServers セクションへの参照を返す。
|
|
56
|
+
* local の場合は projects[project_path].mcpServers を辿り、無ければ作る。
|
|
57
|
+
* config を mutate するので、戻り値を書き換えればそのまま永続化対象になる。
|
|
58
|
+
*/
|
|
59
|
+
const resolveMcpServersSection = (config, change) => {
|
|
60
|
+
if (change.source_scope === 'local') {
|
|
61
|
+
if (!change.project_path) {
|
|
62
|
+
throw new Error('source_scope=local requires project_path on the pending change');
|
|
63
|
+
}
|
|
64
|
+
let projects = config.projects;
|
|
65
|
+
if (!projects || typeof projects !== 'object' || Array.isArray(projects)) {
|
|
66
|
+
projects = {};
|
|
67
|
+
config.projects = projects;
|
|
68
|
+
}
|
|
69
|
+
const projectsMap = projects;
|
|
70
|
+
let entry = projectsMap[change.project_path];
|
|
71
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
72
|
+
entry = {};
|
|
73
|
+
projectsMap[change.project_path] = entry;
|
|
74
|
+
}
|
|
75
|
+
const entryObj = entry;
|
|
76
|
+
let mcpServers = entryObj.mcpServers;
|
|
77
|
+
if (!mcpServers || typeof mcpServers !== 'object' || Array.isArray(mcpServers)) {
|
|
78
|
+
mcpServers = {};
|
|
79
|
+
entryObj.mcpServers = mcpServers;
|
|
80
|
+
}
|
|
81
|
+
return mcpServers;
|
|
82
|
+
}
|
|
83
|
+
// global (= Claude Code の user scope) / null (旧データ互換) は top-level を見る
|
|
84
|
+
let mcpServers = config.mcpServers;
|
|
85
|
+
if (!mcpServers || typeof mcpServers !== 'object' || Array.isArray(mcpServers)) {
|
|
86
|
+
mcpServers = {};
|
|
87
|
+
config.mcpServers = mcpServers;
|
|
88
|
+
}
|
|
89
|
+
return mcpServers;
|
|
90
|
+
};
|
|
91
|
+
const applyChange = async (change, home = homedir()) => {
|
|
92
|
+
if (change.source_scope === 'project' || change.source_scope === 'remote') {
|
|
93
|
+
throw new Error(`claude-code writer does not support source_scope='${change.source_scope}'`);
|
|
94
|
+
}
|
|
95
|
+
const path = globalConfigPath(home);
|
|
96
|
+
const config = await loadGlobalConfig(path);
|
|
97
|
+
const mcpServers = resolveMcpServersSection(config, change);
|
|
98
|
+
if (change.action === 'add_server') {
|
|
99
|
+
if (change.spec == null) {
|
|
100
|
+
throw new Error('add_server requires spec');
|
|
101
|
+
}
|
|
102
|
+
mcpServers[change.target_name] = buildEntryFromSpec(change.spec);
|
|
103
|
+
}
|
|
104
|
+
else if (change.action === 'remove_server') {
|
|
105
|
+
if (!(change.target_name in mcpServers)) {
|
|
106
|
+
const scopeDesc = change.source_scope === 'local' ? `${path}#projects[${change.project_path}]` : path;
|
|
107
|
+
throw new Error(`server "${change.target_name}" not found in ${scopeDesc}; nothing to remove`);
|
|
108
|
+
}
|
|
109
|
+
delete mcpServers[change.target_name];
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
throw new Error(`unsupported action: ${change.action}`);
|
|
113
|
+
}
|
|
114
|
+
await atomicWriteJson(path, config);
|
|
115
|
+
return 'applied';
|
|
116
|
+
};
|
|
117
|
+
export const claudeCodeWriter = {
|
|
118
|
+
apply: async (change) => {
|
|
119
|
+
// Plugin 系アクションは別 writer (`claude plugin` CLI 経由) に委譲する。
|
|
120
|
+
if (PLUGIN_ACTIONS.has(change.action)) {
|
|
121
|
+
return applyPluginChange(change);
|
|
122
|
+
}
|
|
123
|
+
return applyChange(change);
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
// テスト用の home 注入版
|
|
127
|
+
export const _applyForTest = applyChange;
|
|
128
|
+
//# sourceMappingURL=claude-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/writers/claude-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { parse as parseToml, stringify as stringifyToml } from 'smol-toml';
|
|
5
|
+
import { atomicWriteText } from './_fs.js';
|
|
6
|
+
/**
|
|
7
|
+
* Codex CLI writer.
|
|
8
|
+
*
|
|
9
|
+
* 書き込み対象: `~/.codex/config.toml`。
|
|
10
|
+
* 形式 (Codex):
|
|
11
|
+
* [mcp_servers.<name>]
|
|
12
|
+
* transport = "http"
|
|
13
|
+
* url = "https://..."
|
|
14
|
+
*
|
|
15
|
+
* [mcp_servers.<name>.env]
|
|
16
|
+
* API_KEY = ""
|
|
17
|
+
*
|
|
18
|
+
* smol-toml は parse / stringify を両対応するので、既存 config をパースしてから
|
|
19
|
+
* `mcp_servers.<name>` を書き換え、`stringify` で書き戻す。
|
|
20
|
+
*
|
|
21
|
+
* 注意: TOML stringify は元のコメント・順序を保持しない。これは Codex の
|
|
22
|
+
* 設定ファイルが「lnar が管理する箇所」と「ユーザーが手書きしたコメント」を
|
|
23
|
+
* 混在させた時に **コメントが消える** ことを意味する。MVP としては許容するが、
|
|
24
|
+
* 将来は AST 編集ライブラリ (`@iarna/toml` 等) への置き換えを検討する。
|
|
25
|
+
*/
|
|
26
|
+
const buildEntryFromSpec = (spec) => {
|
|
27
|
+
const entry = {
|
|
28
|
+
transport: spec.transport,
|
|
29
|
+
};
|
|
30
|
+
if (spec.command)
|
|
31
|
+
entry.command = spec.command;
|
|
32
|
+
if (spec.args && spec.args.length > 0)
|
|
33
|
+
entry.args = spec.args;
|
|
34
|
+
if (spec.url)
|
|
35
|
+
entry.url = spec.url;
|
|
36
|
+
if (spec.envKeys && spec.envKeys.length > 0) {
|
|
37
|
+
const env = {};
|
|
38
|
+
for (const key of spec.envKeys) {
|
|
39
|
+
env[key] = '';
|
|
40
|
+
}
|
|
41
|
+
entry.env = env;
|
|
42
|
+
}
|
|
43
|
+
return entry;
|
|
44
|
+
};
|
|
45
|
+
const configPath = (home = homedir()) => join(home, '.codex', 'config.toml');
|
|
46
|
+
const loadConfig = async (path) => {
|
|
47
|
+
try {
|
|
48
|
+
const text = await readFile(path, 'utf-8');
|
|
49
|
+
const parsed = parseToml(text);
|
|
50
|
+
return parsed;
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
if (err.code === 'ENOENT')
|
|
54
|
+
return {};
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const applyChange = async (change, home = homedir()) => {
|
|
59
|
+
const path = configPath(home);
|
|
60
|
+
const config = await loadConfig(path);
|
|
61
|
+
const mcpServers = config.mcp_servers && typeof config.mcp_servers === 'object'
|
|
62
|
+
? config.mcp_servers
|
|
63
|
+
: {};
|
|
64
|
+
if (change.action === 'add_server') {
|
|
65
|
+
if (change.spec == null) {
|
|
66
|
+
throw new Error('add_server requires spec');
|
|
67
|
+
}
|
|
68
|
+
mcpServers[change.target_name] = buildEntryFromSpec(change.spec);
|
|
69
|
+
}
|
|
70
|
+
else if (change.action === 'remove_server') {
|
|
71
|
+
if (!(change.target_name in mcpServers)) {
|
|
72
|
+
throw new Error(`server "${change.target_name}" not found in ${path}; nothing to remove`);
|
|
73
|
+
}
|
|
74
|
+
delete mcpServers[change.target_name];
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
throw new Error(`unsupported action: ${change.action}`);
|
|
78
|
+
}
|
|
79
|
+
config.mcp_servers = mcpServers;
|
|
80
|
+
const text = stringifyToml(config);
|
|
81
|
+
await atomicWriteText(path, `${text}\n`);
|
|
82
|
+
return 'applied';
|
|
83
|
+
};
|
|
84
|
+
export const codexWriter = {
|
|
85
|
+
apply: (change) => applyChange(change),
|
|
86
|
+
};
|
|
87
|
+
export const _applyForTest = applyChange;
|
|
88
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/writers/codex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { atomicWriteJson } from './_fs.js';
|
|
5
|
+
/**
|
|
6
|
+
* Cursor writer.
|
|
7
|
+
*
|
|
8
|
+
* 書き込み対象: `~/.cursor/mcp.json` (global scope)。
|
|
9
|
+
* project scope (`<cwd>/.cursor/mcp.json`) は将来必要なら spec で指定可能にする。
|
|
10
|
+
*
|
|
11
|
+
* Cursor の MCP 設定形式は Claude Code と同じく
|
|
12
|
+
* `{ "mcpServers": { "<name>": { ... } } }`
|
|
13
|
+
*/
|
|
14
|
+
const buildEntryFromSpec = (spec) => {
|
|
15
|
+
const entry = {
|
|
16
|
+
type: spec.transport,
|
|
17
|
+
};
|
|
18
|
+
if (spec.command)
|
|
19
|
+
entry.command = spec.command;
|
|
20
|
+
if (spec.args && spec.args.length > 0)
|
|
21
|
+
entry.args = spec.args;
|
|
22
|
+
if (spec.url)
|
|
23
|
+
entry.url = spec.url;
|
|
24
|
+
if (spec.envKeys && spec.envKeys.length > 0) {
|
|
25
|
+
const env = {};
|
|
26
|
+
for (const key of spec.envKeys) {
|
|
27
|
+
env[key] = '';
|
|
28
|
+
}
|
|
29
|
+
entry.env = env;
|
|
30
|
+
}
|
|
31
|
+
return entry;
|
|
32
|
+
};
|
|
33
|
+
const globalConfigPath = (home = homedir()) => join(home, '.cursor', 'mcp.json');
|
|
34
|
+
const loadConfig = async (path) => {
|
|
35
|
+
try {
|
|
36
|
+
const text = await readFile(path, 'utf-8');
|
|
37
|
+
return JSON.parse(text);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
if (err.code === 'ENOENT')
|
|
41
|
+
return {};
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const applyChange = async (change, home = homedir()) => {
|
|
46
|
+
const path = globalConfigPath(home);
|
|
47
|
+
const config = await loadConfig(path);
|
|
48
|
+
const mcpServers = config.mcpServers && typeof config.mcpServers === 'object'
|
|
49
|
+
? config.mcpServers
|
|
50
|
+
: {};
|
|
51
|
+
if (change.action === 'add_server') {
|
|
52
|
+
if (change.spec == null) {
|
|
53
|
+
throw new Error('add_server requires spec');
|
|
54
|
+
}
|
|
55
|
+
mcpServers[change.target_name] = buildEntryFromSpec(change.spec);
|
|
56
|
+
}
|
|
57
|
+
else if (change.action === 'remove_server') {
|
|
58
|
+
if (!(change.target_name in mcpServers)) {
|
|
59
|
+
throw new Error(`server "${change.target_name}" not found in ${path}; nothing to remove`);
|
|
60
|
+
}
|
|
61
|
+
delete mcpServers[change.target_name];
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
throw new Error(`unsupported action: ${change.action}`);
|
|
65
|
+
}
|
|
66
|
+
config.mcpServers = mcpServers;
|
|
67
|
+
await atomicWriteJson(path, config);
|
|
68
|
+
return 'applied';
|
|
69
|
+
};
|
|
70
|
+
export const cursorWriter = {
|
|
71
|
+
apply: (change) => applyChange(change),
|
|
72
|
+
};
|
|
73
|
+
export const _applyForTest = applyChange;
|
|
74
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/writers/cursor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { atomicWriteJson } from './_fs.js';
|
|
5
|
+
/**
|
|
6
|
+
* Google Gemini CLI writer.
|
|
7
|
+
*
|
|
8
|
+
* 書き込み対象: `~/.gemini/settings.json` の `mcpServers` (= global / user scope)。
|
|
9
|
+
*
|
|
10
|
+
* Gemini CLI には Claude Code の `projects[<path>]` のような "local" 階層が無く、
|
|
11
|
+
* project 別設定は `<cwd>/.gemini/settings.json` (= project scope) で扱う。
|
|
12
|
+
* project scope は team 共有用途と被るため本 writer では編集しない方針 (Claude
|
|
13
|
+
* Code の `.mcp.json` と同じ扱い)。
|
|
14
|
+
*
|
|
15
|
+
* 形式: `{ "mcpServers": { "<name>": { type, command, args, url, env } } }`
|
|
16
|
+
*/
|
|
17
|
+
const buildEntryFromSpec = (spec) => {
|
|
18
|
+
const entry = {
|
|
19
|
+
type: spec.transport,
|
|
20
|
+
};
|
|
21
|
+
if (spec.command)
|
|
22
|
+
entry.command = spec.command;
|
|
23
|
+
if (spec.args && spec.args.length > 0)
|
|
24
|
+
entry.args = spec.args;
|
|
25
|
+
if (spec.url)
|
|
26
|
+
entry.url = spec.url;
|
|
27
|
+
if (spec.envKeys && spec.envKeys.length > 0) {
|
|
28
|
+
const env = {};
|
|
29
|
+
for (const key of spec.envKeys) {
|
|
30
|
+
env[key] = '';
|
|
31
|
+
}
|
|
32
|
+
entry.env = env;
|
|
33
|
+
}
|
|
34
|
+
return entry;
|
|
35
|
+
};
|
|
36
|
+
const globalConfigPath = (home = homedir()) => join(home, '.gemini', 'settings.json');
|
|
37
|
+
const loadConfig = async (path) => {
|
|
38
|
+
try {
|
|
39
|
+
const text = await readFile(path, 'utf-8');
|
|
40
|
+
return JSON.parse(text);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
if (err.code === 'ENOENT')
|
|
44
|
+
return {};
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const applyChange = async (change, home = homedir()) => {
|
|
49
|
+
if (change.source_scope === 'project' || change.source_scope === 'remote') {
|
|
50
|
+
throw new Error(`gemini-cli writer does not support source_scope='${change.source_scope}'`);
|
|
51
|
+
}
|
|
52
|
+
if (change.source_scope === 'local') {
|
|
53
|
+
throw new Error("gemini-cli has no 'local' (per-project, per-user) scope; pass source_scope='global' instead");
|
|
54
|
+
}
|
|
55
|
+
const path = globalConfigPath(home);
|
|
56
|
+
const config = await loadConfig(path);
|
|
57
|
+
let mcpServers = config.mcpServers;
|
|
58
|
+
if (!mcpServers ||
|
|
59
|
+
typeof mcpServers !== 'object' ||
|
|
60
|
+
Array.isArray(mcpServers)) {
|
|
61
|
+
mcpServers = {};
|
|
62
|
+
config.mcpServers = mcpServers;
|
|
63
|
+
}
|
|
64
|
+
const map = mcpServers;
|
|
65
|
+
if (change.action === 'add_server') {
|
|
66
|
+
if (change.spec == null) {
|
|
67
|
+
throw new Error('add_server requires spec');
|
|
68
|
+
}
|
|
69
|
+
map[change.target_name] = buildEntryFromSpec(change.spec);
|
|
70
|
+
}
|
|
71
|
+
else if (change.action === 'remove_server') {
|
|
72
|
+
if (!(change.target_name in map)) {
|
|
73
|
+
throw new Error(`server "${change.target_name}" not found in ${path}; nothing to remove`);
|
|
74
|
+
}
|
|
75
|
+
delete map[change.target_name];
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
throw new Error(`unsupported action: ${change.action}`);
|
|
79
|
+
}
|
|
80
|
+
await atomicWriteJson(path, config);
|
|
81
|
+
return 'applied';
|
|
82
|
+
};
|
|
83
|
+
export const geminiCliWriter = {
|
|
84
|
+
apply: (change) => applyChange(change),
|
|
85
|
+
};
|
|
86
|
+
export const _applyForTest = applyChange;
|
|
87
|
+
//# sourceMappingURL=gemini-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini-cli.js","sourceRoot":"","sources":["../../src/writers/gemini-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writer 抽象: 「ある agent_kind の MCP 設定ファイルに対して add / remove を行う」関数の型。
|
|
3
|
+
*
|
|
4
|
+
* - 各 writer は **atomic write** (tmp file + rename) を実装し、書き込み前に
|
|
5
|
+
* `.bak` ファイルを残すこと。
|
|
6
|
+
* - 値が無い env キーは「キーは追加するが空の値で書く」方針。ユーザーが
|
|
7
|
+
* 手動で値を埋める前提。
|
|
8
|
+
* - 失敗時は throw する (daemon 側で catch して pending failed に転送)。
|
|
9
|
+
*/
|
|
10
|
+
import type { PendingChange } from '../pending-client.js';
|
|
11
|
+
export type WriterResult = 'applied' | 'unsupported';
|
|
12
|
+
export interface Writer {
|
|
13
|
+
/**
|
|
14
|
+
* 設定ファイルを変更する。`unsupported` の場合は変更せず、その旨を返す
|
|
15
|
+
* (例: ChatGPT Desktop は暗号化済みで書き込み不可)。
|
|
16
|
+
*/
|
|
17
|
+
apply(change: PendingChange): Promise<WriterResult>;
|
|
18
|
+
}
|