coding-tool-x 3.6.13 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +45 -33
  2. package/dist/web/assets/{Analytics-CnA75GxO.js → Analytics-Dpje9L84.js} +1 -1
  3. package/dist/web/assets/{ConfigTemplates-D8XEscQY.js → ConfigTemplates-Cz6kasek.js} +1 -1
  4. package/dist/web/assets/Home-BwTaYu3q.js +1 -0
  5. package/dist/web/assets/Home-Jom_ZcdY.css +1 -0
  6. package/dist/web/assets/{PluginManager-BzKZ8TYu.js → PluginManager-DsN-xNXx.js} +1 -1
  7. package/dist/web/assets/ProjectList-4BCObimU.js +1 -0
  8. package/dist/web/assets/{ProjectList-DL4JK6ci.css → ProjectList-D3a0oRum.css} +1 -1
  9. package/dist/web/assets/SessionList-D4c8FRzq.js +1 -0
  10. package/dist/web/assets/{SkillManager-fxOvFTYD.js → SkillManager-C-KYbXB5.js} +1 -1
  11. package/dist/web/assets/{WorkspaceManager-Ch2mM8jm.js → WorkspaceManager-B-BlPJHX.js} +1 -1
  12. package/dist/web/assets/{icons-QFXzEgqM.js → icons-Dop5j7Bf.js} +1 -1
  13. package/dist/web/assets/index-Btfvh0GG.js +2 -0
  14. package/dist/web/assets/index-CLNa6DhH.css +1 -0
  15. package/dist/web/assets/{naive-ui-kcWDeJwl.js → naive-ui-CVyvdmg1.js} +1 -1
  16. package/dist/web/index.html +4 -4
  17. package/package.json +3 -1
  18. package/src/commands/channels.js +85 -24
  19. package/src/commands/daemon.js +7 -4
  20. package/src/commands/doctor.js +17 -8
  21. package/src/commands/logs.js +46 -6
  22. package/src/commands/port-config.js +83 -75
  23. package/src/commands/proxy-control.js +59 -26
  24. package/src/commands/stats.js +37 -19
  25. package/src/commands/switch.js +5 -1
  26. package/src/commands/toggle-proxy.js +131 -53
  27. package/src/commands/ui.js +3 -4
  28. package/src/commands/workspace.js +3 -35
  29. package/src/config/default.js +19 -2
  30. package/src/config/loader.js +5 -76
  31. package/src/config/paths.js +96 -11
  32. package/src/index.js +10 -16
  33. package/src/server/api/commands.js +1 -1
  34. package/src/server/api/config-export.js +4 -2
  35. package/src/server/api/config-registry.js +40 -34
  36. package/src/server/api/config.js +5 -1
  37. package/src/server/api/dashboard.js +22 -6
  38. package/src/server/api/mcp.js +5 -3
  39. package/src/server/api/omp-channels.js +349 -0
  40. package/src/server/api/omp-config.js +164 -0
  41. package/src/server/api/omp-projects.js +76 -0
  42. package/src/server/api/omp-proxy.js +154 -0
  43. package/src/server/api/omp-sessions.js +334 -0
  44. package/src/server/api/omp-statistics.js +40 -0
  45. package/src/server/api/plugins.js +1 -1
  46. package/src/server/api/prompts.js +3 -2
  47. package/src/server/api/settings.js +2 -0
  48. package/src/server/api/skills.js +1 -1
  49. package/src/server/api/workspaces.js +3 -41
  50. package/src/server/index.js +41 -0
  51. package/src/server/omp-proxy-server.js +66 -0
  52. package/src/server/services/agents-service.js +2 -1
  53. package/src/server/services/base/proxy-utils.js +1 -0
  54. package/src/server/services/channel-balance.js +151 -1
  55. package/src/server/services/channel-scheduler.js +7 -1
  56. package/src/server/services/commands-service.js +11 -2
  57. package/src/server/services/config-export-service.js +296 -39
  58. package/src/server/services/config-registry-service.js +20 -18
  59. package/src/server/services/config-sync-manager.js +115 -6
  60. package/src/server/services/config-sync-service.js +4 -2
  61. package/src/server/services/config-templates-service.js +183 -45
  62. package/src/server/services/mcp-service.js +217 -33
  63. package/src/server/services/notification-hooks.js +138 -6
  64. package/src/server/services/omp-auth-providers.js +294 -0
  65. package/src/server/services/omp-channels.js +94 -0
  66. package/src/server/services/omp-config.js +269 -0
  67. package/src/server/services/omp-sessions.js +490 -0
  68. package/src/server/services/omp-settings-manager.js +975 -0
  69. package/src/server/services/omp-statistics-service.js +317 -0
  70. package/src/server/services/plugins-service.js +673 -11
  71. package/src/server/services/prompts-service.js +112 -7
  72. package/src/server/services/proxy-log-helper.js +1 -0
  73. package/src/server/services/proxy-runtime.js +5 -1
  74. package/src/server/services/sessions.js +19 -35
  75. package/src/server/services/skill-service.js +13 -3
  76. package/src/server/services/speed-test.js +34 -28
  77. package/src/server/services/ui-config.js +50 -12
  78. package/src/server/services/workspace-service.js +117 -225
  79. package/src/server/websocket-server.js +7 -1
  80. package/src/shared/platforms.js +201 -0
  81. package/src/utils/session.js +4 -10
  82. package/dist/web/assets/Home-BiaKZMah.js +0 -1
  83. package/dist/web/assets/Home-CIua-b-o.css +0 -1
  84. package/dist/web/assets/ProjectList-BPfG_92X.js +0 -1
  85. package/dist/web/assets/SessionList-BIVtw00v.js +0 -1
  86. package/dist/web/assets/index-BpBFF3b2.js +0 -2
  87. package/dist/web/assets/index-Dsh4Q5Cg.css +0 -1
  88. package/src/utils/cli-flags.js +0 -7
@@ -8,11 +8,13 @@ const path = require('path');
8
8
  const AdmZip = require('adm-zip');
9
9
  const toml = require('toml');
10
10
  const tomlStringify = require('@iarna/toml').stringify;
11
+ const yaml = require('js-yaml');
11
12
  const configTemplatesService = require('./config-templates-service');
12
13
  const channelsService = require('./channels');
13
14
  const codexChannelsService = require('./codex-channels');
14
15
  const geminiChannelsService = require('./gemini-channels');
15
16
  const opencodeChannelsService = require('./opencode-channels');
17
+ const ompChannelsService = require('./omp-channels');
16
18
  const { AgentsService } = require('./agents-service');
17
19
  const { CommandsService } = require('./commands-service');
18
20
  const { SkillService } = require('./skill-service');
@@ -23,12 +25,16 @@ const CONFIG_VERSION = '1.4.0';
23
25
  const SKILL_FILE_ENCODING = 'base64';
24
26
  const SKILL_IGNORE_DIRS = new Set(['.git']);
25
27
  const SKILL_IGNORE_FILES = new Set(['.DS_Store']);
28
+ const NATIVE_DIR_FILE_ENCODING = 'base64';
29
+ const NATIVE_DIR_IGNORE_DIRS = new Set(['.git']);
30
+ const NATIVE_DIR_IGNORE_FILES = new Set(['.DS_Store']);
26
31
  const CC_TOOL_DIR = PATHS.base;
27
32
  const LEGACY_CC_TOOL_DIR = PATHS.base;
28
33
  const CLAUDE_SETTINGS_PATH = NATIVE_PATHS.claude.settings;
29
34
  const LEGACY_PLUGINS_DIR = path.join(LEGACY_CC_TOOL_DIR, 'plugins', 'installed');
30
35
  const LEGACY_PLUGINS_REGISTRY = path.join(LEGACY_CC_TOOL_DIR, 'plugins', 'registry.json');
31
- const CLAUDE_PLUGINS_DIR = path.join(path.dirname(NATIVE_PATHS.claude.settings), 'plugins');
36
+ const CLAUDE_PLUGINS_DIR = NATIVE_PATHS.claude.plugins
37
+ || path.join(NATIVE_PATHS.claude.dir || path.dirname(NATIVE_PATHS.claude.settings), 'plugins');
32
38
  const NATIVE_PLUGINS_REGISTRY = path.join(CLAUDE_PLUGINS_DIR, 'installed_plugins.json');
33
39
  const PLUGIN_IGNORE_DIRS = new Set(['.git', 'node_modules', '.DS_Store']);
34
40
  const PLUGIN_IGNORE_FILES = new Set(['.DS_Store']);
@@ -49,16 +55,59 @@ const LEGACY_UI_CONFIG_PATH = PATHS.uiConfig;
49
55
  const LEGACY_NOTIFY_HOOK_PATH = PATHS.notifyHook;
50
56
  const GEMINI_SETTINGS_PATH = path.join(path.dirname(NATIVE_PATHS.gemini.env), 'settings.json');
51
57
  const AGENT_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
52
- const COMMAND_PLATFORMS = ['claude', 'gemini', 'opencode'];
53
- const SKILL_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
54
- const PLUGIN_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode', 'pi'];
58
+ const COMMAND_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode', 'omp'];
59
+ const SKILL_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode', 'omp'];
60
+ const PLUGIN_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode', 'omp'];
55
61
  const CLAUDE_MARKETPLACES_REGISTRY = path.join(CLAUDE_PLUGINS_DIR, 'known_marketplaces.json');
56
62
  const CODEX_PLUGINS_DIR = path.join(path.dirname(NATIVE_PATHS.codex.config), 'plugins');
57
63
  const CODEX_PLUGINS_CACHE_DIR = path.join(CODEX_PLUGINS_DIR, 'cache');
58
64
  const OPENCODE_PLUGINS_DIR = path.join(NATIVE_PATHS.opencode.config, 'plugins');
59
65
  const OPENCODE_LEGACY_PLUGINS_DIR = path.join(NATIVE_PATHS.opencode.config, 'plugin');
60
- const PI_SETTINGS_PATH = NATIVE_PATHS.pi?.settings || path.join(PATHS.base, 'pi-settings.json');
61
- const PI_EXTENSIONS_DIR = NATIVE_PATHS.pi?.extensions || path.join(PATHS.base, 'pi-extensions');
66
+ const OMP_SETTINGS_PATH = NATIVE_PATHS.omp?.settings || path.join(PATHS.base, 'omp-config.yml');
67
+ const OMP_EXTENSIONS_DIR = NATIVE_PATHS.omp?.extensions || path.join(PATHS.base, 'omp-extensions');
68
+
69
+ function normalizeOmpResourceType(value = '') {
70
+ const key = String(value || '').trim().replace(/[\s_-]+/g, '').toLowerCase();
71
+ const map = {
72
+ extension: 'extension',
73
+ extensions: 'extension',
74
+ plugin: 'extension',
75
+ plugins: 'extension',
76
+ skill: 'skill',
77
+ skills: 'skill',
78
+ prompt: 'promptTemplate',
79
+ prompts: 'promptTemplate',
80
+ prompttemplate: 'promptTemplate',
81
+ prompttemplates: 'promptTemplate',
82
+ command: 'promptTemplate',
83
+ commands: 'promptTemplate',
84
+ theme: 'theme',
85
+ themes: 'theme',
86
+ mcp: 'mcp',
87
+ subagent: 'subagent',
88
+ subagents: 'subagent'
89
+ };
90
+ return map[key] || String(value || '').trim();
91
+ }
92
+
93
+ function normalizeOmpResourceTypes(input = []) {
94
+ const result = [];
95
+ const add = (value) => {
96
+ const normalized = normalizeOmpResourceType(value);
97
+ if (normalized && !result.includes(normalized)) result.push(normalized);
98
+ };
99
+ if (Array.isArray(input)) {
100
+ input.forEach(add);
101
+ } else if (input && typeof input === 'object') {
102
+ for (const [key, value] of Object.entries(input)) {
103
+ if (value === false || value == null) continue;
104
+ add(key);
105
+ }
106
+ } else if (input) {
107
+ add(input);
108
+ }
109
+ return result;
110
+ }
62
111
 
63
112
  function getOpenCodeConfigPaths() {
64
113
  try {
@@ -81,6 +130,7 @@ function getOpenCodeNotificationPluginPath() {
81
130
  function getNativeConfigSpecs() {
82
131
  const openCodeConfigPaths = getOpenCodeConfigPaths();
83
132
  const openCodeNotificationPluginPath = getOpenCodeNotificationPluginPath();
133
+ const ompDir = NATIVE_PATHS.omp.dir || path.dirname(NATIVE_PATHS.omp.settings);
84
134
  return {
85
135
  claude: {
86
136
  settings: { path: NATIVE_PATHS.claude.settings, format: 'json' }
@@ -100,6 +150,19 @@ function getNativeConfigSpecs() {
100
150
  ...(openCodeNotificationPluginPath
101
151
  ? { codingToolNotifyPlugin: { path: openCodeNotificationPluginPath, format: 'text' } }
102
152
  : {})
153
+ },
154
+ omp: {
155
+ settings: { path: NATIVE_PATHS.omp.settings, format: 'yaml' },
156
+ auth: { path: NATIVE_PATHS.omp.auth, format: 'json', mode: 0o600 },
157
+ models: { path: NATIVE_PATHS.omp.models, format: 'yaml' },
158
+ commands: { path: NATIVE_PATHS.omp.commands || path.join(ompDir, 'commands'), format: 'directory' },
159
+ prompts: { path: NATIVE_PATHS.omp.prompts, format: 'directory' },
160
+ skills: { path: NATIVE_PATHS.omp.skills, format: 'directory' },
161
+ extensions: { path: NATIVE_PATHS.omp.extensions, format: 'directory' },
162
+ themes: { path: NATIVE_PATHS.omp.themes || path.join(ompDir, 'themes'), format: 'directory' },
163
+ packages: { path: NATIVE_PATHS.omp.packages || path.join(ompDir, 'packages'), format: 'directory' },
164
+ npmPackages: { path: path.join(ompDir, 'npm'), format: 'directory' },
165
+ gitPackages: { path: path.join(ompDir, 'git'), format: 'directory' }
103
166
  }
104
167
  };
105
168
  }
@@ -120,6 +183,17 @@ function readJsonFileSafe(filePath) {
120
183
  }
121
184
  }
122
185
 
186
+ function readYamlFileSafe(filePath) {
187
+ if (!filePath || !fs.existsSync(filePath)) return null;
188
+ try {
189
+ const content = fs.readFileSync(filePath, 'utf8');
190
+ const parsed = yaml.load(content);
191
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
192
+ } catch (err) {
193
+ return null;
194
+ }
195
+ }
196
+
123
197
  function readTextFileSafe(filePath) {
124
198
  if (!filePath || !fs.existsSync(filePath)) return null;
125
199
  try {
@@ -135,6 +209,10 @@ function readNativeConfigSnapshot(spec) {
135
209
  }
136
210
 
137
211
  try {
212
+ if (spec.format === 'directory') {
213
+ return readNativeDirectorySnapshot(spec.path);
214
+ }
215
+
138
216
  const rawContent = fs.readFileSync(spec.path, 'utf8');
139
217
  if (spec.format === 'json') {
140
218
  try {
@@ -152,6 +230,22 @@ function readNativeConfigSnapshot(spec) {
152
230
  }
153
231
  }
154
232
 
233
+ if (spec.format === 'yaml') {
234
+ try {
235
+ return {
236
+ format: 'yaml',
237
+ fileName: path.basename(spec.path),
238
+ content: yaml.load(rawContent) || {}
239
+ };
240
+ } catch (err) {
241
+ return {
242
+ format: 'text',
243
+ fileName: path.basename(spec.path),
244
+ content: rawContent
245
+ };
246
+ }
247
+ }
248
+
155
249
  return {
156
250
  format: spec.format || 'text',
157
251
  fileName: path.basename(spec.path),
@@ -162,6 +256,61 @@ function readNativeConfigSnapshot(spec) {
162
256
  }
163
257
  }
164
258
 
259
+ function collectNativeDirectoryFiles(baseDir) {
260
+ const files = [];
261
+ const stack = [baseDir];
262
+
263
+ while (stack.length > 0) {
264
+ const currentDir = stack.pop();
265
+ let entries = [];
266
+ try {
267
+ entries = fs.readdirSync(currentDir, { withFileTypes: true });
268
+ } catch (err) {
269
+ continue;
270
+ }
271
+
272
+ for (const entry of entries) {
273
+ if (entry.isDirectory()) {
274
+ if (NATIVE_DIR_IGNORE_DIRS.has(entry.name)) {
275
+ continue;
276
+ }
277
+ stack.push(path.join(currentDir, entry.name));
278
+ } else if (entry.isFile()) {
279
+ if (NATIVE_DIR_IGNORE_FILES.has(entry.name)) {
280
+ continue;
281
+ }
282
+ const fullPath = path.join(currentDir, entry.name);
283
+ const relativePath = path.relative(baseDir, fullPath);
284
+ try {
285
+ const content = fs.readFileSync(fullPath);
286
+ files.push({
287
+ path: relativePath,
288
+ encoding: NATIVE_DIR_FILE_ENCODING,
289
+ content: content.toString(NATIVE_DIR_FILE_ENCODING)
290
+ });
291
+ } catch (err) {
292
+ continue;
293
+ }
294
+ }
295
+ }
296
+ }
297
+
298
+ files.sort((a, b) => a.path.localeCompare(b.path));
299
+ return files;
300
+ }
301
+
302
+ function readNativeDirectorySnapshot(dirPath) {
303
+ const files = collectNativeDirectoryFiles(dirPath);
304
+ if (files.length === 0) {
305
+ return null;
306
+ }
307
+ return {
308
+ format: 'directory',
309
+ fileName: path.basename(dirPath),
310
+ files
311
+ };
312
+ }
313
+
165
314
  function writeJsonFileAbsolute(filePath, data, overwrite, options = {}) {
166
315
  if (data === undefined) {
167
316
  return 'failed';
@@ -179,6 +328,18 @@ function writeJsonFileAbsolute(filePath, data, overwrite, options = {}) {
179
328
  return 'success';
180
329
  }
181
330
 
331
+ function writeYamlFileAbsolute(filePath, data, overwrite, options = {}) {
332
+ if (data === undefined) {
333
+ return 'failed';
334
+ }
335
+ return writeTextFileAbsolute(
336
+ filePath,
337
+ yaml.dump(data || {}, { lineWidth: 120, noRefs: true, sortKeys: false }),
338
+ overwrite,
339
+ options
340
+ );
341
+ }
342
+
182
343
  function writeTextFileAbsolute(filePath, content, overwrite, options = {}) {
183
344
  if (content === undefined || content === null) {
184
345
  return 'failed';
@@ -198,6 +359,9 @@ function writeTextFileAbsolute(filePath, content, overwrite, options = {}) {
198
359
 
199
360
  function writeNativeConfigAbsolute(spec, entry, overwrite) {
200
361
  if (!spec?.path || !entry || entry.content === undefined) {
362
+ if (spec?.format === 'directory' && entry?.files !== undefined) {
363
+ return writeNativeDirectoryAbsolute(spec.path, entry.files, overwrite);
364
+ }
201
365
  return 'failed';
202
366
  }
203
367
 
@@ -205,10 +369,56 @@ function writeNativeConfigAbsolute(spec, entry, overwrite) {
205
369
  if (format === 'json' && entry.content && typeof entry.content === 'object') {
206
370
  return writeJsonFileAbsolute(spec.path, entry.content, overwrite, { mode: spec.mode });
207
371
  }
372
+ if (format === 'yaml' && entry.content && typeof entry.content === 'object') {
373
+ return writeTextFileAbsolute(
374
+ spec.path,
375
+ yaml.dump(entry.content, { lineWidth: 120, noRefs: true, sortKeys: false }),
376
+ overwrite,
377
+ { mode: spec.mode }
378
+ );
379
+ }
208
380
 
209
381
  return writeTextFileAbsolute(spec.path, String(entry.content), overwrite, { mode: spec.mode });
210
382
  }
211
383
 
384
+ function writeNativeDirectoryAbsolute(dirPath, files = [], overwrite) {
385
+ if (!dirPath || !Array.isArray(files)) {
386
+ return 'failed';
387
+ }
388
+ if (files.length === 0) {
389
+ return 'skipped';
390
+ }
391
+ if (fs.existsSync(dirPath) && !overwrite) {
392
+ return 'skipped';
393
+ }
394
+ if (fs.existsSync(dirPath)) {
395
+ fs.rmSync(dirPath, { recursive: true, force: true });
396
+ }
397
+ ensureDir(dirPath);
398
+
399
+ let failed = false;
400
+ for (const file of files) {
401
+ const filePath = resolveSafePath(dirPath, file.path);
402
+ if (!filePath) {
403
+ failed = true;
404
+ break;
405
+ }
406
+ try {
407
+ ensureDir(path.dirname(filePath));
408
+ if (file.encoding === NATIVE_DIR_FILE_ENCODING || file.encoding === SKILL_FILE_ENCODING) {
409
+ fs.writeFileSync(filePath, Buffer.from(file.content || '', file.encoding));
410
+ } else {
411
+ fs.writeFileSync(filePath, file.content || '', file.encoding || 'utf8');
412
+ }
413
+ } catch (err) {
414
+ failed = true;
415
+ break;
416
+ }
417
+ }
418
+
419
+ return failed ? 'failed' : 'success';
420
+ }
421
+
212
422
  function getConfigFilePath() {
213
423
  try {
214
424
  const { getConfigFilePath: resolveConfigPath } = require('../../config/loader');
@@ -231,7 +441,7 @@ function buildExportReadme(exportData) {
231
441
  - 插件 (Plugins)
232
442
  - MCP 服务器配置
233
443
  - OAuth 凭证管理池
234
- - 各平台原生配置(Claude / Codex / Gemini / OpenCode)
444
+ - 各平台原生配置(Claude / Codex / Gemini / OpenCode / OMP)
235
445
  - UI 配置(主题、面板显示、排序等)
236
446
  - Prompts 预设
237
447
  - 安全配置
@@ -878,6 +1088,8 @@ function exportOpenCodePluginsByPlatform(service) {
878
1088
  pluginKind: plugin.pluginKind || 'plugin',
879
1089
  name: plugin.name,
880
1090
  directory: plugin.directory || plugin.name,
1091
+ installSource: plugin.installSource || plugin.source || plugin.name,
1092
+ resourceTypes: normalizeOmpResourceTypes(plugin.resourceTypes || plugin.resources),
881
1093
  version: plugin.version || 'latest',
882
1094
  description: plugin.description || '',
883
1095
  enabled: plugin.enabled !== false,
@@ -896,13 +1108,13 @@ function exportOpenCodePluginsByPlatform(service) {
896
1108
  return { plugins, control };
897
1109
  }
898
1110
 
899
- function exportPiPluginsByPlatform(service) {
1111
+ function exportOmpPluginsByPlatform(service) {
900
1112
  const plugins = (service.listPlugins().plugins || [])
901
1113
  .map((plugin) => {
902
- if (plugin.pluginType === 'package' || plugin.source === 'pi-settings') {
1114
+ if (plugin.pluginType === 'package' || plugin.source === 'omp-settings') {
903
1115
  return {
904
- platform: 'pi',
905
- type: 'pi-package',
1116
+ platform: 'omp',
1117
+ type: 'omp-package',
906
1118
  pluginType: 'package',
907
1119
  pluginKind: plugin.pluginKind || 'package',
908
1120
  name: plugin.name,
@@ -911,22 +1123,18 @@ function exportPiPluginsByPlatform(service) {
911
1123
  description: plugin.description || '',
912
1124
  enabled: plugin.enabled !== false,
913
1125
  installed: plugin.installed !== false,
914
- source: plugin.source || 'pi-settings'
1126
+ source: plugin.source || 'omp-settings'
915
1127
  };
916
1128
  }
917
- return buildManagedPluginExportItem(plugin, 'pi', PI_EXTENSIONS_DIR, {
918
- type: 'pi-extension',
1129
+ return buildManagedPluginExportItem(plugin, 'omp', OMP_EXTENSIONS_DIR, {
1130
+ type: 'omp-extension',
919
1131
  pluginType: plugin.pluginType || 'extension',
920
1132
  pluginKind: plugin.pluginKind || 'extension',
921
- manifestCandidates: ['pi.json', 'extension.json', 'plugin.json', 'package.json']
1133
+ manifestCandidates: ['omp.json', 'extension.json', 'plugin.json', 'package.json']
922
1134
  });
923
1135
  })
924
1136
  .filter(Boolean);
925
- const control = exportPluginControlSnapshot('pi', service);
926
- const piSettings = readNativeConfigSnapshot({ path: PI_SETTINGS_PATH, format: 'json' });
927
- if (piSettings) {
928
- control.nativeSettings = piSettings;
929
- }
1137
+ const control = exportPluginControlSnapshot('omp', service);
930
1138
  return { plugins, control };
931
1139
  }
932
1140
 
@@ -940,8 +1148,8 @@ function exportPluginsSnapshotByPlatform() {
940
1148
  result[platform] = exportCodexPluginsByPlatform(service);
941
1149
  } else if (platform === 'opencode') {
942
1150
  result[platform] = exportOpenCodePluginsByPlatform(service);
943
- } else if (platform === 'pi') {
944
- result[platform] = exportPiPluginsByPlatform(service);
1151
+ } else if (platform === 'omp') {
1152
+ result[platform] = exportOmpPluginsByPlatform(service);
945
1153
  } else {
946
1154
  result[platform] = {
947
1155
  plugins: service.listPlugins().plugins || [],
@@ -1307,33 +1515,68 @@ function writeOpenCodePluginConfig(packages = [], overwrite = true) {
1307
1515
  return writeJsonFileAbsolute(configPath, { ...existing, plugin: mergedPlugins }, true);
1308
1516
  }
1309
1517
 
1310
- function writePiPluginSettings(plugins = [], snapshot = {}, overwrite = true) {
1518
+ function writeOmpPluginSettings(plugins = [], snapshot = {}, overwrite = true) {
1311
1519
  const nativeSettings = snapshot.control?.nativeSettings;
1312
1520
  if (nativeSettings?.content !== undefined) {
1313
- return writeNativeConfigAbsolute({ path: PI_SETTINGS_PATH, format: 'json' }, nativeSettings, overwrite);
1521
+ return writeNativeConfigAbsolute({ path: OMP_SETTINGS_PATH, format: 'yaml' }, {
1522
+ ...nativeSettings,
1523
+ format: 'yaml'
1524
+ }, overwrite);
1314
1525
  }
1315
1526
 
1316
- const settings = readJsonFileSafe(PI_SETTINGS_PATH) || {};
1317
- if (fs.existsSync(PI_SETTINGS_PATH) && !overwrite) {
1527
+ const settings = readYamlFileSafe(OMP_SETTINGS_PATH) || {};
1528
+ if (fs.existsSync(OMP_SETTINGS_PATH) && !overwrite) {
1318
1529
  return 'skipped';
1319
1530
  }
1320
1531
  const existingPackages = Array.isArray(settings.packages) ? settings.packages : [];
1321
1532
  const existingDisabled = Array.isArray(settings.disabledPackages) ? settings.disabledPackages : [];
1322
- const packagePlugins = plugins.filter(plugin => plugin.type === 'pi-package' || plugin.pluginType === 'package');
1323
- const packages = packagePlugins.map(plugin => plugin.name).filter(Boolean);
1533
+ const packagePlugins = plugins.filter(plugin => plugin.type === 'omp-package' || plugin.pluginType === 'package');
1534
+ const packageIdentity = (item) => {
1535
+ if (typeof item === 'string') return item;
1536
+ if (!item || typeof item !== 'object') return '';
1537
+ return item.name || item.installSource || item.source || '';
1538
+ };
1539
+ const buildPackageEntry = (plugin) => {
1540
+ const name = plugin.name || plugin.directory || plugin.installSource || '';
1541
+ if (!name) return null;
1542
+ const installSource = plugin.installSource || plugin.packageSource || '';
1543
+ const resourceTypes = normalizeOmpResourceTypes(plugin.resourceTypes || plugin.resources);
1544
+ if (!installSource || installSource === name) {
1545
+ if (resourceTypes.length === 0 && !plugin.version && !plugin.description) return name;
1546
+ }
1547
+ return {
1548
+ name,
1549
+ ...(installSource ? { source: installSource, installSource } : {}),
1550
+ ...(plugin.version ? { version: plugin.version } : {}),
1551
+ ...(plugin.description ? { description: plugin.description } : {}),
1552
+ ...(resourceTypes.length > 0 ? { resourceTypes } : {})
1553
+ };
1554
+ };
1555
+ const mergePackageEntries = (current, additions) => {
1556
+ const result = [];
1557
+ const seen = new Set();
1558
+ for (const item of [...current, ...additions]) {
1559
+ const identity = packageIdentity(item);
1560
+ if (!identity || seen.has(identity)) continue;
1561
+ seen.add(identity);
1562
+ result.push(item);
1563
+ }
1564
+ return result;
1565
+ };
1566
+ const packages = packagePlugins.map(buildPackageEntry).filter(Boolean);
1324
1567
  const disabledPackages = packagePlugins
1325
1568
  .filter(plugin => plugin.enabled === false)
1326
- .map(plugin => plugin.name)
1569
+ .map(buildPackageEntry)
1327
1570
  .filter(Boolean);
1328
1571
 
1329
1572
  if (packages.length === 0 && disabledPackages.length === 0) {
1330
1573
  return 'skipped';
1331
1574
  }
1332
1575
 
1333
- return writeJsonFileAbsolute(PI_SETTINGS_PATH, {
1576
+ return writeYamlFileAbsolute(OMP_SETTINGS_PATH, {
1334
1577
  ...settings,
1335
- packages: Array.from(new Set([...existingPackages, ...packages])),
1336
- disabledPackages: Array.from(new Set([...existingDisabled, ...disabledPackages]))
1578
+ packages: mergePackageEntries(existingPackages, packages),
1579
+ disabledPackages: mergePackageEntries(existingDisabled, disabledPackages)
1337
1580
  }, true);
1338
1581
  }
1339
1582
 
@@ -1419,8 +1662,8 @@ function importPluginsByPlatformSnapshot(snapshotByPlatform = {}, legacyPlugins
1419
1662
  .map(plugin => plugin.name || plugin.directory)
1420
1663
  .filter(Boolean);
1421
1664
  applyImportStatus(results, writeOpenCodePluginConfig(packages, overwrite));
1422
- } else if (platform === 'pi' && (hasControl || plugins.length > 0)) {
1423
- applyImportStatus(results, writePiPluginSettings(plugins, snapshot, overwrite));
1665
+ } else if (platform === 'omp' && (hasControl || plugins.length > 0)) {
1666
+ applyImportStatus(results, writeOmpPluginSettings(plugins, snapshot, overwrite));
1424
1667
  }
1425
1668
 
1426
1669
  for (const plugin of plugins) {
@@ -1432,11 +1675,11 @@ function importPluginsByPlatformSnapshot(snapshotByPlatform = {}, legacyPlugins
1432
1675
  continue;
1433
1676
  }
1434
1677
  applyImportStatus(results, importPluginToDirectory(platform, plugin, getOpenCodePluginsDir(), overwrite));
1435
- } else if (platform === 'pi') {
1436
- if (plugin.type === 'pi-package' || plugin.pluginType === 'package') {
1678
+ } else if (platform === 'omp') {
1679
+ if (plugin.type === 'omp-package' || plugin.pluginType === 'package') {
1437
1680
  continue;
1438
1681
  }
1439
- applyImportStatus(results, importPluginToDirectory(platform, plugin, PI_EXTENSIONS_DIR, overwrite));
1682
+ applyImportStatus(results, importPluginToDirectory(platform, plugin, OMP_EXTENSIONS_DIR, overwrite));
1440
1683
  } else if (platform === 'claude') {
1441
1684
  const isLegacy = plugin.type === 'legacy';
1442
1685
  applyImportStatus(results, importPluginToDirectory(
@@ -1507,7 +1750,8 @@ function getAllChannelsByType() {
1507
1750
  const codex = codexChannelsService.getChannels()?.channels || [];
1508
1751
  const gemini = geminiChannelsService.getChannels()?.channels || [];
1509
1752
  const opencode = opencodeChannelsService.getChannels()?.channels || [];
1510
- return { claude, codex, gemini, opencode };
1753
+ const omp = ompChannelsService.getChannels()?.channels || [];
1754
+ return { claude, codex, gemini, opencode, omp };
1511
1755
  }
1512
1756
 
1513
1757
  /**
@@ -1718,7 +1962,8 @@ async function importConfigs(importData, options = {}) {
1718
1962
  : (Array.isArray(channels) ? channels : []),
1719
1963
  codex: hasTypedChannels && Array.isArray(channelsByType.codex) ? channelsByType.codex : [],
1720
1964
  gemini: hasTypedChannels && Array.isArray(channelsByType.gemini) ? channelsByType.gemini : [],
1721
- opencode: hasTypedChannels && Array.isArray(channelsByType.opencode) ? channelsByType.opencode : []
1965
+ opencode: hasTypedChannels && Array.isArray(channelsByType.opencode) ? channelsByType.opencode : [],
1966
+ omp: hasTypedChannels && Array.isArray(channelsByType.omp) ? channelsByType.omp: []
1722
1967
  };
1723
1968
 
1724
1969
  // 导入配置模板
@@ -1815,6 +2060,18 @@ async function importConfigs(importData, options = {}) {
1815
2060
  (channel.name && channel.baseUrl && c.name === channel.name && c.baseUrl === channel.baseUrl)
1816
2061
  ));
1817
2062
 
2063
+ importTypedChannels('omp', ompChannelsService, channel => {
2064
+ const { name, baseUrl, apiKey, ...extraConfig } = channel;
2065
+ ompChannelsService.createChannel(name, baseUrl, apiKey, extraConfig);
2066
+ }, (existingChannels, channel) => existingChannels.find(c =>
2067
+ (channel.id && c.id === channel.id) ||
2068
+ (channel.providerKey && c.providerKey === channel.providerKey) ||
2069
+ (channel.name && channel.baseUrl && c.name === channel.name && c.baseUrl === channel.baseUrl)
2070
+ ));
2071
+ if ((importChannelsByType.omp || []).length > 0 && typeof ompChannelsService.syncManagedProviderExtension === 'function') {
2072
+ ompChannelsService.syncManagedProviderExtension();
2073
+ }
2074
+
1818
2075
  // 导入工作区配置
1819
2076
  if (workspaces && overwrite) {
1820
2077
  try {
@@ -17,33 +17,32 @@ const REGISTRY_FILE = PATHS.configRegistry;
17
17
  const CONFIGS_DIR = PATHS.configs;
18
18
 
19
19
  // Claude Code native directories
20
- const CLAUDE_HOME_DIR = path.dirname(NATIVE_PATHS.claude.settings);
20
+ const CLAUDE_HOME_DIR = NATIVE_PATHS.claude.dir || path.dirname(NATIVE_PATHS.claude.settings);
21
21
  const CLAUDE_DIRS = {
22
- skills: path.join(CLAUDE_HOME_DIR, 'skills'),
23
- commands: path.join(CLAUDE_HOME_DIR, 'commands'),
24
- agents: path.join(CLAUDE_HOME_DIR, 'agents'),
25
- plugins: path.join(CLAUDE_HOME_DIR, 'plugins')
22
+ skills: NATIVE_PATHS.claude.skills || path.join(CLAUDE_HOME_DIR, 'skills'),
23
+ commands: NATIVE_PATHS.claude.commands || path.join(CLAUDE_HOME_DIR, 'commands'),
24
+ agents: NATIVE_PATHS.claude.agents || path.join(CLAUDE_HOME_DIR, 'agents'),
25
+ plugins: NATIVE_PATHS.claude.plugins || path.join(CLAUDE_HOME_DIR, 'plugins')
26
26
  };
27
27
 
28
28
  // Valid config types
29
29
  const CONFIG_TYPES = ['skills', 'commands', 'agents', 'plugins'];
30
- const SUPPORTED_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
30
+ const SUPPORTED_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode', 'omp'];
31
31
 
32
32
  const PLATFORM_SUPPORT = {
33
- skills: { claude: true, codex: true, gemini: true, opencode: true },
34
- commands: { claude: true, codex: true, gemini: true, opencode: true },
35
- agents: { claude: true, codex: true, gemini: true, opencode: true },
36
- plugins: { claude: true, codex: false, gemini: false, opencode: true }
33
+ skills: { claude: true, codex: true, gemini: true, opencode: true, omp: true },
34
+ commands: { claude: true, codex: true, gemini: true, opencode: true, omp: true },
35
+ agents: { claude: true, codex: true, gemini: true, opencode: true, omp: false },
36
+ plugins: { claude: true, codex: false, gemini: false, opencode: true, omp: true }
37
37
  };
38
38
 
39
39
  function normalizePlatforms(type, platforms = {}) {
40
40
  const support = PLATFORM_SUPPORT[type] || {};
41
- const normalized = {
42
- claude: !!platforms.claude,
43
- codex: !!platforms.codex,
44
- gemini: !!platforms.gemini,
45
- opencode: !!platforms.opencode
46
- };
41
+ const normalized = {};
42
+
43
+ for (const platform of SUPPORTED_PLATFORMS) {
44
+ normalized[platform] = !!platforms?.[platform];
45
+ }
47
46
 
48
47
  for (const platform of SUPPORTED_PLATFORMS) {
49
48
  if (support[platform] === false) {
@@ -585,7 +584,8 @@ class ConfigRegistryService {
585
584
  claude: 0,
586
585
  codex: 0,
587
586
  gemini: 0,
588
- opencode: 0
587
+ opencode: 0,
588
+ omp: 0
589
589
  }
590
590
  };
591
591
 
@@ -598,7 +598,8 @@ class ConfigRegistryService {
598
598
  claude: items.filter(i => i.platforms?.claude).length,
599
599
  codex: items.filter(i => i.platforms?.codex).length,
600
600
  gemini: items.filter(i => i.platforms?.gemini).length,
601
- opencode: items.filter(i => i.platforms?.opencode).length
601
+ opencode: items.filter(i => i.platforms?.opencode).length,
602
+ omp: items.filter(i => i.platforms?.omp).length
602
603
  };
603
604
 
604
605
  stats.byType[type] = typeStats;
@@ -607,6 +608,7 @@ class ConfigRegistryService {
607
608
  stats.byPlatform.codex += typeStats.codex;
608
609
  stats.byPlatform.gemini += typeStats.gemini;
609
610
  stats.byPlatform.opencode += typeStats.opencode;
611
+ stats.byPlatform.omp += typeStats.omp;
610
612
  }
611
613
 
612
614
  return stats;