omnicrawl-cli 0.1.71 → 0.2.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 (3) hide show
  1. package/README.md +59 -75
  2. package/bin/omnicrawl.mjs +130 -29
  3. package/package.json +8 -7
package/README.md CHANGED
@@ -1,75 +1,59 @@
1
- # omnicrawl-cli
2
-
3
- OmniCrawl 启动器:按 `platform-arch` 选包node_modules的内核二进制,把 stdio 转交给它。
4
- 它不做协议转换——宿主直接与内核进程对话(协议见 `rust/docs/protocol-v1.md`)。
5
-
6
- ## 分发形态
7
-
8
- | npm 包 | 内容 |
9
- | --- | --- |
10
- | `omnicrawl-cli` | JS 启动器(`bin/omnicrawl.mjs`) |
11
- | `@omnicrawl/cli-linux-x64` | 内核二进制(Linux x64,musl 静态) |
12
- | `@omnicrawl/cli-linux-arm-musl` | 内核二进制(Linux armv7,musl 静态,嵌入式目标) |
13
- | `@omnicrawl/cli-linux-arm64-musl` | 内核二进制(Linux arm64,嵌入式目标) |
14
- | `@omnicrawl/cli-win32-x64` | 内核二进制(Windows x64) |
15
-
16
- **平台分包、`os`/`cpu` 与平台依赖只出现在发布产物里**(`dist/npm/`)。仓库内的 `packages/cli*/package.json`
17
- 刻意不带 `os`/`cpu`、也不声明平台分包依赖:npm 安装时会校验工作区成员的 `os`/`cpu`,带上就直接
18
- `notsup` 失败。发布产物由 `scripts/prepare.mjs` 生成。
19
-
20
- ## 本地开发
21
-
22
- ```bash
23
- cargo build --release -p omnicrawl-cli # 产物:rust/target/release/omnicrawl[.exe]
24
- node packages/cli/scripts/prepare.mjs # 暂存发布产物(缺的平台只警告)
25
- npm test -w omnicrawl-cli # e2e:真二进制 + 协议 v1
26
- OMNICRAWL_BINARY=/path/to/omnicrawl node packages/cli/bin/omnicrawl.mjs --version
27
- ```
28
-
29
- 工作区里 `@omnicrawl/cli-*` 都是 workspace 成员,启动器靠 `createRequire` 解析到它们;
30
- `OMNICRAWL_BINARY` 是直接指定二进制的逃生口(e2e 用的就是它)。e2e 需要 `rust/target/release`
31
- 里有产物,否则跳过并打印构建命令。
32
-
33
- ## 发布
34
-
35
- 手动发布前先 `npm login`(CI 走 Trusted Publishing,无需登录与 token)。
36
-
37
- 1. 交叉编译各平台(Linux 走 musl 静态,嵌入式目标优先):
38
-
39
- ```bash
40
- cargo build --release -p omnicrawl-cli --target x86_64-pc-windows-msvc
41
- cargo build --release -p omnicrawl-cli --target x86_64-unknown-linux-musl
42
- cargo build --release -p omnicrawl-cli --target aarch64-unknown-linux-musl
43
- cargo build --release -p omnicrawl-cli --target armv7-unknown-linux-musleabihf
44
- ```
45
-
46
- 非 Linux 宿主没有 musl 链接器,Linux 目标改用 `cargo zigbuild`(需 `zig` 与
47
- `rustup target add <triple>`):
48
-
49
- ```bash
50
- cargo zigbuild --release -p omnicrawl-cli --target armv7-unknown-linux-musleabihf
51
- ```
52
-
53
- 2. 门槛检查(缺任何平台会失败并列出缺失平台):
54
-
55
- ```bash
56
- node packages/cli/scripts/prepare.mjs --require-all
57
- ```
58
-
59
- 3. 按顺序发布暂存产物:平台包在前,启动器在后(启动器的 `optionalDependencies` 是精确版本,顺序反了会装不到):
60
-
61
- ```bash
62
- npm publish dist/npm/cli-linux-x64
63
- npm publish dist/npm/cli-linux-arm-musl
64
- npm publish dist/npm/cli-linux-arm64-musl
65
- npm publish dist/npm/cli-win32-x64
66
- npm publish dist/npm/cli
67
- ```
68
-
69
- 脚本跑完会把这几行命令直接打出来。
70
-
71
- 4. 版本reproducibility:平台分包与启动器同版本;平台包只装二进制,升级它们就是换内核。
72
-
73
- ## 发布前自检
74
-
75
- `cargo test --workspace`、`npm test --workspaces`,以及 `prepare --require-all` 门槛。
1
+ # omnicrawl-cli
2
+
3
+ OmniCrawl 启动器:按 `platform-arch` 选平台分包,默认启动**完整宿主**(工作台 / `api` / 插件 CLI);
4
+ `kernel` 子命令把 stdio 交给内核二进制(协议调试与 e2e)。
5
+
6
+ ## 分发形态
7
+
8
+ | npm 包 | 内容 |
9
+ | --- | --- |
10
+ | `omnicrawl-cli` | JS 启动器(`bin/omnicrawl.mjs`) |
11
+ | `@omnicrawl/cli-win32-x64` | 内核二进制 + 宿主载荷(Windows x64 |
12
+ | `@omnicrawl/cli-linux-x64` | 内核二进制 + 宿主载荷(Linux x64,内核为 musl 静态) |
13
+ | `@omnicrawl/cli-linux-arm64-musl` | 内核二进制 + 宿主载荷(Linux arm64 |
14
+ | `@omnicrawl/cli-win32-ia32` | 仅内核(32 位 Windows 缺运行期轮子) |
15
+ | `@omnicrawl/cli-linux-arm-musl` | 仅内核(armv7 嵌入式目标) |
16
+
17
+ 宿主载荷是 PyInstaller one-dir 产物(`host/`):CPython 运行时、依赖、代码与资源都在里面,
18
+ 用户机器不需要 Python pip。没有宿主载荷的平台由启动器退回内核直连,并打印这一边界。
19
+
20
+ **平台分包、`os`/`cpu` 与平台依赖只出现在发布产物里**(`dist/npm/`)。仓库内的 `packages/cli*/package.json`
21
+ 刻意不带 `os`/`cpu`、也不声明平台分包依赖:npm 安装时会校验工作区成员的 `os`/`cpu`,带上就直接
22
+ `notsup` 失败。发布产物由 `scripts/prepare.mjs` 生成。
23
+
24
+ ## 命令面
25
+
26
+ ```
27
+ omnicrawl 启动工作台(TUI)
28
+ omnicrawl api 启动本地 HTTP 服务(无头/服务器场景)
29
+ omnicrawl plugin ... 插件管理
30
+ omnicrawl kernel ... 直连内核进程(协议调试)
31
+ omnicrawl --version 打印启动器、平台包、宿主与内核版本
32
+ ```
33
+
34
+ 无交互式终端时宿主会直接给出改用 `api` 的提示并以 2 退出,不会停在协议等待状态。
35
+
36
+ ## 本地开发
37
+
38
+ ```bash
39
+ cargo build --release -p omnicrawl-cli # 产物:rust/target/release/omnicrawl[.exe]
40
+ node packages/cli/scripts/build-host.mjs # 宿主载荷:dist/host/<platform>/payload
41
+ node packages/cli/scripts/prepare.mjs # 暂存发布产物(缺的平台只警告)
42
+ node packages/cli/scripts/host-smoke.mjs # 宿主冒烟:无终端提示 + 退出码
43
+ node packages/cli/scripts/install-smoke.mjs # 安装冒烟:干净前缀里真装真跑
44
+ npm test -w omnicrawl-cli # e2e:真二进制 + 协议 v1
45
+ ```
46
+
47
+ 宿主载荷只能与构建机同架构(里面有 CPython 与原生扩展),交叉构建不成立:
48
+ 其它平台由 CI 在各自 runner 上构建(见 `.github/workflows/publish-npm.yml`)。
49
+
50
+ 两个逃生口:`OMNICRAWL_HOST` 直接指定宿主、`OMNICRAWL_BINARY` 直接指定内核(此时启动器是纯透传)。
51
+
52
+ ## 发布
53
+
54
+ 1. 升 `packages/cli/package.json` 版本(平台包与启动器同版本)。
55
+ 2. 推 `npm-v<版本>` tag(CI 会校验 tag 与包版本一致)或手动触发 workflow:
56
+ 矩阵各平台构建内核(+ 宿主)→ 宿主冒烟 → 暂存并上传产物;
57
+ 发布任务下载全部产物 → 暂存启动器 → 安装冒烟 → 平台包在前、启动器在后依次发布。
58
+
59
+ 发布前自检:`cargo test --workspace`、`npm test --workspaces`、`prepare.mjs --require-all --require-host`。
package/bin/omnicrawl.mjs CHANGED
@@ -1,9 +1,12 @@
1
- #!/usr/bin/env node
2
- // OmniCrawl 启动器:按 platform/arch 选出内核二进制并转交 stdio。
1
+ #!/usr/bin/env node
2
+ // OmniCrawl 启动器:按 platform/arch 选出平台分包,默认启动完整宿主(工作台 / api / plugin 指令),
3
+ // `kernel` 子命令把 stdio 交给内核二进制(协议调试与 e2e)。
3
4
  //
4
- // 它不做任何协议转换——宿主直接与内核进程对话,启动器只是平台分包的解析器。
5
+ // 两个逃生口(开发与协议测试用):
6
+ // OMNICRAWL_HOST=<可执行文件> 直接指定宿主,跳过平台分包
7
+ // OMNICRAWL_BINARY=<可执行文件> 直接指定内核;此时启动器退化为纯透传,便于协议测试
5
8
  import { spawn } from 'node:child_process'
6
- import { existsSync } from 'node:fs'
9
+ import { chmodSync, existsSync, readFileSync, statSync } from 'node:fs'
7
10
  import { createRequire } from 'node:module'
8
11
  import { dirname, join } from 'node:path'
9
12
  import process from 'node:process'
@@ -16,44 +19,142 @@ const PLATFORM_PACKAGES = {
16
19
  'win32-ia32': '@omnicrawl/cli-win32-ia32',
17
20
  }
18
21
 
19
- function resolveBinary() {
20
- // 开发与测试用的逃生口:跳过分包直接指定二进制。
21
- if (process.env.OMNICRAWL_BINARY) return process.env.OMNICRAWL_BINARY
22
+ const IS_WINDOWS = process.platform === 'win32'
23
+ const HOST_FILE = IS_WINDOWS ? 'omnicrawl-host.exe' : 'omnicrawl-host'
24
+ const KERNEL_FILE = IS_WINDOWS ? 'omnicrawl.exe' : 'omnicrawl'
25
+ const KERNEL_COMMAND = 'kernel'
22
26
 
27
+ const launcherVersion = JSON.parse(
28
+ readFileSync(new URL('../package.json', import.meta.url), 'utf8'),
29
+ ).version
30
+
31
+ function fail(message) {
32
+ console.error(message)
33
+ process.exit(1)
34
+ }
35
+
36
+ /** 解析平台分包根目录;拿不到分包时抛出可直接展示的错误。 */
37
+ function resolvePlatform() {
23
38
  const key = `${process.platform}-${process.arch}`
24
39
  const packageName = PLATFORM_PACKAGES[key]
25
40
  if (!packageName) {
26
41
  throw new Error(`不支持的平台 ${key};已支持:${Object.keys(PLATFORM_PACKAGES).join('、')}`)
27
42
  }
28
-
29
43
  const require = createRequire(import.meta.url)
30
- const packageRoot = dirname(require.resolve(`${packageName}/package.json`))
31
- const binary = join(packageRoot, 'bin', process.platform === 'win32' ? 'omnicrawl.exe' : 'omnicrawl')
32
- if (!existsSync(binary)) {
33
- throw new Error(`平台分包 ${packageName} 里没有二进制:${binary}\n请重装该包,或用 OMNICRAWL_BINARY 指定。`)
44
+ return { packageName, root: dirname(require.resolve(`${packageName}/package.json`)) }
45
+ }
46
+
47
+ /** 目标可执行文件:宿主优先,内核兜底;两个环境变量各自覆盖。 */
48
+ function resolveTargets() {
49
+ if (process.env.OMNICRAWL_HOST) {
50
+ return { host: process.env.OMNICRAWL_HOST, kernel: process.env.OMNICRAWL_BINARY ?? null, passthrough: false }
51
+ }
52
+ if (process.env.OMNICRAWL_BINARY) {
53
+ return { host: null, kernel: process.env.OMNICRAWL_BINARY, passthrough: true }
54
+ }
55
+ const { packageName, root } = resolvePlatform()
56
+ return {
57
+ host: join(root, 'host', HOST_FILE),
58
+ kernel: join(root, 'bin', KERNEL_FILE),
59
+ packageName,
60
+ root,
61
+ passthrough: false,
34
62
  }
35
- return binary
36
63
  }
37
64
 
38
- let binary
39
- try {
40
- binary = resolveBinary()
41
- } catch (error) {
42
- console.error(`omnicrawl:${error.message}`)
43
- process.exit(1)
65
+ function readPayloadMeta(root) {
66
+ if (!root) return null
67
+ try {
68
+ return JSON.parse(readFileSync(join(root, 'version.json'), 'utf8'))
69
+ } catch {
70
+ return null
71
+ }
44
72
  }
45
73
 
46
- const child = spawn(binary, process.argv.slice(2), { stdio: 'inherit' })
74
+ function printVersion(targets) {
75
+ const meta = readPayloadMeta(targets.root)
76
+ const lines = [`omnicrawl ${launcherVersion}`]
77
+ lines.push(
78
+ targets.packageName
79
+ ? `${targets.packageName} ${meta?.version ?? '未知'}(${process.platform}-${process.arch} 平台包)`
80
+ : `omnicrawl-cli ${launcherVersion}(启动器)`,
81
+ )
82
+ if (meta?.hostVersion) lines.push(`omnicrawl-agent ${meta.hostVersion}(宿主)`)
83
+ if (meta?.kernelVersion) lines.push(`omnicrawl ${meta.kernelVersion}(内核)`)
84
+ console.log(lines.join('\n'))
85
+ }
47
86
 
48
- child.on('error', (error) => {
49
- console.error(`omnicrawl:无法启动 ${binary}:${error.message}`)
50
- process.exit(1)
51
- })
87
+ function printHelp() {
88
+ console.log(
89
+ `用法:omnicrawl [选项] [指令]\n\n\
90
+ 无参数 启动工作台(TUI)\n\
91
+ api 启动本地 HTTP 服务(无头/服务器场景)\n\
92
+ plugin ... 插件管理\n\
93
+ kernel ... 直连内核进程(协议调试)\n\n\
94
+ --resume <会话 ID> 启动时恢复指定会话\n\
95
+ --version, -V 打印启动器、平台包、宿主与内核版本\n\
96
+ --help, -h 打印本说明`,
97
+ )
98
+ }
52
99
 
53
- child.on('exit', (code, signal) => {
54
- if (signal) {
55
- console.error(`omnicrawl:内核被信号 ${signal} 终止`)
100
+ /** npm 打包会把未在 bin 里声明的文件权限位归一化成 0644,POSIX 下按需补回可执行位。 */
101
+ function ensureExecutable(path) {
102
+ if (IS_WINDOWS) return
103
+ try {
104
+ const mode = statSync(path).mode
105
+ if ((mode & 0o111) === 0) chmodSync(path, mode | 0o755)
106
+ } catch {
107
+ // 权限改不动(只读文件系统等)时交给 spawn 的错误说明原因。
108
+ }
109
+ }
110
+
111
+ /** 把 stdio 交给目标可执行文件,并把它的退出码/信号作为本次退出状态。 */
112
+ function run(executable, args) {
113
+ if (!executable || !existsSync(executable)) {
114
+ fail(`omnicrawl:找不到可执行文件 ${executable ?? '(未解析到)'}。请重装对应的平台包。`)
115
+ }
116
+ ensureExecutable(executable)
117
+ const child = spawn(executable, args, { stdio: 'inherit' })
118
+
119
+ child.on('error', (error) => {
120
+ console.error(`omnicrawl:无法启动 ${executable}:${error.message}`)
56
121
  process.exit(1)
122
+ })
123
+
124
+ child.on('exit', (code, signal) => {
125
+ if (signal) {
126
+ console.error(`omnicrawl:${executable} 被信号 ${signal} 终止`)
127
+ process.exit(1)
128
+ }
129
+ process.exit(code ?? 0)
130
+ })
131
+ }
132
+
133
+ function main() {
134
+ let targets
135
+ try {
136
+ targets = resolveTargets()
137
+ } catch (error) {
138
+ fail(`omnicrawl:${error.message}`)
57
139
  }
58
- process.exit(code ?? 0)
59
- })
140
+
141
+ const args = process.argv.slice(2)
142
+
143
+ // 内核透传模式(OMNICRAWL_BINARY):启动器不介入 stdio 上的协议帧。
144
+ if (targets.passthrough) return run(targets.kernel, args)
145
+
146
+ if (args[0] === KERNEL_COMMAND) return run(targets.kernel, args.slice(1))
147
+ if (args.includes('--version') || args.includes('-V')) return printVersion(targets)
148
+ if (args.includes('--help') || args.includes('-h')) return printHelp()
149
+
150
+ if (targets.host && existsSync(targets.host)) return run(targets.host, args)
151
+
152
+ // 嵌入式目标(armv7 等)没有宿主载荷:退回内核直连,并说明这一平台的边界。
153
+ console.error(
154
+ `omnicrawl:平台包 ${targets.packageName ?? '(未指定)'} 没有宿主载荷,退回内核直连;` +
155
+ '该平台只支持协议模式(用 omnicrawl kernel 显式进入)。',
156
+ )
157
+ return run(targets.kernel, args)
158
+ }
159
+
160
+ main()
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "omnicrawl-cli",
3
- "version": "0.1.71",
4
- "description": "OmniCrawl 启动器:按平台选择内核二进制并转交 stdio",
3
+ "version": "0.2.1",
4
+ "description": "OmniCrawl 启动器:按平台启动完整宿主(工作台 / api / 插件)",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/cccchiban/omnicrawl.git",
@@ -20,14 +20,15 @@
20
20
  },
21
21
  "scripts": {
22
22
  "test": "node --test",
23
+ "build:host": "node scripts/build-host.mjs",
23
24
  "prepare:binaries": "node scripts/prepare.mjs"
24
25
  },
25
26
  "license": "MIT",
26
27
  "optionalDependencies": {
27
- "@omnicrawl/cli-win32-x64": "0.1.71",
28
- "@omnicrawl/cli-win32-ia32": "0.1.71",
29
- "@omnicrawl/cli-linux-x64": "0.1.71",
30
- "@omnicrawl/cli-linux-arm-musl": "0.1.71",
31
- "@omnicrawl/cli-linux-arm64-musl": "0.1.71"
28
+ "@omnicrawl/cli-win32-x64": "0.2.1",
29
+ "@omnicrawl/cli-win32-ia32": "0.2.1",
30
+ "@omnicrawl/cli-linux-x64": "0.2.1",
31
+ "@omnicrawl/cli-linux-arm-musl": "0.2.1",
32
+ "@omnicrawl/cli-linux-arm64-musl": "0.2.1"
32
33
  }
33
34
  }