codex-to-im 1.0.11 → 1.0.13

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.
@@ -4568,7 +4568,7 @@ var require_lib = __commonJS({
4568
4568
  import http from "node:http";
4569
4569
  import crypto4 from "node:crypto";
4570
4570
  import net from "node:net";
4571
- import os5 from "node:os";
4571
+ import os6 from "node:os";
4572
4572
  import fs9 from "node:fs";
4573
4573
 
4574
4574
  // src/config.ts
@@ -5563,6 +5563,9 @@ import path4 from "node:path";
5563
5563
  function formatChannelLabel(channelType) {
5564
5564
  return channelType === "weixin" ? "\u5FAE\u4FE1" : channelType === "feishu" ? "\u98DE\u4E66" : channelType;
5565
5565
  }
5566
+ function formatBindingChatTarget(binding) {
5567
+ return binding.chatDisplayName?.trim() || binding.chatId;
5568
+ }
5566
5569
  function findConflictingBinding(store, current, match) {
5567
5570
  return store.listChannelBindings().find((binding) => {
5568
5571
  if (binding.channelType === current.channelType && binding.chatId === current.chatId) {
@@ -5579,7 +5582,7 @@ function assertBindingTargetAvailable(store, current, opts) {
5579
5582
  );
5580
5583
  if (!conflict) return;
5581
5584
  throw new Error(
5582
- `\u8BE5\u4F1A\u8BDD\u5DF2\u7ED1\u5B9A\u5230 ${formatChannelLabel(conflict.channelType)} \u804A\u5929 ${conflict.chatId}\u3002\u4E00\u4E2A\u4F1A\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u4E2A\u804A\u5929\u3002`
5585
+ `\u8BE5\u4F1A\u8BDD\u5DF2\u7ED1\u5B9A\u5230 ${formatChannelLabel(conflict.channelType)} \u804A\u5929 ${formatBindingChatTarget(conflict)}\u3002\u4E00\u4E2A\u4F1A\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u4E2A\u804A\u5929\u3002`
5583
5586
  );
5584
5587
  }
5585
5588
  function getSessionName(session) {
@@ -5735,6 +5738,7 @@ function removeBinding(store, bindingId) {
5735
5738
 
5736
5739
  // src/service-manager.ts
5737
5740
  import fs4 from "node:fs";
5741
+ import os4 from "node:os";
5738
5742
  import path5 from "node:path";
5739
5743
  import { spawn } from "node:child_process";
5740
5744
  import { fileURLToPath } from "node:url";
@@ -5901,6 +5905,38 @@ function writeUiServerStatus(status) {
5901
5905
  ensureDirs();
5902
5906
  fs4.writeFileSync(uiStatusFile, JSON.stringify(status, null, 2), "utf-8");
5903
5907
  }
5908
+ async function installCodexIntegration() {
5909
+ const sourceSkill = path5.join(packageRoot, "SKILL.md");
5910
+ if (!fs4.existsSync(sourceSkill)) {
5911
+ throw new Error(`SKILL.md not found at ${sourceSkill}`);
5912
+ }
5913
+ const skillsDir = path5.join(os4.homedir(), ".codex", "skills");
5914
+ const targetDir = path5.join(skillsDir, "codex-to-im");
5915
+ fs4.mkdirSync(skillsDir, { recursive: true });
5916
+ if (fs4.existsSync(targetDir)) {
5917
+ return { targetDir, method: "existing" };
5918
+ }
5919
+ try {
5920
+ fs4.symlinkSync(packageRoot, targetDir, process.platform === "win32" ? "junction" : "dir");
5921
+ return { targetDir, method: "junction" };
5922
+ } catch {
5923
+ fs4.cpSync(packageRoot, targetDir, {
5924
+ recursive: true,
5925
+ filter: (source) => {
5926
+ const relative = path5.relative(packageRoot, source);
5927
+ if (!relative) return true;
5928
+ if (relative === ".git" || relative.startsWith(`.git${path5.sep}`)) return false;
5929
+ if (relative === "node_modules" || relative.startsWith(`node_modules${path5.sep}`)) return false;
5930
+ return true;
5931
+ }
5932
+ });
5933
+ return { targetDir, method: "copy" };
5934
+ }
5935
+ }
5936
+ function isCodexIntegrationInstalled() {
5937
+ const targetDir = path5.join(os4.homedir(), ".codex", "skills", "codex-to-im");
5938
+ return fs4.existsSync(path5.join(targetDir, "SKILL.md"));
5939
+ }
5904
5940
 
5905
5941
  // src/store.ts
5906
5942
  import fs5 from "node:fs";
@@ -6797,10 +6833,10 @@ if (isMainModule) {
6797
6833
 
6798
6834
  // src/codex-models.ts
6799
6835
  import fs8 from "node:fs";
6800
- import os4 from "node:os";
6836
+ import os5 from "node:os";
6801
6837
  import path9 from "node:path";
6802
- var DEFAULT_CODEX_CONFIG_PATH = path9.join(os4.homedir(), ".codex", "config.toml");
6803
- var DEFAULT_CODEX_MODELS_CACHE_PATH = path9.join(os4.homedir(), ".codex", "models_cache.json");
6838
+ var DEFAULT_CODEX_CONFIG_PATH = path9.join(os5.homedir(), ".codex", "config.toml");
6839
+ var DEFAULT_CODEX_MODELS_CACHE_PATH = path9.join(os5.homedir(), ".codex", "models_cache.json");
6804
6840
  function readConfiguredCodexModel(configPath = DEFAULT_CODEX_CONFIG_PATH) {
6805
6841
  try {
6806
6842
  const raw = fs8.readFileSync(configPath, "utf-8");
@@ -6991,7 +7027,7 @@ function isLocalRequest(request) {
6991
7027
  return isLoopbackAddress(getRemoteAddress(request));
6992
7028
  }
6993
7029
  function getLanUrls(currentPort) {
6994
- const interfaces = os5.networkInterfaces();
7030
+ const interfaces = os6.networkInterfaces();
6995
7031
  const urls = /* @__PURE__ */ new Set();
6996
7032
  for (const records of Object.values(interfaces)) {
6997
7033
  for (const record of records || []) {
@@ -8442,6 +8478,10 @@ function renderHtml() {
8442
8478
  <strong>Bridge</strong>
8443
8479
  <div class="status-value" id="bridgeStatus">-</div>
8444
8480
  </div>
8481
+ <div class="status-card">
8482
+ <strong>Codex Skill</strong>
8483
+ <div class="status-value" id="integrationStatus">-</div>
8484
+ </div>
8445
8485
  <div class="status-card">
8446
8486
  <strong>Runtime</strong>
8447
8487
  <div class="status-value" id="runtimeStatus">-</div>
@@ -8481,6 +8521,14 @@ function renderHtml() {
8481
8521
  <div class="notice">\u5DF2\u63A5\u901A\uFF1A\u4FDD\u5B58\u914D\u7F6E\u3001\u540E\u53F0\u542F\u505C\u3001\u98DE\u4E66\u51ED\u636E\u6D4B\u8BD5\u3001\u5FAE\u4FE1\u626B\u7801\u3001Codex \u8FDE\u63A5\u6D4B\u8BD5\u3001\u684C\u9762\u4F1A\u8BDD\u53D1\u73B0\u3001IM \u7ED1\u5B9A\u67E5\u770B\u4E0E\u7F51\u9875\u4FA7\u5207\u6362\u3002</div>
8482
8522
  </div>
8483
8523
 
8524
+ <div class="panel-block">
8525
+ <p class="panel-subtitle">\u53EF\u9009 Codex Skill</p>
8526
+ <div class="notice">bridge \u4E0D\u518D\u6CE8\u5165\u53D1\u9001\u9644\u4EF6\u7684\u63D0\u793A\u8BCD\u3002\u9700\u8981\u8BA9 Codex \u77E5\u9053\u201C\u53EF\u4EE5\u628A\u672C\u5730\u56FE\u7247/\u6587\u4EF6\u56DE\u53D1\u5230 IM\u201D\u65F6\uFF0C\u8BF7\u5B89\u88C5\u8FD9\u4E2A\u53EF\u9009 skill\u3002</div>
8527
+ <div class="actions" style="margin-top: 12px;">
8528
+ <button id="installIntegrationBtn">\u5B89\u88C5\u53EF\u9009 Codex Skill</button>
8529
+ </div>
8530
+ </div>
8531
+
8484
8532
  <div class="message" id="opsMessage"></div>
8485
8533
  </section>
8486
8534
 
@@ -9763,6 +9811,7 @@ function renderHtml() {
9763
9811
  fillForm(config);
9764
9812
  const runningChannelText = runningChannels().length ? ' \xB7 ' + runningChannels().join(', ') : '';
9765
9813
  document.getElementById('bridgeStatus').textContent = status.bridge.running ? 'Running' + runningChannelText : 'Stopped';
9814
+ document.getElementById('integrationStatus').textContent = status.codexIntegrationInstalled ? '\u5DF2\u5B89\u88C5' : '\u672A\u5B89\u88C5';
9766
9815
  document.getElementById('runtimeStatus').textContent = config.runtime || 'codex';
9767
9816
  document.getElementById('homeStatus').textContent = status.home;
9768
9817
  document.getElementById('overviewHomeStatus').textContent = status.home;
@@ -9997,6 +10046,16 @@ function renderHtml() {
9997
10046
  }
9998
10047
  });
9999
10048
 
10049
+ document.getElementById('installIntegrationBtn').addEventListener('click', async () => {
10050
+ try {
10051
+ const result = await api('/api/install-codex-integration', { method: 'POST' });
10052
+ showMessage('opsMessage', 'success', '\u53EF\u9009 Codex Skill \u5DF2\u5904\u7406\uFF1A' + result.method + ' -> ' + result.targetDir);
10053
+ await loadStatus();
10054
+ } catch (error) {
10055
+ showMessage('opsMessage', 'error', error.message);
10056
+ }
10057
+ });
10058
+
10000
10059
  document.getElementById('refreshLogsBtn').addEventListener('click', async () => {
10001
10060
  try {
10002
10061
  await loadLogs();
@@ -10192,6 +10251,7 @@ var server = http.createServer(async (request, response) => {
10192
10251
  uiAccess: buildUiAccessInfo(port, config, request),
10193
10252
  home: CTI_HOME,
10194
10253
  packageRoot: getPackageRoot(),
10254
+ codexIntegrationInstalled: isCodexIntegrationInstalled(),
10195
10255
  weixin: {
10196
10256
  linkedAccounts: getWeixinAccountsPayload()
10197
10257
  },
@@ -10243,6 +10303,11 @@ var server = http.createServer(async (request, response) => {
10243
10303
  json(response, 200, result);
10244
10304
  return;
10245
10305
  }
10306
+ if (request.method === "POST" && url.pathname === "/api/install-codex-integration") {
10307
+ const result = await installCodexIntegration();
10308
+ json(response, 200, result);
10309
+ return;
10310
+ }
10246
10311
  if (request.method === "POST" && url.pathname === "/api/bridge/start") {
10247
10312
  const status = await startBridge();
10248
10313
  json(response, 200, { ok: true, status });
@@ -26,6 +26,8 @@
26
26
  1. 发布一个已经构建好的 npm 包
27
27
  2. 在目标 Windows 主机全局安装
28
28
  3. 运行 `codex-to-im`
29
+
30
+ 可选:如果你希望 Codex 在不依赖 bridge 注入提示词的前提下,知道“可以把本地图片/文件回发到 IM”,可以在工作台里安装附带的 `codex-to-im` skill。
29
31
  4. 在浏览器里完成配置、测试和启动
30
32
 
31
33
  ## 2. 适用前提
package/package.json CHANGED
@@ -1,14 +1,16 @@
1
1
  {
2
2
  "name": "codex-to-im",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "Installable Codex-to-IM bridge with local setup UI and background service",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "files": [
8
8
  "dist/",
9
+ "agents/",
9
10
  "docs/",
10
11
  "references/",
11
12
  "scripts/",
13
+ "SKILL.md",
12
14
  "LICENSE",
13
15
  "README.md",
14
16
  "README_CN.md",
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Install the optional codex-to-im skill for Codex.
5
+ # Usage: bash scripts/install-codex.sh [--link]
6
+ # --link Create a symlink instead of copying (for development)
7
+
8
+ INTEGRATION_NAME="codex-to-im"
9
+ CODEX_SKILLS_DIR="$HOME/.codex/skills"
10
+ TARGET_DIR="$CODEX_SKILLS_DIR/$INTEGRATION_NAME"
11
+ SOURCE_DIR="$(cd "$(dirname "$0")/.." && pwd)"
12
+
13
+ echo "Installing optional $INTEGRATION_NAME skill for Codex..."
14
+
15
+ if [ ! -f "$SOURCE_DIR/SKILL.md" ]; then
16
+ echo "Error: SKILL.md not found in $SOURCE_DIR"
17
+ exit 1
18
+ fi
19
+
20
+ mkdir -p "$CODEX_SKILLS_DIR"
21
+
22
+ if [ -e "$TARGET_DIR" ]; then
23
+ if [ -L "$TARGET_DIR" ]; then
24
+ EXISTING=$(readlink "$TARGET_DIR")
25
+ echo "Already installed as symlink -> $EXISTING"
26
+ else
27
+ echo "Already installed at $TARGET_DIR"
28
+ fi
29
+ exit 0
30
+ fi
31
+
32
+ if [ "${1:-}" = "--link" ]; then
33
+ ln -s "$SOURCE_DIR" "$TARGET_DIR"
34
+ echo "Symlinked: $TARGET_DIR -> $SOURCE_DIR"
35
+ echo ""
36
+ echo "Development mode: no install/build/prune steps were run against the source repo."
37
+ exit 0
38
+ fi
39
+
40
+ cp -R "$SOURCE_DIR" "$TARGET_DIR"
41
+ echo "Copied to: $TARGET_DIR"
42
+
43
+ if [ ! -d "$TARGET_DIR/node_modules" ] || [ ! -d "$TARGET_DIR/node_modules/@openai/codex-sdk" ]; then
44
+ echo "Installing dependencies..."
45
+ (cd "$TARGET_DIR" && npm install)
46
+ fi
47
+
48
+ if [ ! -f "$TARGET_DIR/dist/daemon.mjs" ]; then
49
+ echo "Building daemon bundle..."
50
+ (cd "$TARGET_DIR" && npm run build)
51
+ fi
52
+
53
+ echo "Pruning dev dependencies..."
54
+ (cd "$TARGET_DIR" && npm prune --production)
55
+
56
+ echo ""
57
+ echo "Done. Start a new Codex session and use the installed codex-to-im skill when you need to send artifacts back to IM."