@abyssinia-labs/wake-convex 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +154 -2
- package/dist/client/index.d.ts +153 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +111 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/routes.d.ts +27 -0
- package/dist/client/routes.d.ts.map +1 -0
- package/dist/client/routes.js +104 -0
- package/dist/client/routes.js.map +1 -0
- package/dist/component/_generated/api.d.ts +44 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/dist/component/_generated/api.js +31 -0
- package/dist/component/_generated/api.js.map +1 -0
- package/dist/component/_generated/component.d.ts +150 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +46 -0
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +133 -0
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/dist/component/_generated/server.js +80 -0
- package/dist/component/_generated/server.js.map +1 -0
- package/dist/component/convex.config.d.ts +3 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +3 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/keys.d.ts +10 -0
- package/dist/component/keys.d.ts.map +1 -0
- package/dist/component/keys.js +40 -0
- package/dist/component/keys.js.map +1 -0
- package/dist/component/places.d.ts +50 -0
- package/dist/component/places.d.ts.map +1 -0
- package/dist/component/places.js +151 -0
- package/dist/component/places.js.map +1 -0
- package/dist/component/schema.d.ts +125 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +70 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/component/scopes.d.ts +7 -0
- package/dist/component/scopes.d.ts.map +1 -0
- package/dist/component/scopes.js +27 -0
- package/dist/component/scopes.js.map +1 -0
- package/dist/component/shape.d.ts +17 -0
- package/dist/component/shape.d.ts.map +1 -0
- package/dist/component/shape.js +55 -0
- package/dist/component/shape.js.map +1 -0
- package/dist/component/summonses.d.ts +61 -0
- package/dist/component/summonses.d.ts.map +1 -0
- package/dist/component/summonses.js +170 -0
- package/dist/component/summonses.js.map +1 -0
- package/dist/component/wire.d.ts +38 -0
- package/dist/component/wire.d.ts.map +1 -0
- package/dist/component/wire.js +100 -0
- package/dist/component/wire.js.map +1 -0
- package/package.json +66 -4
- package/src/client/index.ts +177 -0
- package/src/client/routes.ts +138 -0
- package/src/component/_generated/api.ts +60 -0
- package/src/component/_generated/component.ts +197 -0
- package/src/component/_generated/dataModel.ts +60 -0
- package/src/component/_generated/server.ts +169 -0
- package/src/component/convex.config.ts +3 -0
- package/src/component/keys.ts +45 -0
- package/src/component/places.ts +153 -0
- package/src/component/schema.ts +80 -0
- package/src/component/scopes.ts +26 -0
- package/src/component/shape.ts +57 -0
- package/src/component/summonses.ts +179 -0
- package/src/component/wire.ts +106 -0
- package/src/test.ts +21 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abyssinia Labs LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,155 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @abyssinia-labs/wake-convex
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The server half of [wake/v1](../../docs/spec/wake-v1.md) as a Convex
|
|
4
|
+
component. Add it to a Convex app and your users' coding agents (Claude
|
|
5
|
+
Code, Codex, Cursor) start on their own machines when someone assigns them
|
|
6
|
+
work or mentions them, through [Wake](../../README.md).
|
|
7
|
+
|
|
8
|
+
You decide **what** summons an agent (an assignment, a mention) and write
|
|
9
|
+
the prompt. The component keeps everything wake/v1 requires: pair codes and
|
|
10
|
+
place keys (stored as hashes), places, the summons's life (asking, open,
|
|
11
|
+
claimed, done, failed, dropped), the race to claim, the thirty-minute
|
|
12
|
+
unclaimed notice, and the three functions and two routes Wake calls.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bun add @abyssinia-labs/wake-convex
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`convex/convex.config.ts`:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { defineApp } from "convex/server";
|
|
24
|
+
import wake from "@abyssinia-labs/wake-convex/convex.config";
|
|
25
|
+
|
|
26
|
+
const app = defineApp();
|
|
27
|
+
app.use(wake);
|
|
28
|
+
export default app;
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Serve wake/v1
|
|
32
|
+
|
|
33
|
+
**The three functions** must be called `wake:pending`, `wake:claim` and
|
|
34
|
+
`wake:finish`, so they go in `convex/wake.ts`:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { exposeApi } from "@abyssinia-labs/wake-convex";
|
|
38
|
+
import { components } from "./_generated/api";
|
|
39
|
+
|
|
40
|
+
export const { pending, claim, finish } = exposeApi(components.wake, { app: "acme" });
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
They take no auth: the place key is the credential, and the component
|
|
44
|
+
checks it. `app` is your app's name in wake/v1: lower case, digits and
|
|
45
|
+
dashes. Wake uses it as a folder name and as the name of your MCP server in
|
|
46
|
+
the agent.
|
|
47
|
+
|
|
48
|
+
**The two routes**, in `convex/http.ts`:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { httpRouter } from "convex/server";
|
|
52
|
+
import { registerRoutes } from "@abyssinia-labs/wake-convex";
|
|
53
|
+
import { components } from "./_generated/api";
|
|
54
|
+
|
|
55
|
+
const http = httpRouter();
|
|
56
|
+
registerRoutes(http, components.wake, { app: "acme", keyPrefix: "acme_" });
|
|
57
|
+
export default http;
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Discovery.** Serve `https://<your domain>/.well-known/wake` with
|
|
61
|
+
`discovery({ convexUrl, httpBase })`, from wherever your domain is served.
|
|
62
|
+
If your domain is your Convex site, pass `discovery` to `registerRoutes`
|
|
63
|
+
and it is served for you.
|
|
64
|
+
|
|
65
|
+
## Wire it to your app
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { pairCommand, Wake } from "@abyssinia-labs/wake-convex";
|
|
69
|
+
import { components } from "./_generated/api";
|
|
70
|
+
|
|
71
|
+
const wake = new Wake(components.wake, { app: "acme" });
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Pairing.** Give your agents a tool (MCP or API) that answers "pair Wake".
|
|
75
|
+
Make the code in an action or HTTP action (it needs real randomness):
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
const { code } = await wake.issueCode(ctx, {
|
|
79
|
+
agent: agent._id, // the calling agent
|
|
80
|
+
agentName: agent.name,
|
|
81
|
+
owner: agent.ownerId, // its person
|
|
82
|
+
tool: "claude", // the program that asked, when you can tell
|
|
83
|
+
});
|
|
84
|
+
return { command: pairCommand("acme.dev", code) };
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Summoning.** When a person assigns a ticket to an agent or mentions it:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
await wake.summon(ctx, {
|
|
91
|
+
agent: agent._id,
|
|
92
|
+
owner: agent.ownerId,
|
|
93
|
+
askedBy: userId, // a person; an agent's own write never summons
|
|
94
|
+
subject: ticket._id, // one live summons per agent per subject
|
|
95
|
+
kind: "assigned",
|
|
96
|
+
target: { kind: "ticket", ref: "ACME-70", url: "https://acme.dev/t/ACME-70" },
|
|
97
|
+
repo: "acme/widgets", // optional, with branch: Wake makes a worktree there
|
|
98
|
+
branch: "acme-70-fix-the-thing",
|
|
99
|
+
prompt, // which tools to call and how to finish; never the text that asked
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
When the owner asks, it opens at once. When someone else asks, it waits for
|
|
104
|
+
the owner's `wake.answer(ctx, { id, yes })`. A summons Wake would refuse (a
|
|
105
|
+
control character in `ref`, a non-https `url`, a branch like `../main`) is
|
|
106
|
+
refused here with `INVALID_SUMMONS`. Never name a repository's default
|
|
107
|
+
branch: Wake refuses to run on it.
|
|
108
|
+
|
|
109
|
+
**The rest:**
|
|
110
|
+
|
|
111
|
+
| Call | When |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| `wake.settle(ctx, { subject, agent?, kind?, state, reason })` | It stopped being wanted: unassigned, closed, answered from a live session. |
|
|
114
|
+
| `wake.forSubject(ctx, subject)` | Show each agent's latest summons where it was made: state, reason, `unclaimed` after thirty minutes. |
|
|
115
|
+
| `wake.places(ctx, agent)` / `wake.forgetPlace(ctx, { agent, place })` | An agent's machines, on your agents screen. |
|
|
116
|
+
| `wake.revokeAgent(ctx, agent)` | In the same mutation that revokes an agent: its keys, codes and waiting summonses end. |
|
|
117
|
+
| `wake.deleteScope(ctx, scope)` | A tenant (workspace) is deleted: one batch of its rows; call again until `done`. Pass `scope` when you pair and summon. |
|
|
118
|
+
|
|
119
|
+
The component has no `ctx.auth`: check who may do what in your functions
|
|
120
|
+
before you call it.
|
|
121
|
+
|
|
122
|
+
## Check it
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
wakectl check acme.dev
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
It probes your app the way Wake will, with inputs your app must refuse, so
|
|
129
|
+
it pairs nothing and changes nothing.
|
|
130
|
+
|
|
131
|
+
In your own tests, register the component with convex-test:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import wake from "@abyssinia-labs/wake-convex/test";
|
|
135
|
+
|
|
136
|
+
const t = convexTest(schema, modules);
|
|
137
|
+
wake.register(t);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Develop
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
bun install
|
|
144
|
+
bun run --cwd packages/convex test
|
|
145
|
+
bun run --cwd packages/convex typecheck
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Code generation needs a Convex deployment. A local, anonymous one is
|
|
149
|
+
enough: `CONVEX_AGENT_MODE=anonymous bunx convex init`, then
|
|
150
|
+
`bunx convex codegen --component-dir ./src/component`. Commit what it
|
|
151
|
+
writes under `_generated`.
|
|
152
|
+
|
|
153
|
+
## License
|
|
154
|
+
|
|
155
|
+
[MIT](LICENSE) © Abyssinia Labs LLC
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { type GenericActionCtx, type GenericDataModel, type GenericMutationCtx, type GenericQueryCtx } from "convex/server";
|
|
2
|
+
import type { ComponentApi } from "../component/_generated/component.js";
|
|
3
|
+
export { registerRoutes } from "./routes.js";
|
|
4
|
+
export type { ComponentApi };
|
|
5
|
+
type RunQuery = Pick<GenericQueryCtx<GenericDataModel>, "runQuery">;
|
|
6
|
+
type RunMutation = Pick<GenericMutationCtx<GenericDataModel>, "runQuery" | "runMutation">;
|
|
7
|
+
type RunAction = Pick<GenericActionCtx<GenericDataModel>, "runQuery" | "runMutation">;
|
|
8
|
+
export type Tool = "claude" | "codex" | "cursor";
|
|
9
|
+
export type Target = {
|
|
10
|
+
kind: string;
|
|
11
|
+
ref: string;
|
|
12
|
+
url: string;
|
|
13
|
+
};
|
|
14
|
+
export type WakeOptions = {
|
|
15
|
+
/** The app's name in wake/v1: lower case, digits and dashes (`gatherd`). */
|
|
16
|
+
app: string;
|
|
17
|
+
};
|
|
18
|
+
/** The discovery document, for `GET https://<domain>/.well-known/wake`. */
|
|
19
|
+
export declare function discovery(o: {
|
|
20
|
+
convexUrl: string;
|
|
21
|
+
httpBase: string;
|
|
22
|
+
}): {
|
|
23
|
+
version: "wake/v1";
|
|
24
|
+
convexUrl: string;
|
|
25
|
+
httpBase: string;
|
|
26
|
+
};
|
|
27
|
+
/** What the agent tells its person to run. */
|
|
28
|
+
export declare function pairCommand(domain: string, code: string): string;
|
|
29
|
+
export declare class Wake {
|
|
30
|
+
readonly component: ComponentApi;
|
|
31
|
+
readonly app: string;
|
|
32
|
+
constructor(component: ComponentApi, options: WakeOptions);
|
|
33
|
+
/**
|
|
34
|
+
* A pair code for the calling agent, to answer its "pair Wake" request.
|
|
35
|
+
* Needs real randomness, so call it from an action or an HTTP action.
|
|
36
|
+
*/
|
|
37
|
+
issueCode(ctx: RunAction, o: {
|
|
38
|
+
agent: string;
|
|
39
|
+
agentName: string;
|
|
40
|
+
owner: string;
|
|
41
|
+
scope?: string;
|
|
42
|
+
tool?: Tool;
|
|
43
|
+
}): Promise<{
|
|
44
|
+
code: string;
|
|
45
|
+
expiresAt: number;
|
|
46
|
+
}>;
|
|
47
|
+
/** A person asked an agent about a subject: see the component's `summon`. */
|
|
48
|
+
summon(ctx: RunMutation, o: {
|
|
49
|
+
agent: string;
|
|
50
|
+
owner: string;
|
|
51
|
+
askedBy: string;
|
|
52
|
+
scope?: string;
|
|
53
|
+
subject: string;
|
|
54
|
+
kind: string;
|
|
55
|
+
target: Target;
|
|
56
|
+
repo?: string;
|
|
57
|
+
branch?: string;
|
|
58
|
+
prompt: string;
|
|
59
|
+
}): Promise<string>;
|
|
60
|
+
/** The owner's yes or no to a summons someone else made. Check it is the owner first. */
|
|
61
|
+
answer(ctx: RunMutation, o: {
|
|
62
|
+
id: string;
|
|
63
|
+
yes: boolean;
|
|
64
|
+
}): Promise<void>;
|
|
65
|
+
/** End what still waits about a subject: unassigned, closed, answered. */
|
|
66
|
+
settle(ctx: RunMutation, o: {
|
|
67
|
+
subject: string;
|
|
68
|
+
agent?: string;
|
|
69
|
+
kind?: string;
|
|
70
|
+
state: "done" | "dropped";
|
|
71
|
+
reason: string;
|
|
72
|
+
}): Promise<number>;
|
|
73
|
+
/** Each agent's latest summons about a subject, to show where it was made. */
|
|
74
|
+
forSubject(ctx: RunQuery, subject: string): Promise<{
|
|
75
|
+
agent: string;
|
|
76
|
+
askedBy: string;
|
|
77
|
+
at: number;
|
|
78
|
+
id: string;
|
|
79
|
+
kind: string;
|
|
80
|
+
owner: string;
|
|
81
|
+
reason?: string;
|
|
82
|
+
state: "asking" | "open" | "claimed" | "done" | "failed" | "dropped";
|
|
83
|
+
unclaimed: boolean;
|
|
84
|
+
updatedAt: number;
|
|
85
|
+
}[]>;
|
|
86
|
+
/** An agent's paired machines. */
|
|
87
|
+
places(ctx: RunQuery, agent: string): Promise<{
|
|
88
|
+
id: string;
|
|
89
|
+
lastSeenAt?: number;
|
|
90
|
+
machine: string;
|
|
91
|
+
pairedAt: number;
|
|
92
|
+
platform: string;
|
|
93
|
+
tool?: "claude" | "codex" | "cursor";
|
|
94
|
+
}[]>;
|
|
95
|
+
/** Forget one of an agent's machines. Check who may first. */
|
|
96
|
+
forgetPlace(ctx: RunMutation, o: {
|
|
97
|
+
agent: string;
|
|
98
|
+
place: string;
|
|
99
|
+
}): Promise<boolean>;
|
|
100
|
+
/**
|
|
101
|
+
* The host deleted a tenant: one batch of what the component keeps for
|
|
102
|
+
* `scope`. Call it again until `done`, from the host's deletion steps.
|
|
103
|
+
*/
|
|
104
|
+
deleteScope(ctx: RunMutation, scope: string): Promise<{
|
|
105
|
+
deleted: number;
|
|
106
|
+
done: boolean;
|
|
107
|
+
}>;
|
|
108
|
+
/** The host revoked an agent: its keys, codes and waiting summonses end with it. */
|
|
109
|
+
revokeAgent(ctx: RunMutation, agent: string): Promise<void>;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* wake:pending, wake:claim and wake:finish, for the host's `convex/wake.ts`:
|
|
113
|
+
*
|
|
114
|
+
* export const { pending, claim, finish } = exposeApi(components.wake, { app: "acme" });
|
|
115
|
+
*
|
|
116
|
+
* They take no auth: the place key is the credential (the spec), and the
|
|
117
|
+
* component checks it.
|
|
118
|
+
*/
|
|
119
|
+
export declare function exposeApi(component: ComponentApi, options: WakeOptions): {
|
|
120
|
+
pending: import("convex/server").RegisteredQuery<"public", {
|
|
121
|
+
key: string;
|
|
122
|
+
}, Promise<{
|
|
123
|
+
app: string;
|
|
124
|
+
at: number;
|
|
125
|
+
branch?: string;
|
|
126
|
+
id: string;
|
|
127
|
+
kind: string;
|
|
128
|
+
repo?: string;
|
|
129
|
+
target: {
|
|
130
|
+
kind: string;
|
|
131
|
+
ref: string;
|
|
132
|
+
url: string;
|
|
133
|
+
};
|
|
134
|
+
}[]>>;
|
|
135
|
+
claim: import("convex/server").RegisteredMutation<"public", {
|
|
136
|
+
id: string;
|
|
137
|
+
key: string;
|
|
138
|
+
}, Promise<{
|
|
139
|
+
claimed: true;
|
|
140
|
+
run: {
|
|
141
|
+
prompt: string;
|
|
142
|
+
};
|
|
143
|
+
} | {
|
|
144
|
+
claimed: false;
|
|
145
|
+
}>>;
|
|
146
|
+
finish: import("convex/server").RegisteredMutation<"public", {
|
|
147
|
+
id: string;
|
|
148
|
+
key: string;
|
|
149
|
+
outcome: "done" | "failed";
|
|
150
|
+
reason?: string | undefined;
|
|
151
|
+
}, Promise<null>>;
|
|
152
|
+
};
|
|
153
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EAGrB,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAIzE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B,KAAK,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,EAAE,UAAU,CAAC,CAAC;AACpE,KAAK,WAAW,GAAG,IAAI,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,EAAE,UAAU,GAAG,aAAa,CAAC,CAAC;AAC1F,KAAK,SAAS,GAAG,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,EAAE,UAAU,GAAG,aAAa,CAAC,CAAC;AAEtF,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AACjD,MAAM,MAAM,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,MAAM,MAAM,WAAW,GAAG;IACxB,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAOF,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,CAAC,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IACrE,OAAO,EAAE,SAAS,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAEA;AAED,8CAA8C;AAC9C,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,qBAAa,IAAI;IAGb,QAAQ,CAAC,SAAS,EAAE,YAAY;IAFlC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,YACW,SAAS,EAAE,YAAY,EAChC,OAAO,EAAE,WAAW,EAGrB;IAED;;;OAGG;IACG,SAAS,CACb,GAAG,EAAE,SAAS,EACd,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,IAAI,CAAA;KAAE,GAClF,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAS9C;IAED,6EAA6E;IACvE,MAAM,CACV,GAAG,EAAE,WAAW,EAChB,CAAC,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,GACA,OAAO,CAAC,MAAM,CAAC,CAEjB;IAED,yFAAyF;IACnF,MAAM,CAAC,GAAG,EAAE,WAAW,EAAE,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7E;IAED,0EAA0E;IACpE,MAAM,CACV,GAAG,EAAE,WAAW,EAChB,CAAC,EAAE;QACD,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,MAAM,EAAE,MAAM,CAAC;KAChB,GACA,OAAO,CAAC,MAAM,CAAC,CAEjB;IAED,8EAA8E;IACxE,UAAU,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;SAE9C;IAED,kCAAkC;IAC5B,MAAM,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM;;;;;;;SAExC;IAED,8DAA8D;IACxD,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAEzF;IAED;;;OAGG;IACG,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAE9F;IAED,oFAAoF;IAC9E,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhE;CACF;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW;IAGnE,OAAO;;;;;;;;;;;;;;;IAKP,KAAK;;;;;;;;;;;IAIL,MAAM;;;;;;EAUT"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// What a host app imports: `Wake`, to make and settle summonses and manage
|
|
2
|
+
// places from its own functions; `exposeApi`, which gives wake:pending,
|
|
3
|
+
// wake:claim and wake:finish their public names; and `registerRoutes`,
|
|
4
|
+
// which mounts the pair and forget routes. docs/spec/wake-v1.md is the
|
|
5
|
+
// protocol these serve.
|
|
6
|
+
import { mutationGeneric, queryGeneric, } from "convex/server";
|
|
7
|
+
import { v } from "convex/values";
|
|
8
|
+
import { CODE_TTL_MS, hashSecret, newPairCode, normalizeCode } from "../component/keys.js";
|
|
9
|
+
import { APP } from "../component/shape.js";
|
|
10
|
+
export { registerRoutes } from "./routes.js";
|
|
11
|
+
function checkApp(app) {
|
|
12
|
+
if (!APP.test(app))
|
|
13
|
+
throw new Error(`wake/v1 app names are ${APP}; "${app}" is not one.`);
|
|
14
|
+
return app;
|
|
15
|
+
}
|
|
16
|
+
/** The discovery document, for `GET https://<domain>/.well-known/wake`. */
|
|
17
|
+
export function discovery(o) {
|
|
18
|
+
return { version: "wake/v1", convexUrl: o.convexUrl, httpBase: o.httpBase };
|
|
19
|
+
}
|
|
20
|
+
/** What the agent tells its person to run. */
|
|
21
|
+
export function pairCommand(domain, code) {
|
|
22
|
+
return `wakectl pair ${domain} ${code}`;
|
|
23
|
+
}
|
|
24
|
+
export class Wake {
|
|
25
|
+
component;
|
|
26
|
+
app;
|
|
27
|
+
constructor(component, options) {
|
|
28
|
+
this.component = component;
|
|
29
|
+
this.app = checkApp(options.app);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A pair code for the calling agent, to answer its "pair Wake" request.
|
|
33
|
+
* Needs real randomness, so call it from an action or an HTTP action.
|
|
34
|
+
*/
|
|
35
|
+
async issueCode(ctx, o) {
|
|
36
|
+
const code = newPairCode();
|
|
37
|
+
const expiresAt = Date.now() + CODE_TTL_MS;
|
|
38
|
+
await ctx.runMutation(this.component.places.storeCode, {
|
|
39
|
+
...o,
|
|
40
|
+
codeHash: await hashSecret(normalizeCode(code)),
|
|
41
|
+
expiresAt,
|
|
42
|
+
});
|
|
43
|
+
return { code, expiresAt };
|
|
44
|
+
}
|
|
45
|
+
/** A person asked an agent about a subject: see the component's `summon`. */
|
|
46
|
+
async summon(ctx, o) {
|
|
47
|
+
return await ctx.runMutation(this.component.summonses.summon, o);
|
|
48
|
+
}
|
|
49
|
+
/** The owner's yes or no to a summons someone else made. Check it is the owner first. */
|
|
50
|
+
async answer(ctx, o) {
|
|
51
|
+
await ctx.runMutation(this.component.summonses.answer, o);
|
|
52
|
+
}
|
|
53
|
+
/** End what still waits about a subject: unassigned, closed, answered. */
|
|
54
|
+
async settle(ctx, o) {
|
|
55
|
+
return await ctx.runMutation(this.component.summonses.settle, o);
|
|
56
|
+
}
|
|
57
|
+
/** Each agent's latest summons about a subject, to show where it was made. */
|
|
58
|
+
async forSubject(ctx, subject) {
|
|
59
|
+
return await ctx.runQuery(this.component.summonses.forSubject, { subject });
|
|
60
|
+
}
|
|
61
|
+
/** An agent's paired machines. */
|
|
62
|
+
async places(ctx, agent) {
|
|
63
|
+
return await ctx.runQuery(this.component.places.list, { agent });
|
|
64
|
+
}
|
|
65
|
+
/** Forget one of an agent's machines. Check who may first. */
|
|
66
|
+
async forgetPlace(ctx, o) {
|
|
67
|
+
return await ctx.runMutation(this.component.places.remove, o);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The host deleted a tenant: one batch of what the component keeps for
|
|
71
|
+
* `scope`. Call it again until `done`, from the host's deletion steps.
|
|
72
|
+
*/
|
|
73
|
+
async deleteScope(ctx, scope) {
|
|
74
|
+
return await ctx.runMutation(this.component.scopes.deleteScope, { scope });
|
|
75
|
+
}
|
|
76
|
+
/** The host revoked an agent: its keys, codes and waiting summonses end with it. */
|
|
77
|
+
async revokeAgent(ctx, agent) {
|
|
78
|
+
await ctx.runMutation(this.component.places.revokeAgent, { agent });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* wake:pending, wake:claim and wake:finish, for the host's `convex/wake.ts`:
|
|
83
|
+
*
|
|
84
|
+
* export const { pending, claim, finish } = exposeApi(components.wake, { app: "acme" });
|
|
85
|
+
*
|
|
86
|
+
* They take no auth: the place key is the credential (the spec), and the
|
|
87
|
+
* component checks it.
|
|
88
|
+
*/
|
|
89
|
+
export function exposeApi(component, options) {
|
|
90
|
+
const app = checkApp(options.app);
|
|
91
|
+
return {
|
|
92
|
+
pending: queryGeneric({
|
|
93
|
+
args: { key: v.string() },
|
|
94
|
+
handler: async (ctx, args) => await ctx.runQuery(component.wire.pending, { key: args.key, app }),
|
|
95
|
+
}),
|
|
96
|
+
claim: mutationGeneric({
|
|
97
|
+
args: { key: v.string(), id: v.string() },
|
|
98
|
+
handler: async (ctx, args) => await ctx.runMutation(component.wire.claim, args),
|
|
99
|
+
}),
|
|
100
|
+
finish: mutationGeneric({
|
|
101
|
+
args: {
|
|
102
|
+
key: v.string(),
|
|
103
|
+
id: v.string(),
|
|
104
|
+
outcome: v.union(v.literal("done"), v.literal("failed")),
|
|
105
|
+
reason: v.optional(v.string()),
|
|
106
|
+
},
|
|
107
|
+
handler: async (ctx, args) => await ctx.runMutation(component.wire.finish, args),
|
|
108
|
+
}),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type GenericActionCtx, type GenericDataModel, type HttpRouter } from "convex/server";
|
|
2
|
+
import type { ComponentApi } from "../component/_generated/component.js";
|
|
3
|
+
type Ctx = Pick<GenericActionCtx<GenericDataModel>, "runMutation">;
|
|
4
|
+
export declare function handlePair(ctx: Ctx, req: Request, component: ComponentApi, o: {
|
|
5
|
+
app: string;
|
|
6
|
+
keyPrefix?: string;
|
|
7
|
+
}): Promise<Response>;
|
|
8
|
+
export declare function handleForget(ctx: Ctx, req: Request, component: ComponentApi): Promise<Response>;
|
|
9
|
+
/**
|
|
10
|
+
* Mounts the two routes in the host's router:
|
|
11
|
+
*
|
|
12
|
+
* registerRoutes(http, components.wake, { app: "acme" });
|
|
13
|
+
*
|
|
14
|
+
* With `discovery`, it also serves `/.well-known/wake`, for an app whose
|
|
15
|
+
* domain is its Convex site; any other app serves `discovery()` itself.
|
|
16
|
+
*/
|
|
17
|
+
export declare function registerRoutes(http: HttpRouter, component: ComponentApi, o: {
|
|
18
|
+
app: string;
|
|
19
|
+
keyPrefix?: string;
|
|
20
|
+
pathPrefix?: string;
|
|
21
|
+
discovery?: {
|
|
22
|
+
convexUrl: string;
|
|
23
|
+
httpBase: string;
|
|
24
|
+
};
|
|
25
|
+
}): void;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/client/routes.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAEhB,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAIzE,KAAK,GAAG,GAAG,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,EAAE,aAAa,CAAC,CAAC;AAmBnE,wBAAsB,UAAU,CAC9B,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,YAAY,EACvB,CAAC,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACrC,OAAO,CAAC,QAAQ,CAAC,CA6CnB;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,YAAY,GACtB,OAAO,CAAC,QAAQ,CAAC,CAKnB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,YAAY,EACvB,CAAC,EAAE;IACD,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CACrD,GACA,IAAI,CAuBN"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// `POST /wake/v1/pair` and `POST /wake/v1/forget` (the spec's "Pairing"),
|
|
2
|
+
// mounted in the host's `convex/http.ts`. They run in the host, as HTTP
|
|
3
|
+
// actions must, so the place key is made with real randomness here and
|
|
4
|
+
// only its hash reaches the component.
|
|
5
|
+
import { httpActionGeneric, } from "convex/server";
|
|
6
|
+
import { ConvexError } from "convex/values";
|
|
7
|
+
import { hashSecret, newPlaceKey, normalizeCode } from "../component/keys.js";
|
|
8
|
+
import { APP } from "../component/shape.js";
|
|
9
|
+
function json(status, body) {
|
|
10
|
+
return new Response(JSON.stringify(body), {
|
|
11
|
+
status,
|
|
12
|
+
headers: { "content-type": "application/json", "cache-control": "no-store" },
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function refuse(status, error, message) {
|
|
16
|
+
return json(status, { error, message });
|
|
17
|
+
}
|
|
18
|
+
function text(value, max) {
|
|
19
|
+
return typeof value === "string" && value.trim() !== "" && value.length <= max
|
|
20
|
+
? value.trim()
|
|
21
|
+
: null;
|
|
22
|
+
}
|
|
23
|
+
export async function handlePair(ctx, req, component, o) {
|
|
24
|
+
let body = null;
|
|
25
|
+
try {
|
|
26
|
+
body = await req.json();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Not JSON: refused below like any other wrong body.
|
|
30
|
+
}
|
|
31
|
+
const record = typeof body === "object" && body !== null ? body : {};
|
|
32
|
+
const machine = typeof record.machine === "object" && record.machine !== null
|
|
33
|
+
? record.machine
|
|
34
|
+
: {};
|
|
35
|
+
const code = text(record.code, 20);
|
|
36
|
+
const name = text(machine.name, 100);
|
|
37
|
+
const platform = text(machine.platform, 40);
|
|
38
|
+
if (!code || !name || !platform) {
|
|
39
|
+
return refuse(400, "invalid_body", "Send { code, machine: { name, platform } }.");
|
|
40
|
+
}
|
|
41
|
+
const placeKey = newPlaceKey(o.keyPrefix);
|
|
42
|
+
try {
|
|
43
|
+
const paired = await ctx.runMutation(component.places.pair, {
|
|
44
|
+
codeHash: await hashSecret(normalizeCode(code)),
|
|
45
|
+
keyHash: await hashSecret(placeKey),
|
|
46
|
+
machine: name,
|
|
47
|
+
platform,
|
|
48
|
+
});
|
|
49
|
+
return json(200, {
|
|
50
|
+
placeKey,
|
|
51
|
+
app: o.app,
|
|
52
|
+
agent: {
|
|
53
|
+
id: paired.agent,
|
|
54
|
+
name: paired.agentName,
|
|
55
|
+
...(paired.tool === undefined ? {} : { tool: paired.tool }),
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (error instanceof ConvexError && error.data === "CODE_INVALID") {
|
|
61
|
+
return refuse(400, "code_invalid", "That code is wrong, used or older than ten minutes. Ask your agent for a new one.");
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export async function handleForget(ctx, req, component) {
|
|
67
|
+
const key = req.headers.get("authorization")?.match(/^Bearer\s+(\S+)$/i)?.[1];
|
|
68
|
+
if (!key)
|
|
69
|
+
return refuse(401, "unpaired", "Send the place key as a bearer token.");
|
|
70
|
+
const found = await ctx.runMutation(component.places.forget, { keyHash: await hashSecret(key) });
|
|
71
|
+
return found ? new Response(null, { status: 204 }) : refuse(401, "unpaired", "No such place.");
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Mounts the two routes in the host's router:
|
|
75
|
+
*
|
|
76
|
+
* registerRoutes(http, components.wake, { app: "acme" });
|
|
77
|
+
*
|
|
78
|
+
* With `discovery`, it also serves `/.well-known/wake`, for an app whose
|
|
79
|
+
* domain is its Convex site; any other app serves `discovery()` itself.
|
|
80
|
+
*/
|
|
81
|
+
export function registerRoutes(http, component, o) {
|
|
82
|
+
if (!APP.test(o.app))
|
|
83
|
+
throw new Error(`wake/v1 app names are ${APP}; "${o.app}" is not one.`);
|
|
84
|
+
const prefix = (o.pathPrefix ?? "/wake/v1").replace(/\/+$/, "");
|
|
85
|
+
http.route({
|
|
86
|
+
path: `${prefix}/pair`,
|
|
87
|
+
method: "POST",
|
|
88
|
+
handler: httpActionGeneric((ctx, req) => handlePair(ctx, req, component, o)),
|
|
89
|
+
});
|
|
90
|
+
http.route({
|
|
91
|
+
path: `${prefix}/forget`,
|
|
92
|
+
method: "POST",
|
|
93
|
+
handler: httpActionGeneric((ctx, req) => handleForget(ctx, req, component)),
|
|
94
|
+
});
|
|
95
|
+
const found = o.discovery;
|
|
96
|
+
if (found) {
|
|
97
|
+
http.route({
|
|
98
|
+
path: "/.well-known/wake",
|
|
99
|
+
method: "GET",
|
|
100
|
+
handler: httpActionGeneric(async () => json(200, { version: "wake/v1", convexUrl: found.convexUrl, httpBase: found.httpBase })),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/client/routes.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,wEAAwE;AACxE,uEAAuE;AACvE,uCAAuC;AACvC,OAAO,EAIL,iBAAiB,GAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC9E,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAI5C,SAAS,IAAI,CAAC,MAAc,EAAE,IAAa;IACzC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;QACxC,MAAM;QACN,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,eAAe,EAAE,UAAU,EAAE;KAC7E,CAAC,CAAC;AACL,CAAC;AAED,SAAS,MAAM,CAAC,MAAc,EAAE,KAAa,EAAE,OAAe;IAC5D,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,IAAI,CAAC,KAAc,EAAE,GAAW;IACvC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG;QAC5E,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE;QACd,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAQ,EACR,GAAY,EACZ,SAAuB,EACvB,CAAsC;IAEtC,IAAI,IAAI,GAAY,IAAI,CAAC;IACzB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,qDAAqD;IACvD,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAE,IAAgC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClG,MAAM,OAAO,GACX,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI;QAC3D,CAAC,CAAE,MAAM,CAAC,OAAmC;QAC7C,CAAC,CAAC,EAAE,CAAC;IACT,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC5C,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChC,OAAO,MAAM,CAAC,GAAG,EAAE,cAAc,EAAE,6CAA6C,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE;YAC1D,QAAQ,EAAE,MAAM,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YAC/C,OAAO,EAAE,MAAM,UAAU,CAAC,QAAQ,CAAC;YACnC,OAAO,EAAE,IAAI;YACb,QAAQ;SACT,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,GAAG,EAAE;YACf,QAAQ;YACR,GAAG,EAAE,CAAC,CAAC,GAAG;YACV,KAAK,EAAE;gBACL,EAAE,EAAE,MAAM,CAAC,KAAK;gBAChB,IAAI,EAAE,MAAM,CAAC,SAAS;gBACtB,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;aAC5D;SACF,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,WAAW,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAClE,OAAO,MAAM,CACX,GAAG,EACH,cAAc,EACd,mFAAmF,CACpF,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAQ,EACR,GAAY,EACZ,SAAuB;IAEvB,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9E,IAAI,CAAC,GAAG;QAAE,OAAO,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,uCAAuC,CAAC,CAAC;IAClF,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACjG,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC;AACjG,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAgB,EAChB,SAAuB,EACvB,CAKC;IAED,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,MAAM,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC;IAC9F,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,UAAU,IAAI,UAAU,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAChE,IAAI,CAAC,KAAK,CAAC;QACT,IAAI,EAAE,GAAG,MAAM,OAAO;QACtB,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,iBAAiB,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;KAC7E,CAAC,CAAC;IACH,IAAI,CAAC,KAAK,CAAC;QACT,IAAI,EAAE,GAAG,MAAM,SAAS;QACxB,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,iBAAiB,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;KAC5E,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,CAAC,CAAC,SAAS,CAAC;IAC1B,IAAI,KAAK,EAAE,CAAC;QACV,IAAI,CAAC,KAAK,CAAC;YACT,IAAI,EAAE,mBAAmB;YACzB,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,iBAAiB,CAAC,KAAK,IAAI,EAAE,CACpC,IAAI,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CACxF;SACF,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated `api` utility.
|
|
3
|
+
*
|
|
4
|
+
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
5
|
+
*
|
|
6
|
+
* To regenerate, run `npx convex dev`.
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type * as keys from "../keys.js";
|
|
10
|
+
import type * as places from "../places.js";
|
|
11
|
+
import type * as scopes from "../scopes.js";
|
|
12
|
+
import type * as shape from "../shape.js";
|
|
13
|
+
import type * as summonses from "../summonses.js";
|
|
14
|
+
import type * as wire from "../wire.js";
|
|
15
|
+
import type { ApiFromModules, FilterApi, FunctionReference } from "convex/server";
|
|
16
|
+
declare const fullApi: ApiFromModules<{
|
|
17
|
+
keys: typeof keys;
|
|
18
|
+
places: typeof places;
|
|
19
|
+
scopes: typeof scopes;
|
|
20
|
+
shape: typeof shape;
|
|
21
|
+
summonses: typeof summonses;
|
|
22
|
+
wire: typeof wire;
|
|
23
|
+
}>;
|
|
24
|
+
/**
|
|
25
|
+
* A utility for referencing Convex functions in your app's public API.
|
|
26
|
+
*
|
|
27
|
+
* Usage:
|
|
28
|
+
* ```js
|
|
29
|
+
* const myFunctionReference = api.myModule.myFunction;
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare const api: FilterApi<typeof fullApi, FunctionReference<any, "public">>;
|
|
33
|
+
/**
|
|
34
|
+
* A utility for referencing Convex functions in your app's internal API.
|
|
35
|
+
*
|
|
36
|
+
* Usage:
|
|
37
|
+
* ```js
|
|
38
|
+
* const myFunctionReference = internal.myModule.myFunction;
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare const internal: FilterApi<typeof fullApi, FunctionReference<any, "internal">>;
|
|
42
|
+
export declare const components: {};
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/api.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,KAAK,IAAI,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,KAAK,MAAM,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,KAAK,KAAK,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,KAAK,SAAS,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,KAAK,IAAI,MAAM,YAAY,CAAC;AAExC,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EACT,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAGvB,QAAA,MAAM,OAAO,EAAE,cAAc,CAAC;IAC5B,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,IAAI,EAAE,OAAO,IAAI,CAAC;CACnB,CAAiB,CAAC;AAEnB;;;;;;;GAOG;AACH,eAAO,MAAM,GAAG,EAAE,SAAS,CACzB,OAAO,OAAO,EACd,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CACjB,CAAC;AAElB;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,EAAE,SAAS,CAC9B,OAAO,OAAO,EACd,iBAAiB,CAAC,GAAG,EAAE,UAAU,CAAC,CACnB,CAAC;AAElB,eAAO,MAAM,UAAU,EAAqC,EAAE,CAAC"}
|