@kaitranntt/ccs 7.54.0 → 7.55.0-dev.2
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 +26 -4
- package/config/base-codex.settings.json +4 -4
- package/dist/auth/commands/create-command.js +1 -1
- package/dist/auth/commands/create-command.js.map +1 -1
- package/dist/auth/commands/remove-command.js +1 -1
- package/dist/auth/commands/remove-command.js.map +1 -1
- package/dist/ccs.js +70 -370
- package/dist/ccs.js.map +1 -1
- package/dist/cliproxy/codex-plan-compatibility.d.ts +29 -0
- package/dist/cliproxy/codex-plan-compatibility.d.ts.map +1 -0
- package/dist/cliproxy/codex-plan-compatibility.js +130 -0
- package/dist/cliproxy/codex-plan-compatibility.js.map +1 -0
- package/dist/cliproxy/codex-reasoning-proxy.d.ts +5 -0
- package/dist/cliproxy/codex-reasoning-proxy.d.ts.map +1 -1
- package/dist/cliproxy/codex-reasoning-proxy.js +103 -20
- package/dist/cliproxy/codex-reasoning-proxy.js.map +1 -1
- package/dist/cliproxy/executor/index.d.ts.map +1 -1
- package/dist/cliproxy/executor/index.js +8 -0
- package/dist/cliproxy/executor/index.js.map +1 -1
- package/dist/cliproxy/index.d.ts +1 -0
- package/dist/cliproxy/index.d.ts.map +1 -1
- package/dist/cliproxy/index.js +6 -2
- package/dist/cliproxy/index.js.map +1 -1
- package/dist/cliproxy/model-catalog.d.ts.map +1 -1
- package/dist/cliproxy/model-catalog.js +81 -12
- package/dist/cliproxy/model-catalog.js.map +1 -1
- package/dist/cliproxy/model-config.d.ts.map +1 -1
- package/dist/cliproxy/model-config.js +2 -2
- package/dist/cliproxy/model-config.js.map +1 -1
- package/dist/cliproxy/services/variant-settings.d.ts +3 -1
- package/dist/cliproxy/services/variant-settings.d.ts.map +1 -1
- package/dist/cliproxy/services/variant-settings.js +5 -2
- package/dist/cliproxy/services/variant-settings.js.map +1 -1
- package/dist/commands/api-command/copy-command.d.ts +2 -0
- package/dist/commands/api-command/copy-command.d.ts.map +1 -0
- package/dist/commands/api-command/copy-command.js +41 -0
- package/dist/commands/api-command/copy-command.js.map +1 -0
- package/dist/commands/api-command/create-command.d.ts +2 -0
- package/dist/commands/api-command/create-command.d.ts.map +1 -0
- package/dist/commands/api-command/create-command.js +249 -0
- package/dist/commands/api-command/create-command.js.map +1 -0
- package/dist/commands/api-command/discover-command.d.ts +2 -0
- package/dist/commands/api-command/discover-command.d.ts.map +1 -0
- package/dist/commands/api-command/discover-command.js +69 -0
- package/dist/commands/api-command/discover-command.js.map +1 -0
- package/dist/commands/api-command/export-command.d.ts +2 -0
- package/dist/commands/api-command/export-command.d.ts.map +1 -0
- package/dist/commands/api-command/export-command.js +73 -0
- package/dist/commands/api-command/export-command.js.map +1 -0
- package/dist/commands/api-command/help.d.ts +3 -0
- package/dist/commands/api-command/help.d.ts.map +1 -0
- package/dist/commands/api-command/help.js +100 -0
- package/dist/commands/api-command/help.js.map +1 -0
- package/dist/commands/api-command/import-command.d.ts +2 -0
- package/dist/commands/api-command/import-command.d.ts.map +1 -0
- package/dist/commands/api-command/import-command.js +111 -0
- package/dist/commands/api-command/import-command.js.map +1 -0
- package/dist/commands/api-command/index.d.ts +3 -0
- package/dist/commands/api-command/index.d.ts.map +1 -0
- package/dist/commands/api-command/index.js +34 -0
- package/dist/commands/api-command/index.js.map +1 -0
- package/dist/commands/api-command/list-command.d.ts +2 -0
- package/dist/commands/api-command/list-command.d.ts.map +1 -0
- package/dist/commands/api-command/list-command.js +53 -0
- package/dist/commands/api-command/list-command.js.map +1 -0
- package/dist/commands/api-command/remove-command.d.ts +2 -0
- package/dist/commands/api-command/remove-command.d.ts.map +1 -0
- package/dist/commands/api-command/remove-command.js +63 -0
- package/dist/commands/api-command/remove-command.js.map +1 -0
- package/dist/commands/api-command/shared.d.ts +36 -0
- package/dist/commands/api-command/shared.d.ts.map +1 -0
- package/dist/commands/api-command/shared.js +164 -0
- package/dist/commands/api-command/shared.js.map +1 -0
- package/dist/commands/api-command.d.ts +1 -26
- package/dist/commands/api-command.d.ts.map +1 -1
- package/dist/commands/api-command.js +3 -807
- package/dist/commands/api-command.js.map +1 -1
- package/dist/commands/arg-extractor.d.ts +15 -0
- package/dist/commands/arg-extractor.d.ts.map +1 -1
- package/dist/commands/arg-extractor.js +48 -2
- package/dist/commands/arg-extractor.js.map +1 -1
- package/dist/commands/config-auth/index.d.ts.map +1 -1
- package/dist/commands/config-auth/index.js +35 -19
- package/dist/commands/config-auth/index.js.map +1 -1
- package/dist/commands/config-command-options.d.ts +14 -0
- package/dist/commands/config-command-options.d.ts.map +1 -0
- package/dist/commands/config-command-options.js +108 -0
- package/dist/commands/config-command-options.js.map +1 -0
- package/dist/commands/config-command.d.ts +1 -1
- package/dist/commands/config-command.d.ts.map +1 -1
- package/dist/commands/config-command.js +91 -104
- package/dist/commands/config-command.js.map +1 -1
- package/dist/commands/config-dashboard-host.d.ts +17 -0
- package/dist/commands/config-dashboard-host.d.ts.map +1 -0
- package/dist/commands/config-dashboard-host.js +99 -0
- package/dist/commands/config-dashboard-host.js.map +1 -0
- package/dist/commands/env-command.d.ts.map +1 -1
- package/dist/commands/env-command.js +5 -0
- package/dist/commands/env-command.js.map +1 -1
- package/dist/commands/help-command.js +1 -1
- package/dist/commands/help-command.js.map +1 -1
- package/dist/commands/named-command-router.d.ts +17 -0
- package/dist/commands/named-command-router.d.ts.map +1 -0
- package/dist/commands/named-command-router.js +39 -0
- package/dist/commands/named-command-router.js.map +1 -0
- package/dist/commands/persist-command.js +1 -1
- package/dist/commands/persist-command.js.map +1 -1
- package/dist/commands/root-command-router.d.ts +2 -0
- package/dist/commands/root-command-router.d.ts.map +1 -0
- package/dist/commands/root-command-router.js +209 -0
- package/dist/commands/root-command-router.js.map +1 -0
- package/dist/config/unified-config-loader.js +1 -1
- package/dist/config/unified-config-loader.js.map +1 -1
- package/dist/cursor/cursor-anthropic-response.d.ts +6 -0
- package/dist/cursor/cursor-anthropic-response.d.ts.map +1 -0
- package/dist/cursor/cursor-anthropic-response.js +190 -0
- package/dist/cursor/cursor-anthropic-response.js.map +1 -0
- package/dist/cursor/cursor-anthropic-translator.d.ts +11 -0
- package/dist/cursor/cursor-anthropic-translator.d.ts.map +1 -0
- package/dist/cursor/cursor-anthropic-translator.js +167 -0
- package/dist/cursor/cursor-anthropic-translator.js.map +1 -0
- package/dist/cursor/cursor-anthropic-types.d.ts +46 -0
- package/dist/cursor/cursor-anthropic-types.d.ts.map +1 -0
- package/dist/cursor/cursor-anthropic-types.js +3 -0
- package/dist/cursor/cursor-anthropic-types.js.map +1 -0
- package/dist/cursor/cursor-daemon-entry.d.ts.map +1 -1
- package/dist/cursor/cursor-daemon-entry.js +53 -24
- package/dist/cursor/cursor-daemon-entry.js.map +1 -1
- package/dist/cursor/cursor-models.d.ts.map +1 -1
- package/dist/cursor/cursor-models.js +36 -2
- package/dist/cursor/cursor-models.js.map +1 -1
- package/dist/glmt/glmt-proxy.d.ts +4 -3
- package/dist/glmt/glmt-proxy.d.ts.map +1 -1
- package/dist/glmt/glmt-proxy.js +4 -3
- package/dist/glmt/glmt-proxy.js.map +1 -1
- package/dist/glmt/sse-parser.d.ts +2 -0
- package/dist/glmt/sse-parser.d.ts.map +1 -1
- package/dist/glmt/sse-parser.js +4 -0
- package/dist/glmt/sse-parser.js.map +1 -1
- package/dist/management/instance-manager.d.ts +2 -1
- package/dist/management/instance-manager.d.ts.map +1 -1
- package/dist/management/instance-manager.js +23 -8
- package/dist/management/instance-manager.js.map +1 -1
- package/dist/management/profile-context-sync-lock.d.ts +3 -0
- package/dist/management/profile-context-sync-lock.d.ts.map +1 -1
- package/dist/management/profile-context-sync-lock.js +75 -3
- package/dist/management/profile-context-sync-lock.js.map +1 -1
- package/dist/management/recovery-manager.d.ts +2 -2
- package/dist/management/recovery-manager.js +2 -2
- package/dist/management/shared-manager.d.ts +31 -1
- package/dist/management/shared-manager.d.ts.map +1 -1
- package/dist/management/shared-manager.js +420 -23
- package/dist/management/shared-manager.js.map +1 -1
- package/dist/shared/claude-extension-setup.d.ts.map +1 -1
- package/dist/shared/claude-extension-setup.js +16 -2
- package/dist/shared/claude-extension-setup.js.map +1 -1
- package/dist/shared/provider-preset-catalog.d.ts +1 -1
- package/dist/shared/provider-preset-catalog.d.ts.map +1 -1
- package/dist/shared/provider-preset-catalog.js +34 -40
- package/dist/shared/provider-preset-catalog.js.map +1 -1
- package/dist/ui/assets/{accounts-CZEg1_PX.js → accounts-CxIwtPW5.js} +1 -1
- package/dist/ui/assets/{alert-dialog-DhwS38kc.js → alert-dialog-D0M-j0xk.js} +1 -1
- package/dist/ui/assets/{api-sWNND4wP.js → api-D_GvXEjg.js} +1 -1
- package/dist/ui/assets/{auth-section-nJIpOcnm.js → auth-section-DtxR8sof.js} +1 -1
- package/dist/ui/assets/{backups-section-D3A6hmrU.js → backups-section-DAPwVCGp.js} +1 -1
- package/dist/ui/assets/checkbox-D470Q1y9.js +1 -0
- package/dist/ui/assets/{claude-extension-BjInaILv.js → claude-extension-vRIHOe5q.js} +1 -1
- package/dist/ui/assets/cliproxy-DGsWe0cf.js +3 -0
- package/dist/ui/assets/{cliproxy-control-panel-CKO2Sn9B.js → cliproxy-control-panel-BoKpt64d.js} +1 -1
- package/dist/ui/assets/{confirm-dialog-DTKxwrat.js → confirm-dialog-BnNUvB5u.js} +1 -1
- package/dist/ui/assets/copilot-DCkXk9mK.js +3 -0
- package/dist/ui/assets/cursor-9aQfYlGU.js +1 -0
- package/dist/ui/assets/{droid-Cl8QsJJL.js → droid-Bzp6uHqU.js} +2 -2
- package/dist/ui/assets/{globalenv-section-C3dxxoD9.js → globalenv-section-BM9oGUk_.js} +1 -1
- package/dist/ui/assets/{health-BUifaDU7.js → health-DLIgkfxF.js} +1 -1
- package/dist/ui/assets/icons-DtwH984l.js +1 -0
- package/dist/ui/assets/index-C-7tLTU2.js +47 -0
- package/dist/ui/assets/index-ClEn7Y7g.css +1 -0
- package/dist/ui/assets/{index-CPdceT1C.js → index-D9tmeP-H.js} +1 -1
- package/dist/ui/assets/{index-CYo-E5rU.js → index-DGQhzUIq.js} +1 -1
- package/dist/ui/assets/{index-BOsbrhaa.js → index-DimlYMhI.js} +1 -1
- package/dist/ui/assets/{index-xayyyR26.js → index-DykKl5b0.js} +1 -1
- package/dist/ui/assets/providers/llama-cpp.svg +5 -0
- package/dist/ui/assets/{proxy-status-widget-D94htBPb.js → proxy-status-widget-DloYg7yP.js} +1 -1
- package/dist/ui/assets/{radix-ui-BR1vy4kf.js → radix-ui-Dt3edmE5.js} +8 -8
- package/dist/ui/assets/searchable-select-CSREngvO.js +1 -0
- package/dist/ui/assets/{separator-3fBbTn-V.js → separator-DzrBeBn-.js} +1 -1
- package/dist/ui/assets/{shared-q_FNNbjD.js → shared-qRzQxB-N.js} +1 -1
- package/dist/ui/assets/{switch-5N8qBdBr.js → switch-BP9rnaI6.js} +1 -1
- package/dist/ui/assets/{tanstack-e99Cjjy2.js → tanstack-B8i0evp-.js} +1 -1
- package/dist/ui/assets/{updates-CubQ54J0.js → updates-BQvHbU9s.js} +1 -1
- package/dist/ui/icons/novita.svg +9 -0
- package/dist/ui/index.html +5 -5
- package/dist/utils/config-manager.d.ts +1 -1
- package/dist/utils/config-manager.js +1 -1
- package/dist/utils/fetch-proxy-setup.d.ts.map +1 -1
- package/dist/utils/fetch-proxy-setup.js.map +1 -1
- package/dist/utils/glmt-deprecation.d.ts +11 -0
- package/dist/utils/glmt-deprecation.d.ts.map +1 -0
- package/dist/utils/glmt-deprecation.js +64 -0
- package/dist/utils/glmt-deprecation.js.map +1 -0
- package/dist/utils/shell-executor.d.ts.map +1 -1
- package/dist/utils/shell-executor.js +12 -0
- package/dist/utils/shell-executor.js.map +1 -1
- package/dist/web-server/index.d.ts +1 -0
- package/dist/web-server/index.d.ts.map +1 -1
- package/dist/web-server/index.js +39 -3
- package/dist/web-server/index.js.map +1 -1
- package/dist/web-server/routes/account-routes.js +2 -2
- package/dist/web-server/routes/account-routes.js.map +1 -1
- package/dist/web-server/routes/profile-routes.d.ts +1 -0
- package/dist/web-server/routes/profile-routes.d.ts.map +1 -1
- package/dist/web-server/routes/profile-routes.js +3 -0
- package/dist/web-server/routes/profile-routes.js.map +1 -1
- package/dist/web-server/routes/variant-routes.d.ts +1 -0
- package/dist/web-server/routes/variant-routes.d.ts.map +1 -1
- package/dist/web-server/routes/variant-routes.js +3 -0
- package/dist/web-server/routes/variant-routes.js.map +1 -1
- package/package.json +3 -2
- package/scripts/completion/README.md +1 -1
- package/scripts/completion/ccs.fish +1 -2
- package/scripts/completion/ccs.zsh +1 -2
- package/scripts/postinstall.js +3 -3
- package/dist/ui/assets/checkbox-CZrxD1iS.js +0 -1
- package/dist/ui/assets/cliproxy-BGiSCGkl.js +0 -3
- package/dist/ui/assets/copilot-CuRngdBg.js +0 -3
- package/dist/ui/assets/cursor-Dxo0uIiU.js +0 -1
- package/dist/ui/assets/icons-DrEfTmfX.js +0 -1
- package/dist/ui/assets/index-Btf_ow2V.css +0 -1
- package/dist/ui/assets/index-Cw9Urr0S.js +0 -47
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* ~/.claude/ ← ~/.ccs/shared/ ← instance/
|
|
7
7
|
*/
|
|
8
8
|
import { AccountContextPolicy } from '../auth/account-context';
|
|
9
|
+
export declare function normalizePluginMetadataPathString(input: string, targetConfigDir?: string): string;
|
|
10
|
+
export declare function normalizePluginMetadataContent(original: string, targetConfigDir?: string): string;
|
|
9
11
|
/**
|
|
10
12
|
* SharedManager Class
|
|
11
13
|
*/
|
|
@@ -14,7 +16,10 @@ declare class SharedManager {
|
|
|
14
16
|
private readonly sharedDir;
|
|
15
17
|
private readonly claudeDir;
|
|
16
18
|
private readonly instancesDir;
|
|
19
|
+
private readonly pluginLayoutLock;
|
|
17
20
|
private readonly sharedItems;
|
|
21
|
+
private readonly sharedPluginEntries;
|
|
22
|
+
private readonly instanceLocalPluginMetadataFiles;
|
|
18
23
|
private readonly advancedContinuityItems;
|
|
19
24
|
constructor();
|
|
20
25
|
/**
|
|
@@ -30,6 +35,11 @@ declare class SharedManager {
|
|
|
30
35
|
* Link shared directories to instance
|
|
31
36
|
*/
|
|
32
37
|
linkSharedDirectories(instancePath: string): void;
|
|
38
|
+
detachSharedDirectories(instancePath: string): void;
|
|
39
|
+
private ensureSharedPluginLayoutDefaults;
|
|
40
|
+
private linkInstancePlugins;
|
|
41
|
+
private getSharedPluginLinkItems;
|
|
42
|
+
private removeExistingPath;
|
|
33
43
|
/**
|
|
34
44
|
* Sync project workspace context based on account policy.
|
|
35
45
|
*
|
|
@@ -54,6 +64,11 @@ declare class SharedManager {
|
|
|
54
64
|
* ~/.ccs/shared/memory/<project>/
|
|
55
65
|
*/
|
|
56
66
|
syncProjectMemories(instancePath: string): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Normalize plugin metadata and reconcile marketplace metadata for the active config dir.
|
|
69
|
+
*/
|
|
70
|
+
normalizeSharedPluginMetadataPaths(configDir?: string): void;
|
|
71
|
+
normalizeSharedPluginMetadataPathsLocked(configDir?: string): void;
|
|
57
72
|
/**
|
|
58
73
|
* Normalize plugin registry paths to use canonical ~/.claude/ paths
|
|
59
74
|
* instead of instance-specific ~/.ccs/instances/<name>/ paths.
|
|
@@ -61,7 +76,19 @@ declare class SharedManager {
|
|
|
61
76
|
* This ensures installed_plugins.json is consistent regardless of
|
|
62
77
|
* which CCS instance installed the plugin.
|
|
63
78
|
*/
|
|
64
|
-
normalizePluginRegistryPaths(): void;
|
|
79
|
+
normalizePluginRegistryPaths(configDir?: string): void;
|
|
80
|
+
/**
|
|
81
|
+
* Reconcile marketplace registry content into the active config dir while
|
|
82
|
+
* keeping the global ~/.claude copy up to date for non-instance flows.
|
|
83
|
+
*/
|
|
84
|
+
normalizeMarketplaceRegistryPaths(configDir?: string): void;
|
|
85
|
+
private normalizePluginMetadataFiles;
|
|
86
|
+
private getPluginMetadataFilePaths;
|
|
87
|
+
private normalizePluginMetadataFile;
|
|
88
|
+
private getMarketplaceRegistrySourcePaths;
|
|
89
|
+
private buildMarketplaceRegistryContent;
|
|
90
|
+
private discoverMarketplaceEntries;
|
|
91
|
+
private writePluginMetadataFile;
|
|
65
92
|
/**
|
|
66
93
|
* Migrate from v3.1.1 (copied data in ~/.ccs/shared/) to v3.2.0 (symlinks to ~/.claude/)
|
|
67
94
|
* Runs once on upgrade
|
|
@@ -119,6 +146,9 @@ declare class SharedManager {
|
|
|
119
146
|
* Build a non-destructive conflict copy path.
|
|
120
147
|
*/
|
|
121
148
|
private getConflictCopyPath;
|
|
149
|
+
private symlinkPointsTo;
|
|
150
|
+
private detachManagedPluginLayout;
|
|
151
|
+
private reconcileLocalMarketplaceRegistry;
|
|
122
152
|
private resolveCanonicalPath;
|
|
123
153
|
private isPathWithinDirectory;
|
|
124
154
|
private pathExists;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-manager.d.ts","sourceRoot":"","sources":["../../src/management/shared-manager.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"shared-manager.d.ts","sourceRoot":"","sources":["../../src/management/shared-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,18 +29,85 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
29
29
|
__setModuleDefault(result, mod);
|
|
30
30
|
return result;
|
|
31
31
|
};
|
|
32
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
33
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
34
|
+
};
|
|
32
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.normalizePluginMetadataContent = exports.normalizePluginMetadataPathString = void 0;
|
|
33
37
|
const fs = __importStar(require("fs"));
|
|
34
38
|
const path = __importStar(require("path"));
|
|
35
39
|
const os = __importStar(require("os"));
|
|
40
|
+
const profile_context_sync_lock_1 = __importDefault(require("./profile-context-sync-lock"));
|
|
36
41
|
const ui_1 = require("../utils/ui");
|
|
37
42
|
const account_context_1 = require("../auth/account-context");
|
|
38
43
|
const config_manager_1 = require("../utils/config-manager");
|
|
44
|
+
const DEFAULT_INSTALLED_PLUGIN_REGISTRY = JSON.stringify({
|
|
45
|
+
version: 2,
|
|
46
|
+
plugins: {},
|
|
47
|
+
}, null, 2);
|
|
48
|
+
function getPluginPathModule(targetConfigDir, input) {
|
|
49
|
+
return targetConfigDir.includes('\\') || input.includes('\\') ? path.win32 : path.posix;
|
|
50
|
+
}
|
|
51
|
+
function normalizeTargetConfigDir(targetConfigDir, input) {
|
|
52
|
+
const pathModule = getPluginPathModule(targetConfigDir, input);
|
|
53
|
+
return pathModule.normalize(pathModule === path.win32
|
|
54
|
+
? targetConfigDir.replace(/\//g, '\\')
|
|
55
|
+
: targetConfigDir.replace(/\\/g, '/'));
|
|
56
|
+
}
|
|
57
|
+
function normalizePluginMetadataPathString(input, targetConfigDir = path.join(os.homedir(), '.claude')) {
|
|
58
|
+
const match = input.match(/^(.*?)([\\/])(?:\.claude|\.ccs\2shared|\.ccs\2instances\2[^\\/]+)\2plugins(?:(\2.*))?$/);
|
|
59
|
+
if (!match) {
|
|
60
|
+
return input;
|
|
61
|
+
}
|
|
62
|
+
const pathModule = getPluginPathModule(targetConfigDir, input);
|
|
63
|
+
const normalizedTargetConfigDir = normalizeTargetConfigDir(targetConfigDir, input);
|
|
64
|
+
const suffix = match[3] ?? '';
|
|
65
|
+
const suffixSegments = suffix.split(/[\\/]+/).filter(Boolean);
|
|
66
|
+
return pathModule.join(normalizedTargetConfigDir, 'plugins', ...suffixSegments);
|
|
67
|
+
}
|
|
68
|
+
exports.normalizePluginMetadataPathString = normalizePluginMetadataPathString;
|
|
69
|
+
function normalizePluginMetadataValue(value, targetConfigDir) {
|
|
70
|
+
if (typeof value === 'string') {
|
|
71
|
+
const normalized = normalizePluginMetadataPathString(value, targetConfigDir);
|
|
72
|
+
return { normalized, changed: normalized !== value };
|
|
73
|
+
}
|
|
74
|
+
if (Array.isArray(value)) {
|
|
75
|
+
let changed = false;
|
|
76
|
+
const normalized = value.map((item) => {
|
|
77
|
+
const result = normalizePluginMetadataValue(item, targetConfigDir);
|
|
78
|
+
changed = changed || result.changed;
|
|
79
|
+
return result.normalized;
|
|
80
|
+
});
|
|
81
|
+
return { normalized, changed };
|
|
82
|
+
}
|
|
83
|
+
if (value && typeof value === 'object') {
|
|
84
|
+
let changed = false;
|
|
85
|
+
const normalized = Object.fromEntries(Object.entries(value).map(([key, item]) => {
|
|
86
|
+
const result = normalizePluginMetadataValue(item, targetConfigDir);
|
|
87
|
+
changed = changed || result.changed;
|
|
88
|
+
return [key, result.normalized];
|
|
89
|
+
}));
|
|
90
|
+
return { normalized, changed };
|
|
91
|
+
}
|
|
92
|
+
return { normalized: value, changed: false };
|
|
93
|
+
}
|
|
94
|
+
function normalizePluginMetadataContent(original, targetConfigDir = path.join(os.homedir(), '.claude')) {
|
|
95
|
+
const parsed = JSON.parse(original);
|
|
96
|
+
const result = normalizePluginMetadataValue(parsed, targetConfigDir);
|
|
97
|
+
return result.changed ? JSON.stringify(result.normalized, null, 2) : original;
|
|
98
|
+
}
|
|
99
|
+
exports.normalizePluginMetadataContent = normalizePluginMetadataContent;
|
|
39
100
|
/**
|
|
40
101
|
* SharedManager Class
|
|
41
102
|
*/
|
|
42
103
|
class SharedManager {
|
|
43
104
|
constructor() {
|
|
105
|
+
this.sharedPluginEntries = [
|
|
106
|
+
{ name: 'cache', type: 'directory' },
|
|
107
|
+
{ name: 'marketplaces', type: 'directory' },
|
|
108
|
+
{ name: 'installed_plugins.json', type: 'file' },
|
|
109
|
+
];
|
|
110
|
+
this.instanceLocalPluginMetadataFiles = new Set(['known_marketplaces.json']);
|
|
44
111
|
this.advancedContinuityItems = [
|
|
45
112
|
'session-env',
|
|
46
113
|
'file-history',
|
|
@@ -52,6 +119,7 @@ class SharedManager {
|
|
|
52
119
|
this.sharedDir = path.join(ccsDir, 'shared');
|
|
53
120
|
this.claudeDir = path.join(this.homeDir, '.claude');
|
|
54
121
|
this.instancesDir = path.join(ccsDir, 'instances');
|
|
122
|
+
this.pluginLayoutLock = new profile_context_sync_lock_1.default(this.instancesDir);
|
|
55
123
|
this.sharedItems = [
|
|
56
124
|
{ name: 'commands', type: 'directory' },
|
|
57
125
|
{ name: 'skills', type: 'directory' },
|
|
@@ -106,6 +174,7 @@ class SharedManager {
|
|
|
106
174
|
if (!fs.existsSync(this.sharedDir)) {
|
|
107
175
|
fs.mkdirSync(this.sharedDir, { recursive: true, mode: 0o700 });
|
|
108
176
|
}
|
|
177
|
+
this.ensureSharedPluginLayoutDefaults();
|
|
109
178
|
// Create symlinks ~/.ccs/shared/* → ~/.claude/*
|
|
110
179
|
for (const item of this.sharedItems) {
|
|
111
180
|
const claudePath = path.join(this.claudeDir, item.name);
|
|
@@ -176,17 +245,13 @@ class SharedManager {
|
|
|
176
245
|
linkSharedDirectories(instancePath) {
|
|
177
246
|
this.ensureSharedDirectories();
|
|
178
247
|
for (const item of this.sharedItems) {
|
|
248
|
+
if (item.name === 'plugins') {
|
|
249
|
+
this.linkInstancePlugins(instancePath);
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
179
252
|
const linkPath = path.join(instancePath, item.name);
|
|
180
253
|
const targetPath = path.join(this.sharedDir, item.name);
|
|
181
|
-
|
|
182
|
-
if (fs.existsSync(linkPath)) {
|
|
183
|
-
if (item.type === 'directory') {
|
|
184
|
-
fs.rmSync(linkPath, { recursive: true, force: true });
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
fs.unlinkSync(linkPath);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
254
|
+
this.removeExistingPath(linkPath, item.type);
|
|
190
255
|
// Create symlink
|
|
191
256
|
try {
|
|
192
257
|
const symlinkType = item.type === 'directory' ? 'dir' : 'file';
|
|
@@ -208,8 +273,129 @@ class SharedManager {
|
|
|
208
273
|
}
|
|
209
274
|
}
|
|
210
275
|
}
|
|
211
|
-
|
|
212
|
-
|
|
276
|
+
this.normalizeSharedPluginMetadataPaths(instancePath);
|
|
277
|
+
}
|
|
278
|
+
detachSharedDirectories(instancePath) {
|
|
279
|
+
this.ensureSharedDirectories();
|
|
280
|
+
for (const item of this.sharedItems) {
|
|
281
|
+
const managedPath = path.join(instancePath, item.name);
|
|
282
|
+
if (!fs.existsSync(managedPath)) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (item.name === 'plugins') {
|
|
286
|
+
this.detachManagedPluginLayout(instancePath);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
const stats = fs.lstatSync(managedPath);
|
|
290
|
+
if (!stats.isSymbolicLink()) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (this.symlinkPointsTo(managedPath, path.join(this.sharedDir, item.name))) {
|
|
294
|
+
this.removeExistingPath(managedPath, item.type);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
ensureSharedPluginLayoutDefaults() {
|
|
299
|
+
const pluginsDir = path.join(this.claudeDir, 'plugins');
|
|
300
|
+
fs.mkdirSync(pluginsDir, { recursive: true, mode: 0o700 });
|
|
301
|
+
for (const entry of this.sharedPluginEntries) {
|
|
302
|
+
const entryPath = path.join(pluginsDir, entry.name);
|
|
303
|
+
if (fs.existsSync(entryPath)) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
if (entry.type === 'directory') {
|
|
307
|
+
fs.mkdirSync(entryPath, { recursive: true, mode: 0o700 });
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
fs.writeFileSync(entryPath, DEFAULT_INSTALLED_PLUGIN_REGISTRY, 'utf8');
|
|
311
|
+
}
|
|
312
|
+
const marketplaceRegistryPath = path.join(pluginsDir, 'known_marketplaces.json');
|
|
313
|
+
if (!fs.existsSync(marketplaceRegistryPath)) {
|
|
314
|
+
fs.writeFileSync(marketplaceRegistryPath, JSON.stringify({}, null, 2), 'utf8');
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
linkInstancePlugins(instancePath) {
|
|
318
|
+
const linkPath = path.join(instancePath, 'plugins');
|
|
319
|
+
const targetPath = path.join(this.sharedDir, 'plugins');
|
|
320
|
+
let linkStats = null;
|
|
321
|
+
try {
|
|
322
|
+
linkStats = fs.lstatSync(linkPath);
|
|
323
|
+
}
|
|
324
|
+
catch (err) {
|
|
325
|
+
if (err.code !== 'ENOENT') {
|
|
326
|
+
throw err;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (linkStats?.isSymbolicLink() || (linkStats && !linkStats.isDirectory())) {
|
|
330
|
+
this.removeExistingPath(linkPath, linkStats.isDirectory() ? 'directory' : 'file');
|
|
331
|
+
}
|
|
332
|
+
if (!linkStats || !linkStats.isDirectory()) {
|
|
333
|
+
fs.mkdirSync(linkPath, { recursive: true, mode: 0o700 });
|
|
334
|
+
}
|
|
335
|
+
for (const item of this.getSharedPluginLinkItems()) {
|
|
336
|
+
const targetEntryPath = path.join(targetPath, item.name);
|
|
337
|
+
const linkEntryPath = path.join(linkPath, item.name);
|
|
338
|
+
this.removeExistingPath(linkEntryPath, item.type);
|
|
339
|
+
try {
|
|
340
|
+
const symlinkType = item.type === 'directory' ? 'dir' : 'file';
|
|
341
|
+
fs.symlinkSync(targetEntryPath, linkEntryPath, symlinkType);
|
|
342
|
+
}
|
|
343
|
+
catch (_err) {
|
|
344
|
+
if (process.platform === 'win32') {
|
|
345
|
+
if (item.type === 'directory') {
|
|
346
|
+
this.copyDirectoryFallback(targetEntryPath, linkEntryPath);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
fs.copyFileSync(targetEntryPath, linkEntryPath);
|
|
350
|
+
}
|
|
351
|
+
console.log((0, ui_1.warn)(`Symlink failed for plugins/${item.name}, copied instead (enable Developer Mode)`));
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
throw _err;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
getSharedPluginLinkItems() {
|
|
360
|
+
const sharedPluginsPath = path.join(this.sharedDir, 'plugins');
|
|
361
|
+
const items = new Map(this.sharedPluginEntries.map((entry) => [entry.name, { ...entry }]));
|
|
362
|
+
for (const entry of fs.readdirSync(sharedPluginsPath, { withFileTypes: true })) {
|
|
363
|
+
if (items.has(entry.name) || this.instanceLocalPluginMetadataFiles.has(entry.name)) {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
const entryPath = path.join(sharedPluginsPath, entry.name);
|
|
367
|
+
const stats = fs.statSync(entryPath);
|
|
368
|
+
items.set(entry.name, {
|
|
369
|
+
name: entry.name,
|
|
370
|
+
type: stats.isDirectory() ? 'directory' : 'file',
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
return [...items.values()];
|
|
374
|
+
}
|
|
375
|
+
removeExistingPath(targetPath, typeHint) {
|
|
376
|
+
try {
|
|
377
|
+
const stats = fs.lstatSync(targetPath);
|
|
378
|
+
if (stats.isDirectory() && !stats.isSymbolicLink()) {
|
|
379
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (stats.isSymbolicLink() || typeHint === 'file') {
|
|
383
|
+
fs.unlinkSync(targetPath);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
387
|
+
}
|
|
388
|
+
catch (err) {
|
|
389
|
+
if (err.code === 'ENOENT') {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (typeHint === 'directory') {
|
|
393
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
fs.rmSync(targetPath, { force: true });
|
|
397
|
+
}
|
|
398
|
+
}
|
|
213
399
|
}
|
|
214
400
|
/**
|
|
215
401
|
* Sync project workspace context based on account policy.
|
|
@@ -437,6 +623,18 @@ class SharedManager {
|
|
|
437
623
|
console.log((0, ui_1.ok)(`Synced shared project memory: ${migrated} migrated, ${merged} merged conflict(s), ${linked} linked`));
|
|
438
624
|
}
|
|
439
625
|
}
|
|
626
|
+
/**
|
|
627
|
+
* Normalize plugin metadata and reconcile marketplace metadata for the active config dir.
|
|
628
|
+
*/
|
|
629
|
+
normalizeSharedPluginMetadataPaths(configDir) {
|
|
630
|
+
this.normalizePluginRegistryPaths(configDir);
|
|
631
|
+
this.normalizeMarketplaceRegistryPaths(configDir);
|
|
632
|
+
}
|
|
633
|
+
normalizeSharedPluginMetadataPathsLocked(configDir) {
|
|
634
|
+
this.pluginLayoutLock.withNamedLockSync('__plugin-layout__', () => {
|
|
635
|
+
this.normalizeSharedPluginMetadataPaths(configDir);
|
|
636
|
+
});
|
|
637
|
+
}
|
|
440
638
|
/**
|
|
441
639
|
* Normalize plugin registry paths to use canonical ~/.claude/ paths
|
|
442
640
|
* instead of instance-specific ~/.ccs/instances/<name>/ paths.
|
|
@@ -444,29 +642,143 @@ class SharedManager {
|
|
|
444
642
|
* This ensures installed_plugins.json is consistent regardless of
|
|
445
643
|
* which CCS instance installed the plugin.
|
|
446
644
|
*/
|
|
447
|
-
normalizePluginRegistryPaths() {
|
|
448
|
-
|
|
449
|
-
|
|
645
|
+
normalizePluginRegistryPaths(configDir) {
|
|
646
|
+
this.normalizePluginMetadataFiles('installed_plugins.json', configDir, 'Normalized plugin registry paths', 'plugin registry');
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Reconcile marketplace registry content into the active config dir while
|
|
650
|
+
* keeping the global ~/.claude copy up to date for non-instance flows.
|
|
651
|
+
*/
|
|
652
|
+
normalizeMarketplaceRegistryPaths(configDir) {
|
|
653
|
+
const successMessage = 'Synchronized marketplace registry paths';
|
|
654
|
+
const warningLabel = 'marketplace registry';
|
|
655
|
+
try {
|
|
656
|
+
const sourcePaths = this.getMarketplaceRegistrySourcePaths(configDir);
|
|
657
|
+
this.writePluginMetadataFile(path.join(this.claudeDir, 'plugins', 'known_marketplaces.json'), this.buildMarketplaceRegistryContent(sourcePaths, this.claudeDir), successMessage);
|
|
658
|
+
if (configDir && path.resolve(configDir) !== path.resolve(this.claudeDir)) {
|
|
659
|
+
this.writePluginMetadataFile(path.join(configDir, 'plugins', 'known_marketplaces.json'), this.buildMarketplaceRegistryContent(sourcePaths, configDir), successMessage);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
catch (err) {
|
|
663
|
+
console.log((0, ui_1.warn)(`Could not synchronize ${warningLabel}: ${err.message}`));
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
normalizePluginMetadataFiles(fileName, configDir, successMessage, warningLabel) {
|
|
667
|
+
const seen = new Set();
|
|
668
|
+
for (const registryPath of this.getPluginMetadataFilePaths(fileName, configDir)) {
|
|
669
|
+
const dedupeKey = this.resolveCanonicalPath(registryPath);
|
|
670
|
+
if (seen.has(dedupeKey)) {
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
seen.add(dedupeKey);
|
|
674
|
+
this.normalizePluginMetadataFile(registryPath, successMessage, warningLabel);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
getPluginMetadataFilePaths(fileName, configDir) {
|
|
678
|
+
const pluginDirs = new Set([
|
|
679
|
+
path.join(this.claudeDir, 'plugins'),
|
|
680
|
+
path.join(this.sharedDir, 'plugins'),
|
|
681
|
+
]);
|
|
682
|
+
if (configDir && path.resolve(configDir) !== path.resolve(this.claudeDir)) {
|
|
683
|
+
pluginDirs.add(path.join(configDir, 'plugins'));
|
|
684
|
+
}
|
|
685
|
+
return [...pluginDirs].map((pluginDir) => path.join(pluginDir, fileName));
|
|
686
|
+
}
|
|
687
|
+
normalizePluginMetadataFile(registryPath, successMessage, warningLabel) {
|
|
450
688
|
if (!fs.existsSync(registryPath)) {
|
|
451
689
|
return;
|
|
452
690
|
}
|
|
453
691
|
try {
|
|
454
692
|
const original = fs.readFileSync(registryPath, 'utf8');
|
|
455
|
-
|
|
456
|
-
// Pattern: /.ccs/instances/<instance-name>/ -> /.claude/
|
|
457
|
-
const normalized = original.replace(/\/\.ccs\/instances\/[^/]+\//g, '/.claude/');
|
|
458
|
-
// Only write if changes were made
|
|
693
|
+
const normalized = normalizePluginMetadataContent(original);
|
|
459
694
|
if (normalized !== original) {
|
|
460
|
-
// Validate JSON before writing
|
|
461
|
-
JSON.parse(normalized);
|
|
462
695
|
fs.writeFileSync(registryPath, normalized, 'utf8');
|
|
463
|
-
console.log((0, ui_1.ok)(
|
|
696
|
+
console.log((0, ui_1.ok)(successMessage));
|
|
464
697
|
}
|
|
465
698
|
}
|
|
466
699
|
catch (err) {
|
|
467
|
-
|
|
468
|
-
|
|
700
|
+
console.log((0, ui_1.warn)(`Could not normalize ${warningLabel}: ${err.message}`));
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
getMarketplaceRegistrySourcePaths(configDir) {
|
|
704
|
+
const sourcePaths = new Set([
|
|
705
|
+
path.join(this.claudeDir, 'plugins', 'known_marketplaces.json'),
|
|
706
|
+
]);
|
|
707
|
+
if (fs.existsSync(this.instancesDir)) {
|
|
708
|
+
for (const entry of fs.readdirSync(this.instancesDir, { withFileTypes: true })) {
|
|
709
|
+
if (!entry.isDirectory() || entry.name.startsWith('.')) {
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
sourcePaths.add(path.join(this.instancesDir, entry.name, 'plugins', 'known_marketplaces.json'));
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
if (configDir && path.resolve(configDir) !== path.resolve(this.claudeDir)) {
|
|
716
|
+
sourcePaths.add(path.join(configDir, 'plugins', 'known_marketplaces.json'));
|
|
717
|
+
}
|
|
718
|
+
return [...sourcePaths];
|
|
719
|
+
}
|
|
720
|
+
buildMarketplaceRegistryContent(sourcePaths, targetConfigDir) {
|
|
721
|
+
const merged = {};
|
|
722
|
+
for (const registryPath of sourcePaths) {
|
|
723
|
+
if (!fs.existsSync(registryPath)) {
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
try {
|
|
727
|
+
const parsed = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
|
728
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
for (const [name, value] of Object.entries(parsed)) {
|
|
732
|
+
merged[name] = normalizePluginMetadataValue(value, targetConfigDir).normalized;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
catch (err) {
|
|
736
|
+
console.log((0, ui_1.warn)(`Skipping malformed marketplace registry ${registryPath}: ${err.message}`));
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
const discoveredEntries = this.discoverMarketplaceEntries(targetConfigDir);
|
|
740
|
+
for (const [name, value] of Object.entries(discoveredEntries)) {
|
|
741
|
+
const existing = merged[name];
|
|
742
|
+
if (existing && typeof existing === 'object' && !Array.isArray(existing)) {
|
|
743
|
+
merged[name] = {
|
|
744
|
+
...existing,
|
|
745
|
+
installLocation: value.installLocation,
|
|
746
|
+
};
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
merged[name] = value;
|
|
750
|
+
}
|
|
751
|
+
for (const name of Object.keys(merged)) {
|
|
752
|
+
if (!(name in discoveredEntries)) {
|
|
753
|
+
delete merged[name];
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return JSON.stringify(merged, null, 2);
|
|
757
|
+
}
|
|
758
|
+
discoverMarketplaceEntries(targetConfigDir) {
|
|
759
|
+
const marketplacesDir = path.join(targetConfigDir, 'plugins', 'marketplaces');
|
|
760
|
+
if (!fs.existsSync(marketplacesDir)) {
|
|
761
|
+
return {};
|
|
762
|
+
}
|
|
763
|
+
const discovered = {};
|
|
764
|
+
for (const entry of fs.readdirSync(marketplacesDir, { withFileTypes: true })) {
|
|
765
|
+
if (!entry.isDirectory()) {
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
discovered[entry.name] = {
|
|
769
|
+
installLocation: path.join(targetConfigDir, 'plugins', 'marketplaces', entry.name),
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
return discovered;
|
|
773
|
+
}
|
|
774
|
+
writePluginMetadataFile(registryPath, content, successMessage) {
|
|
775
|
+
fs.mkdirSync(path.dirname(registryPath), { recursive: true, mode: 0o700 });
|
|
776
|
+
const current = fs.existsSync(registryPath) ? fs.readFileSync(registryPath, 'utf8') : null;
|
|
777
|
+
if (current === content) {
|
|
778
|
+
return;
|
|
469
779
|
}
|
|
780
|
+
fs.writeFileSync(registryPath, content, 'utf8');
|
|
781
|
+
console.log((0, ui_1.ok)(successMessage));
|
|
470
782
|
}
|
|
471
783
|
/**
|
|
472
784
|
* Migrate from v3.1.1 (copied data in ~/.ccs/shared/) to v3.2.0 (symlinks to ~/.claude/)
|
|
@@ -855,6 +1167,91 @@ class SharedManager {
|
|
|
855
1167
|
}
|
|
856
1168
|
return candidate;
|
|
857
1169
|
}
|
|
1170
|
+
symlinkPointsTo(linkPath, expectedTarget) {
|
|
1171
|
+
try {
|
|
1172
|
+
const currentTarget = fs.readlinkSync(linkPath);
|
|
1173
|
+
const resolvedCurrentTarget = path.resolve(path.dirname(linkPath), currentTarget);
|
|
1174
|
+
return (this.resolveCanonicalPath(resolvedCurrentTarget) ===
|
|
1175
|
+
this.resolveCanonicalPath(expectedTarget));
|
|
1176
|
+
}
|
|
1177
|
+
catch {
|
|
1178
|
+
return false;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
detachManagedPluginLayout(instancePath) {
|
|
1182
|
+
const pluginsPath = path.join(instancePath, 'plugins');
|
|
1183
|
+
if (!fs.existsSync(pluginsPath)) {
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
const stats = fs.lstatSync(pluginsPath);
|
|
1187
|
+
const sharedPluginsPath = path.join(this.sharedDir, 'plugins');
|
|
1188
|
+
if (stats.isSymbolicLink()) {
|
|
1189
|
+
if (this.symlinkPointsTo(pluginsPath, sharedPluginsPath)) {
|
|
1190
|
+
this.removeExistingPath(pluginsPath, 'directory');
|
|
1191
|
+
}
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
if (!stats.isDirectory()) {
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
let removedManagedEntries = false;
|
|
1198
|
+
for (const item of this.getSharedPluginLinkItems()) {
|
|
1199
|
+
const pluginEntryPath = path.join(pluginsPath, item.name);
|
|
1200
|
+
if (!fs.existsSync(pluginEntryPath)) {
|
|
1201
|
+
continue;
|
|
1202
|
+
}
|
|
1203
|
+
const entryStats = fs.lstatSync(pluginEntryPath);
|
|
1204
|
+
if (!entryStats.isSymbolicLink()) {
|
|
1205
|
+
continue;
|
|
1206
|
+
}
|
|
1207
|
+
if (this.symlinkPointsTo(pluginEntryPath, path.join(sharedPluginsPath, item.name))) {
|
|
1208
|
+
this.removeExistingPath(pluginEntryPath, item.type);
|
|
1209
|
+
removedManagedEntries = true;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
if (!removedManagedEntries) {
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
this.reconcileLocalMarketplaceRegistry(instancePath);
|
|
1216
|
+
if (fs.readdirSync(pluginsPath).length === 0) {
|
|
1217
|
+
fs.rmSync(pluginsPath, { recursive: true, force: true });
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
reconcileLocalMarketplaceRegistry(configDir) {
|
|
1221
|
+
const registryPath = path.join(configDir, 'plugins', 'known_marketplaces.json');
|
|
1222
|
+
if (!fs.existsSync(registryPath)) {
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
const discoveredEntries = this.discoverMarketplaceEntries(configDir);
|
|
1226
|
+
if (Object.keys(discoveredEntries).length === 0) {
|
|
1227
|
+
this.removeExistingPath(registryPath, 'file');
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
let parsed = {};
|
|
1231
|
+
try {
|
|
1232
|
+
const raw = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
|
1233
|
+
if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
|
|
1234
|
+
parsed = raw;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
catch {
|
|
1238
|
+
parsed = {};
|
|
1239
|
+
}
|
|
1240
|
+
const reconciled = Object.fromEntries(Object.entries(discoveredEntries).map(([name, value]) => {
|
|
1241
|
+
const existing = parsed[name];
|
|
1242
|
+
if (existing && typeof existing === 'object' && !Array.isArray(existing)) {
|
|
1243
|
+
return [
|
|
1244
|
+
name,
|
|
1245
|
+
{
|
|
1246
|
+
...normalizePluginMetadataValue(existing, configDir).normalized,
|
|
1247
|
+
installLocation: value.installLocation,
|
|
1248
|
+
},
|
|
1249
|
+
];
|
|
1250
|
+
}
|
|
1251
|
+
return [name, value];
|
|
1252
|
+
}));
|
|
1253
|
+
this.writePluginMetadataFile(registryPath, JSON.stringify(reconciled, null, 2), 'Synchronized marketplace registry paths');
|
|
1254
|
+
}
|
|
858
1255
|
resolveCanonicalPath(targetPath) {
|
|
859
1256
|
try {
|
|
860
1257
|
return fs.realpathSync.native(targetPath);
|