aptly 0.1.2 → 0.1.3
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.en.md +2 -2
- package/README.ja.md +2 -2
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/dist/bin/aptly.js +35 -5
- package/dist/src/setup.d.ts +2 -2
- package/dist/src/setup.js +41 -6
- package/dist/src/update-notifier.d.ts +14 -0
- package/dist/src/update-notifier.js +179 -0
- package/dist/src/update.d.ts +13 -0
- package/dist/src/update.js +68 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/docs/guide/en.md +45 -1
- package/docs/guide/ja.md +45 -1
- package/docs/guide/ko.md +45 -1
- package/docs/guide/zh-CN.md +45 -1
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -23,12 +23,12 @@ Our own tests showed **about 52–58% lower API-equivalent costs**. See the [tes
|
|
|
23
23
|
Requires Node.js **24 or later** and Claude Code or Codex. Install the latest stable version globally with these commands.
|
|
24
24
|
|
|
25
25
|
```sh
|
|
26
|
-
npm install -g aptly@latest
|
|
26
|
+
npm install -g aptly@latest --allow-scripts=aptly
|
|
27
27
|
aptly setup --client all --mode defaults
|
|
28
28
|
aptly --version
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
Check the installed version with `aptly --version`.
|
|
31
|
+
Check the installed version with `aptly --version`. On supported npm versions, `--allow-scripts=aptly` allows Aptly’s installation script. If your npm does not support this option, follow the explicit installation and setup steps in the [installation guide](docs/guide/en.md#installation).
|
|
32
32
|
|
|
33
33
|
Before upgrading, stop all related host, MCP, and CLI processes and review the ledger transition in the [installation and upgrade guide](docs/guide/en.md#installation).
|
|
34
34
|
|
package/README.ja.md
CHANGED
|
@@ -23,12 +23,12 @@ Aptly は必要最小限のタスク要約を OpenAI Decisions API で分類し
|
|
|
23
23
|
Node.js **24 以上**と Claude Code または Codex が必要です。以下のコマンドで最新の安定版をグローバルインストールしてください。
|
|
24
24
|
|
|
25
25
|
```sh
|
|
26
|
-
npm install -g aptly@latest
|
|
26
|
+
npm install -g aptly@latest --allow-scripts=aptly
|
|
27
27
|
aptly setup --client all --mode defaults
|
|
28
28
|
aptly --version
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
`aptly --version`
|
|
31
|
+
`aptly --version` でインストール済みのバージョンを確認してください。対応する npm では、`--allow-scripts=aptly` で Aptly のインストールスクリプトを許可します。このオプションに対応していない npm では、[インストール案内](docs/guide/ja.md#インストール)の明示的なインストール・設定手順に従ってください。
|
|
32
32
|
|
|
33
33
|
アップグレード前に、関連するホスト・MCP・CLI の全プロセスを停止し、[インストールとアップグレードの案内](docs/guide/ja.md#インストール)で台帳の移行手順を確認してください。
|
|
34
34
|
|
package/README.md
CHANGED
|
@@ -23,12 +23,12 @@ Aptly는 최소한의 작업 요약을 OpenAI Decisions API로 분류하고 허
|
|
|
23
23
|
Node.js **24 이상**과 Claude Code 또는 Codex가 필요합니다. 다음 명령으로 최신 안정 버전을 전역 설치하십시오.
|
|
24
24
|
|
|
25
25
|
```sh
|
|
26
|
-
npm install -g aptly@latest
|
|
26
|
+
npm install -g aptly@latest --allow-scripts=aptly
|
|
27
27
|
aptly setup --client all --mode defaults
|
|
28
28
|
aptly --version
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
`aptly --version`으로 설치된 버전을 확인하십시오.
|
|
31
|
+
`aptly --version`으로 설치된 버전을 확인하십시오. `--allow-scripts=aptly`는 지원하는 npm에서 Aptly의 설치 스크립트를 허용합니다. 이 옵션을 지원하지 않는 npm은 [설치 안내](docs/guide/ko.md#설치)의 명시적 설치·설정 절차를 따르십시오.
|
|
32
32
|
|
|
33
33
|
업그레이드 전에는 관련 호스트·MCP·CLI 프로세스를 모두 중지하고 [설치·업그레이드 안내](docs/guide/ko.md#설치)의 원장 전환 절차를 확인하십시오.
|
|
34
34
|
|
package/README.zh-CN.md
CHANGED
|
@@ -23,12 +23,12 @@ Aptly 使用 OpenAI Decisions API 对简要任务摘要进行分类,并推荐
|
|
|
23
23
|
需要 Node.js **24 或更高版本**以及 Claude Code 或 Codex。请使用以下命令全局安装最新稳定版。
|
|
24
24
|
|
|
25
25
|
```sh
|
|
26
|
-
npm install -g aptly@latest
|
|
26
|
+
npm install -g aptly@latest --allow-scripts=aptly
|
|
27
27
|
aptly setup --client all --mode defaults
|
|
28
28
|
aptly --version
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
请使用 `aptly --version`
|
|
31
|
+
请使用 `aptly --version` 确认已安装的版本。在支持此选项的 npm 中,`--allow-scripts=aptly` 允许执行 Aptly 的安装脚本。如果 npm 不支持此选项,请按照[安装指南](docs/guide/zh-CN.md#安装)中的显式安装与配置步骤操作。
|
|
32
32
|
|
|
33
33
|
升级前,请停止所有相关宿主、MCP 和 CLI 进程,并查看[安装与升级指南](docs/guide/zh-CN.md#安装)中的账本迁移步骤。
|
|
34
34
|
|
package/dist/bin/aptly.js
CHANGED
|
@@ -12,11 +12,26 @@ import { errorCode } from '../src/types.js';
|
|
|
12
12
|
import { setupHosts, removeSetup, setDefaults, setEnvironment, configurableEnvironmentKeys, migrateLauncher } from '../src/setup.js';
|
|
13
13
|
import { readEnvironmentValue } from '../src/cli-input.js';
|
|
14
14
|
import { prepareApiKey } from '../src/credential-setup.js';
|
|
15
|
+
import { updateGlobalInstallation } from '../src/update.js';
|
|
16
|
+
import { updateNotification } from '../src/update-notifier.js';
|
|
15
17
|
const [command = 'serve', ...args] = process.argv.slice(2);
|
|
16
18
|
const output = (value) => process.stdout.write(JSON.stringify(value, null, 2) + '\n');
|
|
17
19
|
try {
|
|
18
20
|
if (['--help', '-h', 'help'].includes(command)) {
|
|
19
|
-
process.stdout.write('Aptly (preview)\n\nCommands:\n serve Start the stdio MCP server (default: fixture/shadow).\n capabilities Print the active catalog; no API call.\n catalog Print the active catalog; no API call.\n catalog refresh --output ABS_PATH Fetch official model descriptions to a private cache.\n recommend Recommend for a task JSON on stdin; no host evidence required.\n inspect CLIENT FILE Inspect an explicit JSON settings inventory; no API call.\n config CLIENT Print an MCP registration fragment; does not edit settings.\n set default Add missing default MCP environment settings.\n set env ENV_KEY Prompt for an MCP environment value.\n setup Register MCP and automatic PreToolUse Hooks.\n remove Remove only unchanged Aptly-owned registration.\n report Write redacted local diagnostic attachments (default: md + json).\n --format md|json|both Select report formats.\n --output-dir DIR Private output directory (default: reports).\n --version Print the package version.\n\nUse aptly set env OPENAI_API_KEY for OS storage, or pass it through the MCP environment.\nPaid mode additionally requires\nROUTER_PROVIDER=decisions and an absolute ROUTER_LEDGER_PATH in a private directory.\nROUTER_CONFIG_PATH is an optional private operator configuration file.\n');
|
|
21
|
+
process.stdout.write('Aptly (preview)\n\nCommands:\n serve Start the stdio MCP server (default: fixture/shadow).\n capabilities Print the active catalog; no API call.\n catalog Print the active catalog; no API call.\n catalog refresh --output ABS_PATH Fetch official model descriptions to a private cache.\n recommend Recommend for a task JSON on stdin; no host evidence required.\n inspect CLIENT FILE Inspect an explicit JSON settings inventory; no API call.\n config CLIENT Print an MCP registration fragment; does not edit settings.\n set default Add missing default MCP environment settings.\n set env ENV_KEY Prompt for an MCP environment value.\n setup Register MCP and automatic PreToolUse Hooks.\n remove Remove only unchanged Aptly-owned registration.\n update Update this verified global npm installation.\n report Write redacted local diagnostic attachments (default: md + json).\n --format md|json|both Select report formats.\n --output-dir DIR Private output directory (default: reports).\n --version Print the package version.\n\nUse aptly set env OPENAI_API_KEY for OS storage, or pass it through the MCP environment.\nPaid mode additionally requires\nROUTER_PROVIDER=decisions and an absolute ROUTER_LEDGER_PATH in a private directory.\nROUTER_CONFIG_PATH is an optional private operator configuration file.\n');
|
|
22
|
+
}
|
|
23
|
+
else if (command === 'update') {
|
|
24
|
+
if (args.length === 1 && args[0] === '--help')
|
|
25
|
+
process.stdout.write('Usage: aptly update\nRequires a verified global npm installation. Stop other hosts/MCP/CLI writers first.\nUpdates the same npm prefix without lifecycle setup. Run aptly setup and restart hosts afterward; existing launchers are preserved.\n');
|
|
26
|
+
else {
|
|
27
|
+
if (args.length)
|
|
28
|
+
throw new Error('update_invalid_options');
|
|
29
|
+
const packageRoot = fileURLToPath(new URL(import.meta.url.endsWith('.ts') ? '../' : '../../', import.meta.url));
|
|
30
|
+
const result = updateGlobalInstallation({ packageRoot });
|
|
31
|
+
output(result);
|
|
32
|
+
if (result.status === 'error')
|
|
33
|
+
process.exitCode = 1;
|
|
34
|
+
}
|
|
20
35
|
}
|
|
21
36
|
else if (command === '--version')
|
|
22
37
|
process.stdout.write(VERSION + '\n');
|
|
@@ -102,13 +117,14 @@ try {
|
|
|
102
117
|
}
|
|
103
118
|
else if (['setup', 'remove'].includes(command)) {
|
|
104
119
|
if (args.length === 1 && args[0] === '--help')
|
|
105
|
-
process.stdout.write('Usage: aptly setup [--client codex|claude|all] [--mode defaults|advisory] [--automatic]\n aptly setup --client codex|claude --migrate-launcher [--dry-run]\n aptly remove [--client codex|claude|all]\nSetup merges MCP and PreToolUse registration. Codex requires native /hooks trust review.\nAutomatic setup skips CI, APTLY_SKIP_SETUP=1, and absent host directories. No keys are stored.\n');
|
|
120
|
+
process.stdout.write('Usage: aptly setup [--client codex|claude|all] [--mode defaults|advisory] [--automatic]\n aptly setup --client codex|claude --migrate-launcher [--include-existing] [--dry-run]\n aptly remove [--client codex|claude|all]\nSetup merges MCP and PreToolUse registration. Codex requires native /hooks trust review.\nAutomatic setup skips CI, APTLY_SKIP_SETUP=1, and absent host directories. No keys are stored.\n');
|
|
106
121
|
else {
|
|
107
122
|
let clients = ['codex', 'claude'];
|
|
108
123
|
let mode = 'defaults';
|
|
109
124
|
let automatic = false;
|
|
110
125
|
let migrate = false;
|
|
111
126
|
let dryRun = false;
|
|
127
|
+
let includeExisting = false;
|
|
112
128
|
const seen = new Set();
|
|
113
129
|
for (let i = 0; i < args.length; i++) {
|
|
114
130
|
const flag = args[i];
|
|
@@ -119,6 +135,10 @@ try {
|
|
|
119
135
|
migrate = true;
|
|
120
136
|
continue;
|
|
121
137
|
}
|
|
138
|
+
if (flag === '--include-existing' && command === 'setup') {
|
|
139
|
+
includeExisting = true;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
122
142
|
if (flag === '--dry-run' && command === 'setup') {
|
|
123
143
|
dryRun = true;
|
|
124
144
|
continue;
|
|
@@ -136,9 +156,9 @@ try {
|
|
|
136
156
|
throw new Error('setup_invalid_options');
|
|
137
157
|
}
|
|
138
158
|
const packageRoot = fileURLToPath(new URL(import.meta.url.endsWith('.ts') ? '../' : '../../', import.meta.url));
|
|
139
|
-
if ((migrate && (clients.length !== 1 || automatic || seen.has('--mode'))) || (dryRun && !migrate))
|
|
159
|
+
if ((migrate && (clients.length !== 1 || automatic || seen.has('--mode'))) || ((dryRun || includeExisting) && !migrate))
|
|
140
160
|
throw new Error('setup_invalid_options');
|
|
141
|
-
const result = migrate ? migrateLauncher({ clients, packageRoot }, dryRun) : command === 'setup' ? setupHosts({ clients, mode, automatic, packageRoot }) : removeSetup({ clients });
|
|
161
|
+
const result = migrate ? migrateLauncher({ clients, packageRoot }, dryRun, includeExisting) : command === 'setup' ? setupHosts({ clients, mode, automatic, packageRoot }) : removeSetup({ clients });
|
|
142
162
|
output(result);
|
|
143
163
|
if (result.status === 'partial' && !automatic)
|
|
144
164
|
process.exitCode = 1;
|
|
@@ -194,7 +214,17 @@ catch (error) {
|
|
|
194
214
|
const message = error instanceof Error ? error.message : undefined;
|
|
195
215
|
const configCode = [caughtCode, message].find(value => typeof value === 'string' && safeConfigurationCodes.includes(value));
|
|
196
216
|
const setCodes = ['set_environment_key_unsupported', 'set_invalid_options', 'set_value_too_large', 'set_interactive_input_required', 'set_input_cancelled'];
|
|
197
|
-
const code = command === 'set' ? (setCodes.includes(message ?? '') ? message : 'set_failed') : command === 'report' ? (caughtCode && codes.includes(caughtCode) ? caughtCode : 'report_failed') : configCode ?? 'operation_failed';
|
|
217
|
+
const code = command === 'update' ? (message === 'update_invalid_options' ? message : 'update_failed') : command === 'set' ? (setCodes.includes(message ?? '') ? message : 'set_failed') : command === 'report' ? (caughtCode && codes.includes(caughtCode) ? caughtCode : 'report_failed') : configCode ?? 'operation_failed';
|
|
198
218
|
process.stderr.write('aptly: ' + code + '\n');
|
|
199
219
|
process.exitCode = 1;
|
|
200
220
|
}
|
|
221
|
+
finally {
|
|
222
|
+
if (!process.exitCode) {
|
|
223
|
+
try {
|
|
224
|
+
const hint = await updateNotification({ command, args, env: process.env, stdinIsTTY: !!process.stdin.isTTY, stderrIsTTY: !!process.stderr.isTTY, stdoutIsTTY: !!process.stdout.isTTY, currentVersion: VERSION });
|
|
225
|
+
if (hint)
|
|
226
|
+
process.stderr.write(hint + '\n');
|
|
227
|
+
}
|
|
228
|
+
catch { /* Optional update hints cannot fail a successful command. */ }
|
|
229
|
+
}
|
|
230
|
+
}
|
package/dist/src/setup.d.ts
CHANGED
|
@@ -31,6 +31,6 @@ export declare function prepareCredentialReference(options: SetupOptions, client
|
|
|
31
31
|
check: () => SetupClientResult;
|
|
32
32
|
commit: () => SetupClientResult;
|
|
33
33
|
};
|
|
34
|
-
/**
|
|
35
|
-
export declare function migrateLauncher(options: SetupOptions, dryRun?: boolean): SetupResult;
|
|
34
|
+
/** Preserved standard registrations require a separate explicit opt-in; ownership stays unchanged. */
|
|
35
|
+
export declare function migrateLauncher(options: SetupOptions, dryRun?: boolean, includeExisting?: boolean): SetupResult;
|
|
36
36
|
export {};
|
package/dist/src/setup.js
CHANGED
|
@@ -234,6 +234,34 @@ function updateTomlEnv(text, updates) {
|
|
|
234
234
|
fail('unsupported_toml_layout');
|
|
235
235
|
return text;
|
|
236
236
|
}
|
|
237
|
+
/** Replace only unambiguous single-line launcher assignments in an existing table. */
|
|
238
|
+
function updateTomlLauncher(text, entry) {
|
|
239
|
+
const original = toml(text), expected = structuredClone(original);
|
|
240
|
+
expected.mcp_servers.aptly.command = entry.command;
|
|
241
|
+
expected.mcp_servers.aptly.args = entry.args;
|
|
242
|
+
const headers = [...text.matchAll(/^\s*\[([^\n]+)\][ \t]*(?:#[^\n]*)?\r?$/gm)];
|
|
243
|
+
const header = headers.find(h => { try {
|
|
244
|
+
return toml(h[0] + '\naptly_probe = true\n').mcp_servers?.aptly?.aptly_probe === true;
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
return false;
|
|
248
|
+
} });
|
|
249
|
+
if (!header)
|
|
250
|
+
fail('unsupported_toml_layout');
|
|
251
|
+
const start = header.index + header[0].length, end = headers.find(h => h.index > header.index)?.index ?? text.length;
|
|
252
|
+
let body = text.slice(start, end);
|
|
253
|
+
for (const key of ['command', 'args']) {
|
|
254
|
+
const assignment = new RegExp('^[ \\t]*' + key + '[ \\t]*=[^\\n]*(?:\\n|$)', 'gm');
|
|
255
|
+
const matches = [...body.matchAll(assignment)];
|
|
256
|
+
if (matches.length !== 1 || !isDeepStrictEqual(toml(matches[0][0])[key], original.mcp_servers.aptly[key]))
|
|
257
|
+
fail('unsupported_toml_layout');
|
|
258
|
+
body = body.replace(assignment, () => key + ' = ' + JSON.stringify(entry[key]) + (matches[0][0].endsWith('\r\n') ? '\r\n' : matches[0][0].endsWith('\n') ? '\n' : ''));
|
|
259
|
+
}
|
|
260
|
+
const rendered = text.slice(0, start) + body + text.slice(end);
|
|
261
|
+
if (!isDeepStrictEqual(toml(rendered), expected))
|
|
262
|
+
fail('unsupported_toml_layout');
|
|
263
|
+
return rendered;
|
|
264
|
+
}
|
|
237
265
|
function resolved(options, client) {
|
|
238
266
|
const home = resolve(options.home ?? homedir()), env = options.env ?? process.env;
|
|
239
267
|
const override = client === 'codex' ? env.CODEX_HOME : env.CLAUDE_CONFIG_DIR;
|
|
@@ -359,13 +387,16 @@ function prepare(options, client, removing, edit) {
|
|
|
359
387
|
const additions = { APTLY_HOOK_POLICY_PATH: policyPath, ROUTER_LEDGER_PATH: join(stateDir, 'ledger.json') };
|
|
360
388
|
let created = false, tomlBlock;
|
|
361
389
|
if (edit && 'migrate' in edit) {
|
|
362
|
-
if (!oldOwner
|
|
390
|
+
if (!oldOwner || !object(servers.aptly) || (oldOwner.mcp_created ? !isDeepStrictEqual(servers.aptly, oldOwner.mcp) : !edit.includeExisting))
|
|
363
391
|
fail('launcher_migration_not_owned');
|
|
364
392
|
const entry = servers.aptly;
|
|
365
393
|
if (typeof entry.command !== 'string' || !isAbsolute(entry.command) || !['node', 'node.exe'].includes(basename(entry.command).toLowerCase()) || !Array.isArray(entry.args) || entry.args.length !== 1 || typeof entry.args[0] !== 'string' || !isAbsolute(entry.args[0]) || !/(?:^|[\\/])(?:dist[\\/]src[\\/]server\.js|src[\\/]server\.ts)$/.test(entry.args[0]))
|
|
366
394
|
fail('launcher_migration_not_standard');
|
|
367
395
|
if (!entries.some(entry => isDeepStrictEqual(entry, oldOwner.hook)) || !isDeepStrictEqual(json(policy.before), oldOwner.policy))
|
|
368
396
|
fail('managed_entry_changed');
|
|
397
|
+
for (const [key, value] of Object.entries(oldOwner.env_added))
|
|
398
|
+
if (entry.env?.[key] !== value)
|
|
399
|
+
fail('managed_entry_changed');
|
|
369
400
|
}
|
|
370
401
|
if (servers.aptly === undefined) {
|
|
371
402
|
created = true;
|
|
@@ -400,8 +431,11 @@ function prepare(options, client, removing, edit) {
|
|
|
400
431
|
else
|
|
401
432
|
mcp.after = stringify(config);
|
|
402
433
|
}
|
|
403
|
-
else if (edit)
|
|
434
|
+
else if (edit) {
|
|
404
435
|
mcp.after = client === 'codex' ? updateTomlEnv(mcp.after, updates) : stringify(config);
|
|
436
|
+
if (client === 'codex' && 'migrate' in edit && !oldOwner?.mcp_created)
|
|
437
|
+
mcp.after = updateTomlLauncher(mcp.after, servers.aptly);
|
|
438
|
+
}
|
|
405
439
|
const existingRoute = entries.filter(routeHook);
|
|
406
440
|
if (existingRoute.some(entry => !isDeepStrictEqual(entry, hookValue)))
|
|
407
441
|
fail('existing_routing_hook_conflict');
|
|
@@ -656,16 +690,17 @@ export function prepareCredentialReference(options, client) {
|
|
|
656
690
|
return execute({ ...options, clients: [client] }, false, { credential: true }, [plan]).clients[0];
|
|
657
691
|
} };
|
|
658
692
|
}
|
|
659
|
-
/**
|
|
660
|
-
export function migrateLauncher(options, dryRun = false) {
|
|
693
|
+
/** Preserved standard registrations require a separate explicit opt-in; ownership stays unchanged. */
|
|
694
|
+
export function migrateLauncher(options, dryRun = false, includeExisting = false) {
|
|
661
695
|
const client = options.clients?.[0];
|
|
662
696
|
if (options.clients?.length !== 1 || !client || !['codex', 'claude'].includes(client) || options.automatic || options.mode)
|
|
663
697
|
return { status: 'partial', clients: [] };
|
|
664
698
|
try {
|
|
665
|
-
const
|
|
699
|
+
const edit = { migrate: true, includeExisting };
|
|
700
|
+
const plan = prepare(options, client, false, edit);
|
|
666
701
|
if (dryRun)
|
|
667
702
|
return { status: 'complete', clients: [{ client, status: 'unchanged', codes: [plan.changes.length ? 'launcher_migration_available' : 'launcher_current'], requires_hook_review: client === 'codex' }] };
|
|
668
|
-
const result = execute(options, false,
|
|
703
|
+
const result = execute(options, false, edit, [plan]);
|
|
669
704
|
if (result.status === 'complete')
|
|
670
705
|
result.clients[0].codes.push('launcher_migrated', 'mcp_restart_required');
|
|
671
706
|
return result;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface UpdateNotificationOptions {
|
|
2
|
+
command: string;
|
|
3
|
+
args: readonly string[];
|
|
4
|
+
env: NodeJS.ProcessEnv;
|
|
5
|
+
stderrIsTTY: boolean;
|
|
6
|
+
stdoutIsTTY?: boolean;
|
|
7
|
+
stdinIsTTY: boolean;
|
|
8
|
+
currentVersion: string;
|
|
9
|
+
cachePath?: string;
|
|
10
|
+
fetch?: typeof globalThis.fetch;
|
|
11
|
+
now?: number;
|
|
12
|
+
}
|
|
13
|
+
/** Returns one stderr hint; never prints, reads credentials, or changes installed code. */
|
|
14
|
+
export declare function updateNotification(options: UpdateNotificationOptions): Promise<string | null>;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// Optional interactive CLI hint only. Never used by MCP, routing, or provider code.
|
|
2
|
+
import { mkdirSync, lstatSync, openSync, closeSync, writeFileSync, renameSync, unlinkSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, isAbsolute, parse } from 'node:path';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { assertNoRedirectedAncestors, assertPrivateStatePath, readRegularFile } from './files.js';
|
|
7
|
+
import { protectNewWindowsPrivateDirectory } from './windows-private.js';
|
|
8
|
+
const registry = 'https://registry.npmjs.org/aptly/latest';
|
|
9
|
+
const ttl = 24 * 60 * 60 * 1000, cooldown = 60 * 60 * 1000, maximumBody = 16 * 1024, timeout = 800;
|
|
10
|
+
function version(value) {
|
|
11
|
+
if (typeof value !== 'string' || value.length > 64 || !/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(value))
|
|
12
|
+
return null;
|
|
13
|
+
const parts = value.split('.').map(Number);
|
|
14
|
+
return parts.every(Number.isSafeInteger) ? parts : null;
|
|
15
|
+
}
|
|
16
|
+
function newer(candidate, current) {
|
|
17
|
+
const a = version(candidate), b = version(current);
|
|
18
|
+
for (let i = 0; i < 3; i++) {
|
|
19
|
+
if (a[i] !== b[i])
|
|
20
|
+
return a[i] > b[i];
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
function eligible(o) {
|
|
25
|
+
return o.stdinIsTTY && o.stderrIsTTY && o.stdoutIsTTY !== false && !o.env.CI && o.env.APTLY_NO_UPDATE_NOTIFIER !== '1' && !!version(o.currentVersion) &&
|
|
26
|
+
['help', '--help', '-h', '--version', 'config', 'setup', 'remove', 'set', 'report'].includes(o.command) && !o.args.includes('--automatic');
|
|
27
|
+
}
|
|
28
|
+
function directory(path) {
|
|
29
|
+
if (!isAbsolute(path))
|
|
30
|
+
throw Error('cache_path');
|
|
31
|
+
const parent = dirname(path);
|
|
32
|
+
if (parent !== path) {
|
|
33
|
+
try {
|
|
34
|
+
assertNoRedirectedAncestors(parent);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (error.code !== 'ENOENT')
|
|
38
|
+
throw error;
|
|
39
|
+
directory(parent);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
let created = false;
|
|
43
|
+
try {
|
|
44
|
+
mkdirSync(path, { mode: 0o700 });
|
|
45
|
+
created = true;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (error.code !== 'EEXIST')
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
assertNoRedirectedAncestors(path);
|
|
52
|
+
if (!lstatSync(path).isDirectory())
|
|
53
|
+
throw Error('cache_path');
|
|
54
|
+
if (created && process.platform === 'win32')
|
|
55
|
+
protectNewWindowsPrivateDirectory(path);
|
|
56
|
+
}
|
|
57
|
+
function readCache(path, now) {
|
|
58
|
+
assertPrivateStatePath(path);
|
|
59
|
+
try {
|
|
60
|
+
const c = JSON.parse(readRegularFile(path, { privateFile: true, maxBytes: 1024 }).toString('utf8'));
|
|
61
|
+
if (!c || Array.isArray(c) || Object.keys(c).sort().join(',') !== 'checked_at,failed,latest,schema_version' || c.schema_version !== 1 ||
|
|
62
|
+
!Number.isSafeInteger(c.checked_at) || c.checked_at < 0 || c.checked_at > now || typeof c.failed !== 'boolean' ||
|
|
63
|
+
(c.latest !== null && !version(c.latest)) || (!c.failed && c.latest === null))
|
|
64
|
+
return null;
|
|
65
|
+
return c;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error.code === 'ENOENT' || error instanceof SyntaxError)
|
|
69
|
+
return null;
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function writeCache(path, c) {
|
|
74
|
+
let stage;
|
|
75
|
+
try {
|
|
76
|
+
assertPrivateStatePath(path);
|
|
77
|
+
stage = join(dirname(path), '.aptly-update-' + randomUUID());
|
|
78
|
+
const fd = openSync(stage, 'wx', 0o600);
|
|
79
|
+
try {
|
|
80
|
+
writeFileSync(fd, JSON.stringify(c) + '\n');
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
closeSync(fd);
|
|
84
|
+
}
|
|
85
|
+
assertPrivateStatePath(stage);
|
|
86
|
+
assertPrivateStatePath(path);
|
|
87
|
+
renameSync(stage, path);
|
|
88
|
+
stage = undefined;
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
if (stage)
|
|
92
|
+
try {
|
|
93
|
+
unlinkSync(stage);
|
|
94
|
+
}
|
|
95
|
+
catch { /* Best-effort cleanup of our stage only. */ }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function latestStable(fetcher) {
|
|
99
|
+
const controller = new AbortController();
|
|
100
|
+
let timer;
|
|
101
|
+
let reader;
|
|
102
|
+
const work = async () => {
|
|
103
|
+
const response = await fetcher(registry, { signal: controller.signal, redirect: 'error', credentials: 'omit', headers: { accept: 'application/json' } });
|
|
104
|
+
if (!response.ok || !response.body)
|
|
105
|
+
throw Error('registry_response');
|
|
106
|
+
const size = response.headers.get('content-length');
|
|
107
|
+
if (size !== null && (!/^\d+$/.test(size) || Number(size) > maximumBody))
|
|
108
|
+
throw Error('registry_size');
|
|
109
|
+
reader = response.body.getReader();
|
|
110
|
+
let total = 0;
|
|
111
|
+
const chunks = [];
|
|
112
|
+
for (;;) {
|
|
113
|
+
const chunk = await reader.read();
|
|
114
|
+
if (chunk.done)
|
|
115
|
+
break;
|
|
116
|
+
total += chunk.value.byteLength;
|
|
117
|
+
if (total > maximumBody)
|
|
118
|
+
throw Error('registry_size');
|
|
119
|
+
chunks.push(chunk.value);
|
|
120
|
+
}
|
|
121
|
+
const bytes = new Uint8Array(total);
|
|
122
|
+
let offset = 0;
|
|
123
|
+
for (const chunk of chunks) {
|
|
124
|
+
bytes.set(chunk, offset);
|
|
125
|
+
offset += chunk.byteLength;
|
|
126
|
+
}
|
|
127
|
+
const metadata = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
|
|
128
|
+
if (!metadata || metadata.name !== 'aptly' || !version(metadata.version))
|
|
129
|
+
throw Error('registry_metadata');
|
|
130
|
+
return metadata.version;
|
|
131
|
+
};
|
|
132
|
+
try {
|
|
133
|
+
return await Promise.race([work(), new Promise((_, reject) => { timer = setTimeout(() => { controller.abort(); reject(Error('registry_timeout')); }, timeout); })]);
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
if (timer)
|
|
137
|
+
clearTimeout(timer);
|
|
138
|
+
controller.abort();
|
|
139
|
+
if (reader)
|
|
140
|
+
void reader.cancel().catch(() => { });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/** Returns one stderr hint; never prints, reads credentials, or changes installed code. */
|
|
144
|
+
export async function updateNotification(options) {
|
|
145
|
+
if (!eligible(options))
|
|
146
|
+
return null;
|
|
147
|
+
const now = options.now ?? Date.now();
|
|
148
|
+
if (!Number.isSafeInteger(now) || now < 0)
|
|
149
|
+
return null;
|
|
150
|
+
const path = options.cachePath ?? join(homedir(), '.cache', 'aptly', 'update-check.json');
|
|
151
|
+
let cached;
|
|
152
|
+
try {
|
|
153
|
+
if (!isAbsolute(path) || parse(path).root === path)
|
|
154
|
+
return null;
|
|
155
|
+
directory(dirname(path));
|
|
156
|
+
cached = readCache(path, now);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
if (cached && now - cached.checked_at < (cached.failed ? cooldown : ttl))
|
|
162
|
+
return !cached.failed && cached.latest && newer(cached.latest, options.currentVersion) ? hint(cached.latest, options.currentVersion) : null;
|
|
163
|
+
try {
|
|
164
|
+
const latest = await latestStable(options.fetch ?? globalThis.fetch);
|
|
165
|
+
try {
|
|
166
|
+
writeCache(path, { schema_version: 1, checked_at: now, latest, failed: false });
|
|
167
|
+
}
|
|
168
|
+
catch { /* Notification must not make CLI commands fail. */ }
|
|
169
|
+
return newer(latest, options.currentVersion) ? hint(latest, options.currentVersion) : null;
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
try {
|
|
173
|
+
writeCache(path, { schema_version: 1, checked_at: now, latest: null, failed: true });
|
|
174
|
+
}
|
|
175
|
+
catch { /* Silent best effort; no provider/ledger fallback. */ }
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function hint(latest, current) { return `Aptly ${latest} is available (installed: ${current}). Run aptly update to update this global installation; use the installation guide for local installs.`; }
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface UpdateResult {
|
|
2
|
+
status: 'complete' | 'error';
|
|
3
|
+
codes: string[];
|
|
4
|
+
version?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface UpdateDependencies {
|
|
7
|
+
nodePath?: string;
|
|
8
|
+
run?: (executable: string, args: readonly string[], timeout: number) => string;
|
|
9
|
+
}
|
|
10
|
+
/** Explicit global-only update. Never invokes a shell, PATH npm, setup, or an existing host launcher. */
|
|
11
|
+
export declare function updateGlobalInstallation(options: {
|
|
12
|
+
packageRoot: string;
|
|
13
|
+
}, dependencies?: UpdateDependencies): UpdateResult;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { lstatSync, readFileSync, realpathSync } from 'node:fs';
|
|
3
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
4
|
+
const failed = (code) => { throw new Error(code); };
|
|
5
|
+
const runNpm = (executable, args, timeout) => execFileSync(executable, [...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout, maxBuffer: 1024 * 1024, windowsHide: true });
|
|
6
|
+
const metadata = (root) => JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
|
|
7
|
+
const canonical = (path) => realpathSync(path);
|
|
8
|
+
function absoluteOutput(output) {
|
|
9
|
+
const path = output.trim();
|
|
10
|
+
if (!path || !isAbsolute(path) || /[\r\n\0]/.test(path))
|
|
11
|
+
failed('update_global_install_unverified');
|
|
12
|
+
return resolve(path);
|
|
13
|
+
}
|
|
14
|
+
function verifyPackage(root) {
|
|
15
|
+
const pkg = metadata(root);
|
|
16
|
+
if (pkg.name !== 'aptly' || typeof pkg.version !== 'string' || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(pkg.version) || pkg.bin?.aptly !== 'dist/bin/aptly.js')
|
|
17
|
+
failed('update_global_install_unverified');
|
|
18
|
+
const cli = join(root, 'dist', 'bin', 'aptly.js');
|
|
19
|
+
if (!lstatSync(cli).isFile() || canonical(cli) !== join(canonical(root), 'dist', 'bin', 'aptly.js'))
|
|
20
|
+
failed('update_global_install_unverified');
|
|
21
|
+
return pkg.version;
|
|
22
|
+
}
|
|
23
|
+
/** Explicit global-only update. Never invokes a shell, PATH npm, setup, or an existing host launcher. */
|
|
24
|
+
export function updateGlobalInstallation(options, dependencies = {}) {
|
|
25
|
+
let stage = 'update_global_install_unverified';
|
|
26
|
+
try {
|
|
27
|
+
const node = dependencies.nodePath ?? process.execPath, run = dependencies.run ?? runNpm;
|
|
28
|
+
if (!isAbsolute(node) || !isAbsolute(options.packageRoot))
|
|
29
|
+
failed(stage);
|
|
30
|
+
const nodeDirectory = dirname(node);
|
|
31
|
+
const npm = process.platform === 'win32' ? join(nodeDirectory, 'node_modules', 'npm', 'bin', 'npm-cli.js') : resolve(nodeDirectory, '..', 'lib', 'node_modules', 'npm', 'bin', 'npm-cli.js');
|
|
32
|
+
if (!lstatSync(npm).isFile() || metadata(dirname(dirname(npm))).name !== 'npm')
|
|
33
|
+
failed(stage);
|
|
34
|
+
const invoke = (args, timeout = 15000) => run(node, [npm, ...args], timeout);
|
|
35
|
+
const npmVersion = invoke(['--version']).trim().match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
36
|
+
if (!npmVersion || !npmVersion.slice(1).every(part => Number.isSafeInteger(Number(part))))
|
|
37
|
+
failed('update_npm_unsupported');
|
|
38
|
+
const prefix = absoluteOutput(invoke(['prefix', '--global'])), root = absoluteOutput(invoke(['root', '--global']));
|
|
39
|
+
const expectedRoot = process.platform === 'win32' ? join(prefix, 'node_modules') : join(prefix, 'lib', 'node_modules');
|
|
40
|
+
if (canonical(root) !== canonical(expectedRoot))
|
|
41
|
+
failed(stage);
|
|
42
|
+
const target = join(root, 'aptly');
|
|
43
|
+
if (lstatSync(target).isSymbolicLink() || !lstatSync(target).isDirectory() || canonical(target) !== canonical(options.packageRoot))
|
|
44
|
+
failed(stage);
|
|
45
|
+
verifyPackage(target);
|
|
46
|
+
// Query the same effective global npm configuration used for installation.
|
|
47
|
+
// A successful dry-run does not prove the on-disk package was updated.
|
|
48
|
+
stage = 'update_configuration_unverified';
|
|
49
|
+
const dryRun = invoke(['config', 'get', 'dry-run', '--global', '--prefix', prefix]).trim();
|
|
50
|
+
if (dryRun === 'true')
|
|
51
|
+
failed('update_dry_run_enabled');
|
|
52
|
+
if (dryRun !== 'false')
|
|
53
|
+
failed(stage);
|
|
54
|
+
// Explicit prefix pins the operation to the verified installation, including custom prefixes.
|
|
55
|
+
// Lifecycle scripts remain disabled: updating code must not rewrite host registration or trust.
|
|
56
|
+
stage = 'update_install_failed';
|
|
57
|
+
invoke(['install', '-g', 'aptly@latest', '--ignore-scripts', '--prefix', prefix], 120000);
|
|
58
|
+
stage = 'update_result_unverified';
|
|
59
|
+
if (lstatSync(target).isSymbolicLink() || canonical(target) !== canonical(options.packageRoot))
|
|
60
|
+
failed(stage);
|
|
61
|
+
const version = verifyPackage(target);
|
|
62
|
+
return { status: 'complete', codes: ['package_updated', 'host_configuration_preserved', 'setup_required', 'mcp_restart_required'], version };
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const code = error instanceof Error && ['update_global_install_unverified', 'update_npm_unsupported', 'update_dry_run_enabled'].includes(error.message) ? error.message : stage;
|
|
66
|
+
return { status: 'error', codes: [code] };
|
|
67
|
+
}
|
|
68
|
+
}
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.3";
|
package/dist/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.1.
|
|
1
|
+
export const VERSION = '0.1.3';
|
package/docs/guide/en.md
CHANGED
|
@@ -13,7 +13,7 @@ Requires Node.js 24 or later and Claude Code or Codex. Install the latest stable
|
|
|
13
13
|
**Before upgrading an existing persistent ledger to 0.1.2 or later**, stop every host, MCP server, standalone CLI, and older process that can access it. The persistent gate cannot coexist with older writers. Fresh installations need no legacy-lock quarantine. If an old empty lock remains, follow the [ledger transition procedure](#interrupted-processes-and-ledger-locks): quarantine only a confirmed legacy lock while offline and preserve the JSON ledger, `unknown` records, costs, and completed cache.
|
|
14
14
|
|
|
15
15
|
```sh
|
|
16
|
-
npm install -g aptly@latest
|
|
16
|
+
npm install -g aptly@latest --allow-scripts=aptly
|
|
17
17
|
aptly setup --client all --mode defaults
|
|
18
18
|
aptly --version
|
|
19
19
|
```
|
|
@@ -24,6 +24,41 @@ Use `aptly --version` to check the installed version. Switching Node.js environm
|
|
|
24
24
|
|
|
25
25
|
New registrations set `ROUTER_PROVIDER=decisions`, the Hook's `defaults` policy, and a private ledger for each host. Other settings and Hooks, and existing Aptly commands, environments, providers, and ledger paths, are preserved. Existing fixture settings are not automatically switched to paid mode.
|
|
26
26
|
|
|
27
|
+
### npm install-script approval
|
|
28
|
+
|
|
29
|
+
The installation command above uses `--allow-scripts=aptly` to explicitly allow Aptly's `postinstall` for that installation. Support was checked with npm 11.19.1; the command does not change user-wide npm approval settings. See the [official npm guidance](https://docs.npmjs.com/cli/v11/commands/npm-rebuild/#allow-scripts).
|
|
30
|
+
|
|
31
|
+
`npm warn install-scripts ... not yet covered by allowScripts` reports an unreviewed policy entry. In npm 11.19.1, this warning alone does not mean scripts were blocked. Behavior depends on npm version, strict policy, and `ignore-scripts`; installation success alone does not verify MCP or Hook registration. npm may hide postinstall output.
|
|
32
|
+
|
|
33
|
+
For npm versions without this option, or to configure explicitly instead of automatic registration, run:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npm install -g aptly@latest --ignore-scripts
|
|
37
|
+
aptly setup --client all --mode defaults
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Explicit `setup` works regardless of lifecycle execution. It preserves existing launchers; use the recovery steps below for stale paths. Key setup, MCP restart, and Codex `/hooks` trust review remain separate steps.
|
|
41
|
+
|
|
42
|
+
### Package updates
|
|
43
|
+
|
|
44
|
+
`aptly update` requires **Aptly 0.1.3 or later**. It requires a verified global npm installation and installs `aptly@latest` into the same verified prefix as the running package. Local installations, source checkouts, and unverified paths are rejected. If the effective npm configuration has `dry-run=true`, the command refuses with `update_dry_run_enabled` before starting installation. If configuration cannot be verified, it stops with `update_configuration_unverified`. A dry-run is never reported as a successful update.
|
|
45
|
+
|
|
46
|
+
Stop other host, MCP, and CLI processes using the ledger before updating, following the transition steps above. The command skips lifecycle scripts; explicitly configure afterward and restart hosts:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
aptly update
|
|
50
|
+
aptly setup --client all --mode defaults
|
|
51
|
+
aptly --version
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Existing MCP launchers and user wrappers are not automatically migrated. Follow the launcher recovery instructions below if old paths remain. Key setup and Codex `/hooks` trust review remain separate. For npm authentication, network, or permission failures, use the manual installation instructions. Updates are never installed automatically.
|
|
55
|
+
|
|
56
|
+
### New-version notifications
|
|
57
|
+
|
|
58
|
+
**Aptly 0.1.3 or later** checks the latest stable version during interactive human-facing CLI commands and suggests `aptly update` on stderr when a newer version exists. All three standard streams must be terminals. MCP serving, automatic setup, recommendation/classification paths, pipes, and CI skip checks. JSON stdout and MCP/Hook payloads stay unchanged.
|
|
59
|
+
|
|
60
|
+
Only public Aptly version metadata is requested from the npm registry; no key, task summary, or host configuration is sent. Network and body reads are limited to 800ms and 16KiB. Results are cached at `~/.cache/aptly/update-check.json` for 24 hours, with a one-hour cooldown after failure. Network/cache errors cannot fail the requested command. Set `APTLY_NO_UPDATE_NOTIFIER=1` in the CLI environment to disable checks; it is not a supported key for `aptly set env`. Notifications never install updates.
|
|
61
|
+
|
|
27
62
|
### Configure with the CLI
|
|
28
63
|
|
|
29
64
|
The `set` commands require 0.1.1 or later. Commands do not automatically switch existing launch paths. API-key setup accepts only the current package’s standard Node/server registration with a verified OS-source-compatible runtime. Custom wrappers, older versions, and other installation prefixes are rejected with `credential_launcher_unverified`; explicitly switch the registration or upgrade it before trying again.
|
|
@@ -75,6 +110,15 @@ aptly set env OPENAI_API_KEY --client claude
|
|
|
75
110
|
|
|
76
111
|
`--dry-run` makes no changes. Migration replaces the Node/server paths of a receipt-matching, Aptly-owned standard registration with the current build. It preserves environment settings, Hooks, policy and other hosts, accesses no credentials, and never executes the old launcher. Custom wrappers and missing or modified ownership records are rejected. Review such registrations locally; use the wrapper's secure environment or a separately reviewed standard registration. Do not edit or delete ownership records to bypass validation.
|
|
77
112
|
|
|
113
|
+
If `launcher_migration_not_owned` refers to a preserved registration (`mcp_created: false`), **Aptly 0.1.3 or later** offers the explicit `--include-existing` opt-in. Install 0.1.3 or later, then run:
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
aptly setup --client claude --migrate-launcher --include-existing --dry-run
|
|
117
|
+
aptly setup --client claude --migrate-launcher --include-existing
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Review the local registration first. A valid existing Aptly ownership receipt and matching managed environment additions, Hook, and policy are required. Only an absolute Node path with a single standard server path is accepted; wrappers and registrations without a receipt remain unsupported. This does not make the entire MCP registration Aptly-owned: later removal preserves the migrated launcher and original user settings. The option applies only to explicit single-client migration, never automatic installation.
|
|
121
|
+
|
|
78
122
|
A successful Codex configuration needs no reset. Retry only the affected host and restart MCP. Codex still requires `/hooks` trust review. Changes after diagnosis stop with `concurrent_configuration_change`. If recovery observes another key or cannot read the current key, it leaves the store unchanged and reports `credential_rollback_failed`. Files and the OS store do not form an atomic transaction; avoid concurrent configuration operations.
|
|
79
123
|
|
|
80
124
|
### Keys and restarting
|
package/docs/guide/ja.md
CHANGED
|
@@ -13,7 +13,7 @@ Node.js 24 以上と Claude Code または Codex が必要です。npm の最新
|
|
|
13
13
|
**既存の永続台帳を 0.1.2 以降に更新する前に**、その台帳にアクセスする可能性があるすべてのホスト、MCP サーバー、単独 CLI、旧版プロセスを停止してください。新しい永続ロックは旧版の書き込み側と併用できません。新規インストールでは旧ロックの隔離は不要です。旧版の空のロックが残る場合は、[台帳の移行手順](#プロセス中断と台帳ロック)に従い、確認済みの旧ロックだけをオフラインで隔離し、JSON 台帳、`unknown` 記録、費用、完了済みキャッシュを保持してください。
|
|
14
14
|
|
|
15
15
|
```sh
|
|
16
|
-
npm install -g aptly@latest
|
|
16
|
+
npm install -g aptly@latest --allow-scripts=aptly
|
|
17
17
|
aptly setup --client all --mode defaults
|
|
18
18
|
aptly --version
|
|
19
19
|
```
|
|
@@ -24,6 +24,41 @@ aptly --version
|
|
|
24
24
|
|
|
25
25
|
新規登録では `ROUTER_PROVIDER=decisions`、Hook の `defaults` ポリシー、ホストごとの非公開台帳を指定します。他の既存設定・Hook と、既存の Aptly のコマンド・環境・provider・台帳パスは保持します。既存の fixture 設定を自動で有料モードに切り替えることはありません。
|
|
26
26
|
|
|
27
|
+
### npm インストールスクリプトの許可
|
|
28
|
+
|
|
29
|
+
上記の `--allow-scripts=aptly` は、そのインストールで Aptly の `postinstall` を明示的に許可します。npm 11.19.1 で対応を確認しており、ユーザー全体の npm 許可設定は変更しません。[npm 公式ガイド](https://docs.npmjs.com/cli/v11/commands/npm-rebuild/#allow-scripts)を参照してください。
|
|
30
|
+
|
|
31
|
+
`npm warn install-scripts ... not yet covered by allowScripts` は、許可ポリシーに未登録であることを示します。npm 11.19.1 では、この警告だけでスクリプトがブロックされたとは判断できません。npm のバージョン・厳格なポリシー・`ignore-scripts` によって挙動が異なり、インストール成功だけでは MCP・Hook の登録成功を確認できません。postinstall の出力は npm によって非表示になる場合があります。
|
|
32
|
+
|
|
33
|
+
このオプションに未対応の npm を使う場合や、自動登録の代わりに手動設定する場合は、次を実行してください。
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npm install -g aptly@latest --ignore-scripts
|
|
37
|
+
aptly setup --client all --mode defaults
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
スクリプトの実行にかかわらず、明示的な `setup` を利用できます。既存の起動パスは保持するため、旧パスのエラーは以下の復旧手順に従ってください。キー設定・MCP の再起動と Codex の `/hooks` 信頼確認は別途必要です。
|
|
41
|
+
|
|
42
|
+
### パッケージの更新
|
|
43
|
+
|
|
44
|
+
`aptly update` は **Aptly 0.1.3 以降で利用できます**。確認済みの npm グローバルインストールが必要で、実行中のパッケージと一致する確認済み prefix に `aptly@latest` をインストールします。ローカルインストール・ソースチェックアウト・確認できないパスは拒否します。 npm の有効な設定が `dry-run=true` の場合は、インストール前に `update_dry_run_enabled` で拒否します。設定を確認できない場合は `update_configuration_unverified` で停止し、試行のみの実行を更新成功とは報告しません。
|
|
45
|
+
|
|
46
|
+
更新前に、上記の台帳移行手順に従い、台帳を使用する他のホスト・MCP・CLI プロセスを停止してください。インストールスクリプトは実行しないため、更新後に明示的に設定し、ホストを再起動してください。
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
aptly update
|
|
50
|
+
aptly setup --client all --mode defaults
|
|
51
|
+
aptly --version
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
既存の MCP 起動パスやユーザーラッパーは自動移行しません。旧パスが残る場合は以下の復旧手順に従ってください。キー設定と Codex の `/hooks` 信頼確認は別途必要です。npm の認証・ネットワーク・権限エラーが発生した場合は手動インストールの案内を使用してください。更新の自動インストールは行いません。
|
|
55
|
+
|
|
56
|
+
### 新しいバージョンの通知
|
|
57
|
+
|
|
58
|
+
**Aptly 0.1.3 以降では**、対話形式の通常 CLI コマンドで最新の安定版を確認し、新版があれば stderr に `aptly update` を案内します。 標準入出力と標準エラー出力がすべてターミナルの場合のみ動作し、MCP サーバー・自動 setup・推薦や分類の経路・パイプ・CI では確認しません。JSON stdout と MCP・Hook の応答は変更しません。
|
|
59
|
+
|
|
60
|
+
公開 npm レジストリから Aptly のバージョン情報のみを取得し、キー・タスク要約・ホスト設定は送信しません。ネットワークと応答読み取りは 800ms・16KiB に制限し、結果を `~/.cache/aptly/update-check.json` に 24 時間キャッシュします。失敗後は 1 時間待機し、通信・キャッシュエラーで元のコマンドを失敗させません。CLI の環境に `APTLY_NO_UPDATE_NOTIFIER=1` を設定すると確認を停止できます。この変数は `aptly set env` の対象ではありません。通知による自動更新は行いません。
|
|
61
|
+
|
|
27
62
|
### CLI による設定
|
|
28
63
|
|
|
29
64
|
`set` コマンドは 0.1.1 以降で利用できます。既存の起動パスは自動で切り替えません。API キー設定は、現在のパッケージの標準 Node/サーバー登録で、OS 資格情報の参照に対応する実行環境だと確認できた場合にのみ使用できます。独自ラッパー、旧バージョン、別のインストール先は `credential_launcher_unverified` で拒否します。登録を明示的に切り替えるかアップグレードしてから、再度実行してください。
|
|
@@ -75,6 +110,15 @@ aptly set env OPENAI_API_KEY --client claude
|
|
|
75
110
|
|
|
76
111
|
`--dry-run` は変更しません。移行は所有記録と一致する Aptly の標準登録に限り、Node・サーバーパスを現在のビルドへ更新します。環境設定・Hook・ポリシー・他のホストを保持し、資格情報へのアクセスや古い起動プログラムの実行は行いません。独自ラッパー、所有記録のない登録、変更済みの登録は拒否します。その場合は登録をローカルで確認し、ラッパーの安全な環境変数受け渡しを使うか、別途確認した標準登録へ切り替えてください。検証を回避するために所有記録を編集・削除しないでください。
|
|
77
112
|
|
|
113
|
+
`launcher_migration_not_owned` が既存の登録を保持した状態(`mcp_created: false`)を指す場合、**Aptly 0.1.3 以降では** `--include-existing` を明示して移行できます。0.1.3 以降をインストールし、次を実行してください。
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
aptly setup --client claude --migrate-launcher --include-existing --dry-run
|
|
117
|
+
aptly setup --client claude --migrate-launcher --include-existing
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
実行前にローカルの登録を確認してください。有効な既存の Aptly 所有記録と、記録に一致する管理対象の環境変数・Hook・ポリシーが必要です。絶対 Node パスと単一の標準サーバーパスのみを許可し、ラッパーや記録のない登録は拒否します。MCP 登録全体を Aptly 所有には変更しないため、その後の削除でも移行後の起動パスと元のユーザー設定を保持します。明示的な単一ホストの移行専用で、自動インストールには適用されません。
|
|
121
|
+
|
|
78
122
|
成功した Codex の再設定は不要です。失敗したホストだけを指定して再試行し、MCP を再起動してください。Codex では引き続き `/hooks` の信頼確認が必要です。診断後の設定変更は `concurrent_configuration_change` で中断します。復旧時に別のキーを検出した場合や現在値を読めない場合は上書きせず、`credential_rollback_failed` を返します。ファイルと OS ストアは原子的なトランザクションではないため、設定操作を同時に実行しないでください。
|
|
79
123
|
|
|
80
124
|
### キーと再起動
|
package/docs/guide/ko.md
CHANGED
|
@@ -13,7 +13,7 @@ Node.js 24 이상과 Claude Code 또는 Codex가 필요합니다. npm의 최신
|
|
|
13
13
|
**기존 영속 원장을 0.1.2 이상으로 업그레이드하기 전에**, 해당 원장을 사용하는 모든 호스트·MCP 서버·별도 CLI와 구버전 프로세스를 정지하십시오. 새 영구 잠금은 구버전 writer와 함께 사용할 수 없습니다. 새 설치에는 기존 잠금 격리가 필요하지 않습니다. 이전 빈 잠금이 남았다면 [원장 잠금 전환 절차](#중단된-프로세스와-원장-잠금)를 따라 확인된 기존 잠금만 오프라인으로 격리하고, JSON 원장·`unknown`·비용·완료 캐시는 보존하십시오.
|
|
14
14
|
|
|
15
15
|
```sh
|
|
16
|
-
npm install -g aptly@latest
|
|
16
|
+
npm install -g aptly@latest --allow-scripts=aptly
|
|
17
17
|
aptly setup --client all --mode defaults
|
|
18
18
|
aptly --version
|
|
19
19
|
```
|
|
@@ -24,6 +24,41 @@ aptly --version
|
|
|
24
24
|
|
|
25
25
|
새 등록은 `ROUTER_PROVIDER=decisions`, Hook `defaults` 정책과 호스트별 비공개 원장을 지정합니다. 기존 다른 설정·Hook, 기존 Aptly의 실행 명령·환경·분류 방식·원장 경로는 보존합니다. 기존 fixture 설정을 자동으로 유료 모드로 바꾸지 않습니다.
|
|
26
26
|
|
|
27
|
+
### npm 설치 스크립트 허용
|
|
28
|
+
|
|
29
|
+
위 설치 명령의 `--allow-scripts=aptly`는 해당 설치에서 Aptly의 `postinstall`을 명시적으로 허용합니다. npm 11.19.1에서 지원을 확인했으며, 사용자 전체의 npm 허용 설정은 변경하지 않습니다. [npm 공식 안내](https://docs.npmjs.com/cli/v11/commands/npm-rebuild/#allow-scripts)를 참고하십시오.
|
|
30
|
+
|
|
31
|
+
`npm warn install-scripts ... not yet covered by allowScripts`는 허용 정책에 아직 등록되지 않았다는 안내입니다. npm 11.19.1에서는 이 경고만으로 스크립트가 차단됐다고 판단할 수 없습니다. npm 버전·엄격 정책·`ignore-scripts`에 따라 동작이 달라지며, 설치 성공만으로 MCP·Hook 등록 성공을 확인할 수는 없습니다. `postinstall`은 npm 출력에 표시되지 않을 수도 있습니다.
|
|
32
|
+
|
|
33
|
+
옵션을 지원하지 않는 npm이거나 자동 등록 대신 직접 설정하려면 다음 명령을 사용하십시오:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npm install -g aptly@latest --ignore-scripts
|
|
37
|
+
aptly setup --client all --mode defaults
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
명시적 `setup`은 설치 스크립트 실행 여부와 관계없이 사용할 수 있습니다. 기존 실행 경로는 보존하므로 구버전 경로 오류는 아래 복구 절차를 따르십시오. 키 설정·MCP 재시작과 Codex의 `/hooks` 신뢰 승인은 별도로 필요합니다.
|
|
41
|
+
|
|
42
|
+
### 패키지 업데이트
|
|
43
|
+
|
|
44
|
+
`aptly update`는 **Aptly 0.1.3부터 지원합니다**. 검증된 전역 npm 설치에서만 현재 실행 중인 패키지와 동일한 prefix의 `aptly@latest`를 설치합니다. 로컬 설치·소스 체크아웃·확인할 수 없는 경로는 거절합니다. npm의 유효 설정에서 `dry-run=true`이면 실제 설치를 시작하기 전에 `update_dry_run_enabled`로 거절합니다. 설정을 확인할 수 없으면 `update_configuration_unverified`로 중단합니다. 시험 실행을 업데이트 성공으로 보고하지 않습니다.
|
|
45
|
+
|
|
46
|
+
업데이트 전에 위 원장 전환 절차에 따라 다른 호스트·MCP·CLI 프로세스를 중지하십시오. 명령은 설치 스크립트를 실행하지 않으므로 업데이트 후 명시적으로 설정하고 호스트를 재시작하십시오:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
aptly update
|
|
50
|
+
aptly setup --client all --mode defaults
|
|
51
|
+
aptly --version
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
기존 MCP 실행 경로와 사용자 래퍼를 자동으로 전환하지 않습니다. 이전 경로가 남으면 아래 실행 경로 복구 안내를 따르십시오. 키 설정과 Codex의 `/hooks` 신뢰 검토는 별도 단계입니다. npm 인증·네트워크·권한 오류로 실패하면 수동 설치 안내를 사용하십시오. 자동 업데이트는 수행하지 않습니다.
|
|
55
|
+
|
|
56
|
+
### 새 버전 알림
|
|
57
|
+
|
|
58
|
+
**Aptly 0.1.3부터** 일반 CLI의 대화형 실행에서 최신 안정 버전을 확인하고, 더 새 버전이 있으면 stderr에 `aptly update`를 안내합니다. stdin·stdout·stderr가 모두 터미널일 때만 동작하며, MCP 서버·자동 setup·추천/분류 경로·파이프·CI에서는 확인하지 않습니다. 기존 JSON stdout과 MCP·Hook 응답은 유지합니다.
|
|
59
|
+
|
|
60
|
+
공개 npm 레지스트리의 Aptly 버전 메타데이터만 조회하며 키·작업 요약·호스트 설정은 보내지 않습니다. 네트워크와 응답 읽기는 최대 800ms·16KiB로 제한하고, 결과는 `~/.cache/aptly/update-check.json`에 24시간 캐시합니다. 실패 후에는 1시간 대기하며, 조회·캐시 오류로 원래 명령을 실패시키지 않습니다. 버전 조회를 끄려면 CLI 실행 환경에 `APTLY_NO_UPDATE_NOTIFIER=1`을 설정하십시오. 이 변수는 MCP 키 설정용 `aptly set env`의 지원 대상이 아닙니다. 알림은 업데이트를 설치하지 않습니다.
|
|
61
|
+
|
|
27
62
|
### CLI로 설정하기
|
|
28
63
|
|
|
29
64
|
`set` 명령은 0.1.1부터 지원합니다. 명령은 기존 실행 경로를 자동 전환하지 않습니다. API 키 설정은 현재 패키지의 표준 Node/서버 등록과 OS 자격 저장소 참조 지원이 확인된 실행 경로에만 허용됩니다. 사용자 지정 래퍼·구버전·다른 설치 위치는 `credential_launcher_unverified`로 거절하며, 등록을 명시적으로 전환하거나 업그레이드한 뒤 다시 실행해야 합니다.
|
|
@@ -75,6 +110,15 @@ aptly set env OPENAI_API_KEY --client claude
|
|
|
75
110
|
|
|
76
111
|
`--dry-run`은 변경하지 않습니다. 이전 명령은 소유 기록과 일치하는 Aptly 표준 등록의 Node·서버 경로를 현재 빌드로 바꿉니다. 기존 환경·Hook·정책·다른 호스트는 보존하며 키를 읽거나 저장하지 않습니다. 오래된 실행 파일을 실행하지 않습니다. 사용자 래퍼, 소유 기록이 없거나 수정된 등록은 거절합니다. 이 경우 로컬에서 등록을 검토하고 래퍼의 안전한 환경 전달을 사용하거나 별도로 검토한 표준 등록으로 전환하십시오. 소유 기록을 삭제하거나 편집해 검증을 우회하지 마십시오.
|
|
77
112
|
|
|
113
|
+
`launcher_migration_not_owned`가 나오고 기존 등록을 보존한 상태(`mcp_created: false`)라면, **Aptly 0.1.3부터** `--include-existing`으로 명시적으로 이전할 수 있습니다. 0.1.3 이상을 설치한 뒤 다음 명령을 실행하십시오:
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
aptly setup --client claude --migrate-launcher --include-existing --dry-run
|
|
117
|
+
aptly setup --client claude --migrate-launcher --include-existing
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
실행 전에 로컬 등록을 검토하십시오. 유효한 기존 Aptly 소유 기록과 일치하는 관리 환경변수·Hook·정책이 필요하며, 절대 Node 경로와 단일 표준 서버 경로만 허용합니다. 사용자 래퍼나 기록이 없는 등록은 이 옵션으로도 이전하지 않습니다. 기존 MCP 전체를 Aptly 소유로 바꾸지 않으므로, 이후 `remove`도 이전한 MCP 실행 경로와 원래 사용자 설정을 보존합니다. 이 옵션은 단일 호스트의 명시적 이전에만 사용할 수 있고 자동 설치에는 적용되지 않습니다.
|
|
121
|
+
|
|
78
122
|
이미 성공한 Codex는 재설정할 필요가 없습니다. 실패한 호스트만 지정해 재시도하고 MCP를 재시작하십시오. Codex를 대상으로 하면 `/hooks` 신뢰 검토가 계속 필요합니다. 진단 이후 설정 변경은 `concurrent_configuration_change`로 중단합니다. 저장 실패 후 다른 키가 관찰되거나 현재 키를 읽지 못하면 덮어쓰지 않고 `credential_rollback_failed`를 반환합니다. 파일과 OS 저장소 사이의 원자적 트랜잭션은 아니므로 다른 설정 작업을 동시에 실행하지 마십시오.
|
|
79
123
|
|
|
80
124
|
### 키와 재시작
|
package/docs/guide/zh-CN.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
**将现有持久化账本升级到 0.1.2 或更高版本前**,请停止所有可能访问该账本的宿主、MCP 服务器、独立 CLI 和旧版进程。新的持久锁不能与旧版写入进程并用。全新安装无需隔离旧锁。如果仍有旧版空锁,请遵循[账本迁移步骤](#进程中断与账本锁定),仅在离线状态下隔离已确认的旧锁,并保留 JSON 账本、`unknown` 记录、成本和已完成缓存。
|
|
14
14
|
|
|
15
15
|
```sh
|
|
16
|
-
npm install -g aptly@latest
|
|
16
|
+
npm install -g aptly@latest --allow-scripts=aptly
|
|
17
17
|
aptly setup --client all --mode defaults
|
|
18
18
|
aptly --version
|
|
19
19
|
```
|
|
@@ -24,6 +24,41 @@ aptly --version
|
|
|
24
24
|
|
|
25
25
|
新注册项会指定 `ROUTER_PROVIDER=decisions`、Hook 的 `defaults` 策略,以及各宿主独立的私有账本。其他现有配置与 Hook,以及 Aptly 原有的命令、环境、分类方式和账本路径都会保留。现有 fixture 配置不会自动切换为付费模式。
|
|
26
26
|
|
|
27
|
+
### npm 安装脚本许可
|
|
28
|
+
|
|
29
|
+
上面的安装命令通过 `--allow-scripts=aptly` 显式允许本次安装运行 Aptly 的 `postinstall`。已在 npm 11.19.1 中确认支持,不会修改用户级 npm 许可配置。请参阅 [npm 官方说明](https://docs.npmjs.com/cli/v11/commands/npm-rebuild/#allow-scripts)。
|
|
30
|
+
|
|
31
|
+
`npm warn install-scripts ... not yet covered by allowScripts` 表示许可策略尚未记录此项。在 npm 11.19.1 中,仅凭此警告不能判断脚本被阻止。实际行为取决于 npm 版本、严格策略和 `ignore-scripts`;安装成功也不等于 MCP 或 Hook 注册成功。npm 可能隐藏 postinstall 输出。
|
|
32
|
+
|
|
33
|
+
如果 npm 不支持此选项,或希望手动配置而非自动注册,请运行:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npm install -g aptly@latest --ignore-scripts
|
|
37
|
+
aptly setup --client all --mode defaults
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
无论安装脚本是否运行,都可以显式执行 `setup`。它会保留已有启动路径;旧路径错误请按下文恢复步骤处理。密钥配置、MCP 重启和 Codex `/hooks` 信任审核仍需单独完成。
|
|
41
|
+
|
|
42
|
+
### 软件包更新
|
|
43
|
+
|
|
44
|
+
`aptly update` 需要 **Aptly 0.1.3 或更高版本**。仅支持已验证的全局 npm 安装,将 `aptly@latest` 安装到与当前运行软件包一致且已验证的 prefix。局部安装、源码检出和无法验证的路径会被拒绝。 如果 npm 的有效配置为 `dry-run=true`,命令会在开始安装前返回 `update_dry_run_enabled` 并拒绝更新。无法验证配置时返回 `update_configuration_unverified`。不会将试运行报告为更新成功。
|
|
45
|
+
|
|
46
|
+
更新前,请按上文账本迁移步骤停止其他使用账本的宿主、MCP 和 CLI 进程。该命令跳过安装脚本,更新后请显式配置并重启宿主:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
aptly update
|
|
50
|
+
aptly setup --client all --mode defaults
|
|
51
|
+
aptly --version
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
不会自动迁移现有 MCP 启动路径或用户包装程序。如仍有旧路径,请按下文启动路径恢复步骤处理。密钥配置和 Codex `/hooks` 信任审核仍是单独步骤。如遇 npm 认证、网络或权限错误,请使用手动安装说明。不会自动安装更新。
|
|
55
|
+
|
|
56
|
+
### 新版本通知
|
|
57
|
+
|
|
58
|
+
**Aptly 0.1.3 或更高版本**会在交互式普通 CLI 命令中检查最新稳定版本,并在有新版时通过 stderr 提示 `aptly update`。 三个标准流必须都是终端;MCP 服务、自动 setup、推荐或分类路径、管道和 CI 均跳过检查。JSON stdout 和 MCP、Hook 响应保持不变。
|
|
59
|
+
|
|
60
|
+
仅向公开 npm 注册表请求 Aptly 版本元数据,不发送密钥、任务摘要或宿主配置。网络及响应读取限制为 800ms 和 16KiB,结果缓存于 `~/.cache/aptly/update-check.json`,有效期为 24 小时,失败后等待一小时。网络或缓存错误不会使原命令失败。在 CLI 环境中设置 `APTLY_NO_UPDATE_NOTIFIER=1` 可关闭检查;此变量不属于 `aptly set env` 支持的键。通知不会安装更新。
|
|
61
|
+
|
|
27
62
|
### 使用 CLI 设置
|
|
28
63
|
|
|
29
64
|
`set` 命令需要 0.1.1 或更高版本。命令不会自动切换现有启动路径。API 密钥设置仅允许当前软件包的标准 Node/服务器注册,并要求确认运行时支持系统凭据引用。自定义包装程序、旧版本及其他安装位置会以 `credential_launcher_unverified` 被拒绝;请先明确切换注册项或完成升级,再重新执行。
|
|
@@ -75,6 +110,15 @@ aptly set env OPENAI_API_KEY --client claude
|
|
|
75
110
|
|
|
76
111
|
`--dry-run` 不修改文件。迁移仅将与所有权记录一致的 Aptly 标准注册的 Node 和服务器路径更新到当前构建,保留环境设置、Hook、策略及其他宿主,不访问凭据,也不执行旧启动程序。自定义包装程序、缺少所有权记录或已修改的注册会被拒绝。请在本地检查此类注册,使用包装程序的安全环境传递方式,或切换到另行审核的标准注册。不要编辑或删除所有权记录以绕过验证。
|
|
77
112
|
|
|
113
|
+
如果 `launcher_migration_not_owned` 对应的是保留的注册(`mcp_created: false`),**Aptly 0.1.3 或更高版本**提供显式选项 `--include-existing`。请安装 0.1.3 或更高版本,然后运行:
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
aptly setup --client claude --migrate-launcher --include-existing --dry-run
|
|
117
|
+
aptly setup --client claude --migrate-launcher --include-existing
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
请先在本地检查注册。必须已有有效的 Aptly 所有权记录,且受管理的环境变量、Hook 和策略与记录一致。仅接受绝对 Node 路径和单个标准服务器路径;包装程序或缺少记录的注册仍会被拒绝。此选项不会将整个 MCP 注册改为 Aptly 所有,后续移除仍会保留迁移后的启动路径和原有用户设置。仅可用于显式指定的单个宿主迁移,不适用于自动安装。
|
|
121
|
+
|
|
78
122
|
已成功的 Codex 无需重新设置。仅指定失败的宿主重试,然后重启 MCP。Codex 仍需通过 `/hooks` 审核信任。诊断后的配置变更会以 `concurrent_configuration_change` 中止。恢复时若发现其他密钥或无法读取当前值,不会覆盖存储,而是返回 `credential_rollback_failed`。文件与 OS 存储之间不是原子事务,请避免同时运行配置操作。
|
|
79
123
|
|
|
80
124
|
### 密钥与重启
|