@hellolynk-ai/codex-plugin 0.2.101 → 0.2.102-macos.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 (46) hide show
  1. package/README.md +17 -17
  2. package/bin/hellolynk-client-setup.mjs +72 -11
  3. package/hellolynk-client-pack-manifest.json +109 -69
  4. package/hellolynk-client-pack-pin.json +1 -1
  5. package/package.json +8 -4
  6. package/plugins/hellolynk-codex/.codex-plugin/plugin.json +1 -1
  7. package/plugins/hellolynk-codex/.mcp.json +20 -19
  8. package/plugins/hellolynk-codex/package.json +1 -1
  9. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-account.mjs +1 -1
  10. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-profile.mjs +25 -2
  11. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-darwin-host.mjs +256 -0
  12. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-darwin-vault.mjs +153 -0
  13. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-host-protected-store.mjs +20 -8
  14. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-neutral-host-installation-store.mjs +3 -7
  15. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-platform-credential-store.mjs +68 -14
  16. package/plugins/hellolynk-codex/runtime/bootstrap/platform/darwin/hellolynk-keychain-helper.bin +0 -0
  17. package/plugins/hellolynk-codex/runtime/bootstrap/platform/darwin/hellolynk-keychain-helper.swift +149 -0
  18. package/plugins/hellolynk-codex/runtime/connect-activate.mjs +5 -1
  19. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-account.mjs +1 -1
  20. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-profile.mjs +25 -2
  21. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-entry.mjs +7 -3
  22. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-runtime.mjs +24 -8
  23. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update.mjs +23 -9
  24. package/plugins/hellolynk-codex/runtime/core/hellolynk-darwin-host.mjs +256 -0
  25. package/plugins/hellolynk-codex/runtime/core/hellolynk-darwin-vault.mjs +153 -0
  26. package/plugins/hellolynk-codex/runtime/core/hellolynk-host-protected-store.mjs +20 -8
  27. package/plugins/hellolynk-codex/runtime/core/hellolynk-human-confirmation-ledger.mjs +53 -6
  28. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-data-protection.mjs +22 -7
  29. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-human-confirmation-broker.mjs +7 -5
  30. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-installation-store.mjs +3 -7
  31. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-launcher.mjs +9 -3
  32. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-process.mjs +1 -1
  33. package/plugins/hellolynk-codex/runtime/core/hellolynk-platform-credential-store.mjs +68 -14
  34. package/plugins/hellolynk-codex/runtime/core/hellolynk-release-retention.mjs +78 -2
  35. package/plugins/hellolynk-codex/runtime/core/hellolynk-workbench-connection.mjs +5 -3
  36. package/plugins/hellolynk-codex/runtime/neutral-host-release-entry.mjs +1 -1
  37. package/plugins/hellolynk-codex/runtime/neutral-host-release-loader.mjs +1 -1
  38. package/plugins/hellolynk-codex/runtime/neutral-host-release-manifest.json +58 -26
  39. package/plugins/hellolynk-codex/runtime/neutral-host-release-pin.json +1 -1
  40. package/plugins/hellolynk-codex/runtime/platform/darwin/hellolynk-keychain-helper.bin +0 -0
  41. package/plugins/hellolynk-codex/runtime/platform/darwin/hellolynk-keychain-helper.swift +149 -0
  42. package/plugins/hellolynk-codex/server/codex-neutral-production.mjs +15 -9
  43. package/plugins/hellolynk-codex/server/mcp-server.mjs +1193 -1193
  44. package/plugins/hellolynk-codex/server/native-conversation-directory.mjs +33 -33
  45. package/plugins/hellolynk-codex/server/workbench-runtime.mjs +42 -42
  46. package/tools/hellolynk-activate-neutral-host-clients.mjs +5 -1
package/README.md CHANGED
@@ -35,35 +35,35 @@
35
35
 
36
36
  Codex、OpenClaw、TRAE Work、WorkBuddy。装哪个客户端,就选哪个包。同一个账号下,它们共用你的灵信号。
37
37
 
38
- 源码见 [Codex 客户端源码](https://github.com/haoyun18881-beep/HelloLynk/tree/main/clients/codex),完整介绍见 [HelloLynk on GitHub](https://github.com/haoyun18881-beep/HelloLynk)。
38
+ 源码见 [Codex 客户端源码](https://github.com/haoyun18881-beep/HelloLynk/tree/main/clients/codex-macos),完整介绍见 [HelloLynk on GitHub](https://github.com/haoyun18881-beep/HelloLynk)。
39
39
 
40
40
  ## 前提
41
41
 
42
- Windows 10/11、Node.js 22.16 或更高版本、PowerShell 7,以及打开过一次的 Codex。
42
+ Apple 芯片、macOS 13 或更高版本、Node.js 22.16 或更高版本,以及安装在「应用程序」中的 ChatGPT(已打开过 Codex)。
43
43
 
44
44
  ## 安装
45
45
 
46
46
  1. 打开 [HelloLynk 官网(Official site)](https://hellolynk.com)。新用户注册账号;已有账号登录后,查看自己的公开灵信号。
47
47
  2. 两种装法任选一种。
48
48
 
49
- 用安装包,在官网或 [GitHub Release](https://github.com/haoyun18881-beep/HelloLynk/releases) 下载 Codex 安装包,完整解压,在包含 `package` 文件夹的目录打开 PowerShell,运行:
49
+ 推荐用 npm,在终端运行:
50
50
 
51
- ```powershell
52
- node .\package\bin\hellolynk-client-setup.mjs init
51
+ ```sh
52
+ npm install -g @hellolynk-ai/codex-plugin@macos
53
+ hellolynk-codex init
53
54
  ```
54
55
 
55
- 用 npm,在 PowerShell 运行:
56
+ 用安装包,在官网或 [GitHub Release](https://github.com/haoyun18881-beep/HelloLynk/releases) 下载 Codex for Mac 安装包,完整解压,在包含 `package` 文件夹的目录打开 终端,运行:
56
57
 
57
- ```powershell
58
- npm install -g @hellolynk-ai/codex-plugin
59
- hellolynk-codex init
58
+ ```sh
59
+ node ./package/bin/hellolynk-client-setup.mjs init
60
60
  ```
61
61
 
62
62
  3. 完全退出并重新打开 Codex。
63
63
 
64
64
  可以把本说明交给你的本地 AI,请它帮你安装;用安装包时,把解压目录也告诉它。
65
65
 
66
- 下文命令按安装包写法给出。用 npm 装的,把 `node .\package\bin\hellolynk-client-setup.mjs` 换成 `hellolynk-codex`,在任意目录运行。
66
+ 下文命令按安装包写法给出。用 npm 装的,把 `node ./package/bin/hellolynk-client-setup.mjs` 换成 `hellolynk-codex`,在任意目录运行。
67
67
 
68
68
  ## 首次连接
69
69
 
@@ -73,8 +73,8 @@ Windows 10/11、Node.js 22.16 或更高版本、PowerShell 7,以及打开过
73
73
 
74
74
  也可以让 AI 用命令连接,把示例换成自己的公开灵信号:
75
75
 
76
- ```powershell
77
- node .\package\bin\hellolynk-client-setup.mjs connect --hellolynk-id hellolynk:intl:your-name
76
+ ```sh
77
+ node ./package/bin/hellolynk-client-setup.mjs connect --hellolynk-id hellolynk:intl:your-name
78
78
  ```
79
79
 
80
80
  首次连接这台电脑时,命令会给出官网确认链接。AI 把链接给用户;用户确认后,AI 继续执行同一条连接命令。
@@ -83,20 +83,20 @@ node .\package\bin\hellolynk-client-setup.mjs connect --hellolynk-id hellolynk:i
83
83
 
84
84
  插件会自动更新,不用操作。要手动更新,下载新版安装包,在包含 `package` 文件夹的目录运行:
85
85
 
86
- ```powershell
87
- node .\package\bin\hellolynk-client-setup.mjs setup
86
+ ```sh
87
+ node ./package/bin/hellolynk-client-setup.mjs setup
88
88
  ```
89
89
 
90
- 用 npm 装的,先运行 `npm install -g @hellolynk-ai/codex-plugin` 装上新版,再运行 `hellolynk-codex setup`。
90
+ 用 npm 装的,先运行 `npm install -g @hellolynk-ai/codex-plugin@macos` 装上新版,再运行 `hellolynk-codex setup`。
91
91
 
92
92
  ## 卸载
93
93
 
94
- 在包含 `package` 文件夹的目录运行 `node .\package\bin\hellolynk-client-setup.mjs uninstall`。账号、联系人和聊天记录留在这台电脑上,重新安装后不用重新连接。
94
+ 在包含 `package` 文件夹的目录运行 `node ./package/bin/hellolynk-client-setup.mjs uninstall`。账号、联系人和聊天记录留在这台电脑上,重新安装后不用重新连接。
95
95
 
96
96
  用 npm 装的,运行 `hellolynk-codex uninstall`,再运行 `npm uninstall -g @hellolynk-ai/codex-plugin`。
97
97
 
98
98
  ## 求助
99
99
 
100
- 运行 `node .\package\bin\hellolynk-client-setup.mjs doctor` 检查安装,把结果和错误码发到 [haoyun18881@gmail.com](mailto:haoyun18881@gmail.com)。
100
+ 运行 `node ./package/bin/hellolynk-client-setup.mjs doctor` 检查安装,把结果和错误码发到 [haoyun18881@gmail.com](mailto:haoyun18881@gmail.com)。
101
101
 
102
102
  许可:PolyForm Shield License 1.0.0,只覆盖本包内的客户端源码,完整条款见 LICENSE。
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import crypto from "node:crypto";
4
- import { lstatSync, realpathSync, writeSync } from "node:fs";
4
+ import { constants, lstatSync, realpathSync, writeSync } from "node:fs";
5
5
  import fs from "node:fs/promises";
6
6
  import os from "node:os";
7
7
  import path from "node:path";
@@ -21,6 +21,8 @@ const CHINESE = /^zh\b/i.test(Intl.DateTimeFormat().resolvedOptions().locale);
21
21
  const CLIENT_NAMES = Object.freeze({ codex: "Codex", openclaw: "OpenClaw", "trae-work": "TRAE Work",
22
22
  workbuddy: "WorkBuddy", cursor: "Cursor", dsh: "DSH" });
23
23
  const { packageJson, metadata } = await readPackage();
24
+ const macPackage = metadata.client === "codex" && packageJson.os?.[0] === "darwin"
25
+ && packageJson.cpu?.[0] === "arm64";
24
26
  const commandArguments = process.argv.slice(2).filter((value) => value !== "--json");
25
27
  const command = String(commandArguments.shift() || "help").toLowerCase();
26
28
  const waitForPending = command === "--wait-for-pending";
@@ -61,8 +63,14 @@ try {
61
63
  "--expected-manifest-sha256", sourceRelease.outerManifestSha256,
62
64
  ]);
63
65
  } else {
64
- const originalLocations = resolveLocations();
65
66
  const accountModule = await importStableCore(PACKAGE_ROOT, "hellolynk-client-account.mjs", "hellolynk_npm_account_runtime_invalid");
67
+ const originalLocations = resolveLocations(accountModule.clientAccountBaseRoot);
68
+ if (macPackage && command !== "uninstall") {
69
+ const config = await readJson(path.join(sourceReleaseRoot, ".mcp.json"), "hellolynk_npm_release_invalid", true);
70
+ const commandPath = config.mcpServers?.["hellolynk-codex"]?.command;
71
+ if (typeof commandPath !== "string" || !path.isAbsolute(commandPath)) fail("hellolynk_npm_release_invalid");
72
+ await fs.access(commandPath, constants.X_OK).catch(() => fail("hellolynk_npm_codex_mac_node_required"));
73
+ }
66
74
  const baseRoot = accountModule.clientAccountBaseRoot(originalLocations.hostRoot);
67
75
  const packageLocations = { ...originalLocations, hostRoot: baseRoot, finalCurrentRoot: path.dirname(baseRoot) };
68
76
  const accounts = accountModule.createClientAccountActions({ client: metadata.client, storeRoot: baseRoot });
@@ -429,7 +437,8 @@ async function uninstallClient(locations) {
429
437
  if (receiverConfigured) await receiver.configureTraeNativeTaskWorker({ storeRoot: locations.hostRoot, enabled: false });
430
438
  }
431
439
  // A binding names the release it starts, so it cannot outlive the releases removed below.
432
- if (await exists(path.join(locations.hostRoot, "capabilities-v1.dpapi.clixml"))) {
440
+ const protectedFile = macPackage ? "protected-v1.sqlite" : "capabilities-v1.dpapi.clixml";
441
+ if (await exists(path.join(locations.hostRoot, protectedFile))) {
433
442
  const store = await importStableCore(PACKAGE_ROOT, "hellolynk-host-protected-store.mjs", "hellolynk_npm_binding_invalid");
434
443
  await store.createHostProtectedStore({ root: locations.hostRoot, followClientAccount: false }).disconnectLocalClient(metadata.client);
435
444
  }
@@ -838,17 +847,20 @@ async function verifyRelease(releaseRoot, packageRoot, expectedMetadata = metada
838
847
  return Object.freeze({ manifestSha256, outerManifestSha256 });
839
848
  }
840
849
 
841
- function resolveLocations() {
842
- if (process.platform !== "win32") fail("hellolynk_npm_windows_only");
843
- const home = path.resolve(process.env.USERPROFILE || os.homedir());
844
- const localAppData = path.resolve(process.env.LOCALAPPDATA || path.join(home, "AppData", "Local"));
845
- const appData = path.resolve(process.env.APPDATA || path.join(home, "AppData", "Roaming"));
850
+ function resolveLocations(getDefaultHostRoot) {
851
+ if (macPackage) {
852
+ if (process.platform !== "darwin" || process.arch !== "arm64") fail("hellolynk_npm_platform_unsupported");
853
+ } else if (process.platform !== "win32") fail("hellolynk_npm_windows_only");
854
+ const home = path.resolve((macPackage ? process.env.HOME : process.env.USERPROFILE) || os.homedir());
855
+ const localAppData = macPackage ? path.join(home, "Library", "Application Support")
856
+ : path.resolve(process.env.LOCALAPPDATA || path.join(home, "AppData", "Local"));
857
+ const appData = macPackage ? localAppData : path.resolve(process.env.APPDATA || path.join(home, "AppData", "Roaming"));
846
858
  const configuredCodexHome = String(process.env.CODEX_HOME || "");
847
859
  if (configuredCodexHome && !path.isAbsolute(configuredCodexHome)) fail("hellolynk_npm_codex_home_invalid");
848
860
  const codexHome = path.resolve(configuredCodexHome || path.join(home, ".codex"));
849
861
  const configuredHostRoot = String(process.env.HELLOLYNK_NEUTRAL_STORE_ROOT || "").trim();
850
862
  if (configuredHostRoot && !path.isAbsolute(configuredHostRoot)) fail("hellolynk_npm_store_root_invalid");
851
- const hostRoot = path.resolve(configuredHostRoot || path.join(localAppData, "HelloLynk", "host"));
863
+ const hostRoot = path.resolve(configuredHostRoot || getDefaultHostRoot());
852
864
  if (configuredHostRoot && path.basename(hostRoot).toLowerCase() !== "host") {
853
865
  fail("hellolynk_npm_store_root_invalid");
854
866
  }
@@ -1004,6 +1016,12 @@ async function startHostWhenAbsent(runtime, binding) {
1004
1016
  }
1005
1017
 
1006
1018
  async function secureStableReleaseTree(locations, releaseId, options = {}) {
1019
+ const releasePath = path.resolve(locations.finalCurrentRoot, "releases", releaseId);
1020
+ // An existing AppData root can be shared while a new release is redirected
1021
+ // into one desktop app's private cache. Other clients cannot use that release.
1022
+ if (process.platform === "win32" && realpathSync.native(releasePath).toLowerCase() !== releasePath.toLowerCase()) {
1023
+ fail("client_root_alias_rejected");
1024
+ }
1007
1025
  const module = await importCore(
1008
1026
  "hellolynk-platform-credential-store.mjs",
1009
1027
  "hellolynk_npm_client_root_security_unavailable",
@@ -2246,6 +2264,13 @@ function runCodexJson(args, code, codexHome) {
2246
2264
  }
2247
2265
 
2248
2266
  function minimalEnv({ codexHome = null } = {}) {
2267
+ if (macPackage) return Object.freeze({
2268
+ PATH: process.env.PATH || "/usr/bin:/bin",
2269
+ HOME: process.env.HOME || os.homedir(),
2270
+ TMPDIR: process.env.TMPDIR || os.tmpdir(),
2271
+ LANG: process.env.LANG || "en_US.UTF-8",
2272
+ ...(codexHome ? { CODEX_HOME: codexHome } : {}),
2273
+ });
2249
2274
  return Object.freeze({
2250
2275
  PATH: process.env.Path || process.env.PATH || "",
2251
2276
  PATHEXT: process.env.PATHEXT || "",
@@ -2261,13 +2286,18 @@ function minimalEnv({ codexHome = null } = {}) {
2261
2286
 
2262
2287
  function resolveCodexRunner(codexHome) {
2263
2288
  if (resolvedCodexRunner) return resolvedCodexRunner;
2264
- const searchPath = String(process.env.Path || process.env.PATH || "");
2289
+ const searchPath = String((macPackage ? process.env.PATH : process.env.Path || process.env.PATH) || "");
2265
2290
  let sawLoaderCandidate = false;
2266
2291
  let sawNativeCandidate = false;
2267
2292
  for (const rawEntry of searchPath.split(path.delimiter)) {
2268
2293
  try {
2269
2294
  const logical = rawEntry.trim().replace(/^"(.*)"$/, "$1");
2270
2295
  if (!logical || !path.isAbsolute(logical)) continue;
2296
+ if (macPackage) {
2297
+ const runner = probeMacCodex(path.join(logical, "codex"), codexHome);
2298
+ if (runner) return (resolvedCodexRunner = runner);
2299
+ continue;
2300
+ }
2271
2301
  // nvm/fnm and user profile mappings can put a junction on PATH. Resolve
2272
2302
  // that directory once, then retain the ordinary-file checks below it.
2273
2303
  const entry = realpathSync(path.resolve(logical));
@@ -2321,6 +2351,15 @@ function resolveCodexRunner(codexHome) {
2321
2351
  }
2322
2352
 
2323
2353
  function resolveCodexDesktopRunner(codexHome) {
2354
+ if (macPackage) {
2355
+ for (const directory of ["/Applications", path.join(os.homedir(), "Applications")]) {
2356
+ for (const app of ["ChatGPT.app", "Codex.app"]) {
2357
+ const runner = probeMacCodex(path.join(directory, app, "Contents", "Resources", "codex"), codexHome);
2358
+ if (runner) return runner;
2359
+ }
2360
+ }
2361
+ return null;
2362
+ }
2324
2363
  if (process.platform !== "win32") return null;
2325
2364
  const systemRoot = process.env.SystemRoot || process.env.SYSTEMROOT;
2326
2365
  if (!systemRoot || !path.isAbsolute(systemRoot)) return null;
@@ -2348,6 +2387,22 @@ function resolveCodexDesktopRunner(codexHome) {
2348
2387
  return null;
2349
2388
  }
2350
2389
 
2390
+ function probeMacCodex(logicalPath, codexHome) {
2391
+ // npm and Homebrew commonly expose a symlink. Execute the canonical file directly.
2392
+ try {
2393
+ const command = realpathSync(logicalPath);
2394
+ const metadata = lstatSync(command);
2395
+ if (!metadata.isFile() || !(metadata.mode & 0o111)) return null;
2396
+ const result = spawnSync(command, ["--version"], {
2397
+ encoding: "utf8", shell: false, timeout: 5000, env: minimalEnv({ codexHome }),
2398
+ });
2399
+ if (result.status === 0 && /^codex-cli\s+\d+\.\d+\.\d+/u.test(String(result.stdout || "").trim())) {
2400
+ return Object.freeze({ command, argsPrefix: Object.freeze([]), kind: "native" });
2401
+ }
2402
+ } catch { /* Missing or unusable CLI. */ }
2403
+ return null;
2404
+ }
2405
+
2351
2406
  function resolveRegularDescendantSync(anchor, anchorRealPath, segments) {
2352
2407
  let observed = anchor;
2353
2408
  for (let index = 0; index < segments.length; index += 1) {
@@ -2527,6 +2582,7 @@ function samePath(left, right) {
2527
2582
  const resolved = normalizeWindowsNamespacePath(value);
2528
2583
  let canonical = resolved;
2529
2584
  try { canonical = realpathSync(resolved); } catch { /* comparison can still reject a missing path */ }
2585
+ if (macPackage) return canonical;
2530
2586
  return normalizeWindowsNamespacePath(canonical).replace(/[\\/]+$/u, "").toLowerCase();
2531
2587
  };
2532
2588
  return normalize(left) === normalize(right);
@@ -2646,7 +2702,9 @@ function helpText() {
2646
2702
  : [[say("安装", "Install"), "init"], [say("连接账号", "Connect an account"), connect],
2647
2703
  [say("更新或修复", "Update or repair"), "setup"], [say("检查", "Check"), "doctor"],
2648
2704
  [say("卸载", "Uninstall"), "uninstall"]];
2649
- return `${[`HelloLynk for ${CLIENT_NAMES[metadata.client]}${say("(Windows,Node.js 22.16.0+)", " (Windows, Node.js 22.16.0+)")}`,
2705
+ const platform = macPackage ? " (macOS arm64, Node.js 22.16.0+)"
2706
+ : say("(Windows,Node.js 22.16.0+)", " (Windows, Node.js 22.16.0+)");
2707
+ return `${[`HelloLynk for ${CLIENT_NAMES[metadata.client]}${platform}`,
2650
2708
  ...rows.map(([label, args]) => `${label}${say(":", ": ")}${run} ${args}`),
2651
2709
  say("加 --json 输出一行 JSON。", "Add --json for one line of JSON.")].join("\n")}\n`;
2652
2710
  }
@@ -2720,6 +2778,9 @@ function failureText(failure) {
2720
2778
  function failureReason(code, name) {
2721
2779
  if (code === "hellolynk_node_22_16_required") return say("需要 Node.js 22.16 或更高版本。", "Node.js 22.16 or later is required.");
2722
2780
  if (code === "hellolynk_npm_windows_only") return say("只支持 Windows。", "Windows only.");
2781
+ if (code === "hellolynk_npm_platform_unsupported") return say("此安装包适用于 Apple 芯片的 Mac。", "This package requires an Apple silicon Mac.");
2782
+ if (code === "hellolynk_npm_codex_mac_node_required") return say("请将 ChatGPT 安装到「应用程序」,打开 Codex 后再试。",
2783
+ "Install ChatGPT in Applications, open Codex, and try again.");
2723
2784
  if (code === "hellolynk_npm_connect_id_invalid") {
2724
2785
  return say("灵信号不对。请从官网复制从 hellolynk: 开始的完整灵信号。",
2725
2786
  "This HelloLynk ID is not valid. Copy the complete ID, starting with hellolynk:, from the website.");