cortico-world-coding-agent-notify 0.1.0 → 0.1.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## 0.1.1
6
+
7
+ - 修复桌面环境 `spawn codex ENOENT`:四个 Agent 的接入无需手动指定命令路径,CLI 由用户 shell / PowerShell 自动查找;需要外部 Node.js 的 hook 和所有测试按钮使用自动找到的 Node.js 绝对路径。无新增依赖。
8
+ - Resolve CLIs through the user shell or PowerShell and use the discovered absolute Node.js path for command hooks and delivery tests; no manual executable-path settings or new dependencies.
9
+ - 纠正隐私说明并移除误声明的 `sensitive-data` 标签:只转交任务结论及定位信息,不采集截图、麦克风或窗口数据;市场历史警示标签另行申请撤销。
10
+ - Correct the data disclosure and remove the mistakenly declared `sensitive-data` tag. Only final reports and workspace/session metadata are forwarded; no screen, microphone or foreground-window capture. Existing market warnings require a separate withdrawal request.
11
+ - 本次按用户要求未运行测试或类型检查,Windows 兼容性仍待验证。
12
+ - Tests and type checks were not run for this release, as requested; Windows compatibility remains unverified.
13
+
5
14
  ## 0.1.0
6
15
 
7
16
  - 为 Codex、Claude Code、OpenCode、DSH 提供独立的安装 hook 和测试按钮;启动 World 不安装 hook。
package/README.md CHANGED
@@ -19,22 +19,24 @@ ThermalEng 维护的第三方 Coopanion World 扩展,非 Pal AI Lab 官方扩
19
19
 
20
20
  Codex、Claude hook 与配置页的测试需要 `node`(22+)在 PATH 中;Codex、DSH 的安装分别需要 `codex`、`dsh` 在 PATH 中。OpenCode、DSH 插件直接使用自身运行时发送报告。生成的 Agent 插件只是本扩展的入口,不会安装第三方插件。
21
21
 
22
- World 运行时开启带令牌的 `127.0.0.1` 临时接口;hook 将最终报告发来,由 Coopanion 当前连接的模型总结,再使用其内置桌宠能力通知。因此需要 Coopanion 正在运行并已连接模型,模型调用会产生正常用量。**最终报告会进入 Coopanion 的模型上下文和事件历史**,不会发送完整对话。
22
+ World 运行时开启带令牌的 `127.0.0.1` 临时接口;hook 将最终报告发来,由 Coopanion 当前连接的模型总结,再使用其内置桌宠能力通知。因此需要 Coopanion 正在运行并已连接模型,模型调用会产生正常用量。**任务结论会进入 Coopanion 当前配置的模型上下文和事件历史**。通知只转交最终回复/任务结论及项目路径、会话标识,不转交完整对话,不采集屏幕截图、麦克风音频或前台窗口信息。结论本身仍可能包含项目信息,请按所用模型服务的数据政策使用。
23
23
 
24
24
  「测试」验证共用发送器 → World 事件入口,收到后由 Coo 通知;它不替代一次真实 Agent 任务对原生回调的验证。不提供离线队列;Coopanion 关闭时通知跳过,不影响 Agent 完成任务。重新点击安装不会重复添加 hook。
25
25
 
26
- 默认生成目录:`~/.coopanion/agent-hooks`。可通过 `COOPANION_AGENT_HOOK_HOME` 更改;DSH 支持 `COOPANION_DSH_PROFILE`、`COOPANION_DSH_BIN`、`DSH_HOME`。也尊重 Agent 自带的 `CODEX_HOME`、`CLAUDE_CONFIG_DIR` 和 `XDG_CONFIG_HOME`。使用多个配置时,应让 Coopanion 启动环境与对应 Agent 一致。
26
+ 默认生成目录:`~/.coopanion/agent-hooks`。可通过 `COOPANION_AGENT_HOOK_HOME` 更改;DSH 支持 `COOPANION_DSH_PROFILE`、`DSH_HOME`。也尊重 Agent 自带的 `CODEX_HOME`、`CLAUDE_CONFIG_DIR` 和 `XDG_CONFIG_HOME`。使用多个配置时,应让 Coopanion 启动环境与对应 Agent 一致。
27
27
 
28
28
  Codex 的 TOML 配置由自身 CLI 修改。已有 JSON 设置保留其他键;无效 JSON 会报错,不会覆盖。
29
29
 
30
+ 四个 Agent 均不需要手动填写可执行文件路径。需要调用 CLI 时,Linux/macOS 使用用户的交互式登录 shell 查找命令,Windows 使用 PowerShell `Get-Command` 查找,兼容 npm 的 `.cmd` 入口。Codex、DSH 的安装通过各自 CLI 完成;Claude Code、OpenCode 直接使用原生配置或插件目录,不额外启动其 CLI。需要外部 Node.js 的 hook 与四个 Agent 的「测试」按钮共用自动查找,并使用找到的 Node.js 绝对路径,避免桌面进程 PATH 不完整导致 `ENOENT`;OpenCode、DSH 的正常回调继续使用自身运行时。未安装所需程序时会报错,不自动安装程序。
31
+
30
32
  ## Windows
31
33
 
32
34
  面向 Windows 原生运行的 Agent;WSL 中的 Agent 按 Linux 环境部署,不混用两侧的 hook 路径。默认 hook 目录为 `%USERPROFILE%\.coopanion\agent-hooks`,Agent 配置路径仍尊重上述环境变量。
33
35
 
34
36
  - 安装 Codex、DSH hook 时,通过 Windows PowerShell 调用原生 CLI,兼容 npm 的 `.cmd` 入口和带空格、中文的路径。
35
- - Claude Code 使用原生 `command` + `args` 形式直接启动 `node.exe`;需使用支持该形式的版本。
37
+ - Claude Code 使用原生 `command` + `args` 形式直接启动自动找到的 `node.exe`;需使用支持该形式的版本。
36
38
  - Codex 使用 `commandWindows`,通过编码后的 PowerShell 命令转交 UTF-8 报告;需使用支持该字段的版本。
37
- - OpenCode、DSH 仍直接导入共用发送模块;配置页的测试直接启动 `node.exe`,不弹出额外控制台窗口。
39
+ - OpenCode、DSH 仍直接导入共用发送模块;配置页的测试直接启动自动找到的 `node.exe`,不弹出额外控制台窗口。
38
40
 
39
41
  Windows 需要 PATH 中有 Node.js 22+ 和对应 Agent CLI,以及系统自带的 `powershell.exe`。本次 Windows 适配未运行测试。
40
42
 
package/index.ts CHANGED
@@ -3,7 +3,7 @@ import { execFile, spawn } from 'node:child_process';
3
3
  import { randomBytes, timingSafeEqual } from 'node:crypto';
4
4
  import { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http';
5
5
  import { access, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
6
- import { homedir } from 'node:os';
6
+ import { homedir, userInfo } from 'node:os';
7
7
  import { dirname, join } from 'node:path';
8
8
  import { fileURLToPath, pathToFileURL } from 'node:url';
9
9
  import { promisify } from 'node:util';
@@ -39,6 +39,13 @@ function powershellArgs(command: string, args: string[], forwardInput = false) {
39
39
  return ['-NoLogo', '-NoProfile', '-NonInteractive', '-WindowStyle', 'Hidden', '-EncodedCommand', Buffer.from(script, 'utf16le').toString('base64')];
40
40
  }
41
41
 
42
+ // Let the OS shell resolve CLI paths, including those added by shell startup files.
43
+ function nativeCommand(command: string, args: string[], env: NodeJS.ProcessEnv, timeout = 120_000) {
44
+ return exec(WINDOWS ? 'powershell.exe' : env.SHELL || userInfo().shell || '/bin/sh',
45
+ WINDOWS ? powershellArgs(command, args) : ['-ilc', `exec ${[command, ...args].map(shellQuote).join(' ')}`],
46
+ { timeout, windowsHide: true, env: { ...process.env, ...env } });
47
+ }
48
+
42
49
  async function exists(file: string) {
43
50
  try { await access(file); return true; } catch { return false; }
44
51
  }
@@ -109,11 +116,16 @@ export class HookManager {
109
116
  dsh: join(this.dir, 'dsh-plugin'),
110
117
  };
111
118
  this.dshManifest = join(this.env.DSH_HOME || join(home, '.dsh'), 'profiles', this.profile, 'package.json');
112
- this.run = options.run ?? (async (command, args) => {
113
- await exec(WINDOWS ? 'powershell.exe' : command, WINDOWS ? powershellArgs(command, args) : args, {
114
- timeout: 120_000, windowsHide: true, env: { ...process.env, ...this.env },
115
- });
116
- });
119
+ this.run = options.run ?? (async (command, args) => { await nativeCommand(command, args, this.env); });
120
+ }
121
+
122
+ private async nodePath() {
123
+ const marker = '__COOPANION_NODE__';
124
+ // Base64 keeps non-ASCII Windows paths intact across PowerShell output encodings.
125
+ const { stdout } = await nativeCommand(NODE, ['-p', `${JSON.stringify(marker)} + Buffer.from(process.execPath).toString('base64')`], this.env, 10_000);
126
+ const line = stdout.split(/\r?\n/).reverse().find((value) => value.startsWith(marker));
127
+ if (!line) throw new Error('Cannot locate Node.js. Install Node.js 22+ before installing or testing hooks.');
128
+ return Buffer.from(line.slice(marker.length), 'base64').toString('utf8');
117
129
  }
118
130
 
119
131
  async status(agent: Agent): Promise<HookStatus> {
@@ -142,13 +154,14 @@ export class HookManager {
142
154
  const path = this.paths[agent];
143
155
  const settings = await readJson(path);
144
156
  const entries = stopHooks(settings);
145
- const hook: CommandHook = { type: 'command', command: `node ${shellQuote(this.runner)} ${agent}`, async: true };
157
+ const node = await this.nodePath();
158
+ const hook: CommandHook = { type: 'command', command: `${shellQuote(node)} ${shellQuote(this.runner)} ${agent}`, async: true };
146
159
  if (WINDOWS) {
147
160
  if (agent === 'claude') {
148
- hook.command = NODE;
161
+ hook.command = node;
149
162
  hook.args = [this.runner, agent];
150
163
  } else {
151
- hook.commandWindows = `powershell.exe ${powershellArgs(NODE, [this.runner, agent], true).join(' ')}`;
164
+ hook.commandWindows = `powershell.exe ${powershellArgs(node, [this.runner, agent], true).join(' ')}`;
152
165
  }
153
166
  }
154
167
  const existing = managedHook(entries);
@@ -169,15 +182,16 @@ export class HookManager {
169
182
  dsh: { bundle: { patch: './cordis.patch.yml' } },
170
183
  }, null, 2));
171
184
  await writeAtomic(join(dir, 'cordis.patch.yml'), `- insert:\n - id: ${DSH_NAME}\n name: ${DSH_NAME}\n`);
172
- await this.run(this.env.COOPANION_DSH_BIN || 'dsh', ['plugin', '--profile', this.profile, 'add', `link:${dir}`]);
185
+ await this.run('dsh', ['plugin', '--profile', this.profile, 'add', `link:${dir}`]);
173
186
  }
174
187
  if (!(await this.status(agent)).installed) throw new Error(`${LABELS[agent]} hook was not registered`);
175
188
  }
176
189
 
177
190
  async test(agent: Agent) {
178
191
  if (!(await this.status(agent)).installed) throw new Error(`Install the ${LABELS[agent]} hook first.`);
192
+ const node = await this.nodePath();
179
193
  await new Promise<void>((resolve, reject) => {
180
- const child = spawn(NODE, [this.runner, agent], {
194
+ const child = spawn(node, [this.runner, agent], {
181
195
  env: { ...process.env, ...this.env }, stdio: ['pipe', 'ignore', 'pipe'], timeout: 5000, windowsHide: true,
182
196
  });
183
197
  let error = '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cortico-world-coding-agent-notify",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "Coopanion World for short coding-agent completion notifications",
6
6
  "license": "AGPL-3.0-or-later",
@@ -17,8 +17,7 @@
17
17
  "tags": [
18
18
  "apps",
19
19
  "external-setup",
20
- "experimental",
21
- "sensitive-data"
20
+ "experimental"
22
21
  ],
23
22
  "locales": {
24
23
  "zh": {