coding-tool-x 3.7.0 → 3.7.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.
Files changed (79) hide show
  1. package/README.md +10 -11
  2. package/dist/web/assets/{Analytics-Dma_j2Rq.js → Analytics-D5kLOhUo.js} +1 -1
  3. package/dist/web/assets/{ConfigTemplates-C49Y1PLH.js → ConfigTemplates-D_-16blp.js} +1 -1
  4. package/dist/web/assets/Home-DM_56Fyr.js +1 -0
  5. package/dist/web/assets/Home-Jom_ZcdY.css +1 -0
  6. package/dist/web/assets/{PluginManager-B1ECTHyY.js → PluginManager-Cv_rv8Dh.js} +1 -1
  7. package/dist/web/assets/{ProjectList-B_L55-l9.css → ProjectList-D3a0oRum.css} +1 -1
  8. package/dist/web/assets/ProjectList-H_m4Tx4E.js +1 -0
  9. package/dist/web/assets/{SessionList-CH5GwwiJ.js → SessionList-DcUQvsbR.js} +1 -1
  10. package/dist/web/assets/{SkillManager-CRyVhjYQ.js → SkillManager-CRav3m2a.js} +1 -1
  11. package/dist/web/assets/{WorkspaceManager-CO9tclVt.js → WorkspaceManager-B3f8R4EF.js} +1 -1
  12. package/dist/web/assets/index-Bnni1zPN.js +2 -0
  13. package/dist/web/assets/index-CfMuHG8u.css +1 -0
  14. package/dist/web/index.html +2 -2
  15. package/package.json +2 -1
  16. package/src/commands/channels.js +8 -8
  17. package/src/commands/daemon.js +4 -4
  18. package/src/commands/doctor.js +1 -1
  19. package/src/commands/logs.js +1 -3
  20. package/src/commands/port-config.js +4 -4
  21. package/src/commands/proxy-control.js +13 -13
  22. package/src/commands/stats.js +6 -6
  23. package/src/commands/toggle-proxy.js +8 -8
  24. package/src/commands/ui.js +1 -0
  25. package/src/config/default.js +3 -3
  26. package/src/config/paths.js +46 -49
  27. package/src/index.js +7 -7
  28. package/src/server/api/commands.js +1 -1
  29. package/src/server/api/config-export.js +2 -2
  30. package/src/server/api/config-registry.js +1 -1
  31. package/src/server/api/config.js +1 -1
  32. package/src/server/api/dashboard.js +16 -16
  33. package/src/server/api/mcp.js +2 -2
  34. package/src/server/api/oauth-credentials.js +15 -2
  35. package/src/server/api/{pi-channels.js → omp-channels.js} +115 -53
  36. package/src/server/api/{pi-config.js → omp-config.js} +13 -13
  37. package/src/server/api/{pi-projects.js → omp-projects.js} +5 -5
  38. package/src/server/api/{pi-proxy.js → omp-proxy.js} +18 -18
  39. package/src/server/api/{pi-sessions.js → omp-sessions.js} +21 -21
  40. package/src/server/api/{pi-statistics.js → omp-statistics.js} +1 -1
  41. package/src/server/api/plugins.js +1 -1
  42. package/src/server/api/skills.js +1 -1
  43. package/src/server/api/workspaces.js +1 -1
  44. package/src/server/index.js +23 -15
  45. package/src/server/{pi-proxy-server.js → omp-proxy-server.js} +13 -13
  46. package/src/server/services/channel-balance.js +3 -3
  47. package/src/server/services/channel-scheduler.js +4 -4
  48. package/src/server/services/commands-service.js +5 -5
  49. package/src/server/services/config-export-service.js +53 -53
  50. package/src/server/services/config-registry-service.js +8 -8
  51. package/src/server/services/config-sync-manager.js +26 -26
  52. package/src/server/services/config-templates-service.js +28 -28
  53. package/src/server/services/mcp-service.js +20 -20
  54. package/src/server/services/model-detector.js +15 -2
  55. package/src/server/services/native-oauth-adapters.js +212 -1
  56. package/src/server/services/network-access.js +5 -0
  57. package/src/server/services/notification-hooks.js +138 -6
  58. package/src/server/services/oauth-credentials-service.js +110 -3
  59. package/src/server/services/{pi-auth-providers.js → omp-auth-providers.js} +2 -2
  60. package/src/server/services/{pi-channels.js → omp-channels.js} +10 -5
  61. package/src/server/services/{pi-config.js → omp-config.js} +47 -65
  62. package/src/server/services/{pi-sessions.js → omp-sessions.js} +20 -20
  63. package/src/server/services/{pi-settings-manager.js → omp-settings-manager.js} +422 -18
  64. package/src/server/services/{pi-statistics-service.js → omp-statistics-service.js} +2 -2
  65. package/src/server/services/plugins-service.js +122 -122
  66. package/src/server/services/prompts-service.js +20 -20
  67. package/src/server/services/proxy-log-helper.js +1 -1
  68. package/src/server/services/proxy-runtime.js +1 -1
  69. package/src/server/services/skill-service.js +7 -7
  70. package/src/server/services/speed-test.js +29 -29
  71. package/src/server/services/ui-config.js +3 -3
  72. package/src/server/services/workspace-service.js +8 -8
  73. package/src/server/websocket-server.js +4 -4
  74. package/src/shared/platforms.js +201 -0
  75. package/dist/web/assets/Home-DGGC8H4g.js +0 -1
  76. package/dist/web/assets/Home-ewgeIhF7.css +0 -1
  77. package/dist/web/assets/ProjectList-DyiGyT-H.js +0 -1
  78. package/dist/web/assets/index-CM26UrnJ.css +0 -1
  79. package/dist/web/assets/index-EQq56pRa.js +0 -2
@@ -2,6 +2,7 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const os = require('os');
4
4
  const crypto = require('crypto');
5
+ const { spawnSync } = require('child_process');
5
6
  const toml = require('toml');
6
7
  const tomlStringify = require('@iarna/toml').stringify;
7
8
  const { NATIVE_PATHS, PATHS } = require('../../config/paths');
@@ -13,7 +14,7 @@ const { syncCodexUserEnvironment } = require('./codex-env-manager');
13
14
  const nativeKeychain = require('./native-keychain');
14
15
  const { maskToken, decodeJwtPayload, removeFileIfExists, sha256 } = require('./oauth-utils');
15
16
 
16
- const SUPPORTED_TOOLS = ['claude', 'codex', 'gemini', 'opencode'];
17
+ const SUPPORTED_TOOLS = ['claude', 'codex', 'gemini', 'opencode', 'omp'];
17
18
  const GEMINI_MAIN_ACCOUNT_KEY = 'main-account';
18
19
  const GEMINI_KEYCHAIN_SERVICE = 'gemini-cli-oauth';
19
20
  const CODEX_KEYCHAIN_SERVICE = 'Codex Auth';
@@ -1006,6 +1007,202 @@ function inspectOpenCodeState() {
1006
1007
  };
1007
1008
  }
1008
1009
 
1010
+ function resolveOmpRuntime() {
1011
+ try {
1012
+ const ompConfig = require('./omp-config');
1013
+ const runtime = ompConfig.resolveOmpRuntime(process.env, {});
1014
+ return runtime && runtime.runtime === 'omp' && runtime.installed ? runtime : null;
1015
+ } catch {
1016
+ return null;
1017
+ }
1018
+ }
1019
+
1020
+ function runOmpCommand(args = [], options = {}) {
1021
+ const runtime = options.runtime || resolveOmpRuntime();
1022
+ if (!runtime) {
1023
+ return { ok: false, status: 127, stdout: '', stderr: 'OMP CLI not installed' };
1024
+ }
1025
+
1026
+ const result = spawnSync(runtime.command, args, {
1027
+ encoding: 'utf8',
1028
+ env: {
1029
+ ...process.env,
1030
+ ...(options.env || {})
1031
+ },
1032
+ timeout: options.timeout || 10000
1033
+ });
1034
+ const status = result?.status === undefined || result?.status === null ? 0 : result.status;
1035
+ return {
1036
+ ok: !result?.error && status === 0,
1037
+ status,
1038
+ stdout: String(result?.stdout || ''),
1039
+ stderr: String(result?.stderr || ''),
1040
+ error: result?.error || null,
1041
+ command: runtime.command
1042
+ };
1043
+ }
1044
+
1045
+ function getOmpAuthSnapshot(options = {}) {
1046
+ try {
1047
+ const { getOmpAuthProviderSnapshot } = require('./omp-auth-providers');
1048
+ return getOmpAuthProviderSnapshot({
1049
+ accountCheck: true,
1050
+ includeStatus: false,
1051
+ ...options
1052
+ });
1053
+ } catch {
1054
+ return null;
1055
+ }
1056
+ }
1057
+
1058
+ function readAllOmpNativeOAuth() {
1059
+ const snapshot = getOmpAuthSnapshot({ forceRefresh: true });
1060
+ if (!snapshot?.available || !Array.isArray(snapshot.providers)) {
1061
+ return [];
1062
+ }
1063
+
1064
+ return snapshot.providers
1065
+ .filter(provider => provider?.loggedIn || Number(provider?.accountCount || 0) > 0)
1066
+ .flatMap((provider) => {
1067
+ const accounts = Array.isArray(provider.accounts) && provider.accounts.length > 0
1068
+ ? provider.accounts
1069
+ : [{ index: 1, identity: '' }];
1070
+ return accounts.map((account) => ({
1071
+ providerId: provider.id,
1072
+ accountId: String(account.index || '').trim(),
1073
+ accountEmail: String(account.identity || '').trim(),
1074
+ storage: 'auth-broker',
1075
+ primaryToken: ''
1076
+ }));
1077
+ });
1078
+ }
1079
+
1080
+ function readOmpNativeOAuth() {
1081
+ return readAllOmpNativeOAuth()[0] || null;
1082
+ }
1083
+
1084
+ function clearOmpOAuth() {
1085
+ const credentials = readAllOmpNativeOAuth();
1086
+ const providerIds = [...new Set(credentials.map(item => item.providerId).filter(Boolean))];
1087
+ providerIds.forEach((providerId) => {
1088
+ runOmpCommand(['auth-broker', 'logout', providerId], { timeout: 10000 });
1089
+ });
1090
+ try {
1091
+ require('./omp-auth-providers').clearOmpAuthProviderCache();
1092
+ } catch {
1093
+ // cache invalidation is best-effort
1094
+ }
1095
+ }
1096
+
1097
+ function disableOmpOAuthCredential(credential = {}) {
1098
+ const providerId = String(credential.providerId || '').trim();
1099
+ if (!providerId) {
1100
+ clearOmpOAuth();
1101
+ return;
1102
+ }
1103
+ runOmpCommand(['auth-broker', 'logout', providerId], { timeout: 10000 });
1104
+ try {
1105
+ require('./omp-auth-providers').clearOmpAuthProviderCache();
1106
+ } catch {
1107
+ // cache invalidation is best-effort
1108
+ }
1109
+ }
1110
+
1111
+ function buildOmpImportPayload(credential = {}) {
1112
+ if (credential.importPayload && typeof credential.importPayload === 'object') {
1113
+ return credential.importPayload;
1114
+ }
1115
+
1116
+ const access = String(credential.accessToken || credential.primaryToken || '').trim();
1117
+ if (!access) {
1118
+ return null;
1119
+ }
1120
+
1121
+ return {
1122
+ provider: credential.providerId,
1123
+ credential_type: credential.credentialType || 'oauth',
1124
+ identity_key: credential.identityKey || credential.accountId || credential.accountEmail || undefined,
1125
+ data: {
1126
+ access,
1127
+ refresh: credential.refreshToken || '',
1128
+ expires: credential.expiresAt || null,
1129
+ accountId: credential.accountId || undefined,
1130
+ accountEmail: credential.accountEmail || undefined
1131
+ }
1132
+ };
1133
+ }
1134
+
1135
+ function hasOmpNativeCredential(providerId) {
1136
+ return readAllOmpNativeOAuth().some((entry) => (
1137
+ !providerId || entry.providerId === providerId
1138
+ ));
1139
+ }
1140
+
1141
+ function applyOmpOAuth(credential = {}) {
1142
+ const providerId = String(credential.providerId || '').trim();
1143
+ if (!providerId) {
1144
+ throw new Error('OMP OAuth credential requires providerId');
1145
+ }
1146
+
1147
+ const payload = buildOmpImportPayload(credential);
1148
+ if (!payload) {
1149
+ if (hasOmpNativeCredential(providerId)) {
1150
+ return { storage: 'auth-broker-existing' };
1151
+ }
1152
+ throw new Error('OMP OAuth credential requires an import payload or access token');
1153
+ }
1154
+
1155
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ctx-omp-oauth-'));
1156
+ const tempPath = path.join(tempDir, `${providerId}.json`);
1157
+ try {
1158
+ fs.writeFileSync(tempPath, JSON.stringify(payload, null, 2), 'utf8');
1159
+ ensureFileMode(tempPath);
1160
+ const result = runOmpCommand(['auth-broker', 'import', tempPath, '--provider', providerId], { timeout: 15000 });
1161
+ if (!result.ok) {
1162
+ const detail = result.stderr.trim() || result.stdout.trim() || result.error?.message || `exit code ${result.status}`;
1163
+ throw new Error(`OMP auth-broker import failed: ${detail}`);
1164
+ }
1165
+ try {
1166
+ require('./omp-auth-providers').clearOmpAuthProviderCache();
1167
+ } catch {
1168
+ // cache invalidation is best-effort
1169
+ }
1170
+ return { storage: 'auth-broker' };
1171
+ } finally {
1172
+ try {
1173
+ removeFileIfExists(tempPath);
1174
+ fs.rmSync(tempDir, { recursive: true, force: true });
1175
+ } catch {
1176
+ // ignore cleanup failures
1177
+ }
1178
+ }
1179
+ }
1180
+
1181
+ function inspectOmpState() {
1182
+ const { getOmpProxyStatus } = require('../omp-proxy-server');
1183
+ const { isManagedOmpProvidersActive } = require('./omp-settings-manager');
1184
+ const proxyStatus = getOmpProxyStatus();
1185
+ const nativeCredentials = readAllOmpNativeOAuth();
1186
+
1187
+ let channelConfigured = false;
1188
+ try {
1189
+ channelConfigured = Boolean(isManagedOmpProvidersActive());
1190
+ } catch {
1191
+ channelConfigured = false;
1192
+ }
1193
+
1194
+ return {
1195
+ tool: 'omp',
1196
+ mode: proxyStatus.running || channelConfigured
1197
+ ? 'proxy'
1198
+ : (nativeCredentials.length > 0 ? 'oauth' : 'idle'),
1199
+ proxyRunning: Boolean(proxyStatus.running || channelConfigured),
1200
+ oauthPresent: nativeCredentials.length > 0,
1201
+ channelConfigured,
1202
+ nativeCredential: nativeCredentials[0] ? buildNativeSummary(nativeCredentials[0]) : null
1203
+ };
1204
+ }
1205
+
1009
1206
  function inspectTool(tool) {
1010
1207
  switch (tool) {
1011
1208
  case 'claude':
@@ -1016,6 +1213,8 @@ function inspectTool(tool) {
1016
1213
  return inspectGeminiState();
1017
1214
  case 'opencode':
1018
1215
  return inspectOpenCodeState();
1216
+ case 'omp':
1217
+ return inspectOmpState();
1019
1218
  default:
1020
1219
  throw new Error(`Unsupported OAuth tool: ${tool}`);
1021
1220
  }
@@ -1031,6 +1230,8 @@ function readNativeOAuth(tool) {
1031
1230
  return readGeminiNativeOAuth();
1032
1231
  case 'opencode':
1033
1232
  return readOpenCodeNativeOAuth();
1233
+ case 'omp':
1234
+ return readOmpNativeOAuth();
1034
1235
  default:
1035
1236
  throw new Error(`Unsupported OAuth tool: ${tool}`);
1036
1237
  }
@@ -1052,6 +1253,8 @@ function readAllNativeOAuth(tool) {
1052
1253
  }
1053
1254
  case 'opencode':
1054
1255
  return readAllOpenCodeNativeOAuth();
1256
+ case 'omp':
1257
+ return readAllOmpNativeOAuth();
1055
1258
  default:
1056
1259
  throw new Error(`Unsupported OAuth tool: ${tool}`);
1057
1260
  }
@@ -1071,6 +1274,9 @@ function clearNativeOAuth(tool) {
1071
1274
  case 'opencode':
1072
1275
  clearOpenCodeOAuth();
1073
1276
  return;
1277
+ case 'omp':
1278
+ clearOmpOAuth();
1279
+ return;
1074
1280
  default:
1075
1281
  throw new Error(`Unsupported OAuth tool: ${tool}`);
1076
1282
  }
@@ -1090,6 +1296,9 @@ function disableNativeOAuthCredential(tool, credential = {}) {
1090
1296
  case 'opencode':
1091
1297
  disableOpenCodeOAuthCredential(credential);
1092
1298
  return;
1299
+ case 'omp':
1300
+ disableOmpOAuthCredential(credential);
1301
+ return;
1093
1302
  default:
1094
1303
  throw new Error(`Unsupported OAuth tool: ${tool}`);
1095
1304
  }
@@ -1105,6 +1314,8 @@ function applyOAuthCredential(tool, credential) {
1105
1314
  return applyGeminiOAuth(credential);
1106
1315
  case 'opencode':
1107
1316
  return applyOpenCodeOAuth(credential);
1317
+ case 'omp':
1318
+ return applyOmpOAuth(credential);
1108
1319
  default:
1109
1320
  throw new Error(`Unsupported OAuth tool: ${tool}`);
1110
1321
  }
@@ -72,6 +72,10 @@ function createRemoteMutationGuard(options = {}) {
72
72
  };
73
73
  }
74
74
 
75
+ function isRemoteMutationAllowedByEnv(env = process.env) {
76
+ return String(env.CC_TOOL_ALLOW_REMOTE_WRITE || '').toLowerCase() !== 'false';
77
+ }
78
+
75
79
  function createRemoteRouteGuard(options = {}) {
76
80
  const enabled = options.enabled === true;
77
81
  const allowRemoteAccess = options.allowRemoteAccess === true;
@@ -113,6 +117,7 @@ module.exports = {
113
117
  isLoopbackRequest,
114
118
  isSameOriginRequest,
115
119
  createRemoteMutationGuard,
120
+ isRemoteMutationAllowedByEnv,
116
121
  createRemoteRouteGuard,
117
122
  createSameOriginGuard
118
123
  };
@@ -13,6 +13,7 @@ const geminiSettingsManager = require('./gemini-settings-manager');
13
13
 
14
14
  const MANAGED_HOOK_NAME = 'coding-tool-notify';
15
15
  const MANAGED_OPENCODE_PLUGIN_FILE = 'coding-tool-notify.js';
16
+ const MANAGED_OMP_EXTENSION_FILE = 'coding-tool-notify.ts';
16
17
  const REMOTE_PROVIDER_TYPES = [
17
18
  'wechatBot',
18
19
  'qqBot',
@@ -371,6 +372,13 @@ function getOpenCodeManagedPluginPath() {
371
372
  return path.join(NATIVE_PATHS.opencode.config, 'plugins', MANAGED_OPENCODE_PLUGIN_FILE);
372
373
  }
373
374
 
375
+ function getOmpManagedExtensionPath() {
376
+ const ompPaths = NATIVE_PATHS.omp || {};
377
+ const extensionsDir = ompPaths.extensions ||
378
+ path.join(ompPaths.dir || path.dirname(ompPaths.settings || PATHS.notifyHook), 'extensions');
379
+ return path.join(extensionsDir, MANAGED_OMP_EXTENSION_FILE);
380
+ }
381
+
374
382
  function buildOpenCodePluginContent(type) {
375
383
  const mode = normalizeType(type);
376
384
  return `// Managed by Coding Tool. Do not edit manually.
@@ -410,6 +418,64 @@ export const CodingToolNotifyPlugin = async () => ({
410
418
  `;
411
419
  }
412
420
 
421
+ function buildOmpExtensionContent(type) {
422
+ const mode = normalizeType(type);
423
+ return `// Managed by Coding Tool. Do not edit manually.
424
+ // mode:${mode}
425
+ import { spawn } from 'node:child_process'
426
+
427
+ const SCRIPT_PATH = ${JSON.stringify(PATHS.notifyHook)}
428
+ const MODE = ${JSON.stringify(mode)}
429
+ let lastFireAt = 0
430
+
431
+ function shouldFire() {
432
+ const now = Date.now()
433
+ if (now - lastFireAt < 1000) {
434
+ return false
435
+ }
436
+ lastFireAt = now
437
+ return true
438
+ }
439
+
440
+ function fire(eventType) {
441
+ if (!shouldFire()) {
442
+ return
443
+ }
444
+
445
+ try {
446
+ const child = spawn('node', [
447
+ SCRIPT_PATH,
448
+ '--source=omp',
449
+ \`--mode=\${MODE}\`,
450
+ \`--cc-notify-type=\${MODE}\`,
451
+ \`--event-type=\${eventType}\`
452
+ ], {
453
+ detached: true,
454
+ stdio: 'ignore',
455
+ windowsHide: true
456
+ })
457
+ child.unref()
458
+ } catch (error) {
459
+ // Ignore notification failures.
460
+ }
461
+ }
462
+
463
+ export default async function CodingToolNotifyExtension(omp) {
464
+ const register = (eventName) => {
465
+ if (typeof omp?.on !== 'function') {
466
+ return
467
+ }
468
+ omp.on(eventName, async () => {
469
+ fire(eventName)
470
+ })
471
+ }
472
+
473
+ register('agent_settled')
474
+ register('turn_end')
475
+ }
476
+ `;
477
+ }
478
+
413
479
  function escapeForAppleScript(value) {
414
480
  return String(value || '').replace(/\\/g, '\\\\').replace(/"/g, '\\"');
415
481
  }
@@ -712,6 +778,10 @@ function resolveMessage(source, eventType, payload) {
712
778
  return 'OpenCode 响应已完成 | 等待交互'
713
779
  }
714
780
 
781
+ if (source === 'omp') {
782
+ return 'OMP 回合已完成 | 等待交互'
783
+ }
784
+
715
785
  return 'Claude Code 任务已完成 | 等待交互'
716
786
  }
717
787
 
@@ -886,12 +956,17 @@ function sendTelegramMessage(config = {}, ctx, send = postJson) {
886
956
  return send(url, payload, {}, agent ? { agent } : {})
887
957
  }
888
958
 
959
+ function resolveDisplaySource(source) {
960
+ if (source === 'omp') return 'OMP'
961
+ return source
962
+ }
963
+
889
964
  function buildNotificationContext(message, source, eventType) {
890
965
  const timestamp = new Date().toLocaleString('zh-CN')
891
966
  return {
892
967
  title: 'Coding Tool - 通知',
893
968
  message,
894
- source,
969
+ source: resolveDisplaySource(source),
895
970
  eventType,
896
971
  timestamp,
897
972
  hostname: os.hostname()
@@ -1545,6 +1620,24 @@ function parseOpenCodeNotificationStatus(content = '') {
1545
1620
  };
1546
1621
  }
1547
1622
 
1623
+ function parseOmpNotificationStatus(content = '') {
1624
+ if (!content) {
1625
+ return {
1626
+ enabled: false,
1627
+ external: false,
1628
+ type: 'notification',
1629
+ method: 'Extension Events'
1630
+ };
1631
+ }
1632
+
1633
+ return {
1634
+ enabled: true,
1635
+ external: false,
1636
+ type: parseManagedType(content) || 'notification',
1637
+ method: 'Extension Events'
1638
+ };
1639
+ }
1640
+
1548
1641
  function getOpenCodeHookStatus() {
1549
1642
  const pluginPath = getOpenCodeManagedPluginPath();
1550
1643
  if (!fs.existsSync(pluginPath)) {
@@ -1554,6 +1647,15 @@ function getOpenCodeHookStatus() {
1554
1647
  return parseOpenCodeNotificationStatus(fs.readFileSync(pluginPath, 'utf8'));
1555
1648
  }
1556
1649
 
1650
+ function getOmpHookStatus() {
1651
+ const extensionPath = getOmpManagedExtensionPath();
1652
+ if (!fs.existsSync(extensionPath)) {
1653
+ return parseOmpNotificationStatus('');
1654
+ }
1655
+
1656
+ return parseOmpNotificationStatus(fs.readFileSync(extensionPath, 'utf8'));
1657
+ }
1658
+
1557
1659
  function saveOpenCodeHook(enabled, type) {
1558
1660
  const pluginPath = getOpenCodeManagedPluginPath();
1559
1661
  const opencodeSettingsManager = require('./opencode-settings-manager');
@@ -1608,6 +1710,20 @@ function saveOpenCodeHook(enabled, type) {
1608
1710
  }
1609
1711
  }
1610
1712
 
1713
+ function saveOmpHook(enabled, type) {
1714
+ const extensionPath = getOmpManagedExtensionPath();
1715
+
1716
+ if (!enabled) {
1717
+ if (fs.existsSync(extensionPath)) {
1718
+ fs.unlinkSync(extensionPath);
1719
+ }
1720
+ return;
1721
+ }
1722
+
1723
+ ensureParentDir(extensionPath);
1724
+ fs.writeFileSync(extensionPath, buildOmpExtensionContent(type), 'utf8');
1725
+ }
1726
+
1611
1727
  function getNotificationSettings() {
1612
1728
  const uiConfig = loadUIConfig();
1613
1729
  const remoteNotifications = getRemoteNotificationsConfig(uiConfig);
@@ -1623,7 +1739,8 @@ function getNotificationSettings() {
1623
1739
  claude: getClaudeHookStatus(),
1624
1740
  codex: getCodexHookStatus(),
1625
1741
  gemini: getGeminiHookStatus(),
1626
- opencode: getOpenCodeHookStatus()
1742
+ opencode: getOpenCodeHookStatus(),
1743
+ omp: getOmpHookStatus()
1627
1744
  }
1628
1745
  };
1629
1746
  }
@@ -1643,6 +1760,8 @@ function resolveBrowserNotificationTitle(source = 'claude') {
1643
1760
  return 'Gemini CLI';
1644
1761
  case 'opencode':
1645
1762
  return 'OpenCode';
1763
+ case 'omp':
1764
+ return 'OMP';
1646
1765
  default:
1647
1766
  return 'Claude Code';
1648
1767
  }
@@ -1656,6 +1775,8 @@ function resolveBrowserNotificationUrl(source = 'claude') {
1656
1775
  return '/gemini';
1657
1776
  case 'opencode':
1658
1777
  return '/opencode';
1778
+ case 'omp':
1779
+ return '/omp';
1659
1780
  default:
1660
1781
  return '/claude';
1661
1782
  }
@@ -1694,7 +1815,8 @@ function saveNotificationSettings(input = {}) {
1694
1815
  claude: normalizePlatformInput(input?.platforms?.claude),
1695
1816
  codex: normalizePlatformInput(input?.platforms?.codex),
1696
1817
  gemini: normalizePlatformInput(input?.platforms?.gemini),
1697
- opencode: normalizePlatformInput(input?.platforms?.opencode)
1818
+ opencode: normalizePlatformInput(input?.platforms?.opencode),
1819
+ omp: normalizePlatformInput(input?.platforms?.omp)
1698
1820
  };
1699
1821
 
1700
1822
  saveNotificationUiConfig(remoteNotifications, platforms.claude.enabled);
@@ -1708,6 +1830,7 @@ function saveNotificationSettings(input = {}) {
1708
1830
  saveCodexHook(platforms.codex.enabled, platforms.codex.type);
1709
1831
  saveGeminiHook(platforms.gemini.enabled, platforms.gemini.type);
1710
1832
  saveOpenCodeHook(platforms.opencode.enabled, platforms.opencode.type);
1833
+ saveOmpHook(platforms.omp.enabled, platforms.omp.type);
1711
1834
 
1712
1835
  if (!hasManagedPlatform) {
1713
1836
  removeNotifyScript();
@@ -1834,7 +1957,8 @@ function buildLegacyClaudeSaveInput(input = {}, currentSettings = getNotificatio
1834
1957
  : { enabled: false, type: 'notification' },
1835
1958
  codex: normalizeSavedPlatformStatus(currentSettings?.platforms?.codex),
1836
1959
  gemini: normalizeSavedPlatformStatus(currentSettings?.platforms?.gemini),
1837
- opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode)
1960
+ opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode),
1961
+ omp: normalizeSavedPlatformStatus(currentSettings?.platforms?.omp)
1838
1962
  },
1839
1963
  remoteNotifications: currentSettings?.remoteNotifications || { providers: [] }
1840
1964
  };
@@ -1873,7 +1997,8 @@ function initDefaultHooks() {
1873
1997
  claude: { enabled: true, type: currentStatus.type || 'notification' },
1874
1998
  codex: normalizeSavedPlatformStatus(currentSettings?.platforms?.codex),
1875
1999
  gemini: normalizeSavedPlatformStatus(currentSettings?.platforms?.gemini),
1876
- opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode)
2000
+ opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode),
2001
+ omp: normalizeSavedPlatformStatus(currentSettings?.platforms?.omp)
1877
2002
  },
1878
2003
  remoteNotifications: currentSettings?.remoteNotifications || { providers: [] }
1879
2004
  });
@@ -1890,7 +2015,8 @@ function initDefaultHooks() {
1890
2015
  claude: { enabled: true, type: 'notification' },
1891
2016
  codex: normalizeSavedPlatformStatus(currentSettings?.platforms?.codex),
1892
2017
  gemini: normalizeSavedPlatformStatus(currentSettings?.platforms?.gemini),
1893
- opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode)
2018
+ opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode),
2019
+ omp: normalizeSavedPlatformStatus(currentSettings?.platforms?.omp)
1894
2020
  },
1895
2021
  remoteNotifications: currentSettings?.remoteNotifications || { providers: [] }
1896
2022
  });
@@ -2277,7 +2403,9 @@ module.exports = {
2277
2403
  initDefaultHooks,
2278
2404
  syncManagedNotificationAssets,
2279
2405
  getOpenCodeManagedPluginPath,
2406
+ getOmpManagedExtensionPath,
2280
2407
  buildOpenCodePluginContent,
2408
+ buildOmpExtensionContent,
2281
2409
  buildCodexNotifyCommand,
2282
2410
  writeNotifyScript,
2283
2411
  generateNotifyScript,
@@ -2292,9 +2420,11 @@ module.exports = {
2292
2420
  getCodexHookStatus,
2293
2421
  getGeminiHookStatus,
2294
2422
  getOpenCodeHookStatus,
2423
+ getOmpHookStatus,
2295
2424
  parseCodexNotificationStatus,
2296
2425
  parseGeminiNotificationStatus,
2297
2426
  parseOpenCodeNotificationStatus,
2427
+ parseOmpNotificationStatus,
2298
2428
  validateFeishuWebhookUrl,
2299
2429
  normalizeRemoteNotificationsConfig,
2300
2430
  validateRemoteProviderConfig,
@@ -2303,7 +2433,9 @@ module.exports = {
2303
2433
  buildStopHookCommand,
2304
2434
  buildClaudeCommand,
2305
2435
  buildOpenCodePluginContent,
2436
+ buildOmpExtensionContent,
2306
2437
  getOpenCodeManagedPluginPath,
2438
+ getOmpManagedExtensionPath,
2307
2439
  generateNotifyScript,
2308
2440
  generateSystemNotificationCommand,
2309
2441
  emitBrowserNotification,