overmux 0.0.4 → 0.0.6

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 (151) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +16 -2
  3. package/dist/bin.js +140 -127
  4. package/dist/bin.js.map +1 -1
  5. package/dist/docs/000-index.md +2 -4
  6. package/dist/docs/100-introduction/200-how-overmux-works.md +126 -2
  7. package/dist/docs/100-introduction/{300-why-overmux.md → 300-why-i-built-overmux.md} +1 -1
  8. package/dist/docs/200-getting-started/100-install-and-run-overmux.md +2 -2
  9. package/dist/docs/200-getting-started/400-secure-with-https/200-tailscale-serve.md +3 -3
  10. package/dist/docs/200-getting-started/400-secure-with-https/300-cloudflare-tunnel.md +2 -2
  11. package/dist/docs/200-getting-started/400-secure-with-https/400-self-hosted-reverse-proxy.md +2 -2
  12. package/dist/docs/200-getting-started/500-set-up-overmux-with-packages.md +42 -0
  13. package/dist/docs/400-reference/100-project-structure.md +83 -0
  14. package/dist/docs/400-reference/200-configuration.md +257 -0
  15. package/dist/docs/400-reference/300-storage-locations.md +46 -0
  16. package/dist/docs/400-reference/400-authentication-and-security.md +37 -0
  17. package/dist/docs/400-reference/500-server/000-index.md +13 -0
  18. package/dist/docs/400-reference/500-server/100-resources.md +191 -0
  19. package/dist/docs/400-reference/500-server/200-operations.md +111 -0
  20. package/dist/docs/400-reference/500-server/300-streams.md +140 -0
  21. package/dist/docs/400-reference/500-server/400-notifications.md +32 -0
  22. package/dist/docs/400-reference/500-server/500-api.md +118 -0
  23. package/dist/docs/400-reference/600-client/000-index.md +9 -0
  24. package/dist/docs/400-reference/600-client/005-setting-up-your-ui.md +137 -0
  25. package/dist/docs/400-reference/600-client/007-deep-links.md +87 -0
  26. package/dist/docs/400-reference/600-client/010-commands.md +189 -0
  27. package/dist/docs/400-reference/600-client/020-shortcuts.md +171 -0
  28. package/dist/docs/400-reference/600-client/100-api.md +370 -0
  29. package/dist/docs/400-reference/600-client/200-theming.md +209 -0
  30. package/dist/docs/400-reference/600-client/300-tech-stack-recommendations.md +12 -0
  31. package/dist/docs/400-reference/{400-cli → 700-cli}/050-init.md +2 -4
  32. package/{docs/400-reference/400-cli → dist/docs/400-reference/700-cli}/100-serve.md +9 -4
  33. package/{docs/400-reference/400-cli → dist/docs/400-reference/700-cli}/200-auth.md +1 -4
  34. package/dist/docs/400-reference/{400-cli → 700-cli}/300-call.md +1 -4
  35. package/dist/docs/400-reference/{400-cli → 700-cli}/350-instance.md +1 -4
  36. package/dist/docs/400-reference/700-cli/600-docs.md +128 -0
  37. package/dist/docs/400-reference/{400-cli → 700-cli}/700-desktop.md +1 -3
  38. package/dist/docs/500-hosted-pages.md +0 -1
  39. package/dist/exports/client.d.ts +5 -14
  40. package/dist/exports/client.d.ts.map +1 -1
  41. package/dist/exports/client.js +141 -46
  42. package/dist/exports/client.js.map +1 -1
  43. package/dist/exports/{index-DS70rKzo.d.ts → index-Cz3xkCa4.d.ts} +50 -36
  44. package/dist/exports/index-Cz3xkCa4.d.ts.map +1 -0
  45. package/dist/exports/index.d.ts +1 -1
  46. package/dist/exports/index.js +19 -5
  47. package/dist/exports/index.js.map +1 -1
  48. package/dist/exports/{notifications-av0FK0yZ.js → notifications-BFAD3QQl.js} +29 -4
  49. package/dist/exports/notifications-BFAD3QQl.js.map +1 -0
  50. package/dist/exports/server.d.ts +1 -44
  51. package/dist/exports/server.d.ts.map +1 -1
  52. package/dist/exports/server.js +9 -1061
  53. package/dist/exports/server.js.map +1 -1
  54. package/dist/internal/server/coordinator/server-child.js +47 -43
  55. package/dist/internal/server/coordinator/server-child.js.map +1 -1
  56. package/docs/000-index.md +2 -4
  57. package/docs/100-introduction/200-how-overmux-works.md +126 -2
  58. package/docs/100-introduction/{300-why-overmux.md → 300-why-i-built-overmux.md} +1 -1
  59. package/docs/200-getting-started/100-install-and-run-overmux.md +2 -2
  60. package/docs/200-getting-started/400-secure-with-https/200-tailscale-serve.md +3 -3
  61. package/docs/200-getting-started/400-secure-with-https/300-cloudflare-tunnel.md +2 -2
  62. package/docs/200-getting-started/400-secure-with-https/400-self-hosted-reverse-proxy.md +2 -2
  63. package/docs/200-getting-started/500-set-up-overmux-with-packages.md +42 -0
  64. package/docs/400-reference/100-project-structure.md +83 -0
  65. package/docs/400-reference/200-configuration.md +257 -0
  66. package/docs/400-reference/300-storage-locations.md +46 -0
  67. package/docs/400-reference/400-authentication-and-security.md +37 -0
  68. package/docs/400-reference/500-server/000-index.md +13 -0
  69. package/docs/400-reference/500-server/100-resources.md +191 -0
  70. package/docs/400-reference/500-server/200-operations.md +111 -0
  71. package/docs/400-reference/500-server/300-streams.md +140 -0
  72. package/docs/400-reference/500-server/400-notifications.md +32 -0
  73. package/docs/400-reference/500-server/500-api.md +118 -0
  74. package/docs/400-reference/600-client/000-index.md +9 -0
  75. package/docs/400-reference/600-client/005-setting-up-your-ui.md +137 -0
  76. package/docs/400-reference/600-client/007-deep-links.md +87 -0
  77. package/docs/400-reference/600-client/010-commands.md +189 -0
  78. package/docs/400-reference/600-client/020-shortcuts.md +171 -0
  79. package/docs/400-reference/600-client/100-api.md +370 -0
  80. package/docs/400-reference/600-client/200-theming.md +209 -0
  81. package/docs/400-reference/600-client/300-tech-stack-recommendations.md +12 -0
  82. package/docs/400-reference/{400-cli → 700-cli}/050-init.md +2 -4
  83. package/{dist/docs/400-reference/400-cli → docs/400-reference/700-cli}/100-serve.md +9 -4
  84. package/{dist/docs/400-reference/400-cli → docs/400-reference/700-cli}/200-auth.md +1 -4
  85. package/docs/400-reference/{400-cli → 700-cli}/300-call.md +1 -4
  86. package/docs/400-reference/{400-cli → 700-cli}/350-instance.md +1 -4
  87. package/docs/400-reference/700-cli/600-docs.md +128 -0
  88. package/docs/400-reference/{400-cli → 700-cli}/700-desktop.md +1 -3
  89. package/docs/500-hosted-pages.md +0 -1
  90. package/package.json +4 -3
  91. package/src/internal/cli/app.ts +3 -5
  92. package/src/internal/cli/commands/docs-ai-context.ts +33 -0
  93. package/src/internal/cli/commands/docs.ts +19 -2
  94. package/src/internal/cli/commands/init-template.ts +1 -1
  95. package/src/internal/cli/commands/serve.ts +3 -0
  96. package/src/internal/cli/login.ts +9 -9
  97. package/src/internal/client/client-definition.ts +6 -8
  98. package/src/internal/client/host/deep-link-navigation.ts +75 -0
  99. package/src/internal/client/host/overmux-host.tsx +9 -0
  100. package/src/internal/client/index.ts +0 -6
  101. package/src/internal/client/overmux-react.ts +71 -40
  102. package/src/internal/server/auth/auth-service.ts +2 -2
  103. package/src/internal/server/auth/instance-control.ts +65 -14
  104. package/src/internal/server/coordinator/ipc-protocol.ts +0 -1
  105. package/src/internal/server/runtime/create-runtime.ts +5 -3
  106. package/src/internal/server/runtime/runtime-instance.ts +5 -4
  107. package/src/internal/server/runtime/runtime-operations.ts +9 -3
  108. package/src/internal/server/runtime/runtime-resources.ts +12 -32
  109. package/src/internal/server/runtime/runtime-streams.ts +5 -6
  110. package/src/internal/server/server-logger.ts +5 -10
  111. package/src/internal/server/server-startup-options.ts +5 -10
  112. package/src/internal/server/start-application-server.ts +4 -7
  113. package/src/public/ai-context.ts +7 -29
  114. package/src/public/client.ts +0 -6
  115. package/src/public/config.ts +156 -31
  116. package/src/public/server.ts +1 -16
  117. package/dist/docs/100-introduction/100-what-is-overmux.md +0 -7
  118. package/dist/docs/300-fundamentals/100-project-structure.md +0 -23
  119. package/dist/docs/300-fundamentals/200-configuration.md +0 -3
  120. package/dist/docs/300-fundamentals/300-theming.md +0 -54
  121. package/dist/docs/300-fundamentals/400-server.md +0 -3
  122. package/dist/docs/300-fundamentals/500-client.md +0 -3
  123. package/dist/docs/300-fundamentals/600-operations.md +0 -3
  124. package/dist/docs/300-fundamentals/700-resources.md +0 -3
  125. package/dist/docs/300-fundamentals/800-streams.md +0 -3
  126. package/dist/docs/300-fundamentals/900-authentication-and-security.md +0 -3
  127. package/dist/docs/400-reference/100-configuration.md +0 -23
  128. package/dist/docs/400-reference/200-server-api.md +0 -21
  129. package/dist/docs/400-reference/300-client-api.md +0 -39
  130. package/dist/docs/400-reference/400-cli/400-check.md +0 -20
  131. package/dist/docs/400-reference/400-cli/500-ai-context.md +0 -102
  132. package/dist/docs/400-reference/400-cli/600-docs.md +0 -23
  133. package/dist/exports/index-DS70rKzo.d.ts.map +0 -1
  134. package/dist/exports/notifications-av0FK0yZ.js.map +0 -1
  135. package/docs/100-introduction/100-what-is-overmux.md +0 -7
  136. package/docs/300-fundamentals/100-project-structure.md +0 -23
  137. package/docs/300-fundamentals/200-configuration.md +0 -3
  138. package/docs/300-fundamentals/300-theming.md +0 -54
  139. package/docs/300-fundamentals/400-server.md +0 -3
  140. package/docs/300-fundamentals/500-client.md +0 -3
  141. package/docs/300-fundamentals/600-operations.md +0 -3
  142. package/docs/300-fundamentals/700-resources.md +0 -3
  143. package/docs/300-fundamentals/800-streams.md +0 -3
  144. package/docs/300-fundamentals/900-authentication-and-security.md +0 -3
  145. package/docs/400-reference/100-configuration.md +0 -23
  146. package/docs/400-reference/200-server-api.md +0 -21
  147. package/docs/400-reference/300-client-api.md +0 -39
  148. package/docs/400-reference/400-cli/400-check.md +0 -20
  149. package/docs/400-reference/400-cli/500-ai-context.md +0 -102
  150. package/docs/400-reference/400-cli/600-docs.md +0 -23
  151. package/src/internal/cli/commands/ai.ts +0 -44
package/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # overmux
2
2
 
3
+ ## 0.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [`a84e0c8`](https://github.com/richardgill/overmux/commit/a84e0c82d6d259a650af7c52b18dfb79c76562f6) Thanks [@richardgill](https://github.com/richardgill)! - Republish all npm packages with the latest changes.
8
+
9
+ - Updated dependencies [[`a84e0c8`](https://github.com/richardgill/overmux/commit/a84e0c82d6d259a650af7c52b18dfb79c76562f6)]:
10
+ - @overmux/keybindings@0.0.4
11
+
12
+ ## 0.0.5
13
+
14
+ ### Patch Changes
15
+
16
+ - [#149](https://github.com/richardgill/overmux/pull/149) [`99221b3`](https://github.com/richardgill/overmux/commit/99221b393196a714bd48b54c34eb5d848e8dbb85) Thanks [@richardgill](https://github.com/richardgill)! - Group documentation commands under `overmux docs`: use `overmux docs path` to locate installed documentation and `overmux docs ai-context` to print coding-agent guidance. Bare `overmux docs` now shows subcommand help; the top-level `ai` command is removed. Update generated agent instructions and clarify that documentation paths are not package source paths.
17
+
18
+ - [#148](https://github.com/richardgill/overmux/pull/148) [`3d50362`](https://github.com/richardgill/overmux/commit/3d503621030efeb39440a4180d58f67edf2dac94) Thanks [@richardgill](https://github.com/richardgill)! - Format inline code in documentation frontmatter titles.
19
+
20
+ - [#142](https://github.com/richardgill/overmux/pull/142) [`358649f`](https://github.com/richardgill/overmux/commit/358649fc9a681bd5aa89e240da12ddce694bde10) Thanks [@richardgill](https://github.com/richardgill)! - Navigate same-instance Overmux terminal links and HTML anchors internally in browsers and PWAs, with optional application-router integration and shared Desktop URL validation.
21
+
22
+ - [#144](https://github.com/richardgill/overmux/pull/144) [`c60a41b`](https://github.com/richardgill/overmux/commit/c60a41b26fcc7cf76fdf35fe21fe8cd02f552419) Thanks [@richardgill](https://github.com/richardgill)! - Use deterministic, owner-only `/tmp/overmux-<uid>` runtime directories when XDG runtime storage is unset, and a hashed `/tmp` path when it is too long for Unix sockets. Reject unsafe runtime directories and registration files. Restart existing servers after upgrading to make them discoverable at the new fallback location.
23
+
24
+ - [#153](https://github.com/richardgill/overmux/pull/153) [`7635e69`](https://github.com/richardgill/overmux/commit/7635e69458458b2ef752e6ac7aed6ee96897dbe4) Thanks [@richardgill](https://github.com/richardgill)! - Remove `checkOvermux`, `getOvermuxPaths`, `startOvermuxServer`, and their associated types from the public `overmux/server` entry point. The entry point remains importable as an empty module. Server lifecycle and inspection remain internal; use the CLI to start the server or check configuration.
25
+
26
+ - [#143](https://github.com/richardgill/overmux/pull/143) [`88cc80e`](https://github.com/richardgill/overmux/commit/88cc80e15ccfaa64c0e1cdfbe1e5729409451362) Thanks [@richardgill](https://github.com/richardgill)! - Make login output easier to scan with grouped URLs, shorter instructions and privacy warnings, and the remaining lifetime before the expiry timestamp. Only show the hint to create another login grant during serve.
27
+
28
+ - [`c5a5b8d`](https://github.com/richardgill/overmux/commit/c5a5b8dfc612c8ba784852d301b5c7ea5cb856f7) Thanks [@richardgill](https://github.com/richardgill)! - Simplify the CLI login privacy warning for multiple links.
29
+
30
+ - [#152](https://github.com/richardgill/overmux/pull/152) [`e01a8d9`](https://github.com/richardgill/overmux/commit/e01a8d9d5c11db4d65e2eb440d50b269fa807a56) Thanks [@richardgill](https://github.com/richardgill)! - Breaking API change: invalidate resources with `context.invalidate("resourceId", input?)` instead of passing a resource contract. IDs select exactly one registered resource and its derived dependants; unknown IDs throw. Optional input targeting is unchanged.
31
+
32
+ Inline `defineOvermuxServer` operations are now plain definitions rather than `defineOperation(...)` calls. Their handlers infer registered resource IDs and raw input types for invalidation. Independently defined reusable operations retain `defineOperation`, and operations that invalidate resources must declare their required resource inputs with `OperationContext`.
33
+
34
+ Git operation handlers now require `gitOperationHandlers({ repositories, resourceId: "sourceControl" })`, where `resourceId` matches the application's source-control resource key.
35
+
36
+ - [#156](https://github.com/richardgill/overmux/pull/156) [`ab2942a`](https://github.com/richardgill/overmux/commit/ab2942a035e9bff95b6fc150430ba2d7757b82bb) Thanks [@richardgill](https://github.com/richardgill)! - Restore `getOvermuxPaths` from the `overmux/server` public API.
37
+
38
+ - [#146](https://github.com/richardgill/overmux/pull/146) [`0acc04b`](https://github.com/richardgill/overmux/commit/0acc04b19ad5c123bc76aebb3933d9a767b0863c) Thanks [@richardgill](https://github.com/richardgill)! - Write server logs to the standard state-directory destination, removing the configurable log-file setting. Resolve debug logging and instance identity defaults explicitly; identity still evaluates its hostname-port value only after the listener binds.
39
+
40
+ - [#145](https://github.com/richardgill/overmux/pull/145) [`b1cdc75`](https://github.com/richardgill/overmux/commit/b1cdc75ab28c0637431d86a351bfcd6655b762d8) Thanks [@richardgill](https://github.com/richardgill)! - Rename the unlimited `auth.sessionLifetime` value from `"never"` to `"forever"`. The default is now `"forever"`; `"never"` is no longer accepted.
41
+
42
+ - [#139](https://github.com/richardgill/overmux/pull/139) [`719b27c`](https://github.com/richardgill/overmux/commit/719b27c31a81c648f264a683be3a064fb8718bec) Thanks [@richardgill](https://github.com/richardgill)! - Expose a server-confirmed `connectionId` on stream hooks and require it on custom tmux transports. Keep readiness scoped to the actual stream lifetime across reconnects and hot reloads; method rebinding no longer closes reused streams. Make `useTmuxTerminal` automatically reattach to its last confirmed session, following tmux's current window and pane. Explicit `goTo` requests may wait for opening, but reject on interruption and are not replayed. Automatic attachment failures are exposed through `error` and `onError` without endless retries. Custom stream owners retire replaced handles; the hook closes only its current stream on final unmount.
43
+
44
+ - [#154](https://github.com/richardgill/overmux/pull/154) [`376690d`](https://github.com/richardgill/overmux/commit/376690dbc752706c086dab5385a0a311c7a63780) Thanks [@richardgill](https://github.com/richardgill)! - Remove redundant keybinding exports from `overmux/client`.
45
+
3
46
  ## 0.0.4
4
47
 
5
48
  ### Patch Changes
package/README.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  Overmux is a local or self-hosted web UI for terminal sessions, agents, and Git workspaces.
4
4
 
5
- Documentation for this exact package version is included in [`docs`](./docs). Run `overmux docs` to print the absolute path to the installed documentation directory.
5
+ ## Installation
6
6
 
7
- Start with [What is Overmux?](./docs/100-introduction/100-what-is-overmux.md) and [Install and Run Overmux](./docs/200-getting-started/100-install-and-run-overmux.md).
7
+ Install the CLI with Mise:
8
+
9
+ ```sh
10
+ mise use --global npm:overmux
11
+ ```
12
+
13
+ Or with pnpm:
14
+
15
+ ```sh
16
+ pnpm add --global overmux
17
+ ```
18
+
19
+ ## Documentation
20
+
21
+ See the [Overmux documentation](./docs/000-index.md).
package/dist/bin.js CHANGED
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire, findPackageJSON } from "node:module";
3
- import { access, chmod, readFile, readdir, realpath } from "node:fs/promises";
3
+ import { createHash } from "node:crypto";
4
+ import { constants, cpSync, createWriteStream, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, statSync, watch, writeFileSync } from "node:fs";
5
+ import { access, open, readFile, readdir, realpath } from "node:fs/promises";
4
6
  import { createConnection, isIP } from "node:net";
5
7
  import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
6
- import { createJiti } from "jiti";
7
8
  import { ZodError, z } from "zod";
8
- import { homedir, hostname, tmpdir } from "node:os";
9
- import { createHash } from "node:crypto";
9
+ import { createJiti } from "jiti";
10
+ import { homedir, hostname } from "node:os";
10
11
  import ts from "typescript";
11
12
  import { isDeepStrictEqual } from "node:util";
12
13
  import { fork, spawnSync } from "node:child_process";
13
14
  import { fileURLToPath, pathToFileURL } from "node:url";
14
- import { cpSync, createWriteStream, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, statSync, watch, writeFileSync } from "node:fs";
15
15
  import { createInterface } from "node:readline/promises";
16
16
  import { Readable, Transform } from "node:stream";
17
17
  import { pipeline } from "node:stream/promises";
@@ -1825,6 +1825,35 @@ async function run(app, inputs, context) {
1825
1825
  /* v8 ignore if -- @preserve */
1826
1826
  /* v8 ignore else -- @preserve */
1827
1827
  //#endregion
1828
+ //#region ../shared/dist/index.js
1829
+ var safeRelativeNotificationLink = (link) => {
1830
+ let decoded;
1831
+ try {
1832
+ decoded = decodeURIComponent(link);
1833
+ } catch {
1834
+ return false;
1835
+ }
1836
+ return !decoded.startsWith("//") && !decoded.includes("\\") && ![...decoded].some((character) => {
1837
+ const codePoint = character.codePointAt(0) ?? 0;
1838
+ return codePoint < 32 || codePoint === 127;
1839
+ });
1840
+ };
1841
+ var notificationLinkSchema = z.union([z.string().max(4096).startsWith("/").refine(safeRelativeNotificationLink), z.string().max(4096).url().refine((link) => {
1842
+ if (!URL.canParse(link)) return false;
1843
+ const url = new URL(link);
1844
+ return ["http:", "https:"].includes(url.protocol) && !url.username && !url.password;
1845
+ })]);
1846
+ var notificationSchema = z.object({
1847
+ body: z.string().max(4096).optional(),
1848
+ open: z.object({ link: notificationLinkSchema }).strict().optional(),
1849
+ title: z.string().min(1).max(4096)
1850
+ }).strict();
1851
+ var instanceIdSchema$1 = z.string().max(253).regex(/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?![\s\S])/, "Expected a lowercase instance ID with only ASCII letters, digits, internal dots and hyphens");
1852
+ var createInstanceIdentity = (instanceId) => ({
1853
+ instanceId: instanceIdSchema$1.parse(instanceId),
1854
+ deepLinkPrefix: `overmux://${instanceId}`
1855
+ });
1856
+ //#endregion
1828
1857
  //#region ../shared/dist/node/index.js
1829
1858
  var durationPattern = /^([1-9]\d*)(ms|s|m|h|d)$/;
1830
1859
  var millisecondsByUnit = {
@@ -1842,7 +1871,7 @@ var durationToMilliseconds = (duration) => {
1842
1871
  return Number.isSafeInteger(milliseconds) ? milliseconds : void 0;
1843
1872
  };
1844
1873
  var aiContextSnippets = ["package-source", "tech-stack-recommendations"];
1845
- var instanceIdSchema$1 = z.string().max(253).regex(/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?![\s\S])/, "Expected a lowercase instance ID with only ASCII letters, digits, internal dots and hyphens");
1874
+ var instanceIdSchema = z.string().max(253).regex(/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?![\s\S])/, "Expected a lowercase instance ID with only ASCII letters, digits, internal dots and hyphens");
1846
1875
  var zodTypeSchema = z.custom((value) => value instanceof z.ZodType, "Expected a Zod schema");
1847
1876
  var functionSchema = z.custom((value) => typeof value === "function", "Expected a function");
1848
1877
  var contractRuntimeSchema = z.object({
@@ -1891,12 +1920,11 @@ var loopbackPeerSchema = z.string().refine((value) => isIP(value) !== 0 && (valu
1891
1920
  var authConfigRuntimeSchema = z.object({
1892
1921
  mode: z.literal("cli-login"),
1893
1922
  origins: z.array(exactOriginSchema).min(1).refine((origins) => new Set(origins).size === origins.length, "Expected unique origins").optional(),
1894
- sessionLifetime: z.union([z.literal("never"), authDurationSchema]).default("never"),
1923
+ sessionLifetime: z.union([z.literal("forever"), authDurationSchema]).default("forever"),
1895
1924
  trustedProxyPeer: loopbackPeerSchema.optional()
1896
1925
  }).strict();
1897
1926
  var serverConfigRuntimeSchema = z.object({
1898
1927
  host: z.string().min(1).optional(),
1899
- logFile: z.string().min(1).optional(),
1900
1928
  port: z.number().int().min(1).max(65535).optional(),
1901
1929
  productionWebAssetsDir: z.string().min(1).optional(),
1902
1930
  watch: z.boolean().optional()
@@ -1910,7 +1938,7 @@ var configDefinitionRuntimeSchema = serverConfigRuntimeSchema.extend({
1910
1938
  aiContextSnippets: z.array(z.enum(aiContextSnippets)).optional(),
1911
1939
  auth: authConfigRuntimeSchema,
1912
1940
  debug: z.boolean().optional(),
1913
- instanceId: z.union([instanceIdSchema$1, z.custom((value) => typeof value === "function", "Expected an instance ID or a function of the bound port")]).optional(),
1941
+ instanceId: z.union([instanceIdSchema, z.custom((value) => typeof value === "function", "Expected an instance ID or a function of the bound port")]).optional(),
1914
1942
  server: serverDefinitionRuntimeSchema,
1915
1943
  vite: z.string().min(1).optional()
1916
1944
  });
@@ -1966,91 +1994,9 @@ var getOvermuxPaths = ({ environment = process.env, homeDir = homedir(), xdgCach
1966
1994
  };
1967
1995
  };
1968
1996
  //#endregion
1969
- //#region ../shared/dist/index.js
1970
- var packageSourceSnippet = "package-source";
1971
- var techStackRecommendationsSnippet = "tech-stack-recommendations";
1972
- var safeRelativeNotificationLink = (link) => {
1973
- let decoded;
1974
- try {
1975
- decoded = decodeURIComponent(link);
1976
- } catch {
1977
- return false;
1978
- }
1979
- return !decoded.startsWith("//") && !decoded.includes("\\") && ![...decoded].some((character) => {
1980
- const codePoint = character.codePointAt(0) ?? 0;
1981
- return codePoint < 32 || codePoint === 127;
1982
- });
1983
- };
1984
- var notificationLinkSchema = z.union([z.string().max(4096).startsWith("/").refine(safeRelativeNotificationLink), z.string().max(4096).url().refine((link) => {
1985
- if (!URL.canParse(link)) return false;
1986
- const url = new URL(link);
1987
- return ["http:", "https:"].includes(url.protocol) && !url.username && !url.password;
1988
- })]);
1989
- var notificationSchema = z.object({
1990
- body: z.string().max(4096).optional(),
1991
- open: z.object({ link: notificationLinkSchema }).strict().optional(),
1992
- title: z.string().min(1).max(4096)
1993
- }).strict();
1994
- var instanceIdSchema = z.string().max(253).regex(/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?![\s\S])/, "Expected a lowercase instance ID with only ASCII letters, digits, internal dots and hyphens");
1995
- var createInstanceIdentity = (instanceId) => ({
1996
- instanceId: instanceIdSchema.parse(instanceId),
1997
- deepLinkPrefix: `overmux://${instanceId}`
1998
- });
1999
- var defaultAiContextSnippets = [...[packageSourceSnippet], techStackRecommendationsSnippet];
2000
- var packageSourceSnippetContent = `Overmux ships with documentation and TypeScript source.
2001
-
2002
- Run \`overmux docs\` to find the installed documentation and source for the overmux cli.
2003
-
2004
- E.g. \`node_modules/overmux\` and \`node_modules/@overmux/xterm\`.
2005
-
2006
- Inspect these files so guidance matches the versions used by the application.`;
2007
- var techStackRecommendationsSnippetContent = `Prefer pnpm as the package manager, TanStack Router for routing, shadcn/ui for UI components, and Zod for schemas and runtime validation. Follow the application's established stack when it already differs.`;
2008
- var aiContextContentBySnippet = {
2009
- [packageSourceSnippet]: packageSourceSnippetContent,
2010
- [techStackRecommendationsSnippet]: techStackRecommendationsSnippetContent
2011
- };
2012
- var composeAiContext = (snippets) => snippets.length === 0 ? "" : `${snippets.map((snippet) => `## ${snippet}\n\n${aiContextContentBySnippet[snippet]}`).join("\n\n")}\n`;
2013
- //#endregion
2014
1997
  //#region src/internal/server/paths.ts
2015
1998
  var getDefaultConfigPath = (options = {}) => join(getOvermuxPaths(options).configDir, "overmux.config.ts");
2016
1999
  //#endregion
2017
- //#region src/internal/cli/commands/ai.ts
2018
- var printAiContext = async (flags, process) => {
2019
- const { config } = await loadOvermuxConfig({ configPath: flags.config });
2020
- process.stdout.write(composeAiContext(config.aiContextSnippets ?? defaultAiContextSnippets));
2021
- };
2022
- var createAiContextCommand = (process) => buildCommand({
2023
- func: (flags) => printAiContext(flags, process),
2024
- parameters: {
2025
- aliases: { c: "config" },
2026
- flags: { config: {
2027
- brief: "Configuration file",
2028
- default: getDefaultConfigPath(),
2029
- kind: "parsed",
2030
- parse: String,
2031
- placeholder: "path"
2032
- } }
2033
- },
2034
- docs: { brief: "Print the configured Overmux AI context" }
2035
- });
2036
- var createAiRoute = (process) => buildRouteMap({
2037
- docs: { brief: "Print Overmux AI context" },
2038
- routes: { context: createAiContextCommand(process) }
2039
- });
2040
- //#endregion
2041
- //#region src/internal/server/notifications/private-json-file.ts
2042
- var missingFile = (cause) => cause.code === "ENOENT";
2043
- var readPrivateJson = async (path, schema) => {
2044
- try {
2045
- const contents = await readFile(path, "utf8");
2046
- await chmod(path, 384);
2047
- return schema.parse(JSON.parse(contents));
2048
- } catch (cause) {
2049
- if (missingFile(cause)) return;
2050
- throw cause;
2051
- }
2052
- };
2053
- //#endregion
2054
2000
  //#region src/internal/server/auth/instance-control.ts
2055
2001
  var maxControlMessageBytes = 16384;
2056
2002
  z.discriminatedUnion("type", [
@@ -2081,7 +2027,7 @@ var sessionSchema = z.object({
2081
2027
  }).strict();
2082
2028
  var controlResponseSchemas = {
2083
2029
  "get-instance": z.object({
2084
- instanceId: instanceIdSchema,
2030
+ instanceId: instanceIdSchema$1,
2085
2031
  ok: z.literal(true)
2086
2032
  }).strict(),
2087
2033
  "create-login": z.object({
@@ -2121,12 +2067,36 @@ var registrationSchema = z.object({
2121
2067
  port: z.number().int().min(1).max(65535),
2122
2068
  url: z.string().url()
2123
2069
  });
2070
+ var currentUid = () => {
2071
+ if (!process.getuid) throw new Error("Overmux instance control requires a Unix user ID");
2072
+ return process.getuid();
2073
+ };
2124
2074
  var runtimeDirectory = () => {
2125
- const paths = getOvermuxPaths();
2126
- const preferred = paths.runtimeDir ?? join(paths.stateDir, "runtime");
2075
+ const fallback = `/tmp/overmux-${currentUid()}`;
2076
+ const preferred = getOvermuxPaths().runtimeDir;
2077
+ if (!preferred) return fallback;
2127
2078
  if (Buffer.byteLength(preferred) <= 80) return preferred;
2128
- const suffix = createHash("sha256").update(preferred).digest("hex").slice(0, 12);
2129
- return join(tmpdir(), `overmux-${process.getuid?.() ?? "user"}-${suffix}`);
2079
+ return `${fallback}-${createHash("sha256").update(preferred).digest("hex").slice(0, 12)}`;
2080
+ };
2081
+ var secureRuntimeDirectory = async (directory) => {
2082
+ const handle = await open(directory, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
2083
+ try {
2084
+ const metadata = await handle.stat();
2085
+ if (!metadata.isDirectory() || metadata.uid !== currentUid()) throw new Error(`Unsafe Overmux runtime directory: ${directory}`);
2086
+ await handle.chmod(448);
2087
+ } finally {
2088
+ await handle.close();
2089
+ }
2090
+ };
2091
+ var readRegistration = async (path) => {
2092
+ const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
2093
+ try {
2094
+ const metadata = await handle.stat();
2095
+ if (!metadata.isFile() || metadata.uid !== currentUid() || (metadata.mode & 63) !== 0) return;
2096
+ return registrationSchema.parse(JSON.parse(await handle.readFile("utf8")));
2097
+ } finally {
2098
+ await handle.close();
2099
+ }
2130
2100
  };
2131
2101
  var readNewlineFrame = ({ maximumBytes, socket, timeout }) => new Promise((resolve, reject) => {
2132
2102
  let buffer = Buffer.alloc(0);
@@ -2178,12 +2148,13 @@ var discoverInstance = async (port) => {
2178
2148
  const directory = runtimeDirectory();
2179
2149
  let names;
2180
2150
  try {
2151
+ await secureRuntimeDirectory(directory);
2181
2152
  names = await readdir(directory);
2182
2153
  } catch (cause) {
2183
2154
  if (cause.code === "ENOENT") throw new Error("No running Overmux server was found");
2184
2155
  throw cause;
2185
2156
  }
2186
- const candidates = (await Promise.all(names.filter((name) => name.endsWith(".json")).map((name) => readPrivateJson(join(directory, name), registrationSchema).catch(() => void 0)))).filter((registration) => Boolean(registration && processIsLive(registration.pid) && (port === void 0 || registration.port === port)));
2157
+ const candidates = (await Promise.all(names.filter((name) => name.endsWith(".json")).map((name) => readRegistration(join(directory, name)).catch(() => void 0)))).filter((registration) => Boolean(registration && processIsLive(registration.pid) && (port === void 0 || registration.port === port)));
2187
2158
  const registrations = (await Promise.all(candidates.map(async (registration) => {
2188
2159
  try {
2189
2160
  await sendControlRequest(registration, { type: "ping" });
@@ -2229,18 +2200,18 @@ var issueLoginGrant = async (port) => {
2229
2200
  var printLoginFallback = (port, { stdout }) => {
2230
2201
  stdout.write(`Login with \`overmux auth login --port ${port}\`\n`);
2231
2202
  };
2232
- var printLoginGrant = ({ login, output, port }) => {
2203
+ var printLoginGrant = ({ login, output }) => {
2233
2204
  const expiresIn = Math.max(0, Math.ceil((Date.parse(login.expiresAt) - Date.now()) / 6e4));
2234
2205
  output.stdout.write([
2235
- `Code: ${login.code}`,
2236
- ...login.urls.map((url) => `Open: ${url}`),
2237
- "This single-use grant authenticates one origin. Create another grant to authenticate another origin.",
2238
- "Enter the code on the login page shown by an unauthenticated Overmux origin.",
2239
- `Expires at ${login.expiresAt} (in ${expiresIn} minutes).`,
2240
- `Need another grant? Run \`overmux auth login --port ${port}\`.`,
2206
+ `Code: ${login.code} (enter on the login page)`,
2207
+ "",
2208
+ ...login.urls.length === 1 ? [`Login URL: ${login.urls[0]}`] : ["Login URLs:", ...login.urls.map((url) => ` ${url}`)],
2209
+ "",
2210
+ `Expiry in ${expiresIn} minutes (at ${login.expiresAt}).`,
2211
+ "",
2241
2212
  ""
2242
2213
  ].join("\n"));
2243
- output.stderr.write("The login URL is a temporary credential; avoid sharing terminal output or screenshots.\n");
2214
+ output.stderr.write(`Keep the code and link${login.urls.length === 1 ? "" : "s"} private.\n`);
2244
2215
  };
2245
2216
  //#endregion
2246
2217
  //#region src/internal/cli/parse-port.ts
@@ -2535,7 +2506,7 @@ var protocolErrorMessageSchema = z.object({
2535
2506
  });
2536
2507
  var serverInfoMessageSchema = z.object({
2537
2508
  type: z.literal("server-info"),
2538
- instanceId: instanceIdSchema
2509
+ instanceId: instanceIdSchema$1
2539
2510
  }).strict();
2540
2511
  z.discriminatedUnion("type", [
2541
2512
  serverInfoMessageSchema,
@@ -2551,6 +2522,7 @@ z.discriminatedUnion("type", [
2551
2522
  ]);
2552
2523
  //#endregion
2553
2524
  //#region src/internal/server/runtime/runtime-instance.ts
2525
+ var defaultInstanceId = ({ port }) => `${hostname()}-${port}`;
2554
2526
  var createRuntimeInstance = (configured) => {
2555
2527
  let identity;
2556
2528
  const get = () => {
@@ -2565,7 +2537,7 @@ var createRuntimeInstance = (configured) => {
2565
2537
  establish: (port) => {
2566
2538
  if (identity) throw new Error("Overmux instance identity is already established");
2567
2539
  try {
2568
- identity = createInstanceIdentity(typeof configured === "function" ? configured({ port }) : configured ?? `${hostname()}-${port}`);
2540
+ identity = createInstanceIdentity(typeof configured === "function" ? configured({ port }) : configured);
2569
2541
  } catch (cause) {
2570
2542
  throw new Error("Invalid Overmux instance ID; configure instanceId with a canonical lowercase ID", { cause });
2571
2543
  }
@@ -2664,6 +2636,26 @@ var createRuntimeOperations = ({ context, definitions, notifications }) => {
2664
2636
  names: [...operations.keys()]
2665
2637
  };
2666
2638
  };
2639
+ //#endregion
2640
+ //#region ../ai-context/dist/index.js
2641
+ var packageSourceSnippet = "package-source";
2642
+ var techStackRecommendationsSnippet = "tech-stack-recommendations";
2643
+ var defaultAiContextSnippets = [...[packageSourceSnippet], techStackRecommendationsSnippet];
2644
+ var packageSourceSnippetContent = `Overmux ships with documentation and TypeScript source.
2645
+
2646
+ Run \`overmux docs path\` to find the installed documentation directory for the Overmux CLI. This prints a documentation directory, not source paths.
2647
+
2648
+ Inspect TypeScript source in installed packages, e.g. \`node_modules/overmux/src\` and \`node_modules/@overmux/xterm/src\`.
2649
+
2650
+ Inspect these files so guidance matches the versions used by the application.`;
2651
+ var techStackRecommendationsSnippetContent = `Prefer pnpm as the package manager, TanStack Router for routing, shadcn/ui for UI components, and Zod for schemas and runtime validation. Follow the application's established stack when it already differs.`;
2652
+ var aiContextContentBySnippet = {
2653
+ [packageSourceSnippet]: packageSourceSnippetContent,
2654
+ [techStackRecommendationsSnippet]: techStackRecommendationsSnippetContent
2655
+ };
2656
+ //#endregion
2657
+ //#region src/public/ai-context.ts
2658
+ var composeAiContext = (snippets) => snippets.length === 0 ? "" : `${snippets.map((snippet) => `## ${snippet}\n\n${aiContextContentBySnippet[snippet]}`).join("\n\n")}\n`;
2667
2659
  z.void();
2668
2660
  var jsonValueSchema = z.lazy(() => z.union([
2669
2661
  z.string(),
@@ -2743,11 +2735,7 @@ var getInvalidatedResourceIds = (graph, resourceId) => {
2743
2735
  var createRuntimeResources = ({ definitions, instance, lifecycle }) => {
2744
2736
  const graph = createResourceDependencyGraph(definitions);
2745
2737
  const invalidationListeners = /* @__PURE__ */ new Set();
2746
- const contracts = /* @__PURE__ */ new Map();
2747
2738
  const resources = /* @__PURE__ */ new Map();
2748
- Object.entries(definitions).forEach(([id, definition]) => {
2749
- contracts.set(definition.contract, [...contracts.get(definition.contract) ?? [], id]);
2750
- });
2751
2739
  const invalidateResource = (id, input, allInputs = false) => {
2752
2740
  if (lifecycle.signal.aborted) return;
2753
2741
  const ids = getInvalidatedResourceIds(graph, id);
@@ -2755,14 +2743,11 @@ var createRuntimeResources = ({ definitions, instance, lifecycle }) => {
2755
2743
  if (ids.has(entry.resourceId) && (allInputs || isDeepStrictEqual(entry.input, input))) entry.listener();
2756
2744
  });
2757
2745
  };
2758
- const invalidate = (contract, input) => {
2759
- const ids = contracts.get(contract) ?? Object.entries(definitions).filter(([, definition]) => definition.contract.input === contract.input && definition.contract.output === contract.output).map(([id]) => id);
2760
- if (!ids.length) throw new Error("Cannot invalidate a resource outside this runtime");
2761
- ids.forEach((id) => {
2762
- const definition = definitions[id];
2763
- const parsedInput = input === void 0 ? void 0 : definition.contract.input.parse(input);
2764
- invalidateResource(id, parsedInput, input === void 0);
2765
- });
2746
+ const invalidate = (resourceId, input) => {
2747
+ if (!resources.has(resourceId)) throw new Error(`Unknown resource ID: ${resourceId}`);
2748
+ const definition = definitions[resourceId];
2749
+ const parsedInput = input === void 0 ? void 0 : definition.contract.input.parse(input);
2750
+ invalidateResource(resourceId, parsedInput, input === void 0);
2766
2751
  };
2767
2752
  const context = (signal) => ({
2768
2753
  instance,
@@ -2898,9 +2883,9 @@ var createRuntimeStreams = ({ context, definitions, lifecycle }) => {
2898
2883
  };
2899
2884
  //#endregion
2900
2885
  //#region src/internal/server/runtime/create-runtime.ts
2901
- var createRuntime = async ({ config, debug = false, notifications = { send: async () => void 0 }, signal }) => {
2886
+ var createRuntime = async ({ config, debug = true, notifications = { send: async () => void 0 }, signal }) => {
2902
2887
  const lifecycle = createRuntimeLifecycle(signal);
2903
- const instance = createRuntimeInstance(config.instanceId);
2888
+ const instance = createRuntimeInstance(config.instanceId ?? defaultInstanceId);
2904
2889
  try {
2905
2890
  const resources = createRuntimeResources({
2906
2891
  definitions: config.server.resources ?? {},
@@ -3145,7 +3130,6 @@ var childOptionsSchema = z.object({
3145
3130
  debug: z.boolean().optional(),
3146
3131
  developmentWebTarget: z.url().optional(),
3147
3132
  host: z.string().optional(),
3148
- logFile: z.string().optional(),
3149
3133
  port: z.number().int().min(0).max(65535).optional(),
3150
3134
  productionWebAssetsDir: z.string().optional(),
3151
3135
  watch: z.boolean().optional()
@@ -3843,6 +3827,25 @@ var desktopRoute = buildRouteMap({
3843
3827
  }
3844
3828
  });
3845
3829
  //#endregion
3830
+ //#region src/internal/cli/commands/docs-ai-context.ts
3831
+ var createAiContextCommand = (process) => buildCommand({
3832
+ func: async (flags) => {
3833
+ const { config } = await loadOvermuxConfig({ configPath: flags.config });
3834
+ process.stdout.write(composeAiContext(config.aiContextSnippets ?? defaultAiContextSnippets));
3835
+ },
3836
+ parameters: {
3837
+ aliases: { c: "config" },
3838
+ flags: { config: {
3839
+ brief: "Configuration file",
3840
+ default: getDefaultConfigPath(),
3841
+ kind: "parsed",
3842
+ parse: String,
3843
+ placeholder: "path"
3844
+ } }
3845
+ },
3846
+ docs: { brief: "Print the configured Overmux AI context" }
3847
+ });
3848
+ //#endregion
3846
3849
  //#region src/internal/cli/commands/docs.ts
3847
3850
  var resolveInstalledDocsRoot = (moduleUrl) => {
3848
3851
  const docsRoot = [
@@ -3853,13 +3856,23 @@ var resolveInstalledDocsRoot = (moduleUrl) => {
3853
3856
  if (!docsRoot) throw new Error("Installed Overmux documentation is missing");
3854
3857
  return resolve(docsRoot);
3855
3858
  };
3856
- var createDocsCommand = ({ docsRoot, output }) => buildCommand({
3859
+ var createDocsPathCommand = ({ docsRoot, output }) => buildCommand({
3857
3860
  func: () => {
3858
3861
  output.write(`${resolve(docsRoot)}\n`);
3859
3862
  },
3860
3863
  parameters: {},
3861
3864
  docs: { brief: "Print the installed documentation directory" }
3862
3865
  });
3866
+ var createDocsRoute = ({ docsRoot, process }) => buildRouteMap({
3867
+ docs: { brief: "Find documentation and print coding-agent guidance" },
3868
+ routes: {
3869
+ path: createDocsPathCommand({
3870
+ docsRoot,
3871
+ output: process.stdout
3872
+ }),
3873
+ "ai-context": createAiContextCommand(process)
3874
+ }
3875
+ });
3863
3876
  //#endregion
3864
3877
  //#region src/internal/cli/commands/init-template.ts
3865
3878
  var initScaffoldPaths = [
@@ -3901,7 +3914,7 @@ var packageJson = (overmuxVersion) => `${JSON.stringify({
3901
3914
  }, null, 2)}\n`;
3902
3915
  var createInitScaffold = ({ overmuxVersion, toolchain }) => ({
3903
3916
  ".gitignore": "node_modules\ndist\n",
3904
- "AGENTS.md": "Immediately run `overmux ai context` for documentation about Overmux.\n",
3917
+ "AGENTS.md": "Immediately run `overmux docs ai-context` for documentation about Overmux.\n",
3905
3918
  "CLAUDE.md": "@AGENTS.md\n",
3906
3919
  ...toolchain === "mise" ? { "mise.toml": `[tools]
3907
3920
  node = "22"
@@ -4472,6 +4485,7 @@ var announceServeLogin = async ({ createLogin, port, process }) => {
4472
4485
  ...await issueLoginGrant(port),
4473
4486
  output: process
4474
4487
  });
4488
+ process.stdout.write(`\nNeed another grant? Run \`overmux auth login --port ${port}\`.\n`);
4475
4489
  };
4476
4490
  var runServe = async ({ environment, flags, process }) => {
4477
4491
  if (flags.noBuild && !flags.production) throw new Error("--no-build requires --production");
@@ -5132,14 +5146,13 @@ var detectCliEnvironment = async (process = globalThis.process) => {
5132
5146
  var createCliApp = (process = globalThis.process) => buildApplication(buildRouteMap({
5133
5147
  docs: { brief: "Initialize and manage Overmux applications" },
5134
5148
  routes: {
5135
- ai: createAiRoute(process),
5136
5149
  auth: authRoute,
5137
5150
  check: checkCommand,
5138
5151
  call: callCommand,
5139
5152
  desktop: desktopRoute,
5140
- docs: createDocsCommand({
5153
+ docs: createDocsRoute({
5141
5154
  docsRoot: resolveInstalledDocsRoot(import.meta.url),
5142
- output: process.stdout
5155
+ process
5143
5156
  }),
5144
5157
  init: createInitCommand(process),
5145
5158
  integration: integrationCommand,