askhuman 0.1.0

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 ADDED
@@ -0,0 +1,57 @@
1
+ # askhuman
2
+
3
+ 跨平台「Human-in-the-loop」交互工具。命令行 `AskHuman` 在 AI 助手需要确认/补充时弹出窗口(或经 Telegram)收集人类回应,并把结果按固定区块写到 stdout。
4
+
5
+ 底层为单一可执行文件(Tauri 2 / Rust)。本 npm 包通过「平台子包」分发:安装时只会拉取与当前平台匹配的一个二进制。
6
+
7
+ ## 单独使用
8
+
9
+ ```bash
10
+ npm i -g askhuman
11
+ AskHuman "要不要继续?" -o "继续" -o "停止"
12
+ ```
13
+
14
+ ## 作为依赖(程序集成)
15
+
16
+ ```bash
17
+ npm i askhuman
18
+ ```
19
+
20
+ ```js
21
+ import { getBinaryPath, isAvailable } from "askhuman";
22
+ import { spawnSync } from "node:child_process";
23
+
24
+ if (!isAvailable()) {
25
+ // 二进制未就位:跳过人工确认环节,避免阻塞流程
26
+ } else {
27
+ const r = spawnSync(getBinaryPath(), ["要继续吗?", "-o", "继续", "-o", "停止"], {
28
+ encoding: "utf8",
29
+ });
30
+ if (r.status === 3) {
31
+ // 当前环境无任何可用 channel(GUI 打不开且未配置 Telegram):降级处理
32
+ } else if (r.status === 0) {
33
+ // 成功:解析 r.stdout 的结果区块
34
+ console.log(r.stdout);
35
+ }
36
+ }
37
+ ```
38
+
39
+ `getBinaryPath()` 解析顺序:环境变量 `HUMANINLOOP_BINARY` → 平台子包 → 系统 `PATH`。
40
+
41
+ ## 退出码契约
42
+
43
+ | 退出码 | 含义 |
44
+ |---|---|
45
+ | `0` | 成功拿到结果,或用户取消(输出 `[状态]`) |
46
+ | `3` | 无任何可用 channel(本地弹窗打不开且未配置 Telegram)——下游应降级 |
47
+ | `1` | 其他异常 |
48
+
49
+ stdout 只含结果区块(`[选择的选项]`/`[用户输入]`/`[图片]`/`[状态]`),所有日志与报错走 stderr。
50
+
51
+ ## 平台与系统依赖
52
+
53
+ 支持 macOS(arm64/x64)、Windows(x64)、Linux(x64)。
54
+
55
+ > Linux 运行 GUI 弹窗需系统具备 WebKitGTK(如 `libwebkit2gtk-4.1`)。若缺失且配置了 Telegram,会自动改走 Telegram;若两者都不可用,则以退出码 `3` 提示降级。
56
+
57
+ 更多信息见项目仓库:<https://github.com/Naituw/HumanInLoop>
package/bin/cli.js ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // 全局命令入口(npm i -g humaninloop 后的 `AskHuman`):
5
+ // 解析当前平台二进制并透传 argv 与退出码。
6
+ // 下游程序集成请直接使用 require("humaninloop").getBinaryPath(),不必经此 shim。
7
+
8
+ const { spawn } = require("child_process");
9
+ const { getBinaryPath } = require("../index.js");
10
+
11
+ const bin = getBinaryPath();
12
+ if (!bin) {
13
+ process.stderr.write(
14
+ "humaninloop: 未找到当前平台的 AskHuman 二进制。\n" +
15
+ "请确认对应平台子包已安装,或设置环境变量 HUMANINLOOP_BINARY 指向二进制。\n"
16
+ );
17
+ process.exit(1);
18
+ }
19
+
20
+ const child = spawn(bin, process.argv.slice(2), { stdio: "inherit" });
21
+
22
+ child.on("error", (err) => {
23
+ process.stderr.write(`humaninloop: 启动失败: ${err.message}\n`);
24
+ process.exit(1);
25
+ });
26
+
27
+ child.on("exit", (code, signal) => {
28
+ if (signal) {
29
+ process.kill(process.pid, signal);
30
+ } else {
31
+ process.exit(code == null ? 0 : code);
32
+ }
33
+ });
package/index.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 返回当前平台的 AskHuman 二进制绝对路径。
3
+ * 解析顺序:环境变量 `HUMANINLOOP_BINARY` → 平台子包 → 系统 `PATH`。
4
+ * 找不到时返回 `null`。
5
+ */
6
+ export declare function getBinaryPath(): string | null;
7
+
8
+ /**
9
+ * 二进制是否已就位且可执行。
10
+ * 注意:仅代表二进制可被调用,不代表 GUI 弹窗可用;
11
+ * 具体哪个 channel 可用由二进制运行期决定(见退出码契约:3 = 无可用 channel)。
12
+ */
13
+ export declare function isAvailable(): boolean;
package/index.js ADDED
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+
3
+ // 解析当前平台的 AskHuman 二进制路径,供下游程序集成调用。
4
+ // 解析顺序:环境变量 HUMANINLOOP_BINARY → 平台子包 → 系统 PATH。
5
+
6
+ const fs = require("fs");
7
+ const path = require("path");
8
+
9
+ // platformKey -> 平台子包名
10
+ const PLATFORM_PACKAGES = {
11
+ "darwin-arm64": "@humaninloop/darwin-arm64",
12
+ "darwin-x64": "@humaninloop/darwin-x64",
13
+ "win32-x64": "@humaninloop/win32-x64",
14
+ "linux-x64": "@humaninloop/linux-x64",
15
+ };
16
+
17
+ function binName() {
18
+ return process.platform === "win32" ? "AskHuman.exe" : "AskHuman";
19
+ }
20
+
21
+ function platformKey() {
22
+ return `${process.platform}-${process.arch}`;
23
+ }
24
+
25
+ function isExecutableFile(p) {
26
+ try {
27
+ const st = fs.statSync(p);
28
+ if (!st.isFile()) return false;
29
+ } catch {
30
+ return false;
31
+ }
32
+ if (process.platform === "win32") return true; // X_OK 在 Windows 无意义
33
+ try {
34
+ fs.accessSync(p, fs.constants.X_OK);
35
+ return true;
36
+ } catch {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ function fromEnv() {
42
+ const p = process.env.HUMANINLOOP_BINARY;
43
+ return p && fs.existsSync(p) ? p : null;
44
+ }
45
+
46
+ function fromPlatformPackage() {
47
+ const pkg = PLATFORM_PACKAGES[platformKey()];
48
+ if (!pkg) return null;
49
+ try {
50
+ return require.resolve(`${pkg}/bin/${binName()}`);
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ function fromPath() {
57
+ const exe = binName();
58
+ const dirs = (process.env.PATH || "").split(path.delimiter);
59
+ for (const dir of dirs) {
60
+ if (!dir) continue;
61
+ const candidate = path.join(dir, exe);
62
+ if (fs.existsSync(candidate)) return candidate;
63
+ }
64
+ return null;
65
+ }
66
+
67
+ // 返回二进制绝对路径;找不到返回 null。
68
+ function getBinaryPath() {
69
+ return fromEnv() || fromPlatformPackage() || fromPath() || null;
70
+ }
71
+
72
+ // 二进制是否已就位且可执行。
73
+ // 注意:仅代表二进制可被调用,不代表 GUI 弹窗可用;
74
+ // 具体哪个 channel 可用由二进制运行期决定(见退出码契约)。
75
+ function isAvailable() {
76
+ const p = getBinaryPath();
77
+ return p != null && isExecutableFile(p);
78
+ }
79
+
80
+ module.exports = { getBinaryPath, isAvailable };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "askhuman",
3
+ "version": "0.1.0",
4
+ "description": "Human-in-the-loop interaction tool. The `AskHuman` CLI pops up a window (or uses Telegram) to collect human input and prints the result to stdout — usable standalone or as a dependency.",
5
+ "license": "MIT",
6
+ "author": "Naituw",
7
+ "homepage": "https://github.com/Naituw/HumanInLoop",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/Naituw/HumanInLoop.git"
11
+ },
12
+ "type": "commonjs",
13
+ "main": "index.js",
14
+ "types": "index.d.ts",
15
+ "bin": {
16
+ "AskHuman": "bin/cli.js"
17
+ },
18
+ "files": [
19
+ "index.js",
20
+ "index.d.ts",
21
+ "bin",
22
+ "README.md"
23
+ ],
24
+ "engines": {
25
+ "node": ">=16"
26
+ },
27
+ "keywords": [
28
+ "human-in-the-loop",
29
+ "cli",
30
+ "tauri",
31
+ "ai",
32
+ "askhuman",
33
+ "prompt"
34
+ ],
35
+ "optionalDependencies": {
36
+ "@humaninloop/darwin-arm64": "0.1.0",
37
+ "@humaninloop/darwin-x64": "0.1.0",
38
+ "@humaninloop/win32-x64": "0.1.0",
39
+ "@humaninloop/linux-x64": "0.1.0"
40
+ }
41
+ }