hilos-agent 0.9.3 → 0.9.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 +9 -5
- package/bin/hilos-agent.mjs +17 -3
- package/package.json +1 -1
- package/src/join-input.mjs +80 -0
package/README.md
CHANGED
|
@@ -25,16 +25,20 @@ machine until then).
|
|
|
25
25
|
|
|
26
26
|
## Quick start
|
|
27
27
|
|
|
28
|
-
In hilos: open your agent's profile → **Connect** →
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
needed
|
|
28
|
+
In hilos: open your agent's profile → **Connect** → **Run in channel**. Copy the
|
|
29
|
+
terminal command and run it from inside your repo's folder. It waits at a hidden
|
|
30
|
+
prompt; copy the private join code from Hilos and paste it there. The daemon
|
|
31
|
+
matches the repo by its git remote, so no config is needed, and the reusable
|
|
32
|
+
credential never enters shell history or the process list:
|
|
32
33
|
|
|
33
34
|
```sh
|
|
34
35
|
cd ~/code/your-repo
|
|
35
|
-
npx hilos-agent@latest --join
|
|
36
|
+
npx hilos-agent@latest --join-stdin # then paste the private join code when asked
|
|
36
37
|
```
|
|
37
38
|
|
|
39
|
+
Previously copied `--join <blob>` commands remain compatible. New commands use
|
|
40
|
+
stdin because the blob contains the agent token and should not live in argv.
|
|
41
|
+
|
|
38
42
|
Running from elsewhere, or want to map several repos explicitly? Use a config:
|
|
39
43
|
|
|
40
44
|
```jsonc
|
package/bin/hilos-agent.mjs
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
//
|
|
6
6
|
// Usage:
|
|
7
7
|
// hilos-agent --join <blob> connect with a copy-paste link from hilos
|
|
8
|
+
// hilos-agent --join-stdin read a private link without exposing it in argv
|
|
8
9
|
// hilos-agent init write a starter config (~/.hilos/agent.json)
|
|
9
10
|
// hilos-agent run with the resolved config (default)
|
|
10
11
|
// hilos-agent run same as above, explicit
|
|
@@ -20,6 +21,7 @@ import { readFileSync } from "node:fs";
|
|
|
20
21
|
import { fileURLToPath } from "node:url";
|
|
21
22
|
|
|
22
23
|
import { resolveConfig, decodeJoin, writeStarterConfig, GLOBAL_CONFIG } from "../src/config.mjs";
|
|
24
|
+
import { readPrivateJoin } from "../src/join-input.mjs";
|
|
23
25
|
import { run } from "../src/run.mjs";
|
|
24
26
|
import { hookMain, hooksMain } from "../src/hook.mjs";
|
|
25
27
|
|
|
@@ -34,6 +36,7 @@ function parseArgs(argv) {
|
|
|
34
36
|
for (let i = 0; i < argv.length; i++) {
|
|
35
37
|
const a = argv[i];
|
|
36
38
|
if (a === "--join") flags.join = argv[++i];
|
|
39
|
+
else if (a === "--join-stdin") flags.joinStdin = true;
|
|
37
40
|
else if (a === "--config") flags.config = argv[++i];
|
|
38
41
|
else if (a === "--channel") flags.channelId = argv[++i];
|
|
39
42
|
else if (a === "--url") flags.url = argv[++i];
|
|
@@ -62,6 +65,7 @@ function parseArgs(argv) {
|
|
|
62
65
|
const HELP = `hilos-agent — your coding agent as a teammate in hilos
|
|
63
66
|
|
|
64
67
|
hilos-agent --join <blob> connect using a link copied from hilos
|
|
68
|
+
hilos-agent --join-stdin paste the private link at a no-echo prompt
|
|
65
69
|
hilos-agent init write a starter config to ~/.hilos/agent.json
|
|
66
70
|
hilos-agent run the daemon (watch @mentions, propose diffs)
|
|
67
71
|
hilos-agent hooks install stream this repo's Codex, Claude, and Cursor
|
|
@@ -118,9 +122,18 @@ async function main() {
|
|
|
118
122
|
return;
|
|
119
123
|
}
|
|
120
124
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
125
|
+
if (flags.join && flags.joinStdin) {
|
|
126
|
+
console.error("Use either --join or --join-stdin, not both.");
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
const joinBlob = flags.joinStdin ? await readPrivateJoin() : flags.join;
|
|
130
|
+
if (flags.joinStdin && !joinBlob) {
|
|
131
|
+
console.error("No private join code was provided. Re-copy it from hilos.");
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
const joinPayload = joinBlob ? decodeJoin(joinBlob) : undefined;
|
|
135
|
+
if (joinBlob && !joinPayload) {
|
|
136
|
+
console.error("That private join code is invalid. Re-copy it from hilos.");
|
|
124
137
|
process.exit(1);
|
|
125
138
|
}
|
|
126
139
|
|
|
@@ -140,6 +153,7 @@ async function main() {
|
|
|
140
153
|
// run (default) — when --join is passed without init, connect straight away.
|
|
141
154
|
const cliFlags = { ...flags };
|
|
142
155
|
delete cliFlags.join;
|
|
156
|
+
delete cliFlags.joinStdin;
|
|
143
157
|
delete cliFlags.help;
|
|
144
158
|
const cfg = resolveConfig({ flags: cliFlags, join: joinPayload });
|
|
145
159
|
await run(cfg);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hilos-agent",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "Run your own coding agent (Claude Code / Codex / Cursor) as an autonomous teammate in a hilos channel. Picks up @mentions in channels and threads, makes the change, and opens a PR for review — your code and credentials never leave your machine. (Approve-before-push is available via gate:true.)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const MAX_JOIN_BYTES = 64 * 1024;
|
|
2
|
+
|
|
3
|
+
/** @typedef {import("node:stream").Readable & {
|
|
4
|
+
* isTTY?: boolean,
|
|
5
|
+
* isRaw?: boolean,
|
|
6
|
+
* setRawMode?: (enabled: boolean) => unknown,
|
|
7
|
+
* }} PrivateJoinInput */
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Read a private join blob without putting it in process argv or shell history.
|
|
11
|
+
* Piped stdin is consumed normally. On a terminal, raw mode keeps pasted input
|
|
12
|
+
* off-screen while still supporting backspace and Ctrl-C.
|
|
13
|
+
* @param {PrivateJoinInput} input
|
|
14
|
+
* @param {NodeJS.WritableStream} output
|
|
15
|
+
*/
|
|
16
|
+
export async function readPrivateJoin(input = process.stdin, output = process.stderr) {
|
|
17
|
+
if (!input?.isTTY || typeof input.setRawMode !== "function") {
|
|
18
|
+
let value = "";
|
|
19
|
+
for await (const chunk of input) {
|
|
20
|
+
value += Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
|
|
21
|
+
if (Buffer.byteLength(value, "utf8") > MAX_JOIN_BYTES) {
|
|
22
|
+
throw new Error("The private join code is too large.");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return value.trim();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
output.write("Paste the private join code from hilos, then press Enter: ");
|
|
29
|
+
const wasRaw = Boolean(input.isRaw);
|
|
30
|
+
const wasPaused = typeof input.isPaused === "function" ? input.isPaused() : false;
|
|
31
|
+
const setRawMode = input.setRawMode.bind(input);
|
|
32
|
+
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
let value = "";
|
|
35
|
+
let settled = false;
|
|
36
|
+
|
|
37
|
+
const cleanup = () => {
|
|
38
|
+
input.off("data", onData);
|
|
39
|
+
input.off("error", onError);
|
|
40
|
+
setRawMode(wasRaw);
|
|
41
|
+
if (wasPaused) input.pause();
|
|
42
|
+
};
|
|
43
|
+
const finish = (error) => {
|
|
44
|
+
if (settled) return;
|
|
45
|
+
settled = true;
|
|
46
|
+
cleanup();
|
|
47
|
+
output.write("\n");
|
|
48
|
+
if (error) reject(error);
|
|
49
|
+
else resolve(value.trim());
|
|
50
|
+
};
|
|
51
|
+
const onError = (error) => finish(error);
|
|
52
|
+
const onData = (chunk) => {
|
|
53
|
+
for (const char of String(chunk)) {
|
|
54
|
+
if (char === "\u0003") {
|
|
55
|
+
finish(new Error("Private join cancelled."));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (char === "\r" || char === "\n") {
|
|
59
|
+
finish();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (char === "\u007f" || char === "\b") {
|
|
63
|
+
value = value.slice(0, -1);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
value += char;
|
|
67
|
+
if (Buffer.byteLength(value, "utf8") > MAX_JOIN_BYTES) {
|
|
68
|
+
finish(new Error("The private join code is too large."));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
input.setEncoding?.("utf8");
|
|
75
|
+
setRawMode(true);
|
|
76
|
+
input.on("data", onData);
|
|
77
|
+
input.on("error", onError);
|
|
78
|
+
input.resume();
|
|
79
|
+
});
|
|
80
|
+
}
|