claude-yes 1.84.0 → 1.86.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/dist/{SUPPORTED_CLIS-DM0fJTMR.js → SUPPORTED_CLIS-5njwpFxr.js} +3 -3
- package/dist/cli.js +5 -5
- package/dist/index.js +2 -2
- package/dist/remotes-CFrho898.js +131 -0
- package/dist/remotes-kfUzk-JT.js +3 -0
- package/dist/serve-D0NnTXRD.js +303 -0
- package/dist/subcommands-BDiS305D.js +6 -0
- package/dist/{subcommands-DjO8lthH.js → subcommands-BpGEGOQM.js} +440 -78
- package/dist/{tray-CH_G7aXM.js → tray-DHuD0nEk.js} +1 -1
- package/dist/{ts-Bw6gQKyU.js → ts-DVlEOYHB.js} +2 -2
- package/dist/{versionChecker-CspuhOwO.js → versionChecker-XivGWuhd.js} +2 -2
- package/package.json +1 -1
- package/ts/remotes.ts +161 -0
- package/ts/serve.ts +373 -0
- package/ts/subcommands.spec.ts +478 -35
- package/ts/subcommands.ts +544 -95
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as CLIS_CONFIG } from "./ts-
|
|
1
|
+
import { t as CLIS_CONFIG } from "./ts-DVlEOYHB.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-XivGWuhd.js";
|
|
4
4
|
import "./pidStore-C1JXxoPi.js";
|
|
5
5
|
import "./globalPidIndex-Cr-g75QF.js";
|
|
6
6
|
|
|
@@ -9,4 +9,4 @@ const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
|
|
|
9
9
|
|
|
10
10
|
//#endregion
|
|
11
11
|
export { SUPPORTED_CLIS };
|
|
12
|
-
//# sourceMappingURL=SUPPORTED_CLIS-
|
|
12
|
+
//# sourceMappingURL=SUPPORTED_CLIS-5njwpFxr.js.map
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { n as logger } from "./logger-B9h0djqx.js";
|
|
3
|
-
import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-
|
|
3
|
+
import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-XivGWuhd.js";
|
|
4
4
|
import { argv } from "process";
|
|
5
5
|
import { execFileSync, spawn } from "child_process";
|
|
6
6
|
import ms from "ms";
|
|
@@ -480,7 +480,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
|
|
|
480
480
|
}
|
|
481
481
|
}
|
|
482
482
|
{
|
|
483
|
-
const { isSubcommand, runSubcommand } = await import("./subcommands-
|
|
483
|
+
const { isSubcommand, runSubcommand } = await import("./subcommands-BDiS305D.js");
|
|
484
484
|
if (isSubcommand(process.argv[2])) {
|
|
485
485
|
const code = await runSubcommand(process.argv);
|
|
486
486
|
process.exit(code ?? 0);
|
|
@@ -490,12 +490,12 @@ await checkAndAutoUpdate();
|
|
|
490
490
|
logger.info(versionString());
|
|
491
491
|
const config = parseCliArgs(process.argv);
|
|
492
492
|
if (config.tray) {
|
|
493
|
-
const { startTray } = await import("./tray-
|
|
493
|
+
const { startTray } = await import("./tray-DHuD0nEk.js");
|
|
494
494
|
await startTray();
|
|
495
495
|
await new Promise(() => {});
|
|
496
496
|
}
|
|
497
497
|
{
|
|
498
|
-
const { ensureTray } = await import("./tray-
|
|
498
|
+
const { ensureTray } = await import("./tray-DHuD0nEk.js");
|
|
499
499
|
ensureTray();
|
|
500
500
|
}
|
|
501
501
|
if (config.useRust) {
|
|
@@ -509,7 +509,7 @@ if (config.useRust) {
|
|
|
509
509
|
}
|
|
510
510
|
}
|
|
511
511
|
if (rustBinary) {
|
|
512
|
-
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-
|
|
512
|
+
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-5njwpFxr.js");
|
|
513
513
|
const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
|
|
514
514
|
if (config.verbose) {
|
|
515
515
|
console.log(`[rust] Using binary: ${rustBinary}`);
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-
|
|
1
|
+
import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-DVlEOYHB.js";
|
|
2
2
|
import "./logger-B9h0djqx.js";
|
|
3
|
-
import "./versionChecker-
|
|
3
|
+
import "./versionChecker-XivGWuhd.js";
|
|
4
4
|
import "./pidStore-C1JXxoPi.js";
|
|
5
5
|
import "./globalPidIndex-Cr-g75QF.js";
|
|
6
6
|
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import yaml from "yaml";
|
|
5
|
+
|
|
6
|
+
//#region ts/remotes.ts
|
|
7
|
+
function remotesPath() {
|
|
8
|
+
const dir = process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
|
|
9
|
+
return path.join(dir, "remotes.yaml");
|
|
10
|
+
}
|
|
11
|
+
async function readRemotes() {
|
|
12
|
+
let raw;
|
|
13
|
+
try {
|
|
14
|
+
raw = await readFile(remotesPath(), "utf-8");
|
|
15
|
+
} catch {
|
|
16
|
+
return /* @__PURE__ */ new Map();
|
|
17
|
+
}
|
|
18
|
+
const remotes = (yaml.parse(raw) ?? {}).remotes ?? {};
|
|
19
|
+
const map = /* @__PURE__ */ new Map();
|
|
20
|
+
for (const [alias, cfg] of Object.entries(remotes)) if (cfg && typeof cfg.url === "string" && typeof cfg.token === "string") map.set(alias, {
|
|
21
|
+
url: cfg.url,
|
|
22
|
+
token: cfg.token
|
|
23
|
+
});
|
|
24
|
+
return map;
|
|
25
|
+
}
|
|
26
|
+
async function writeRemoteAlias(alias, config) {
|
|
27
|
+
const remotes = await readRemotes();
|
|
28
|
+
remotes.set(alias, config);
|
|
29
|
+
const doc = {};
|
|
30
|
+
for (const [k, v] of remotes) doc[k] = v;
|
|
31
|
+
await mkdir(path.dirname(remotesPath()), { recursive: true });
|
|
32
|
+
await writeFile(remotesPath(), yaml.stringify({ remotes: doc }));
|
|
33
|
+
}
|
|
34
|
+
async function deleteRemoteAlias(alias) {
|
|
35
|
+
const remotes = await readRemotes();
|
|
36
|
+
remotes.delete(alias);
|
|
37
|
+
const doc = {};
|
|
38
|
+
for (const [k, v] of remotes) doc[k] = v;
|
|
39
|
+
await writeFile(remotesPath(), yaml.stringify({ remotes: doc }));
|
|
40
|
+
}
|
|
41
|
+
/** Parse token@host:port[:keyword] — the `@` is a hard signal this is remote. */
|
|
42
|
+
function parseDirectRemoteSpec(spec) {
|
|
43
|
+
const m = /^([^@]+)@([^:@]+):(\d+)(?::(.+))?$/.exec(spec);
|
|
44
|
+
if (!m) return null;
|
|
45
|
+
const host = m[2];
|
|
46
|
+
const port = parseInt(m[3], 10);
|
|
47
|
+
return {
|
|
48
|
+
token: m[1],
|
|
49
|
+
host,
|
|
50
|
+
port,
|
|
51
|
+
keyword: m[4] || void 0,
|
|
52
|
+
baseUrl: `http://${host}:${port}`
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Resolve a spec to connection details.
|
|
57
|
+
* Accepts:
|
|
58
|
+
* token@host:port[:keyword] — direct
|
|
59
|
+
* alias[:keyword] — looked up in ~/.agent-yes/remotes.yaml
|
|
60
|
+
* Returns null if the spec doesn't match any remote.
|
|
61
|
+
*/
|
|
62
|
+
async function resolveRemoteSpec(spec) {
|
|
63
|
+
const direct = parseDirectRemoteSpec(spec);
|
|
64
|
+
if (direct) return {
|
|
65
|
+
url: direct.baseUrl,
|
|
66
|
+
token: direct.token,
|
|
67
|
+
keyword: direct.keyword
|
|
68
|
+
};
|
|
69
|
+
const colonIdx = spec.indexOf(":");
|
|
70
|
+
const alias = colonIdx >= 0 ? spec.slice(0, colonIdx) : spec;
|
|
71
|
+
const keyword = colonIdx >= 0 ? spec.slice(colonIdx + 1) || void 0 : void 0;
|
|
72
|
+
const cfg = (await readRemotes()).get(alias);
|
|
73
|
+
if (!cfg) return null;
|
|
74
|
+
return {
|
|
75
|
+
url: cfg.url,
|
|
76
|
+
token: cfg.token,
|
|
77
|
+
keyword
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async function cmdRemote(rest) {
|
|
81
|
+
const sub = rest[0];
|
|
82
|
+
if (!sub || sub === "ls" || sub === "list") {
|
|
83
|
+
const remotes = await readRemotes();
|
|
84
|
+
if (remotes.size === 0) {
|
|
85
|
+
process.stdout.write("no remotes configured\n");
|
|
86
|
+
process.stderr.write("\n ay remote add <alias> <url> <token> # add a remote\n ay serve # start server (prints token)\n");
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
for (const [alias, cfg] of remotes) {
|
|
90
|
+
const preview = cfg.token.length > 8 ? cfg.token.slice(0, 8) + "..." : cfg.token;
|
|
91
|
+
process.stdout.write(`${alias}\t${cfg.url}\ttoken:${preview}\n`);
|
|
92
|
+
}
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
if (sub === "add") {
|
|
96
|
+
const [, alias, url, token] = rest;
|
|
97
|
+
if (!alias || !url || !token) {
|
|
98
|
+
process.stderr.write("usage: ay remote add <alias> <url> <token>\n");
|
|
99
|
+
process.stderr.write(" example: ay remote add work-mac http://192.168.1.5:7432 mytoken123\n");
|
|
100
|
+
return 1;
|
|
101
|
+
}
|
|
102
|
+
await writeRemoteAlias(alias, {
|
|
103
|
+
url,
|
|
104
|
+
token
|
|
105
|
+
});
|
|
106
|
+
process.stdout.write(`remote '${alias}' added → ${url}\n`);
|
|
107
|
+
process.stderr.write(`\n ay ls ${alias} # list agents on ${alias}\n`);
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
if (sub === "rm" || sub === "remove" || sub === "delete") {
|
|
111
|
+
const alias = rest[1];
|
|
112
|
+
if (!alias) {
|
|
113
|
+
process.stderr.write("usage: ay remote rm <alias>\n");
|
|
114
|
+
return 1;
|
|
115
|
+
}
|
|
116
|
+
if (!(await readRemotes()).has(alias)) {
|
|
117
|
+
process.stderr.write(`remote '${alias}' not found\n`);
|
|
118
|
+
return 1;
|
|
119
|
+
}
|
|
120
|
+
await deleteRemoteAlias(alias);
|
|
121
|
+
process.stdout.write(`remote '${alias}' removed\n`);
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
process.stderr.write(`ay remote: unknown subcommand '${sub}'\n`);
|
|
125
|
+
process.stderr.write(" ay remote ls # list configured remotes\n ay remote add <alias> <url> <token> # add a remote\n ay remote rm <alias> # remove a remote\n");
|
|
126
|
+
return 1;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
//#endregion
|
|
130
|
+
export { resolveRemoteSpec as a, readRemotes as i, deleteRemoteAlias as n, writeRemoteAlias as o, parseDirectRemoteSpec as r, cmdRemote as t };
|
|
131
|
+
//# sourceMappingURL=remotes-CFrho898.js.map
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import "./logger-B9h0djqx.js";
|
|
2
|
+
import "./globalPidIndex-Cr-g75QF.js";
|
|
3
|
+
import "./remotes-CFrho898.js";
|
|
4
|
+
import { c as resolveOne, d as writeToIpc, i as listRecords, o as readNotes, s as renderRawLog, t as controlCodeFromName, u as snapshotStatus } from "./subcommands-BpGEGOQM.js";
|
|
5
|
+
import yargs from "yargs";
|
|
6
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { randomBytes, timingSafeEqual } from "crypto";
|
|
10
|
+
|
|
11
|
+
//#region ts/serve.ts
|
|
12
|
+
const DEFAULT_PORT = 7432;
|
|
13
|
+
function agentYesHome() {
|
|
14
|
+
return process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
|
|
15
|
+
}
|
|
16
|
+
function tokenPath() {
|
|
17
|
+
return path.join(agentYesHome(), ".serve-token");
|
|
18
|
+
}
|
|
19
|
+
async function loadOrCreateToken(tokenFlag) {
|
|
20
|
+
if (tokenFlag) return tokenFlag;
|
|
21
|
+
try {
|
|
22
|
+
return (await readFile(tokenPath(), "utf-8")).trim();
|
|
23
|
+
} catch {
|
|
24
|
+
const token = randomBytes(20).toString("hex");
|
|
25
|
+
await mkdir(agentYesHome(), { recursive: true });
|
|
26
|
+
await writeFile(tokenPath(), token, { mode: 384 });
|
|
27
|
+
return token;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function checkAuth(req, expectedToken) {
|
|
31
|
+
const auth = req.headers.get("authorization") ?? "";
|
|
32
|
+
if (!auth.startsWith("Bearer ")) return false;
|
|
33
|
+
const provided = auth.slice(7);
|
|
34
|
+
const maxLen = Math.max(provided.length, expectedToken.length);
|
|
35
|
+
return timingSafeEqual(Buffer.from(provided.padEnd(maxLen, "\0")), Buffer.from(expectedToken.padEnd(maxLen, "\0"))) && provided.length === expectedToken.length;
|
|
36
|
+
}
|
|
37
|
+
const defaultOpts = (overrides = {}) => ({
|
|
38
|
+
all: false,
|
|
39
|
+
active: false,
|
|
40
|
+
json: true,
|
|
41
|
+
latest: true,
|
|
42
|
+
cwdScope: null,
|
|
43
|
+
...overrides
|
|
44
|
+
});
|
|
45
|
+
const DAEMON_NAME = "agent-yes";
|
|
46
|
+
async function cmdServeDaemon(sub, args) {
|
|
47
|
+
const oxmgrBin = Bun.which("oxmgr");
|
|
48
|
+
if (!oxmgrBin) {
|
|
49
|
+
process.stderr.write("ay serve install: oxmgr not found\n install with: cargo install oxmgr\n or: bun add -g oxmgr\n");
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
if (sub === "install") {
|
|
53
|
+
const token = await loadOrCreateToken(void 0);
|
|
54
|
+
const serveCmd = [
|
|
55
|
+
"ay",
|
|
56
|
+
"serve",
|
|
57
|
+
...args
|
|
58
|
+
].join(" ");
|
|
59
|
+
const code = await Bun.spawn([
|
|
60
|
+
oxmgrBin,
|
|
61
|
+
"start",
|
|
62
|
+
serveCmd,
|
|
63
|
+
"--name",
|
|
64
|
+
DAEMON_NAME,
|
|
65
|
+
"--restart",
|
|
66
|
+
"always"
|
|
67
|
+
], { stdio: [
|
|
68
|
+
"ignore",
|
|
69
|
+
"inherit",
|
|
70
|
+
"inherit"
|
|
71
|
+
] }).exited;
|
|
72
|
+
if (code === 0) {
|
|
73
|
+
process.stdout.write(`\ninstalled '${DAEMON_NAME}' as a daemon via oxmgr\n`);
|
|
74
|
+
process.stdout.write(`token: ${token}\n\n`);
|
|
75
|
+
process.stdout.write(` ay ls ${token}@<host>:${DEFAULT_PORT}\n`);
|
|
76
|
+
process.stdout.write(` ay serve logs # view server logs\n`);
|
|
77
|
+
process.stdout.write(` ay serve uninstall # remove daemon\n`);
|
|
78
|
+
}
|
|
79
|
+
return code ?? 1;
|
|
80
|
+
}
|
|
81
|
+
if (sub === "uninstall") return await Bun.spawn([
|
|
82
|
+
oxmgrBin,
|
|
83
|
+
"delete",
|
|
84
|
+
DAEMON_NAME
|
|
85
|
+
], { stdio: [
|
|
86
|
+
"ignore",
|
|
87
|
+
"inherit",
|
|
88
|
+
"inherit"
|
|
89
|
+
] }).exited ?? 1;
|
|
90
|
+
if (sub === "logs") return await Bun.spawn([
|
|
91
|
+
oxmgrBin,
|
|
92
|
+
"logs",
|
|
93
|
+
DAEMON_NAME,
|
|
94
|
+
...args
|
|
95
|
+
], { stdio: [
|
|
96
|
+
"ignore",
|
|
97
|
+
"inherit",
|
|
98
|
+
"inherit"
|
|
99
|
+
] }).exited ?? 1;
|
|
100
|
+
return 1;
|
|
101
|
+
}
|
|
102
|
+
async function cmdServe(rest) {
|
|
103
|
+
const sub = rest[0];
|
|
104
|
+
if (sub === "install" || sub === "uninstall" || sub === "logs") return cmdServeDaemon(sub, rest.slice(1));
|
|
105
|
+
const argv = await yargs(rest).usage("Usage: ay serve [options]").option("port", {
|
|
106
|
+
type: "number",
|
|
107
|
+
default: DEFAULT_PORT,
|
|
108
|
+
description: "Port to listen on"
|
|
109
|
+
}).option("host", {
|
|
110
|
+
type: "string",
|
|
111
|
+
default: "127.0.0.1",
|
|
112
|
+
description: "Interface to bind (use 0.0.0.0 to expose)"
|
|
113
|
+
}).option("token", {
|
|
114
|
+
type: "string",
|
|
115
|
+
description: "Auth token (auto-generated if omitted)"
|
|
116
|
+
}).option("tls-cert", {
|
|
117
|
+
type: "string",
|
|
118
|
+
description: "TLS certificate file (PEM)"
|
|
119
|
+
}).option("tls-key", {
|
|
120
|
+
type: "string",
|
|
121
|
+
description: "TLS private key file (PEM)"
|
|
122
|
+
}).help(false).version(false).exitProcess(false).parseAsync();
|
|
123
|
+
const port = argv.port ?? DEFAULT_PORT;
|
|
124
|
+
const host = argv.host ?? "127.0.0.1";
|
|
125
|
+
const tokenFlag = typeof argv.token === "string" ? argv.token : void 0;
|
|
126
|
+
const certPath = typeof argv["tls-cert"] === "string" ? argv["tls-cert"] : void 0;
|
|
127
|
+
const keyPath = typeof argv["tls-key"] === "string" ? argv["tls-key"] : void 0;
|
|
128
|
+
if (certPath && !keyPath || !certPath && keyPath) {
|
|
129
|
+
process.stderr.write("ay serve: --tls-cert and --tls-key must both be provided\n");
|
|
130
|
+
return 1;
|
|
131
|
+
}
|
|
132
|
+
const useHttps = !!(certPath && keyPath);
|
|
133
|
+
const scheme = useHttps ? "https" : "http";
|
|
134
|
+
if (host !== "127.0.0.1" && host !== "localhost") process.stderr.write("ay serve: warning: binding to non-loopback — ensure your network is trusted or use Tailscale/VPN\n");
|
|
135
|
+
const token = await loadOrCreateToken(tokenFlag);
|
|
136
|
+
const serverOpts = {
|
|
137
|
+
hostname: host,
|
|
138
|
+
port,
|
|
139
|
+
async fetch(req) {
|
|
140
|
+
if (!checkAuth(req, token)) return new Response("Unauthorized", { status: 401 });
|
|
141
|
+
const url = new URL(req.url);
|
|
142
|
+
const p = url.pathname;
|
|
143
|
+
if (req.method === "GET" && p === "/api/ls") {
|
|
144
|
+
const keyword = url.searchParams.get("keyword") ?? void 0;
|
|
145
|
+
const opts = defaultOpts({
|
|
146
|
+
all: url.searchParams.get("all") === "1",
|
|
147
|
+
active: url.searchParams.get("active") === "1"
|
|
148
|
+
});
|
|
149
|
+
try {
|
|
150
|
+
const records = await listRecords(keyword, opts);
|
|
151
|
+
return Response.json(records);
|
|
152
|
+
} catch (e) {
|
|
153
|
+
return new Response(e.message, { status: 500 });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (req.method === "GET" && p === "/api/notes") {
|
|
157
|
+
const notes = await readNotes();
|
|
158
|
+
return Response.json(Object.fromEntries(notes));
|
|
159
|
+
}
|
|
160
|
+
const statusM = /^\/api\/status\/(.+)$/.exec(p);
|
|
161
|
+
if (req.method === "GET" && statusM) {
|
|
162
|
+
const keyword = decodeURIComponent(statusM[1]);
|
|
163
|
+
try {
|
|
164
|
+
const snap = await snapshotStatus(await resolveOne(keyword, defaultOpts({ all: true })));
|
|
165
|
+
return Response.json(snap);
|
|
166
|
+
} catch (e) {
|
|
167
|
+
return new Response(e.message, { status: 404 });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const readM = /^\/api\/read\/(.+)$/.exec(p);
|
|
171
|
+
if (req.method === "GET" && readM) {
|
|
172
|
+
const keyword = decodeURIComponent(readM[1]);
|
|
173
|
+
const mode = url.searchParams.get("mode") ?? "tail";
|
|
174
|
+
const n = parseInt(url.searchParams.get("n") ?? "96", 10) || 96;
|
|
175
|
+
try {
|
|
176
|
+
const record = await resolveOne(keyword, defaultOpts());
|
|
177
|
+
if (!record.log_file) return new Response(`pid ${record.pid}: no log_file`, { status: 404 });
|
|
178
|
+
const text = await renderRawLog(await readFile(record.log_file), {
|
|
179
|
+
mode,
|
|
180
|
+
n
|
|
181
|
+
});
|
|
182
|
+
return new Response(text, { headers: { "Content-Type": "text/plain; charset=utf-8" } });
|
|
183
|
+
} catch (e) {
|
|
184
|
+
return new Response(e.message, { status: 404 });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const tailM = /^\/api\/tail\/(.+)$/.exec(p);
|
|
188
|
+
if (req.method === "GET" && tailM) {
|
|
189
|
+
const keyword = decodeURIComponent(tailM[1]);
|
|
190
|
+
try {
|
|
191
|
+
const record = await resolveOne(keyword, defaultOpts());
|
|
192
|
+
if (!record.log_file) return new Response(`pid ${record.pid}: no log_file`, { status: 404 });
|
|
193
|
+
const logPath = record.log_file;
|
|
194
|
+
const stream = new ReadableStream({ async start(ctrl) {
|
|
195
|
+
const enc = new TextEncoder();
|
|
196
|
+
const send = (text) => ctrl.enqueue(enc.encode(`data: ${JSON.stringify(text)}\n\n`));
|
|
197
|
+
const ping = () => ctrl.enqueue(enc.encode(": ping\n\n"));
|
|
198
|
+
const initBuf = await readFile(logPath).catch(() => Buffer.alloc(0));
|
|
199
|
+
send(await renderRawLog(initBuf, {
|
|
200
|
+
mode: "tail",
|
|
201
|
+
n: 96
|
|
202
|
+
}));
|
|
203
|
+
let offset = initBuf.length;
|
|
204
|
+
let closed = false;
|
|
205
|
+
const heartbeat = setInterval(() => {
|
|
206
|
+
if (closed) {
|
|
207
|
+
clearInterval(heartbeat);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
ping();
|
|
211
|
+
}, 15e3);
|
|
212
|
+
const ansiRe = /\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g;
|
|
213
|
+
const ctrlRe = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
|
|
214
|
+
const poller = setInterval(async () => {
|
|
215
|
+
if (closed) {
|
|
216
|
+
clearInterval(poller);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
const full = await readFile(logPath);
|
|
221
|
+
if (full.length <= offset) return;
|
|
222
|
+
const chunk = full.slice(offset);
|
|
223
|
+
offset = full.length;
|
|
224
|
+
const text = new TextDecoder().decode(chunk).replace(ansiRe, "").replace(ctrlRe, "");
|
|
225
|
+
if (text.trim()) send(text.trimStart());
|
|
226
|
+
} catch {}
|
|
227
|
+
}, 300);
|
|
228
|
+
req.signal.addEventListener("abort", () => {
|
|
229
|
+
closed = true;
|
|
230
|
+
clearInterval(heartbeat);
|
|
231
|
+
clearInterval(poller);
|
|
232
|
+
try {
|
|
233
|
+
ctrl.close();
|
|
234
|
+
} catch {}
|
|
235
|
+
});
|
|
236
|
+
} });
|
|
237
|
+
return new Response(stream, { headers: {
|
|
238
|
+
"Content-Type": "text/event-stream",
|
|
239
|
+
"Cache-Control": "no-cache",
|
|
240
|
+
Connection: "keep-alive"
|
|
241
|
+
} });
|
|
242
|
+
} catch (e) {
|
|
243
|
+
return new Response(e.message, { status: 404 });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (req.method === "POST" && p === "/api/send") {
|
|
247
|
+
let body;
|
|
248
|
+
try {
|
|
249
|
+
body = await req.json();
|
|
250
|
+
} catch {
|
|
251
|
+
return new Response("invalid JSON body", { status: 400 });
|
|
252
|
+
}
|
|
253
|
+
const { keyword, msg = "", code = "enter" } = body;
|
|
254
|
+
if (!keyword || typeof keyword !== "string") return new Response("missing keyword", { status: 400 });
|
|
255
|
+
try {
|
|
256
|
+
const record = await resolveOne(keyword, defaultOpts());
|
|
257
|
+
if (!record.fifo_file) return new Response(`pid ${record.pid}: no fifo_file`, { status: 409 });
|
|
258
|
+
const trailing = controlCodeFromName(code.toLowerCase());
|
|
259
|
+
if (msg && trailing) {
|
|
260
|
+
await writeToIpc(record.fifo_file, msg);
|
|
261
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
262
|
+
await writeToIpc(record.fifo_file, trailing);
|
|
263
|
+
} else await writeToIpc(record.fifo_file, msg + trailing);
|
|
264
|
+
return Response.json({
|
|
265
|
+
ok: true,
|
|
266
|
+
pid: record.pid
|
|
267
|
+
});
|
|
268
|
+
} catch (e) {
|
|
269
|
+
return new Response(e.message, { status: 404 });
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return new Response("Not Found", { status: 404 });
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
if (useHttps) serverOpts.tls = {
|
|
276
|
+
cert: Bun.file(certPath),
|
|
277
|
+
key: Bun.file(keyPath)
|
|
278
|
+
};
|
|
279
|
+
const server = Bun.serve(serverOpts);
|
|
280
|
+
process.stdout.write(`ay serve ${scheme}://${host}:${port}\n`);
|
|
281
|
+
process.stdout.write(`token: ${token}\n\n`);
|
|
282
|
+
process.stdout.write(`connect from another machine:\n`);
|
|
283
|
+
process.stdout.write(` ay ls ${token}@<host>:${port}\n`);
|
|
284
|
+
process.stdout.write(` ay tail ${token}@<host>:${port}:<keyword>\n`);
|
|
285
|
+
process.stdout.write(` ay send ${token}@<host>:${port}:<keyword> "message"\n\n`);
|
|
286
|
+
if (!useHttps) process.stdout.write("for HTTPS: ay serve --tls-cert cert.pem --tls-key key.pem\n openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj '/CN=localhost'\n\n");
|
|
287
|
+
process.stdout.write(`(Ctrl-C to stop)\n`);
|
|
288
|
+
await new Promise((resolve) => {
|
|
289
|
+
process.on("SIGINT", () => {
|
|
290
|
+
server.stop();
|
|
291
|
+
resolve();
|
|
292
|
+
});
|
|
293
|
+
process.on("SIGTERM", () => {
|
|
294
|
+
server.stop();
|
|
295
|
+
resolve();
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
return 0;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
//#endregion
|
|
302
|
+
export { cmdServe };
|
|
303
|
+
//# sourceMappingURL=serve-D0NnTXRD.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import "./logger-B9h0djqx.js";
|
|
2
|
+
import "./globalPidIndex-Cr-g75QF.js";
|
|
3
|
+
import "./remotes-CFrho898.js";
|
|
4
|
+
import { a as matchKeyword, c as resolveOne, d as writeToIpc, i as listRecords, l as runSubcommand, n as isPidAlive, o as readNotes, r as isSubcommand, s as renderRawLog, t as controlCodeFromName, u as snapshotStatus } from "./subcommands-BpGEGOQM.js";
|
|
5
|
+
|
|
6
|
+
export { isSubcommand, runSubcommand };
|