linkitty 0.17.0 → 0.17.4

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/README.md CHANGED
@@ -1,70 +1,90 @@
1
- # Linkitty
1
+ # Linkitty CLI
2
2
 
3
- [English](README.en.md) · 简体中文
3
+ English · [简体中文](README.zh-CN.md)
4
4
 
5
- 连接自己的电脑服务,在手机或桌面继续使用。
5
+ Connect agents, local websites and APIs from your computer. Install `linkitty` from npm. On macOS, install the matching `linkitty-desktop` package too; Linux installs only the CLI. Node.js 22+ is required.
6
6
 
7
- 从[官网下载页](https://linkitty.spark-data.cn/#downloads)取得 CLI tarball。macOS 桌面需同版桌面 tarball 一起安装,Linux 仅安装 CLI。按页面的系统页签操作;npm 公共仓库暂缓发布。
7
+ ## Published npm 0.17.4 and upgrades
8
8
 
9
- linkitty --lang zh
10
- linkitty login
11
- linkitty connect codex
9
+ The immutable CLI 0.17.4 package is published for macOS ARM64 and Linux x64. The published tarball matches the release artifact checked on macOS ARM64 and Linux x64; see platform limits. Node.js 22+ is required.
12
10
 
13
- 界面语言支持中文、英文、日文和俄文:`linkitty --lang zh|en|ja|ru` 保存默认值,`LINKITTY_LANG` 覆盖本次进程。语言不改变账号地域;桌面与 CLI 共用 `~/.linkitty/config/preferences.json`,桌面下次启动读取该偏好。
11
+ ```sh
12
+ npm install -g --allow-scripts=linkitty linkitty@0.17.4
13
+ ```
14
14
 
15
- Codex 共享接入目前支持 macOS。首次接入时,若 Codex 正在独立运行,请先完成任务并退出 Codex,再执行接入命令;成功后正常打开 Codex 即可。接入不会在旁边另起执行器,也不会抢占现有会话。系统后台启动会使用已启用的 macOS 系统代理,已有代理环境变量优先。
15
+ For an existing installation upgraded with npm12, rebuild the verified runtime after installing:
16
16
 
17
- 使用手机认领:
17
+ ```sh
18
+ npm rebuild -g linkitty --allow-scripts=linkitty
19
+ ```
18
20
 
19
- linkitty connect codex -qr
21
+ Keep the same custom `--prefix` for both commands. macOS Apple Silicon can also install `linkitty-desktop@0.17.4`; Linux installs only the CLI. New source features require a matching verified release. Use a matching verified release for candidate-only features. The [download page](https://linkitty.spark-data.cn/?lang=en#downloads) also provides matching tarballs.
20
22
 
21
- 区域与网站:
23
+ ## Connect
22
24
 
23
- linkitty --region cn
24
- linkitty --region sg --endpoint https://你的新加坡入口/gateway
25
- linkitty connect web 127.0.0.1:8080 --name 我的工作台
25
+ ```sh
26
+ linkitty --lang en
27
+ linkitty --region cn
28
+ linkitty login
29
+ linkitty connect codex
30
+ linkitty connect pi --executable /absolute/path/to/pi
31
+ linkitty connect claude --profile /absolute/private/claude/profile.json
32
+ linkitty connect dsh --profile /absolute/private/deepseek/profile.json
33
+ linkitty connect web 127.0.0.1:8080 --name Workbench
34
+ linkitty connect api 127.0.0.1:9000 --routes /absolute/api-routes.json
35
+ ```
26
36
 
27
- 首次使用后台连接会注册当前系统用户的服务。CLI 和桌面共用同一个 Linkitty 连接进程,电脑登录后恢复,异常退出由系统重新拉起。电脑睡眠或断网期间不可达,业务软件仍需运行。暂停连接使用 linkitty disconnect ID,停止系统后台使用 linkitty service uninstall;配置和绑定会保留。
37
+ All `connect` commands accept `-qr` for phone claiming. Omit it when signed in to claim directly. No project is bound by default; add `--workspace /absolute/project` when needed. Pi may use `--agent-dir` for its existing authentication directory. DSH means DeepSeek Harness. Each agent keeps its own model login, tool permissions and approvals; Linkitty does not turn it into a Codex sandbox.
28
38
 
29
- 永久删除服务使用 `linkitty delete 实例ID --yes`,需要配套平台支持删除接口。此操作物理删除中继登记、分享和访问授权,电脑项目、原生会话和文件保留;再次使用需明确执行接入。桌面和手机在“我的服务”的详情中提供同一删除能力,与停用独立。
39
+ Codex desktop sharing supports macOS only. Before first connection, finish tasks and quit independently running Codex. Reopen it normally after connecting; no daily shared-control launcher is required. Background startup honors enabled macOS system proxies, with existing proxy environment variables taking precedence.
30
40
 
31
- 中国区默认入口为 `https://cn.linkitty.spark-data.cn/gateway`,既有 IP 配置和账号继续保留;sg/us 未开放时拒绝登录。切区不迁移账号或服务。平台账号与 Codex 模型登录分别处理。
41
+ Qoder CLI 1.1.37 uses its own sign-in. With Node.js 22+, npm and jq installed, the first connection prepares its SDK automatically:
32
42
 
33
- 支持六种接入:
43
+ ```sh
44
+ linkitty connect qoder
45
+ ```
46
+
47
+ Use a matching Connector and Coding plugin that declare `relay.qoder`. This local integration does not update published packages. Qoder keeps its official local sign-in; its profile must not contain a model key or custom endpoint. IDE shared control is unavailable.
48
+
49
+ ## Manage connections
34
50
 
35
51
  ```sh
36
- linkitty connect codex
37
- # Optional: linkitty connect codex --workspace /absolute/project
38
- linkitty connect pi --workspace /absolute/project --executable /absolute/path/to/pi
39
- linkitty connect claude --workspace /absolute/project --profile /absolute/private/claude/profile.json
40
- linkitty connect dsh --workspace /absolute/project --profile /absolute/private/deepseek/profile.json
41
- linkitty connect web 127.0.0.1:8080
42
- linkitty connect api 127.0.0.1:9000 --routes /absolute/api-routes.json
52
+ linkitty status
53
+ linkitty list
54
+ linkitty disconnect ID
55
+ linkitty service status
56
+ linkitty service uninstall
57
+ linkitty delete INSTANCE_ID --yes
43
58
  ```
44
59
 
45
- 所有 connect 都可使用 `-qr`;已登录时省略该参数可直接认领。DSH 对应 DeepSeek Harness。Codex/Pi 使用已有登录环境,Claude/DSH 使用既有私有运行配置,Linkitty 不代替模型登录。Pi、Claude 和 DSH 保留各自工具权限与审批机制,不能视为 Codex 沙箱。自定义 Pi 认证目录用 `--agent-dir`。四种引擎的项目目录必须与 Linkitty 私有目录分离,不建议把整个用户主目录作为项目。
60
+ The CLI and desktop share one user background service. Closing a terminal or window leaves it running. Sleep, network loss or a stopped business application makes the service unavailable. `disconnect` pauses a connection; uninstalling the background service retains configuration. `delete` permanently removes service registration and sharing grants, retaining projects, native sessions and computer files.
46
61
 
47
- API 路由文件最小示例:
62
+ ## API access
63
+
64
+ The route file declares a business contract and explicit capabilities:
48
65
 
49
66
  ```json
50
67
  {
51
- "business_api": { "id": "my.service.api", "version": "1.0.0" },
68
+ "business_api": {"id": "example.api", "version": "1.0.0"},
52
69
  "routes": [
53
- { "methods": ["GET"], "path": "/health", "requires": ["api.read"] },
54
- { "methods": ["POST"], "path": "/jobs", "requires": ["api.write"] }
70
+ {"methods": ["GET"], "path": "/health", "requires": ["api.read"]},
71
+ {"methods": ["POST"], "path": "/jobs", "requires": ["api.write"]}
55
72
  ]
56
73
  }
57
74
  ```
58
75
 
59
- 上游固定为本机 HTTP origin。HTTP/SSE 使用原业务路径;WebSocket 路由需设置 `websocket: true`。未知路由和缺少能力的请求被拒绝。更改权限文件产生新的实例,旧连接需自行暂停。
76
+ The upstream is fixed to a loopback HTTP origin. HTTP/SSE keep business paths; WebSocket routes require `websocket: true`. Unknown routes and missing capabilities are rejected. Changing the permission file creates a new instance; pause the old one explicitly.
60
77
 
61
78
  ```sh
62
- linkitty list
63
- linkitty access 实例ID --capabilities api.read --output /absolute/private/access.json
79
+ linkitty access INSTANCE_ID --capabilities api.read --output /absolute/private/access.json
64
80
  ```
65
81
 
66
- 私有文件包含短期 `endpoint` 与 `access_token`,权限为 600,请求使用 `Relay-Authorization: Bearer <access_token>`;不要提交或分享该文件。过期后重新生成,不覆盖已有文件。CLI 不向标准输出显示凭据。
82
+ The mode-600 file contains a short-lived `endpoint` and `access_token`; requests use `Relay-Authorization: Bearer <access_token>`. Never print, commit or share this file. Create a new file after expiry; existing files are not overwritten. See the [online developer guide](https://github.com/lingzhi-wlz/relay-gateway/blob/main/docs/third-party-developer.md) for stream handling and plugin integration.
83
+
84
+ ## Platforms, region and language
85
+
86
+ Payloads support macOS ARM64 and Linux x64; installation selects the platform and verifies its hash. Linux targets Ubuntu 24.04 / glibc 2.39 and needs user-level systemd. Binaries/installers were checked in containers; real user-systemd startup remains unverified. Windows uses WSL 2 / Ubuntu 24.04 and is not verified on a real machine. Native Windows, Linux ARM64 and musl have no payload. Use Pi, Claude or DSH on Linux/WSL.
67
87
 
68
- 当前 CLI 包含 macOS ARM64、Linux x64 两种原生组件,安装器按系统选择并校验摘要。Linux 基线 Ubuntu 24.04 / glibc 2.39,容器已验证二进制与安装;真实用户级 systemd 自启仍需实机验收。Windows 通过 WSL 2 / Ubuntu 24.04 使用 Linux 指引,尚未实机验证。原生 Windows、Linux ARM64、musl 和其他缺少制品的系统会明确拒绝安装。Codex 桌面共享连接仅支持 macOS;Linux / WSL 使用 Pi、Claude 或 DSH。
88
+ China is the available account region. Singapore and US require their own configured endpoints; switching regions does not migrate accounts or services. `linkitty --lang zh|en|ja|ru` saves a preference; `LINKITTY_LANG` overrides the current process. Language does not change region.
69
89
 
70
- Coding 连接默认不绑定项目,执行目录需要时使用用户 `~`;显式 `--workspace` 保留。Codex 首次连接自动准备固定共享服务与后续启动配置。当前独立 Codex 正在运行时,只需在任务结束后正常重启一次;以后直接打开,无需共控脚本。
90
+ Desktop and CLI share `~/.linkitty/`. Keep configuration and credentials protected, and never package private data. Platform login and agent model login are separate.
@@ -0,0 +1,90 @@
1
+ # Linkitty CLI
2
+
3
+ [English](README.md) · 简体中文
4
+
5
+ 接入电脑上的 Agent、本机网站和 API。从 npm 安装 `linkitty`;macOS 同时安装匹配的 `linkitty-desktop` 包,Linux 只装 CLI。需要 Node.js 22+。
6
+
7
+ ## 已发布的 npm 0.17.4 与升级
8
+
9
+ CLI 0.17.4 不可变包已发布,包含 macOS ARM64 与 Linux x64;公开 tarball 与已在这两个平台验证的发行制品一致,见平台边界。需要 Node.js 22+。
10
+
11
+ ```sh
12
+ npm install -g --allow-scripts=linkitty linkitty@0.17.4
13
+ ```
14
+
15
+ 已有安装使用 npm12 升级后,重建已验证的运行组件:
16
+
17
+ ```sh
18
+ npm rebuild -g linkitty --allow-scripts=linkitty
19
+ ```
20
+
21
+ 两条命令的自定义 `--prefix` 必须相同。macOS Apple Silicon 可另装 `linkitty-desktop@0.17.4`,Linux 只装 CLI。新源码功能须使用相匹配的已验证发行;候选功能须使用相匹配的已验证发行。[官网下载页](https://linkitty.spark-data.cn/?lang=zh#downloads)继续提供匹配tarball。
22
+
23
+ ## 接入
24
+
25
+ ```sh
26
+ linkitty --lang zh
27
+ linkitty --region cn
28
+ linkitty login
29
+ linkitty connect codex
30
+ linkitty connect pi --executable /absolute/path/to/pi
31
+ linkitty connect claude --profile /absolute/private/claude/profile.json
32
+ linkitty connect dsh --profile /absolute/private/deepseek/profile.json
33
+ linkitty connect web 127.0.0.1:8080 --name Workbench
34
+ linkitty connect api 127.0.0.1:9000 --routes /absolute/api-routes.json
35
+ ```
36
+
37
+ 所有 `connect` 命令可加 `-qr` 由手机扫码;已登录时省略即可直接认领。不默认绑定项目,需要时加 `--workspace /absolute/project`。Pi 可用 `--agent-dir` 指定既有认证目录。DSH 指 DeepSeek Harness。各 Agent 保留自身模型登录、工具权限和审批,Linkitty 不会把它变成 Codex 沙箱。
38
+
39
+ Codex 桌面共用仅支持 macOS。首次接入前完成任务并退出独立运行的 Codex,接入成功后正常打开,无需每日运行共控脚本。后台启动使用已启用的 macOS 系统代理,既有代理环境变量优先。
40
+
41
+ Qoder CLI 1.1.37 使用自身登录。安装 Node.js 22+、npm 和 jq 后,首次接入会自动准备 SDK:
42
+
43
+ ```sh
44
+ linkitty connect qoder
45
+ ```
46
+
47
+ 需要声明 `relay.qoder` 的匹配 Connector 与 Coding 插件,本地接入不会自动更新已发布包。Qoder 保留自身官方登录,配置不得包含模型 Key 或自定义端点,未接入 IDE 共控。
48
+
49
+ ## 管理连接
50
+
51
+ ```sh
52
+ linkitty status
53
+ linkitty list
54
+ linkitty disconnect ID
55
+ linkitty service status
56
+ linkitty service uninstall
57
+ linkitty delete INSTANCE_ID --yes
58
+ ```
59
+
60
+ CLI 与桌面共用一个用户后台服务,关闭终端或窗口后继续运行。电脑休眠、断网或业务程序停止时不可用。`disconnect` 暂停连接;卸载后台服务保留配置;`delete` 永久删除服务登记及分享授权,保留项目、原生会话和电脑文件。
61
+
62
+ ## API 访问
63
+
64
+ 路由文件声明业务契约和明确能力:
65
+
66
+ ```json
67
+ {
68
+ "business_api": {"id": "example.api", "version": "1.0.0"},
69
+ "routes": [
70
+ {"methods": ["GET"], "path": "/health", "requires": ["api.read"]},
71
+ {"methods": ["POST"], "path": "/jobs", "requires": ["api.write"]}
72
+ ]
73
+ }
74
+ ```
75
+
76
+ 上游固定为 loopback HTTP 地址。HTTP/SSE 保留业务路径;WebSocket 路由需加 `websocket: true`。未知路由和缺失能力均拒绝。权限文件变化会形成新实例,旧连接需显式暂停。
77
+
78
+ ```sh
79
+ linkitty access INSTANCE_ID --capabilities api.read --output /absolute/private/access.json
80
+ ```
81
+
82
+ 600 权限文件包含短期 `endpoint` 和 `access_token`,请求使用 `Relay-Authorization: Bearer <access_token>`。不打印、提交或分享该文件;到期后另建,不能覆盖已有文件。流处理和插件接入见[在线开发者指南](https://github.com/lingzhi-wlz/relay-gateway/blob/main/docs/third-party-developer.zh-CN.md)。
83
+
84
+ ## 平台、地域和语言
85
+
86
+ 安装包支持 macOS ARM64、Linux x64,安装时选择平台并校验摘要。Linux 面向 Ubuntu 24.04 / glibc 2.39,需要用户级 systemd。二进制与安装器已在容器检查,真实用户 systemd 启动仍未验证。Windows 使用 WSL 2 / Ubuntu 24.04,尚未实机验证。没有原生 Windows、Linux ARM64 或 musl 制品。Linux/WSL 使用 Pi、Claude 或 DSH。
87
+
88
+ 中国区可用;新加坡和美国需另行配置入口,切换地域不迁移账号或服务。`linkitty --lang zh|en|ja|ru` 保存语言,`LINKITTY_LANG` 覆盖当前进程;语言不改变地域。
89
+
90
+ 桌面与 CLI 共用 `~/.linkitty/`。配置与凭据须受保护,不打包私人目录。平台登录与模型登录各自独立。
@@ -1,5 +1,5 @@
1
- # 第三方软件 / Third-party software
1
+ # Third-party software
2
2
 
3
- 原生组件使用 Rust/iroh、Flutter 及各自依赖,许可证不因 Linkitty 包名改变。制品内 licenses 目录保留对应通知。Linkitty 自有代码未另行授予开源许可。
3
+ [简体中文](THIRD_PARTY_NOTICES.zh-CN.md)
4
4
 
5
5
  Native components use Rust/iroh, Flutter and their dependencies. Their licenses are unchanged by the Linkitty package name; corresponding notices are included in the payload licenses directory. No open-source license is granted for Linkitty first-party code at this stage.
@@ -0,0 +1,5 @@
1
+ # 第三方软件
2
+
3
+ [English](THIRD_PARTY_NOTICES.md)
4
+
5
+ 原生组件使用 Rust/iroh、Flutter 及各自依赖,许可证不因 Linkitty 包名改变。制品内 licenses 目录保留对应通知。Linkitty 自有代码未另行授予开源许可。
package/artifacts.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "darwin-arm64": {
3
3
  "archive": "payload/darwin-arm64.tgz",
4
- "sha256": "4a5b0d54b36dd8f2da2b0763ee549bf691e991cb7ead55efb8879349bfbc8624"
4
+ "sha256": "dd559eaed3c3c663d6877aaeebe1dddbd6d51f737cb9d960795b37aff3a2da05"
5
5
  },
6
6
  "linux-x64": {
7
7
  "archive": "payload/linux-x64.tgz",
8
- "sha256": "566a65fba4d69b6e5c60d9fa8bf6a9ac7f85359bdc15b16da55d962ea9a027cc"
8
+ "sha256": "0e04e86f784ff4e2e62a8e3298ba1fc8d82fd2aa1f6e19fbfd7e3ac723e0ee1f"
9
9
  }
10
10
  }
package/bin/linkitty.cjs CHANGED
@@ -11,7 +11,7 @@ const pkg=require('../package.json');
11
11
  const help=()=>'Linkitty '+pkg.version+'\n\n'+
12
12
  t(' linkitty login 登录中继账号\n')+
13
13
  t(' linkitty connect codex [-qr] 接入 Codex,不默认绑定项目;-qr 显示认领码\n')+
14
- t(' linkitty connect pi|claude|dsh [-qr] 接入现有 Coding 运行环境\n')+
14
+ t(' linkitty connect pi|claude|dsh|qoder [-qr] 接入现有 Coding 运行环境\n')+
15
15
  t(' linkitty connect web 127.0.0.1:8080 接入本机网站\n')+
16
16
  t(' linkitty connect api 地址 --routes 文件 按明确路由接入纯 API\n')+
17
17
  t(' linkitty access 实例ID --capabilities api.read --output 文件\n')+
@@ -65,6 +65,12 @@ function friendly(code) {
65
65
  codex_prepare_failed:t('Coding 接入准备失败,请核对执行器、认证环境和 profile 配置。'),
66
66
  harness_profile_required:t('请先配置本机 Claude/DSH 运行环境,再用 --profile 指定受保护的 profile.json。'),
67
67
  coding_executable_missing:t('没有找到本机执行器,请用 --executable 指定完整路径。'),
68
+ qoder_executable_missing:t('请先安装 Qoder CLI 并执行 qoder login,再重新接入。'),
69
+ qoder_login_required:t('Qoder 登录已失效,请在电脑执行 qoder login 后重试。'),
70
+ qoder_version_unsupported:t('当前接入支持 Qoder CLI 1.1.37,请核对电脑上的 Qoder 版本。'),
71
+ qoder_setup_component_required:t('当前安装包缺少 Qoder 接入资源,请更新 Linkitty。'),
72
+ qoder_runtime_setup_failed:t('Qoder 运行库准备失败,请确认 Node.js 22+、npm、jq 和网络可用后重试。'),
73
+ qoder_setup_timeout:t('Qoder 运行库准备超时,请检查网络后重新接入。'),
68
74
  harness_file_missing:t('运行配置中的执行器、桥接程序或凭据文件不存在。'),
69
75
  api_routes_required:t('纯 API 接入需要 --routes 文件,包含 business_api 和 routes。'),
70
76
  legacy_connection_running:t('此项目的旧版连接仍在运行。请完成当前任务并停止旧连接,再接入 Linkitty;将复用原实例绑定。'),
@@ -140,7 +146,7 @@ async function main(argv) {
140
146
  result=request('login',credentials);break;
141
147
  }
142
148
  case 'connect': {
143
- if(!['codex','pi','claude','dsh','web','api'].includes(pos[1]))throw new Error(t('接入类型:codex、pi、claude、dsh、web、api。'));
149
+ if(!['codex','pi','claude','dsh','qoder','web','api'].includes(pos[1]))throw new Error(t('接入类型:codex、pi、claude、dsh、qoder、web、api。'));
144
150
  const proxy=['web','api'].includes(pos[1]);
145
151
  if(proxy&&(!pos[2]||pos.length!==3))throw new Error(t('请提供本机地址,例如 127.0.0.1:8080。'));
146
152
  if(!proxy&&pos.length!==2)throw new Error(t('请使用 --workspace 指定项目目录。'));
@@ -152,7 +158,7 @@ async function main(argv) {
152
158
  try{routes=JSON.parse(fs.readFileSync(file,'utf8'));}catch{throw new Error(t('路由文件不是有效的 JSON。'));}
153
159
  }
154
160
  const executable=opts.executable?path.resolve(opts.executable):pos[1]==='pi'?findExecutable('pi'):undefined;
155
- result=request('connect',{application:pos[1],upstream:pos[2],workspace:path.resolve(opts.workspace||os.homedir()),name:opts.name,qr:opts.qr===true,adapter:opts.adapter?path.resolve(opts.adapter):undefined,codex:opts.codex?path.resolve(opts.codex):undefined,codex_home:process.env.CODEX_HOME,search_path:process.env.PATH,executable,profile:opts.profile?path.resolve(opts.profile):process.env.CODING_HARNESS_HOME&&['claude','dsh'].includes(pos[1])?path.join(process.env.CODING_HARNESS_HOME,pos[1]==='dsh'?'deepseek':'claude','profile.json'):undefined,agent_dir:opts['agent-dir']?path.resolve(opts['agent-dir']):process.env.PI_CODING_AGENT_DIR,routes});
161
+ result=request('connect',{application:pos[1],upstream:pos[2],workspace:path.resolve(opts.workspace||os.homedir()),name:opts.name,qr:opts.qr===true,adapter:opts.adapter?path.resolve(opts.adapter):undefined,codex:opts.codex?path.resolve(opts.codex):undefined,codex_home:process.env.CODEX_HOME,search_path:process.env.PATH,executable,profile:opts.profile?path.resolve(opts.profile):process.env.CODING_HARNESS_HOME&&['claude','dsh','qoder'].includes(pos[1])?path.join(process.env.CODING_HARNESS_HOME,pos[1]==='dsh'?'deepseek':pos[1],'profile.json'):undefined,agent_dir:opts['agent-dir']?path.resolve(opts['agent-dir']):process.env.PI_CODING_AGENT_DIR,routes});
156
162
  if(result.qr&&!opts.json){require('qrcode-terminal').generate(result.qr,{small:true});console.log(t('使用已登录的 Linkitty 手机端扫码认领。'));}
157
163
  break;
158
164
  }
package/lib/install.cjs CHANGED
@@ -6,6 +6,8 @@ const path = require('node:path');
6
6
  const os = require('node:os');
7
7
  const crypto = require('node:crypto');
8
8
  const {execFileSync} = require('node:child_process');
9
+ // Git Bash GNU tar interprets drive letters as remote hosts; always use Windows' native archive tool.
10
+ const tarCommand=process.platform==='win32'?path.join(process.env.SystemRoot||'C:\\Windows','System32','tar.exe'):'tar';
9
11
  function home() {
10
12
  const root=path.resolve(process.env.LINKITTY_HOME || path.join(os.homedir(), '.linkitty'));
11
13
  fs.mkdirSync(path.join(root,'config'),{recursive:true,mode:0o700});
@@ -44,9 +46,9 @@ function install(packageRoot, name) {
44
46
  fs.mkdirSync(path.dirname(destination),{recursive:true,mode:0o700});
45
47
  const stage=fs.mkdtempSync(destination+'.install-');
46
48
  try {
47
- const entries=execFileSync('tar',['-tzf',archive],{encoding:'utf8',maxBuffer:16*1024*1024}).split('\n').filter(Boolean);
49
+ const entries=execFileSync(tarCommand,['-tzf',archive],{encoding:'utf8',maxBuffer:16*1024*1024}).split('\n').filter(Boolean);
48
50
  if (entries.some(p=>p.startsWith('/')||p.includes('\\')||p.split('/').includes('..'))) throw new Error(t('安装包包含不安全路径。'));
49
- execFileSync('tar',['-xzf',archive,'-C',stage],{stdio:'pipe'});
51
+ execFileSync(tarCommand,['-xzf',archive,'-C',stage],{stdio:'pipe'});
50
52
  fs.writeFileSync(path.join(stage,'.complete'),hash,{mode:0o600});
51
53
  try { fs.renameSync(stage,destination); }
52
54
  catch(error) { if(!fs.existsSync(path.join(destination,'.complete'))) throw error; fs.rmSync(stage,{recursive:true,force:true}); }
@@ -78,17 +80,29 @@ function install(packageRoot, name) {
78
80
  }
79
81
  return JSON.parse(fs.readFileSync(pointer,'utf8'));
80
82
  }
83
+ function ensureInstalled(packageRoot,name) {
84
+ const pkg=JSON.parse(fs.readFileSync(path.join(packageRoot,'package.json'),'utf8'));
85
+ const key=name==='linkitty'?'runtime':'executable';
86
+ const pointer=path.join(home(),'config',name==='linkitty'?'installation.json':'desktop.json');
87
+ try {
88
+ const current=JSON.parse(fs.readFileSync(pointer,'utf8'));
89
+ if(current.version===pkg.version&&current.platform===platformTag()&&typeof current[key]==='string'&&path.isAbsolute(current[key])){
90
+ const entry=fs.statSync(current[key]);
91
+ if((name==='linkitty-desktop'&&process.platform==='darwin')?entry.isDirectory():entry.isFile())return current;
92
+ }
93
+ } catch {}
94
+ // npm can skip postinstall on upgrades; repair stale or missing pointers from this package's verified payload.
95
+ return install(packageRoot,name);
96
+ }
81
97
  function runtime() {
82
98
  const override=process.env.LINKITTY_RUNTIME_BIN;
83
99
  if(override) {
84
100
  if(!path.isAbsolute(override)||!fs.existsSync(override)) throw new Error(t('LINKITTY_RUNTIME_BIN 必须指向实际原生程序。'));
85
101
  return override;
86
102
  }
87
- const pointer=path.join(home(),'config','installation.json');
88
- if(!fs.existsSync(pointer)) install(path.resolve(__dirname,'..'),'linkitty');
89
- return JSON.parse(fs.readFileSync(pointer,'utf8')).runtime;
103
+ return ensureInstalled(path.resolve(__dirname,'..'),'linkitty').runtime;
90
104
  }
91
- module.exports={home,write,install,runtime,platformTag};
105
+ module.exports={home,write,install,ensureInstalled,runtime,platformTag};
92
106
  if(require.main===module) {
93
107
  try{install(path.resolve(__dirname,'..'),'linkitty');process.stdout.write(t('Linkitty 已安装。运行 linkitty --help 开始。\n'));}
94
108
  catch(error){process.stderr.write(error.message+'\n');process.exitCode=1;}
package/lib/messages.json CHANGED
@@ -9,10 +9,10 @@
9
9
  "ja": " linkitty connect codex [-qr] Codex に接続。プロジェクトの既定の紐付けなし。-qr で登録用コードを表示\n",
10
10
  "ru": " linkitty connect codex [-qr] Подключить Codex без привязки проекта по умолчанию; -qr показывает код привязки\n"
11
11
  },
12
- " linkitty connect pi|claude|dsh [-qr] 接入现有 Coding 运行环境\n": {
13
- "en": " linkitty connect pi|claude|dsh [-qr] Connect an existing Coding runtime\n",
14
- "ja": " linkitty connect pi|claude|dsh [-qr] 既存の Coding 実行環境に接続\n",
15
- "ru": " linkitty connect pi|claude|dsh [-qr] Подключить существующую среду Coding\n"
12
+ " linkitty connect pi|claude|dsh|qoder [-qr] 接入现有 Coding 运行环境\n": {
13
+ "en": " linkitty connect pi|claude|dsh|qoder [-qr] Connect an existing Coding runtime\n",
14
+ "ja": " linkitty connect pi|claude|dsh|qoder [-qr] 既存の Coding 実行環境に接続\n",
15
+ "ru": " linkitty connect pi|claude|dsh|qoder [-qr] Подключить существующую среду Coding\n"
16
16
  },
17
17
  " linkitty connect web 127.0.0.1:8080 接入本机网站\n": {
18
18
  "en": " linkitty connect web 127.0.0.1:8080 Connect a local website\n",
@@ -279,10 +279,10 @@
279
279
  "ja": "username と password が必要です。",
280
280
  "ru": "Требуются username и password."
281
281
  },
282
- "接入类型:codex、pi、claude、dsh、web、api。": {
283
- "en": "Connection types: codex, pi, claude, dsh, web, api.",
284
- "ja": "接続タイプ:codex、pi、claude、dsh、web、api。",
285
- "ru": "Типы подключения: codex, pi, claude, dsh, web, api."
282
+ "接入类型:codex、pi、claude、dsh、qoder、web、api。": {
283
+ "en": "Connection types: codex, pi, claude, dsh, qoder, web, api.",
284
+ "ja": "接続タイプ:codex、pi、claude、dsh、qoder、web、api。",
285
+ "ru": "Типы подключения: codex, pi, claude, dsh, qoder, web, api."
286
286
  },
287
287
  "请提供本机地址,例如 127.0.0.1:8080。": {
288
288
  "en": "Provide a local address, such as 127.0.0.1:8080.",
@@ -453,5 +453,35 @@
453
453
  "en": " linkitty --lang zh|en|ja|ru Save the interface language\n",
454
454
  "ja": " linkitty --lang zh|en|ja|ru 表示言語を保存\n",
455
455
  "ru": " linkitty --lang zh|en|ja|ru Сохранить язык интерфейса\n"
456
+ },
457
+ "请先安装 Qoder CLI 并执行 qoder login,再重新接入。": {
458
+ "en": "Install Qoder CLI and run qoder login, then reconnect.",
459
+ "ja": "Qoder CLI をインストールし、qoder login を実行してから再接続してください。",
460
+ "ru": "Установите Qoder CLI, выполните qoder login и подключитесь снова."
461
+ },
462
+ "Qoder 登录已失效,请在电脑执行 qoder login 后重试。": {
463
+ "en": "Qoder sign-in has expired. Run qoder login on the computer and retry.",
464
+ "ja": "Qoder のログイン期限が切れました。コンピューターで qoder login を実行して再試行してください。",
465
+ "ru": "Сеанс Qoder истёк. Выполните qoder login на компьютере и повторите."
466
+ },
467
+ "当前接入支持 Qoder CLI 1.1.37,请核对电脑上的 Qoder 版本。": {
468
+ "en": "This integration supports Qoder CLI 1.1.37. Check the installed Qoder version.",
469
+ "ja": "この接続は Qoder CLI 1.1.37 に対応しています。インストール済みのバージョンを確認してください。",
470
+ "ru": "Подключение поддерживает Qoder CLI 1.1.37. Проверьте установленную версию."
471
+ },
472
+ "当前安装包缺少 Qoder 接入资源,请更新 Linkitty。": {
473
+ "en": "This package is missing Qoder integration files. Update Linkitty.",
474
+ "ja": "Qoder 接続用ファイルがありません。Linkitty を更新してください。",
475
+ "ru": "В пакете отсутствуют файлы подключения Qoder. Обновите Linkitty."
476
+ },
477
+ "Qoder 运行库准备失败,请确认 Node.js 22+、npm、jq 和网络可用后重试。": {
478
+ "en": "Qoder setup failed. Check Node.js 22+, npm, jq and network access, then retry.",
479
+ "ja": "Qoder の準備に失敗しました。Node.js 22+、npm、jq、ネットワークを確認して再試行してください。",
480
+ "ru": "Не удалось подготовить Qoder. Проверьте Node.js 22+, npm, jq и доступ к сети."
481
+ },
482
+ "Qoder 运行库准备超时,请检查网络后重新接入。": {
483
+ "en": "Qoder setup timed out. Check the network and reconnect.",
484
+ "ja": "Qoder の準備がタイムアウトしました。ネットワークを確認して再接続してください。",
485
+ "ru": "Подготовка Qoder превысила время ожидания. Проверьте сеть и подключитесь снова."
456
486
  }
457
487
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "linkitty",
3
- "version": "0.17.0",
4
- "description": "Linkitty 私人服务接入、登录与共享后台连接",
3
+ "version": "0.17.4",
4
+ "description": "Linkitty \u79c1\u4eba\u670d\u52a1\u63a5\u5165\u3001\u767b\u5f55\u4e0e\u5171\u4eab\u540e\u53f0\u8fde\u63a5",
5
5
  "license": "UNLICENSED",
6
6
  "engines": {
7
7
  "node": ">=22"
@@ -17,7 +17,8 @@
17
17
  "payload",
18
18
  "README.md",
19
19
  "THIRD_PARTY_NOTICES.md",
20
- "README.en.md"
20
+ "THIRD_PARTY_NOTICES.zh-CN.md",
21
+ "README.zh-CN.md"
21
22
  ],
22
23
  "scripts": {
23
24
  "postinstall": "node lib/install.cjs"
Binary file
Binary file
package/README.en.md DELETED
@@ -1,70 +0,0 @@
1
- English · [简体中文](README.md)
2
-
3
- # Linkitty
4
-
5
- Connect services on your own computer and keep using them from your phone or desktop.
6
-
7
- Download the CLI tarball from the [official website](https://linkitty.spark-data.cn/?lang=en#downloads). On macOS, install the matching desktop tarball alongside it; Linux installs only the CLI. Follow the system tabs on the website. Public npm registry publication remains deferred.
8
-
9
- linkitty --lang en
10
- linkitty login
11
- linkitty connect codex
12
-
13
- The interface supports Chinese, English, Japanese and Russian. Save a default with `linkitty --lang zh|en|ja|ru`, or override the current process with `LINKITTY_LANG`. Language never changes account region. Desktop and CLI share `~/.linkitty/config/preferences.json`; the desktop reads this preference on its next launch.
14
-
15
- Shared Codex integration currently supports macOS. For first-time integration, if Codex is running independently, finish your tasks and quit Codex before running the connection command. After it succeeds, open Codex normally. Integration neither starts a second executor alongside it nor takes over existing sessions. System background startup uses enabled macOS system proxies; existing proxy environment variables take precedence.
16
-
17
- Claim from your phone:
18
-
19
- linkitty connect codex -qr
20
-
21
- Regions and websites:
22
-
23
- linkitty --region cn
24
- linkitty --region sg --endpoint https://你的新加坡入口/gateway
25
- linkitty connect web 127.0.0.1:8080 --name 我的工作台
26
-
27
- The first background connection registers a service for the current system user. CLI and desktop share one Linkitty connection process, restored after computer login and restarted by the system after unexpected exits. Connections are unreachable while the computer sleeps or is offline; the business application must also remain running. Pause a connection with `linkitty disconnect ID`, or stop the system background service with `linkitty service uninstall`; configuration and bindings are retained.
28
-
29
- Permanently delete a service with `linkitty delete 实例ID --yes`; this requires a platform version supporting the deletion API. It physically deletes relay registration, shares, and access grants while retaining computer projects, native sessions, and files. Reconnecting afterward requires an explicit connection command. Desktop and phone provide the same deletion action in service details under My Services, separate from disablement.
30
-
31
- The cn development version uses the currently verified endpoint; sg/us require configuration before use. Switching regions does not migrate accounts or services. Platform accounts and Codex model login are managed separately.
32
-
33
- Six connection types are supported:
34
-
35
- ```sh
36
- linkitty connect codex
37
- # Optional: linkitty connect codex --workspace /absolute/project
38
- linkitty connect pi --workspace /absolute/project --executable /absolute/path/to/pi
39
- linkitty connect claude --workspace /absolute/project --profile /absolute/private/claude/profile.json
40
- linkitty connect dsh --workspace /absolute/project --profile /absolute/private/deepseek/profile.json
41
- linkitty connect web 127.0.0.1:8080
42
- linkitty connect api 127.0.0.1:9000 --routes /absolute/api-routes.json
43
- ```
44
-
45
- All `connect` commands accept `-qr`; when logged in, omit it to claim directly. DSH means DeepSeek Harness. Codex/Pi use their existing login environments, while Claude/DSH use existing private runtime configuration. Linkitty does not replace model login. Pi, Claude, and DSH retain their own tool permissions and approval mechanisms and must not be treated as a Codex sandbox. Use `--agent-dir` for a custom Pi authentication directory. All four engines require project directories separate from Linkitty's private directories; using the entire user home directory as a project is discouraged.
46
-
47
- Minimal API route file:
48
-
49
- ```json
50
- {
51
- "business_api": { "id": "my.service.api", "version": "1.0.0" },
52
- "routes": [
53
- { "methods": ["GET"], "path": "/health", "requires": ["api.read"] },
54
- { "methods": ["POST"], "path": "/jobs", "requires": ["api.write"] }
55
- ]
56
- }
57
- ```
58
-
59
- The upstream is fixed to a local HTTP origin. HTTP/SSE retain original business paths; WebSocket routes require `websocket: true`. Unknown routes and requests missing required capabilities are rejected. Changing the permission file creates a new instance; pause the old connection yourself.
60
-
61
- ```sh
62
- linkitty list
63
- linkitty access 实例ID --capabilities api.read --output /absolute/private/access.json
64
- ```
65
-
66
- The private file contains a short-lived `endpoint` and `access_token` with mode 600. Requests use `Relay-Authorization: Bearer <access_token>`; never commit or share this file. Generate a new file after expiry without overwriting an existing one. CLI never prints credentials to stdout.
67
-
68
- The CLI contains macOS ARM64 and Linux x64 payloads; the installer selects the system and checks its hash. Linux targets Ubuntu 24.04 / glibc 2.39; binaries and installation are checked in containers, while real user-level systemd startup remains unverified. Windows uses the WSL 2 / Ubuntu 24.04 guide and has not been tested on a real machine. Native Windows, Linux ARM64, musl and other systems without payloads fail explicitly. Codex desktop sharing supports macOS only; use Pi, Claude or DSH on Linux / WSL.
69
-
70
- Coding connections do not bind a project by default and use the user's `~` when an execution directory is needed; explicit `--workspace` settings are retained. The first Codex connection automatically prepares the fixed shared service and subsequent launch configuration. If a standalone Codex is currently running, restart it normally once after finishing its tasks. Later launches work directly without a shared-control script.