@pablopunk/tau 0.1.0 → 0.1.2

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.
@@ -1,8 +1,6 @@
1
- import { t as cliEntry } from "./cli-entry-DcDpQ30L.js";
2
1
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
3
2
  import { delimiter, dirname, join, resolve } from "node:path";
4
3
  import { homedir, release, userInfo } from "node:os";
5
- import { parseArgs } from "node:util";
6
4
  import { createHash } from "node:crypto";
7
5
  import { spawn, spawnSync } from "node:child_process";
8
6
  //#region src/service/entry-guard.ts
@@ -588,34 +586,4 @@ async function uninstallHome(context, home) {
588
586
  }, home).uninstall();
589
587
  }
590
588
  //#endregion
591
- //#region src/service/service-command.ts
592
- const USAGE = "Usage: tau service install | uninstall [--home <path>] | list";
593
- /** `tau service install | uninstall [--home <path>] | list`. `argv` starts after the word `service`. */
594
- async function runServiceCommandWith(deps, argv) {
595
- const { positionals, values } = parseArgs({
596
- args: [...argv],
597
- allowPositionals: true,
598
- options: { home: { type: "string" } }
599
- });
600
- const home = values.home ?? deps.context.env.TAU_HOME ?? deps.context.defaultHome;
601
- const [action] = positionals;
602
- if (action === "install") {
603
- const warnings = await createServiceManager(deps.context, cliEntry(), home).install();
604
- for (const warning of warnings) deps.out(`warning: ${warning}`);
605
- deps.out("tau service installed.");
606
- } else if (action === "uninstall") {
607
- await uninstallHome(deps.context, home);
608
- deps.out("tau service removed.");
609
- } else if (action === "list") {
610
- const names = await listServices(deps.context);
611
- deps.out(names.length === 0 ? "No tau services." : names.join("\n"));
612
- } else throw new Error(USAGE);
613
- }
614
- async function runServiceCommand(argv) {
615
- await runServiceCommandWith({
616
- context: realContext(),
617
- out: (message) => console.log(message)
618
- }, argv);
619
- }
620
- //#endregion
621
- export { TemporaryEntryError as a, realContext as i, runServiceCommandWith as n, createServiceManager as r, runServiceCommand as t };
589
+ export { TemporaryEntryError as a, realContext as i, listServices as n, uninstallHome as r, createServiceManager as t };
@@ -0,0 +1,89 @@
1
+ import { n as readRunningInstance } from "./src-DO9LUlhi.js";
2
+ import { a as realLifecycleDeps, d as discoverServer, l as processEnv, t as fetchLinkedMachines, u as callRoute } from "./machines-behind-BjGgmSs_.js";
3
+ import { i as spawnCommand, n as fetchLatestVersion } from "./latest-version-di9hD7cY.js";
4
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import { createInterface } from "node:readline/promises";
7
+ //#region src/update/ancestors.ts
8
+ const MAX_DEPTH = 64;
9
+ /** Best effort (POSIX): the pids above this process, from `ps -o ppid= -p`. */
10
+ async function ancestorPidsOf(run, start = process.pid) {
11
+ const pids = [];
12
+ let current = start;
13
+ for (let depth = 0; depth < MAX_DEPTH; depth++) {
14
+ const result = await run("ps", [
15
+ "-o",
16
+ "ppid=",
17
+ "-p",
18
+ String(current)
19
+ ], {
20
+ stdio: "capture",
21
+ timeoutMs: 5e3
22
+ });
23
+ const parent = Number(result.stdout.trim());
24
+ if (result.code !== 0 || !Number.isInteger(parent) || parent <= 1 || pids.includes(parent)) break;
25
+ pids.push(parent);
26
+ current = parent;
27
+ }
28
+ return pids;
29
+ }
30
+ //#endregion
31
+ //#region src/update/real-deps.ts
32
+ const POLL_MS = 500;
33
+ const POLL_DEADLINE_MS = 6e4;
34
+ async function pollForVersion(home, version) {
35
+ const deadline = Date.now() + POLL_DEADLINE_MS;
36
+ for (;;) {
37
+ const instance = await readRunningInstance(home);
38
+ if (instance?.version === version || Date.now() >= deadline) return instance;
39
+ await new Promise((resolve) => setTimeout(resolve, POLL_MS));
40
+ }
41
+ }
42
+ async function ask(question) {
43
+ const rl = createInterface({
44
+ input: process.stdin,
45
+ output: process.stdout
46
+ });
47
+ try {
48
+ return /^y(es)?$/i.test((await rl.question(`${question} [y/N] `)).trim());
49
+ } finally {
50
+ rl.close();
51
+ }
52
+ }
53
+ async function realUpdateDeps() {
54
+ const base = await realLifecycleDeps();
55
+ const entry = process.argv[1] === void 0 ? "" : realpathSync(process.argv[1]);
56
+ return {
57
+ ...base,
58
+ err: (message) => console.error(message),
59
+ runCommand: spawnCommand,
60
+ latestVersion: () => fetchLatestVersion(spawnCommand, process.execPath),
61
+ listConversations: async (home) => {
62
+ const env = {
63
+ ...processEnv(),
64
+ home
65
+ };
66
+ const server = await discoverServer(env);
67
+ return await callRoute(server, "listAllConversations", {}, env);
68
+ },
69
+ confirm: ask,
70
+ isTty: Boolean(process.stdin.isTTY && process.stdout.isTTY),
71
+ env: process.env,
72
+ execPath: process.execPath,
73
+ entry,
74
+ realpath: realpathSync,
75
+ readPackageVersion: (dir) => {
76
+ try {
77
+ return JSON.parse(readFileSync(join(dir, "package.json"), "utf8")).version;
78
+ } catch {
79
+ return;
80
+ }
81
+ },
82
+ ancestorPids: process.platform === "win32" ? void 0 : () => ancestorPidsOf(spawnCommand),
83
+ pollInstance: pollForVersion,
84
+ hasHubFile: (home) => existsSync(join(home, "hub.json")),
85
+ linkedMachines: fetchLinkedMachines
86
+ };
87
+ }
88
+ //#endregion
89
+ export { realUpdateDeps };
@@ -0,0 +1 @@
1
+ export { t as serveInForeground } from "./serve-Uc4Nin5f.js";
@@ -1,4 +1,6 @@
1
- import { $ as routes, A as changedFrame, C as LIVE_MAX_FRAME_BYTES, D as TERMINAL_STREAM_PATH, E as TERMINAL_STREAM_MAX_INPUT_BYTES, F as ERROR_STATUS, I as isAppError, J as LIVE_PATH, N as encodeFrame, O as TerminalClientFrameSchema, P as AppError, Q as iconCandidateUrl, R as toErrorBody, S as encodeDataFrame, T as TERMINAL_STREAM_CLOSE_UNKNOWN, X as PAIR_PAGE_PATH, Y as MESSAGE_BODY_LIMIT, a as readRunningInstance, b as chunkBody, d as CHUNK_BYTES, et as HUB_LINK_NONE, f as CREDIT_WINDOW_BYTES, i as publishServerUrl, j as decodeClientFrame, k as WS_MAX_PAYLOAD_BYTES, m as LINK_CLOSE_UPDATE_REQUIRED, n as waitForFreeHome, o as createCore, p as LINK_CLOSE_NO_CREDIT, rt as RETIRED_OPS, s as createExitMarker, tt as MACHINE_PROXY_PREFIX, u as PROTOCOL_VERSION, v as LinkFrameSchema, w as TERMINAL_STREAM_CLOSE_SLOW, x as decodeDataFrame, y as MACHINE_LINK_PATH } from "./wait-for-lock-D2V3vuye.js";
1
+ import { $ as routes, A as changedFrame, C as LIVE_MAX_FRAME_BYTES, D as TERMINAL_STREAM_PATH, E as TERMINAL_STREAM_MAX_INPUT_BYTES, F as ERROR_STATUS, I as isAppError, J as LIVE_PATH, N as encodeFrame, O as TerminalClientFrameSchema, P as AppError, Q as iconCandidateUrl, R as toErrorBody, S as encodeDataFrame, T as TERMINAL_STREAM_CLOSE_UNKNOWN, X as PAIR_PAGE_PATH, Y as MESSAGE_BODY_LIMIT, b as chunkBody, d as CHUNK_BYTES, et as HUB_LINK_NONE, f as CREDIT_WINDOW_BYTES, i as createExitMarker, j as decodeClientFrame, k as WS_MAX_PAYLOAD_BYTES, m as LINK_CLOSE_UPDATE_REQUIRED, n as readRunningInstance, p as LINK_CLOSE_NO_CREDIT, r as createCore, rt as RETIRED_OPS, t as publishServerUrl, tt as MACHINE_PROXY_PREFIX, u as PROTOCOL_VERSION, v as LinkFrameSchema, w as TERMINAL_STREAM_CLOSE_SLOW, x as decodeDataFrame, y as MACHINE_LINK_PATH } from "./src-DO9LUlhi.js";
2
+ import { n as waitForFreeHome } from "./wait-for-lock-CrSl2mi1.js";
3
+ import { a as cliVersion, c as writePrivateFile, i as spawnCommand, n as fetchLatestVersion, o as readOrCreateToken, s as ensurePrivateDirectory } from "./latest-version-di9hD7cY.js";
2
4
  import { t as cliEntry } from "./cli-entry-DcDpQ30L.js";
3
5
  import { Worker } from "node:worker_threads";
4
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
@@ -6,8 +8,8 @@ import { dirname, join } from "node:path";
6
8
  import { arch, homedir, hostname, platform } from "node:os";
7
9
  import { fileURLToPath } from "node:url";
8
10
  import { format, parseArgs } from "node:util";
9
- import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
10
- import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
11
+ import { createHash, timingSafeEqual } from "node:crypto";
12
+ import { readFile, rm } from "node:fs/promises";
11
13
  import { Value } from "typebox/value";
12
14
  import { createServer } from "node:net";
13
15
  import { request } from "node:http";
@@ -67,23 +69,6 @@ function startWatchdog(options) {
67
69
  } };
68
70
  }
69
71
  //#endregion
70
- //#region src/private-files.ts
71
- const PRIVATE_DIRECTORY_MODE = 448;
72
- const PRIVATE_FILE_MODE = 384;
73
- async function ensurePrivateDirectory(path) {
74
- await mkdir(path, {
75
- recursive: true,
76
- mode: PRIVATE_DIRECTORY_MODE
77
- });
78
- }
79
- /** Temp file, then rename, so readers never see a half-written file. */
80
- async function writePrivateFile(path, content) {
81
- await ensurePrivateDirectory(dirname(path));
82
- const temporary = `${path}.${process.pid}.tmp`;
83
- await writeFile(temporary, content, { mode: PRIVATE_FILE_MODE });
84
- await rename(temporary, path);
85
- }
86
- //#endregion
87
72
  //#region src/machines/hub-file.ts
88
73
  const hubFilePath = (home) => join(home, "hub.json");
89
74
  function isHubFile(value) {
@@ -106,33 +91,6 @@ function createHubFileStore(home) {
106
91
  };
107
92
  }
108
93
  //#endregion
109
- //#region src/token.ts
110
- const TOKEN_BYTES = 32;
111
- const MIN_TOKEN_LENGTH = 32;
112
- const tokenPath = (home) => join(home, "token");
113
- async function readExistingToken(home) {
114
- try {
115
- const token = (await readFile(tokenPath(home), "utf8")).trim();
116
- return token.length >= MIN_TOKEN_LENGTH ? token : void 0;
117
- } catch {
118
- return;
119
- }
120
- }
121
- /** The token outlives restarts so open browser tabs keep their cookie. */
122
- async function readOrCreateToken(home) {
123
- const existing = await readExistingToken(home);
124
- if (existing) return existing;
125
- const token = randomBytes(TOKEN_BYTES).toString("base64url");
126
- await writePrivateFile(tokenPath(home), `${token}\n`);
127
- return token;
128
- }
129
- //#endregion
130
- //#region src/version.ts
131
- /** From the `package.json` next to `src/` and next to `dist/`. */
132
- function cliVersion() {
133
- return JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
134
- }
135
- //#endregion
136
94
  //#region src/lifecycle/flags.ts
137
95
  function portOf$1(raw) {
138
96
  if (raw === void 0) return void 0;
@@ -1117,6 +1075,7 @@ const DENIED_FIRST_SEGMENTS = /* @__PURE__ */ new Set([
1117
1075
  "access",
1118
1076
  "remote",
1119
1077
  "machines",
1078
+ "update",
1120
1079
  "pair",
1121
1080
  "health",
1122
1081
  "m"
@@ -1666,6 +1625,8 @@ function startLoginFor(core, id) {
1666
1625
  }
1667
1626
  function registerRoutes(app, core, sockets) {
1668
1627
  defineRoute(app, routes.suggestDirectories, ({ body }) => core.fs.suggestDirectories(body));
1628
+ defineRoute(app, routes.getUpdateStatus, () => core.updates.status());
1629
+ defineRoute(app, routes.refreshUpdateStatus, () => core.updates.refresh());
1669
1630
  defineRoute(app, routes.getSettings, () => core.settings.get());
1670
1631
  defineRoute(app, routes.setDefaultModel, ({ body }) => core.settings.setDefaultModel(body));
1671
1632
  defineRoute(app, routes.setDefaultThinking, ({ body }) => core.settings.setDefaultThinkingLevel(body.thinkingLevel));
@@ -2816,6 +2777,17 @@ async function listenOnFreePort(preferred, start) {
2816
2777
  throw new Error(`No free port between ${preferred} and ${last}`);
2817
2778
  }
2818
2779
  //#endregion
2780
+ //#region src/update/latest-source.ts
2781
+ /**
2782
+ * Where core learns the latest published version. `TAU_FAKE_LATEST` is a test hook (e2e): its value is the
2783
+ * answer, so no npm runs.
2784
+ */
2785
+ function latestVersionSource(env = process.env) {
2786
+ const fake = env.TAU_FAKE_LATEST;
2787
+ if (fake) return async () => fake;
2788
+ return () => fetchLatestVersion(spawnCommand, process.execPath);
2789
+ }
2790
+ //#endregion
2819
2791
  //#region src/run.ts
2820
2792
  const withToken = (origin, token) => `${origin}/?token=${encodeURIComponent(token)}`;
2821
2793
  async function reportExistingInstance(options, dependencies, token) {
@@ -2844,7 +2816,9 @@ async function run(options, dependencies) {
2844
2816
  faux: options.faux,
2845
2817
  cliEntry: dependencies.cliEntry,
2846
2818
  tunnelTarget: tunnelTarget.get,
2847
- forgetHub: () => hubFile.delete()
2819
+ forgetHub: () => hubFile.delete(),
2820
+ currentVersion: dependencies.version ?? "",
2821
+ latestVersion: dependencies.latestVersion ?? latestVersionSource()
2848
2822
  });
2849
2823
  try {
2850
2824
  const server = await listenOnFreePort(options.port, (port) => dependencies.createServer({
@@ -2879,8 +2853,8 @@ async function run(options, dependencies) {
2879
2853
  //#endregion
2880
2854
  //#region src/static-dir.ts
2881
2855
  const here = (relative) => fileURLToPath(new URL(relative, import.meta.url));
2882
- /** Bundled (`dist/web` next to `dist/bin.js`) or, when run from source, the sibling web package build. */
2883
- const CANDIDATES = [here("./web"), here("../../web/dist")];
2856
+ /** Bundled (`dist/web/dist` next to `dist/bin.js`, as tsdown copies the folder inside the target) or, when run from source, the sibling web package build. */
2857
+ const CANDIDATES = [here("./web/dist"), here("../../web/dist")];
2884
2858
  function findStaticDir(candidates = CANDIDATES) {
2885
2859
  return candidates.find((directory) => existsSync(join(directory, "index.html")));
2886
2860
  }
@@ -2955,4 +2929,4 @@ async function serveInForeground(argv) {
2955
2929
  }
2956
2930
  }
2957
2931
  //#endregion
2958
- export { lifecycleFlagsOf as a, createHubFileStore as c, commandWordOf as i, alreadyLinkedRefusal as l, serverLogOf as n, cliVersion as o, writeStoredPort as r, readOrCreateToken as s, serveInForeground as t, connectRefusal as u };
2932
+ export { lifecycleFlagsOf as a, connectRefusal as c, commandWordOf as i, serverLogOf as n, createHubFileStore as o, writeStoredPort as r, alreadyLinkedRefusal as s, serveInForeground as t };
@@ -0,0 +1,34 @@
1
+ import { i as realContext, n as listServices, r as uninstallHome, t as createServiceManager } from "./manager-B_WHbb1U.js";
2
+ import { t as cliEntry } from "./cli-entry-DcDpQ30L.js";
3
+ import { parseArgs } from "node:util";
4
+ //#region src/service/service-command.ts
5
+ const USAGE = "Usage: tau service install | uninstall [--home <path>] | list";
6
+ /** `tau service install | uninstall [--home <path>] | list`. `argv` starts after the word `service`. */
7
+ async function runServiceCommandWith(deps, argv) {
8
+ const { positionals, values } = parseArgs({
9
+ args: [...argv],
10
+ allowPositionals: true,
11
+ options: { home: { type: "string" } }
12
+ });
13
+ const home = values.home ?? deps.context.env.TAU_HOME ?? deps.context.defaultHome;
14
+ const [action] = positionals;
15
+ if (action === "install") {
16
+ const warnings = await createServiceManager(deps.context, cliEntry(), home).install();
17
+ for (const warning of warnings) deps.out(`warning: ${warning}`);
18
+ deps.out("tau service installed.");
19
+ } else if (action === "uninstall") {
20
+ await uninstallHome(deps.context, home);
21
+ deps.out("tau service removed.");
22
+ } else if (action === "list") {
23
+ const names = await listServices(deps.context);
24
+ deps.out(names.length === 0 ? "No tau services." : names.join("\n"));
25
+ } else throw new Error(USAGE);
26
+ }
27
+ async function runServiceCommand(argv) {
28
+ await runServiceCommandWith({
29
+ context: realContext(),
30
+ out: (message) => console.log(message)
31
+ }, argv);
32
+ }
33
+ //#endregion
34
+ export { runServiceCommandWith as n, runServiceCommand as t };
@@ -0,0 +1 @@
1
+ export { t as runServiceCommand } from "./service-command-C74K0saj.js";