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.
- package/CHANGELOG.md +43 -0
- package/README.md +16 -2
- package/dist/bin.js +140 -127
- package/dist/bin.js.map +1 -1
- package/dist/docs/000-index.md +2 -4
- package/dist/docs/100-introduction/200-how-overmux-works.md +126 -2
- package/dist/docs/100-introduction/{300-why-overmux.md → 300-why-i-built-overmux.md} +1 -1
- package/dist/docs/200-getting-started/100-install-and-run-overmux.md +2 -2
- package/dist/docs/200-getting-started/400-secure-with-https/200-tailscale-serve.md +3 -3
- package/dist/docs/200-getting-started/400-secure-with-https/300-cloudflare-tunnel.md +2 -2
- package/dist/docs/200-getting-started/400-secure-with-https/400-self-hosted-reverse-proxy.md +2 -2
- package/dist/docs/200-getting-started/500-set-up-overmux-with-packages.md +42 -0
- package/dist/docs/400-reference/100-project-structure.md +83 -0
- package/dist/docs/400-reference/200-configuration.md +257 -0
- package/dist/docs/400-reference/300-storage-locations.md +46 -0
- package/dist/docs/400-reference/400-authentication-and-security.md +37 -0
- package/dist/docs/400-reference/500-server/000-index.md +13 -0
- package/dist/docs/400-reference/500-server/100-resources.md +191 -0
- package/dist/docs/400-reference/500-server/200-operations.md +111 -0
- package/dist/docs/400-reference/500-server/300-streams.md +140 -0
- package/dist/docs/400-reference/500-server/400-notifications.md +32 -0
- package/dist/docs/400-reference/500-server/500-api.md +118 -0
- package/dist/docs/400-reference/600-client/000-index.md +9 -0
- package/dist/docs/400-reference/600-client/005-setting-up-your-ui.md +137 -0
- package/dist/docs/400-reference/600-client/007-deep-links.md +87 -0
- package/dist/docs/400-reference/600-client/010-commands.md +189 -0
- package/dist/docs/400-reference/600-client/020-shortcuts.md +171 -0
- package/dist/docs/400-reference/600-client/100-api.md +370 -0
- package/dist/docs/400-reference/600-client/200-theming.md +209 -0
- package/dist/docs/400-reference/600-client/300-tech-stack-recommendations.md +12 -0
- package/dist/docs/400-reference/{400-cli → 700-cli}/050-init.md +2 -4
- package/{docs/400-reference/400-cli → dist/docs/400-reference/700-cli}/100-serve.md +9 -4
- package/{docs/400-reference/400-cli → dist/docs/400-reference/700-cli}/200-auth.md +1 -4
- package/dist/docs/400-reference/{400-cli → 700-cli}/300-call.md +1 -4
- package/dist/docs/400-reference/{400-cli → 700-cli}/350-instance.md +1 -4
- package/dist/docs/400-reference/700-cli/600-docs.md +128 -0
- package/dist/docs/400-reference/{400-cli → 700-cli}/700-desktop.md +1 -3
- package/dist/docs/500-hosted-pages.md +0 -1
- package/dist/exports/client.d.ts +5 -14
- package/dist/exports/client.d.ts.map +1 -1
- package/dist/exports/client.js +141 -46
- package/dist/exports/client.js.map +1 -1
- package/dist/exports/{index-DS70rKzo.d.ts → index-Cz3xkCa4.d.ts} +50 -36
- package/dist/exports/index-Cz3xkCa4.d.ts.map +1 -0
- package/dist/exports/index.d.ts +1 -1
- package/dist/exports/index.js +19 -5
- package/dist/exports/index.js.map +1 -1
- package/dist/exports/{notifications-av0FK0yZ.js → notifications-BFAD3QQl.js} +29 -4
- package/dist/exports/notifications-BFAD3QQl.js.map +1 -0
- package/dist/exports/server.d.ts +1 -44
- package/dist/exports/server.d.ts.map +1 -1
- package/dist/exports/server.js +9 -1061
- package/dist/exports/server.js.map +1 -1
- package/dist/internal/server/coordinator/server-child.js +47 -43
- package/dist/internal/server/coordinator/server-child.js.map +1 -1
- package/docs/000-index.md +2 -4
- package/docs/100-introduction/200-how-overmux-works.md +126 -2
- package/docs/100-introduction/{300-why-overmux.md → 300-why-i-built-overmux.md} +1 -1
- package/docs/200-getting-started/100-install-and-run-overmux.md +2 -2
- package/docs/200-getting-started/400-secure-with-https/200-tailscale-serve.md +3 -3
- package/docs/200-getting-started/400-secure-with-https/300-cloudflare-tunnel.md +2 -2
- package/docs/200-getting-started/400-secure-with-https/400-self-hosted-reverse-proxy.md +2 -2
- package/docs/200-getting-started/500-set-up-overmux-with-packages.md +42 -0
- package/docs/400-reference/100-project-structure.md +83 -0
- package/docs/400-reference/200-configuration.md +257 -0
- package/docs/400-reference/300-storage-locations.md +46 -0
- package/docs/400-reference/400-authentication-and-security.md +37 -0
- package/docs/400-reference/500-server/000-index.md +13 -0
- package/docs/400-reference/500-server/100-resources.md +191 -0
- package/docs/400-reference/500-server/200-operations.md +111 -0
- package/docs/400-reference/500-server/300-streams.md +140 -0
- package/docs/400-reference/500-server/400-notifications.md +32 -0
- package/docs/400-reference/500-server/500-api.md +118 -0
- package/docs/400-reference/600-client/000-index.md +9 -0
- package/docs/400-reference/600-client/005-setting-up-your-ui.md +137 -0
- package/docs/400-reference/600-client/007-deep-links.md +87 -0
- package/docs/400-reference/600-client/010-commands.md +189 -0
- package/docs/400-reference/600-client/020-shortcuts.md +171 -0
- package/docs/400-reference/600-client/100-api.md +370 -0
- package/docs/400-reference/600-client/200-theming.md +209 -0
- package/docs/400-reference/600-client/300-tech-stack-recommendations.md +12 -0
- package/docs/400-reference/{400-cli → 700-cli}/050-init.md +2 -4
- package/{dist/docs/400-reference/400-cli → docs/400-reference/700-cli}/100-serve.md +9 -4
- package/{dist/docs/400-reference/400-cli → docs/400-reference/700-cli}/200-auth.md +1 -4
- package/docs/400-reference/{400-cli → 700-cli}/300-call.md +1 -4
- package/docs/400-reference/{400-cli → 700-cli}/350-instance.md +1 -4
- package/docs/400-reference/700-cli/600-docs.md +128 -0
- package/docs/400-reference/{400-cli → 700-cli}/700-desktop.md +1 -3
- package/docs/500-hosted-pages.md +0 -1
- package/package.json +4 -3
- package/src/internal/cli/app.ts +3 -5
- package/src/internal/cli/commands/docs-ai-context.ts +33 -0
- package/src/internal/cli/commands/docs.ts +19 -2
- package/src/internal/cli/commands/init-template.ts +1 -1
- package/src/internal/cli/commands/serve.ts +3 -0
- package/src/internal/cli/login.ts +9 -9
- package/src/internal/client/client-definition.ts +6 -8
- package/src/internal/client/host/deep-link-navigation.ts +75 -0
- package/src/internal/client/host/overmux-host.tsx +9 -0
- package/src/internal/client/index.ts +0 -6
- package/src/internal/client/overmux-react.ts +71 -40
- package/src/internal/server/auth/auth-service.ts +2 -2
- package/src/internal/server/auth/instance-control.ts +65 -14
- package/src/internal/server/coordinator/ipc-protocol.ts +0 -1
- package/src/internal/server/runtime/create-runtime.ts +5 -3
- package/src/internal/server/runtime/runtime-instance.ts +5 -4
- package/src/internal/server/runtime/runtime-operations.ts +9 -3
- package/src/internal/server/runtime/runtime-resources.ts +12 -32
- package/src/internal/server/runtime/runtime-streams.ts +5 -6
- package/src/internal/server/server-logger.ts +5 -10
- package/src/internal/server/server-startup-options.ts +5 -10
- package/src/internal/server/start-application-server.ts +4 -7
- package/src/public/ai-context.ts +7 -29
- package/src/public/client.ts +0 -6
- package/src/public/config.ts +156 -31
- package/src/public/server.ts +1 -16
- package/dist/docs/100-introduction/100-what-is-overmux.md +0 -7
- package/dist/docs/300-fundamentals/100-project-structure.md +0 -23
- package/dist/docs/300-fundamentals/200-configuration.md +0 -3
- package/dist/docs/300-fundamentals/300-theming.md +0 -54
- package/dist/docs/300-fundamentals/400-server.md +0 -3
- package/dist/docs/300-fundamentals/500-client.md +0 -3
- package/dist/docs/300-fundamentals/600-operations.md +0 -3
- package/dist/docs/300-fundamentals/700-resources.md +0 -3
- package/dist/docs/300-fundamentals/800-streams.md +0 -3
- package/dist/docs/300-fundamentals/900-authentication-and-security.md +0 -3
- package/dist/docs/400-reference/100-configuration.md +0 -23
- package/dist/docs/400-reference/200-server-api.md +0 -21
- package/dist/docs/400-reference/300-client-api.md +0 -39
- package/dist/docs/400-reference/400-cli/400-check.md +0 -20
- package/dist/docs/400-reference/400-cli/500-ai-context.md +0 -102
- package/dist/docs/400-reference/400-cli/600-docs.md +0 -23
- package/dist/exports/index-DS70rKzo.d.ts.map +0 -1
- package/dist/exports/notifications-av0FK0yZ.js.map +0 -1
- package/docs/100-introduction/100-what-is-overmux.md +0 -7
- package/docs/300-fundamentals/100-project-structure.md +0 -23
- package/docs/300-fundamentals/200-configuration.md +0 -3
- package/docs/300-fundamentals/300-theming.md +0 -54
- package/docs/300-fundamentals/400-server.md +0 -3
- package/docs/300-fundamentals/500-client.md +0 -3
- package/docs/300-fundamentals/600-operations.md +0 -3
- package/docs/300-fundamentals/700-resources.md +0 -3
- package/docs/300-fundamentals/800-streams.md +0 -3
- package/docs/300-fundamentals/900-authentication-and-security.md +0 -3
- package/docs/400-reference/100-configuration.md +0 -23
- package/docs/400-reference/200-server-api.md +0 -21
- package/docs/400-reference/300-client-api.md +0 -39
- package/docs/400-reference/400-cli/400-check.md +0 -20
- package/docs/400-reference/400-cli/500-ai-context.md +0 -102
- package/docs/400-reference/400-cli/600-docs.md +0 -23
- package/src/internal/cli/commands/ai.ts +0 -44
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Authentication and Security
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Overmux includes authentication by default to make sure your terminals and agents are protected.
|
|
6
|
+
|
|
7
|
+
Overmux requires authentication to access its server APIs and your application's client bundle. Only the login page and its assets, authentication endpoints, and health check are accessible before login.
|
|
8
|
+
|
|
9
|
+
## Log in
|
|
10
|
+
|
|
11
|
+
Run `overmux serve` and open your server's URL in your browser. You'll be asked to enter a code.
|
|
12
|
+
|
|
13
|
+
On the server machine, as the same OS user running Overmux, run:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
overmux auth login
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Enter the code or open one of the printed login links.
|
|
20
|
+
|
|
21
|
+
- Codes and links expire after 10 minutes.
|
|
22
|
+
- Each code and its links share one login: using any one invalidates the others.
|
|
23
|
+
- Each browser, device, or origin (scheme, hostname, and port) needs its own login.
|
|
24
|
+
|
|
25
|
+
Your browser remembers your session across server restarts. Sessions have no server-side expiry by default; you can change this with [`auth.sessionLifetime`](./200-configuration.md#auth).
|
|
26
|
+
|
|
27
|
+
Use [`overmux auth`](./700-cli/200-auth.md) to list sessions or revoke access.
|
|
28
|
+
|
|
29
|
+
## How access is secured
|
|
30
|
+
|
|
31
|
+
Overmux trusts your OS account. Login creation and session administration use a local Unix socket protected by owner-only filesystem permissions, not a public HTTP endpoint. Stored authentication data is also owner-only, and credentials are stored as hashes.
|
|
32
|
+
|
|
33
|
+
Any process running as your OS user, or root, can administer access. Authentication does not protect against malicious software already running as your user.
|
|
34
|
+
|
|
35
|
+
Browser sessions use protected cookies. There are no separate accounts, roles, or read-only permissions: logging in grants access to everything your setup exposes. Keep login codes and links secret.
|
|
36
|
+
|
|
37
|
+
Remote browser access requires HTTPS and a configured allowed origin. Follow [Secure with HTTPS](../200-getting-started/400-secure-with-https/100-choose-an-https-setup.md).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Server
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
`overmux serve` runs Overmux's server on your dev machine.
|
|
6
|
+
|
|
7
|
+
Overmux's server architecture provides three primitives that package authors can take advantage of:
|
|
8
|
+
|
|
9
|
+
- [Resources](./100-resources.md): Expose server-side data to your UI. Read data on request, subscribe to changes, or derive values from other resources.
|
|
10
|
+
- [Operations](./200-operations.md): Expose server-side actions you can call from your UI or CLI.
|
|
11
|
+
- [Streams](./300-streams.md): Provide low-latency, bidirectional data flow between your UI and server, such as terminal input/output or live AI agent output.
|
|
12
|
+
|
|
13
|
+
See [Notifications](./400-notifications.md) for sending notifications and [Server API](./500-api.md) for definition helpers and handler context.
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Resources
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Resources expose server-side data to your Overmux UI.
|
|
6
|
+
|
|
7
|
+
There are three kinds of resource: [query](#query), [subscription](#subscription), and [derived](#derived).
|
|
8
|
+
|
|
9
|
+
## Query
|
|
10
|
+
|
|
11
|
+
Query resources read server-side data on request.
|
|
12
|
+
|
|
13
|
+
For example, to run `ls /` on your server and expose the result:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
// server.ts
|
|
17
|
+
defineOvermuxServer({
|
|
18
|
+
resources: {
|
|
19
|
+
ls: {
|
|
20
|
+
kind: "query",
|
|
21
|
+
contract: defineResourceContract({
|
|
22
|
+
input: noInputSchema,
|
|
23
|
+
output: z.array(z.string()),
|
|
24
|
+
}),
|
|
25
|
+
read: async () => {
|
|
26
|
+
const { stdout } = await execFileAsync("ls", ["/"]);
|
|
27
|
+
return stdout.split("\n").filter(Boolean);
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
See [Accessing resources from your Overmux UI](#accessing-resources-from-your-overmux-ui) to display the listing in React.
|
|
35
|
+
|
|
36
|
+
## Subscription
|
|
37
|
+
|
|
38
|
+
Subscription resources read server-side data and notify clients when it changes.
|
|
39
|
+
|
|
40
|
+
To keep the `ls /` listing up to date, watch the directory for changes:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { watch } from "node:fs";
|
|
44
|
+
|
|
45
|
+
// server.ts
|
|
46
|
+
defineOvermuxServer({
|
|
47
|
+
resources: {
|
|
48
|
+
ls: {
|
|
49
|
+
kind: "subscription",
|
|
50
|
+
contract: defineResourceContract({
|
|
51
|
+
input: noInputSchema,
|
|
52
|
+
output: z.array(z.string()),
|
|
53
|
+
}),
|
|
54
|
+
read: async () => {
|
|
55
|
+
const { stdout } = await execFileAsync("ls", ["/"]);
|
|
56
|
+
return stdout.split("\n").filter(Boolean);
|
|
57
|
+
},
|
|
58
|
+
subscribe: (_input, invalidate) => {
|
|
59
|
+
// Watch the root directory for filesystem changes.
|
|
60
|
+
const watcher = watch("/", () => {
|
|
61
|
+
// Tell Overmux the data may have changed so it reads it again.
|
|
62
|
+
invalidate();
|
|
63
|
+
});
|
|
64
|
+
// Subscription cleanup function.
|
|
65
|
+
return () => watcher.close();
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
When the subscription ends, Overmux calls the cleanup function returned by `subscribe`. In this example, it closes the filesystem watcher.
|
|
73
|
+
|
|
74
|
+
The React code stays the same: `useResource({ id: "ls" })`.
|
|
75
|
+
|
|
76
|
+
## Derived
|
|
77
|
+
|
|
78
|
+
Derived resources compute a value from other resources.
|
|
79
|
+
|
|
80
|
+
For example, count the entries returned by the `ls` resource above. Add `entryCount` alongside `ls` in your `resources` object:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
defineOvermuxServer({
|
|
84
|
+
resources: {
|
|
85
|
+
ls: {
|
|
86
|
+
// The subscription resource above:
|
|
87
|
+
// reads ls / and watches for directory changes.
|
|
88
|
+
},
|
|
89
|
+
entryCount: {
|
|
90
|
+
kind: "derived",
|
|
91
|
+
contract: defineResourceContract({
|
|
92
|
+
input: noInputSchema,
|
|
93
|
+
output: z.number(),
|
|
94
|
+
}),
|
|
95
|
+
dependencies: { ls: "ls" },
|
|
96
|
+
combine: ({ ls }) => ls.length,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`dependencies` maps the names used in `combine` to resource IDs. Here, `combine` receives the value of `ls` and returns its length.
|
|
103
|
+
|
|
104
|
+
If `ls` returns `["bin", "etc", "home"]`, `entryCount` returns `3`. With the subscription version of `ls`, the count updates when the directory listing changes.
|
|
105
|
+
|
|
106
|
+
Access it from your UI with `useResource({ id: "entryCount" })`.
|
|
107
|
+
|
|
108
|
+
## Accessing resources from your Overmux UI
|
|
109
|
+
|
|
110
|
+
Use `useResource` to access query, subscription, and derived resources. Create your typed hooks with `createOvermuxHooks` from `overmux/client`:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
// hooks.ts
|
|
114
|
+
import { createOvermuxHooks } from "overmux/client";
|
|
115
|
+
import type { serverConfig } from "./server";
|
|
116
|
+
|
|
117
|
+
export const { useResource } = createOvermuxHooks<typeof serverConfig>();
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Here, `serverConfig` is the exported result of `defineOvermuxServer`. The type-only import provides type checking without including your server code in the browser.
|
|
121
|
+
|
|
122
|
+
Use the hook inside a component under your Overmux provider:
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import { useResource } from "./hooks";
|
|
126
|
+
|
|
127
|
+
const DirectoryListing = () => {
|
|
128
|
+
const ls = useResource({ id: "ls" });
|
|
129
|
+
|
|
130
|
+
if (ls.status === "pending") return <p>Loading...</p>;
|
|
131
|
+
if (ls.status === "error") return <p>{ls.error.message}</p>;
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div>
|
|
135
|
+
{ls.data.map((name) => (
|
|
136
|
+
<div key={name}>{name}</div>
|
|
137
|
+
))}
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
};
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The same component works with either `ls` example above. Overmux handles reads and change notifications, updating your component when fresh data arrives.
|
|
144
|
+
|
|
145
|
+
See [Client API: Resources](../600-client/100-api.md#resources) for input arguments, return values, and manual refreshes.
|
|
146
|
+
|
|
147
|
+
## Invalidating resources
|
|
148
|
+
|
|
149
|
+
After server-side data changes, call `invalidate()` to tell clients: **“Your data may be out of date. Read this resource again.”**
|
|
150
|
+
|
|
151
|
+
Clients will request the data by calling the resource's `read` function again.
|
|
152
|
+
|
|
153
|
+
### From a subscription
|
|
154
|
+
|
|
155
|
+
Call the `invalidate` callback provided to `subscribe` when your watcher detects a change:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
subscribe: (_input, invalidate) => {
|
|
159
|
+
const watcher = watch("/", () => {
|
|
160
|
+
// Tell clients to read the directory listing again.
|
|
161
|
+
invalidate();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
return () => watcher.close();
|
|
165
|
+
},
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### From an operation
|
|
169
|
+
|
|
170
|
+
After changing server-side state in an [operation](./200-operations.md), call `context.invalidate(resourceId)`:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
handle: async ({ path }, context) => {
|
|
174
|
+
await mkdir(path);
|
|
175
|
+
|
|
176
|
+
// Refresh clients using the directory listing resource.
|
|
177
|
+
context.invalidate("ls");
|
|
178
|
+
},
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Pass the resource's key in `resources`. Here, `"ls"` identifies the directory listing.
|
|
182
|
+
|
|
183
|
+
Omit the second argument to invalidate all inputs. For a resource that accepts input, pass it to refresh only matching data: `context.invalidate("files", { path: "/" })`.
|
|
184
|
+
|
|
185
|
+
## How resources work
|
|
186
|
+
|
|
187
|
+
Resources communicate over a persistent WebSocket connection between your browser and server. This connection lets the client request data and the server send change notifications.
|
|
188
|
+
|
|
189
|
+
When you call `useResource`, the client requests the resource's value. For subscription resources, it also listens for change notifications.
|
|
190
|
+
|
|
191
|
+
Calling `invalidate()` tells the client its data may be stale. It does not send the updated value; the client requests a fresh read, and React updates with the result.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Operations
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Operations expose server-side functions you can call from your UI or the `overmux call` CLI.
|
|
6
|
+
|
|
7
|
+
## Defining an operation
|
|
8
|
+
|
|
9
|
+
Define operations in your server's `operations` object:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
// server.ts
|
|
13
|
+
export const serverConfig = defineOvermuxServer({
|
|
14
|
+
operations: {
|
|
15
|
+
echo: {
|
|
16
|
+
input: z.object({ message: z.string() }),
|
|
17
|
+
output: z.string(),
|
|
18
|
+
handle: ({ message }) => message,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The object key, `echo`, is the operation's ID. `handle` runs on your Overmux server machine and returns the message it receives.
|
|
25
|
+
|
|
26
|
+
### Input and output
|
|
27
|
+
|
|
28
|
+
Use [Zod schemas](https://zod.dev/) to define the input an operation accepts and the result it returns. Here, `echo` accepts an object with a `message` string and returns that string unchanged.
|
|
29
|
+
|
|
30
|
+
Handlers can be asynchronous, so you can run commands, access files, or call other services before returning a result.
|
|
31
|
+
|
|
32
|
+
### Handler API
|
|
33
|
+
|
|
34
|
+
The `handle` function receives the operation's input and a `context` object:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
handle: async (input, context) => {
|
|
38
|
+
// Run your server-side logic here.
|
|
39
|
+
},
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`context` provides:
|
|
43
|
+
|
|
44
|
+
- **`invalidate(resourceId, input?)`**: tells clients to read an affected resource again after you change server-side data. See [Invalidating resources](./100-resources.md#invalidating-resources).
|
|
45
|
+
- **[`notifications.send(...)`](./400-notifications.md)**: sends a notification through Overmux.
|
|
46
|
+
|
|
47
|
+
See [Server API](./500-api.md#handler-context) for the full handler context.
|
|
48
|
+
|
|
49
|
+
## Calling operations from your Overmux UI
|
|
50
|
+
|
|
51
|
+
Create typed hooks with `createOvermuxHooks` from `overmux/client`:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
// hooks.ts
|
|
55
|
+
import { createOvermuxHooks } from "overmux/client";
|
|
56
|
+
import type { serverConfig } from "./server";
|
|
57
|
+
|
|
58
|
+
export const { useOperation } = createOvermuxHooks<typeof serverConfig>();
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The type-only import provides type checking without including server code in the browser.
|
|
62
|
+
|
|
63
|
+
Use the hook inside a component under your Overmux provider:
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { useOperation } from "./hooks";
|
|
67
|
+
|
|
68
|
+
const EchoButton = () => {
|
|
69
|
+
const echo = useOperation({ id: "echo" });
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<button onClick={() => echo.mutate({ message: "Hello world" })}>
|
|
73
|
+
Echo message
|
|
74
|
+
</button>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Pass the operation's input to `mutate`.
|
|
80
|
+
|
|
81
|
+
Use `mutateAsync` when you need to await the result:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const message = await echo.mutateAsync({ message: "Hello world" });
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
See [Client API](../600-client/100-api.md) for operation hooks, pending state, errors, and results.
|
|
88
|
+
|
|
89
|
+
## Calling operations from the CLI
|
|
90
|
+
|
|
91
|
+
Call an operation by its ID and pass input as JSON:
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
overmux call echo --input '{"message":"Hello world"}'
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The CLI prints the returned result as JSON:
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
"Hello world"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Agents can use the same CLI commands to call your operations.
|
|
104
|
+
|
|
105
|
+
## How operations work
|
|
106
|
+
|
|
107
|
+
Each call sends an HTTP `POST` to `/api/operations/{operationName}`, with any input encoded as JSON.
|
|
108
|
+
|
|
109
|
+
Overmux handles authentication, validates the input, and calls your handler. If an output schema is defined, Overmux validates the result before returning it.
|
|
110
|
+
|
|
111
|
+
Unlike resource subscriptions, an operation is a single request and response. It returns a result or an error; it does not keep listening for updates.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Streams
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Streams send and receive messages between your Overmux UI and server over a persistent connection. Use them for ongoing communication, such as terminal input and output or live logs.
|
|
6
|
+
|
|
7
|
+
Unlike [operations](./200-operations.md), streams can send and receive multiple messages. Unlike [resources](./100-resources.md), they deliver individual messages rather than a value clients read again when it changes.
|
|
8
|
+
|
|
9
|
+
## Defining a stream
|
|
10
|
+
|
|
11
|
+
Define streams in your server's `streams` object:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
// server.ts
|
|
15
|
+
import {
|
|
16
|
+
defineOvermuxServer,
|
|
17
|
+
defineStreamContract,
|
|
18
|
+
defineStreamHandler,
|
|
19
|
+
noInputSchema,
|
|
20
|
+
} from "overmux";
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
|
|
23
|
+
const echoContract = defineStreamContract({
|
|
24
|
+
input: noInputSchema,
|
|
25
|
+
clientMessage: z.object({ message: z.string() }),
|
|
26
|
+
serverMessage: z.object({ message: z.string() }),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const serverConfig = defineOvermuxServer({
|
|
30
|
+
streams: {
|
|
31
|
+
echo: defineStreamHandler(echoContract, (_input, { emit }) => ({
|
|
32
|
+
onMessage: ({ message }) => {
|
|
33
|
+
emit({ message });
|
|
34
|
+
},
|
|
35
|
+
})),
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The object key, `echo`, is the stream's ID. Each time a client opens the stream, Overmux calls its handler to create a session.
|
|
41
|
+
|
|
42
|
+
Here, `onMessage` receives a message from the client and `emit` sends it back to that client.
|
|
43
|
+
|
|
44
|
+
### Input and messages
|
|
45
|
+
|
|
46
|
+
Use [Zod schemas](https://zod.dev/) to define:
|
|
47
|
+
|
|
48
|
+
- **`input`**: the input provided when opening the stream.
|
|
49
|
+
- **`clientMessage`**: messages sent from the UI to the server.
|
|
50
|
+
- **`serverMessage`**: messages sent from the server to the UI.
|
|
51
|
+
|
|
52
|
+
This example needs no opening input. Other streams might accept a terminal ID or a log file name.
|
|
53
|
+
|
|
54
|
+
The server can also call `emit` independently of client messages, for example when a process produces output.
|
|
55
|
+
|
|
56
|
+
## Accessing streams from your Overmux UI
|
|
57
|
+
|
|
58
|
+
Create typed hooks with `createOvermuxHooks` from `overmux/client`:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// hooks.ts
|
|
62
|
+
import { createOvermuxHooks } from "overmux/client";
|
|
63
|
+
import type { serverConfig } from "./server";
|
|
64
|
+
|
|
65
|
+
export const { useStream } = createOvermuxHooks<typeof serverConfig>();
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The type-only import provides type checking without including server code in the browser.
|
|
69
|
+
|
|
70
|
+
Use the hook inside a component under your Overmux provider:
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import { useEffect, useState } from "react";
|
|
74
|
+
import { useStream } from "./hooks";
|
|
75
|
+
|
|
76
|
+
const Echo = () => {
|
|
77
|
+
const { status, error, send, subscribe } = useStream({ id: "echo" });
|
|
78
|
+
const [message, setMessage] = useState("");
|
|
79
|
+
|
|
80
|
+
useEffect(
|
|
81
|
+
() => subscribe(({ message }) => setMessage(message)),
|
|
82
|
+
[subscribe],
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (error) return <p>{error.message}</p>;
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div>
|
|
89
|
+
<button
|
|
90
|
+
disabled={status !== "open"}
|
|
91
|
+
onClick={() => send({ message: "Hello world" })}
|
|
92
|
+
>
|
|
93
|
+
Send message
|
|
94
|
+
</button>
|
|
95
|
+
<p>{message}</p>
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`send` sends a client message. `subscribe` registers a listener for server messages and returns a function that removes it, which the effect uses for cleanup.
|
|
102
|
+
|
|
103
|
+
Unlike `useResource`, `useStream` does not store the latest message as `data`. Your component decides how to display or process incoming messages.
|
|
104
|
+
|
|
105
|
+
See [Client API: Streams](../600-client/100-api.md#streams) for connection state and reconnection details.
|
|
106
|
+
|
|
107
|
+
## Cleaning up a stream
|
|
108
|
+
|
|
109
|
+
Return `dispose` from your handler to release resources when the session closes:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
defineStreamHandler(echoContract, (_input, { emit }) => {
|
|
113
|
+
const timer = setInterval(() => {
|
|
114
|
+
emit({ message: "Still connected" });
|
|
115
|
+
}, 1_000);
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
onMessage: ({ message }) => {
|
|
119
|
+
emit({ message });
|
|
120
|
+
},
|
|
121
|
+
dispose: () => {
|
|
122
|
+
clearInterval(timer);
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Overmux calls `dispose` once when the session closes. Use it to stop timers, remove listeners, or release other session-owned resources.
|
|
129
|
+
|
|
130
|
+
`useStream` closes its session when the component unmounts. You can also close it explicitly with `close()`.
|
|
131
|
+
|
|
132
|
+
The handler context provides an abort `signal` for cancelling background work and `fail(cause)` for failing and closing the session. See [Server API: Stream handlers](./500-api.md#stream-handlers).
|
|
133
|
+
|
|
134
|
+
## How streams work
|
|
135
|
+
|
|
136
|
+
Streams communicate over a persistent WebSocket connection between your browser and server.
|
|
137
|
+
|
|
138
|
+
Overmux validates the opening input and messages against the stream contract. Client messages are passed to `onMessage` in order; calls to `emit` send messages to that session's client.
|
|
139
|
+
|
|
140
|
+
A restored stream after a WebSocket reconnect creates a new server session. Do not assume that state held by the previous session survives.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Notifications
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Send notifications from an [operation handler](./200-operations.md#handler-api) using `context.notifications.send()`. There is no separate notification API to import; Overmux provides it through the handler's `context`:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
// server.ts
|
|
9
|
+
import { defineOperation, defineOvermuxServer, noInputSchema } from "overmux";
|
|
10
|
+
|
|
11
|
+
export const serverConfig = defineOvermuxServer({
|
|
12
|
+
resources: {},
|
|
13
|
+
operations: {
|
|
14
|
+
myOperation: defineOperation({
|
|
15
|
+
input: noInputSchema,
|
|
16
|
+
handle: async (_input, context) => {
|
|
17
|
+
await context.notifications.send({
|
|
18
|
+
title: "Task finished",
|
|
19
|
+
body: "Your results are ready.",
|
|
20
|
+
open: { link: "/results" },
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
}),
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- `title`: required, non-empty text.
|
|
29
|
+
- `body`: optional text.
|
|
30
|
+
- `open.link`: optional destination to open when the notification is activated. Use an app-relative path starting with `/` or an HTTP(S) URL without credentials.
|
|
31
|
+
|
|
32
|
+
Each text field and link has a maximum length of 4,096 characters. `send()` returns `Promise<void>`.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Server API Reference
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Server definitions
|
|
6
|
+
|
|
7
|
+
Import definition helpers from `overmux`:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
defineOperation,
|
|
12
|
+
defineOvermuxServer,
|
|
13
|
+
defineResourceContract,
|
|
14
|
+
defineStreamContract,
|
|
15
|
+
defineStreamHandler,
|
|
16
|
+
} from "overmux";
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`defineOvermuxServer()` defines the capabilities available from one server. It preserves types for `overmux/client` hooks and defaults omitted operation output schemas to `z.void()`.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
export const serverConfig = defineOvermuxServer({
|
|
23
|
+
resources: {
|
|
24
|
+
// Resource definitions, keyed by resource ID.
|
|
25
|
+
},
|
|
26
|
+
operations: {
|
|
27
|
+
// defineOperation(...) values, keyed by operation ID.
|
|
28
|
+
},
|
|
29
|
+
streams: {
|
|
30
|
+
// defineStreamHandler(...) values, keyed by stream ID.
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
| Option | Required | Value |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| `resources` | Yes | An object of query, subscription, or derived resource definitions. |
|
|
38
|
+
| `operations` | No | Inline operation definitions or reusable `defineOperation(...)` values. |
|
|
39
|
+
| `streams` | No | An object of `defineStreamHandler(...)` definitions. |
|
|
40
|
+
|
|
41
|
+
Object keys are the IDs used by clients. See [Resources](./100-resources.md), [Operations](./200-operations.md), and [Streams](./300-streams.md) for usage guides.
|
|
42
|
+
|
|
43
|
+
## Resource handlers
|
|
44
|
+
|
|
45
|
+
A resource definition has a `contract` created with `defineResourceContract({ input, output })`. Overmux parses handler input with `input` and validates every result with `output`.
|
|
46
|
+
|
|
47
|
+
| Kind | Required handler API |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| `query` | `read(input, context) => output \| Promise<output>` |
|
|
50
|
+
| `subscription` | `read(input, context)` and `subscribe(input, invalidate, context) => disposer` |
|
|
51
|
+
| `derived` | `dependencies` and `combine(dependencies) => output` |
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const count = defineResourceContract({
|
|
55
|
+
input: noInputSchema,
|
|
56
|
+
output: z.number(),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const resources = {
|
|
60
|
+
count: {
|
|
61
|
+
kind: "subscription",
|
|
62
|
+
contract: count,
|
|
63
|
+
read: () => readCount(),
|
|
64
|
+
subscribe: (_input, invalidate) => {
|
|
65
|
+
const stop = watchCount(invalidate);
|
|
66
|
+
return () => stop();
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`subscribe` must synchronously return a cleanup function. Overmux calls it when that client subscription, or the server runtime, closes. A derived resource reads the resources named by `dependencies`; invalidating a dependency also invalidates its dependants. See [Resources](./100-resources.md) for contracts, derived dependencies, and invalidation.
|
|
73
|
+
|
|
74
|
+
## Stream handlers
|
|
75
|
+
|
|
76
|
+
A stream contract defines `input`, `clientMessage`, and `serverMessage` schemas. Define its handler with:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const terminal = defineStreamHandler(contract, (input, context) => ({
|
|
80
|
+
onMessage: async (message) => {
|
|
81
|
+
// Handle a validated client message.
|
|
82
|
+
},
|
|
83
|
+
dispose: async () => {
|
|
84
|
+
// Release per-session resources.
|
|
85
|
+
},
|
|
86
|
+
}));
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`open(input, context)` may return the session object or a promise for it. Its context extends the [handler context](#handler-context) with:
|
|
90
|
+
|
|
91
|
+
| API | Purpose |
|
|
92
|
+
| --- | --- |
|
|
93
|
+
| `emit(message)` | Validate and send a server message to this session. Calls after closure are ignored. |
|
|
94
|
+
| `fail(cause)` | Fail and close this session. A later call has no effect. |
|
|
95
|
+
|
|
96
|
+
The returned `StreamSession` may contain:
|
|
97
|
+
|
|
98
|
+
| API | Purpose |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| `onMessage(message)` | Receive validated client messages. Calls are processed in order. |
|
|
101
|
+
| `dispose()` | Release session resources. It may be async and is called once when the session closes. |
|
|
102
|
+
|
|
103
|
+
The stream `signal` aborts when its session or the runtime closes. Stop background work in response to it, and put resource cleanup in `dispose`. See [Streams](./300-streams.md) for stream usage from the client.
|
|
104
|
+
|
|
105
|
+
## Handler context
|
|
106
|
+
|
|
107
|
+
Inline resource, operation, and stream handlers infer valid resource IDs and inputs.
|
|
108
|
+
|
|
109
|
+
Resource and stream handlers receive a context with:
|
|
110
|
+
|
|
111
|
+
| API | Purpose |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| `invalidate(resourceId, input?)` | Ask clients to [read a resource again](./100-resources.md#invalidating-resources). Pass the resource's key in `resources`. Omit `input` to invalidate all inputs; otherwise it is validated and only matching inputs are invalidated. Unknown IDs throw. |
|
|
114
|
+
| `signal` | An `AbortSignal` that aborts when the request or session, or the server runtime, closes. |
|
|
115
|
+
| `instance.getInstanceId()` | Get this server instance's ID. |
|
|
116
|
+
| `instance.getDeepLinkPrefix()` | Get this instance's Overmux deep-link prefix. |
|
|
117
|
+
|
|
118
|
+
Operation handlers receive the same context plus `notifications.send(notification)`, which returns `Promise<void>`. See [Notifications](./400-notifications.md).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Client
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Overmux's client is your React application, running in a browser or Overmux Desktop.
|
|
6
|
+
|
|
7
|
+
- [Client API](./100-api.md): Define your application, connect to the server, and register commands.
|
|
8
|
+
- [Theming](./200-theming.md): Customize your application's appearance.
|
|
9
|
+
- [Tech Stack Recommendations](./300-tech-stack-recommendations.md): Choose libraries for your UI.
|