@muhkoo/connect 0.3.0-alpha.0 → 0.4.0-alpha.1
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/README.md +73 -6
- package/dist/browser/index.js +265 -8
- package/dist/browser/index.js.map +1 -1
- package/dist/connect.d.ts +328 -3
- package/dist/server/index.js +265 -8
- package/dist/server/index.js.map +1 -1
- package/dist/workers/index.js +20 -1
- package/dist/workers/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
# @muhkoo/connect
|
|
2
2
|
|
|
3
3
|
Client SDK for the Muhkoo Accelerator. The headline surface is a single
|
|
4
|
-
**`Client`** that hangs everything off
|
|
5
|
-
`client.auth` / `client.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
**`Client`** that hangs everything off its namespaces —
|
|
5
|
+
`client.auth` / `client.kv` / `client.storage` / `client.message` /
|
|
6
|
+
`client.space` / `client.agents` / `client.functions` — backed by
|
|
7
|
+
end-to-end-encrypted messaging (fan-out group channels with history, plus Double
|
|
8
|
+
Ratchet + ECDH P-384 for direct messages), ZK identity + personal/shared spaces,
|
|
9
|
+
server-side Programmable Agents, developer-authored serverless functions, and a
|
|
10
|
+
Cloudflare-Workers-compatible Groth16 verifier — all bundled from a single
|
|
11
|
+
TypeScript source tree.
|
|
12
|
+
|
|
13
|
+
See [`CHANGELOG.md`](./CHANGELOG.md) for what's new.
|
|
10
14
|
|
|
11
15
|
> Status: alpha. The library is consumed in tree by `../accelerator` and the
|
|
12
16
|
> `../web` SPA.
|
|
@@ -163,6 +167,69 @@ blind (it only ever sees opaque, ECIES-wrapped key blobs). The space handle was
|
|
|
163
167
|
once `Room`; that name is still exported as an alias, and `client.message.room()`
|
|
164
168
|
returns the same handle.
|
|
165
169
|
|
|
170
|
+
**Shareable invite links + member roles.** A private Space (`createChannel(name,
|
|
171
|
+
{ private: true })`) admits only allowlisted members. Instead of inviting
|
|
172
|
+
people one username at a time, mint a **capability link** anyone can redeem —
|
|
173
|
+
the keeper admits them on redemption. Members carry a **role** (`viewer` /
|
|
174
|
+
`editor`) the owner controls; apps use roles to gate delegated management (e.g.
|
|
175
|
+
who can edit an app's config).
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// Owner: mint a shareable invite (optionally time-/use-bounded, with a role).
|
|
179
|
+
const { token } = await client.space.createInviteLink(space.id, { role: "viewer", maxUses: 10 });
|
|
180
|
+
const url = `https://yourapp.com/join?space=${space.id}&token=${token}`;
|
|
181
|
+
await client.space.listInviteLinks(space.id); // owner: active links
|
|
182
|
+
await client.space.revokeInviteLink(space.id, token);
|
|
183
|
+
|
|
184
|
+
// Recipient: redeem the link → admitted by the keeper, group key wrapped for them.
|
|
185
|
+
const joined = await client.space.joinByInvite(space.id, token);
|
|
186
|
+
|
|
187
|
+
// Owner: see members + roles, and promote/demote.
|
|
188
|
+
await client.space.members(space.id); // [{ memberId, role }]
|
|
189
|
+
await client.space.setMemberRole(space.id, "alice", "editor");
|
|
190
|
+
await client.space.roster(space.id); // member identity pubkeys
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Programmable Agents (`client.agents`).** Server-side "virtual users" backed by
|
|
194
|
+
Cloudflare Workers AI: an app editor gives an agent a persona (system prompt), a
|
|
195
|
+
model, and **triggers** (e.g. reply on @-mention), then enables it per-Space;
|
|
196
|
+
members chat with it inside that Space. Management is session-authed (owner or
|
|
197
|
+
Space editor) and **paid-tier-only**. Enabling an agent on a Space makes that
|
|
198
|
+
Space readable by the app's keeper (per-Space opt-in); every other Space stays
|
|
199
|
+
blind.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const { config } = await client.agents.create(appId, {
|
|
203
|
+
handle: "@assistant",
|
|
204
|
+
displayName: "Assistant",
|
|
205
|
+
model: "@cf/meta/llama-3.1-8b-instruct",
|
|
206
|
+
systemPrompt: "You are a concise, friendly teammate in this Space.",
|
|
207
|
+
triggers: [{ type: "mention" }],
|
|
208
|
+
});
|
|
209
|
+
await client.agents.enable(appId, config.agentId, space.id); // per-Space opt-in
|
|
210
|
+
// list / update / delete / disable also available.
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Serverless Functions (`client.functions`).** Deploy your own code that runs on
|
|
214
|
+
the accelerator. A function is an untrusted single-module ES worker
|
|
215
|
+
(`export default { fetch }`) with two triggers: **HTTP** (reachable at its own
|
|
216
|
+
`<name>--<slug>.fns.<zone>` subdomain) and **Space-bound** (invoked on Space
|
|
217
|
+
messages, like an agent). Source is encrypted at rest and uploaded just-in-time
|
|
218
|
+
on invocation. Management is session-authed (owner / Space editor) and
|
|
219
|
+
**paid-tier-only**.
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const { config } = await client.functions.deploy(appId, {
|
|
223
|
+
name: "hello",
|
|
224
|
+
displayName: "Hello",
|
|
225
|
+
code: `export default { fetch: () => new Response("hi from Muhkoo!") }`,
|
|
226
|
+
triggers: { http: { enabled: true } },
|
|
227
|
+
});
|
|
228
|
+
// → reachable at https://hello--<app-slug>.fns.<zone>/
|
|
229
|
+
await client.functions.enable(appId, config.functionId, space.id); // Space-bound
|
|
230
|
+
// list / get / code / update / delete / disable also available.
|
|
231
|
+
```
|
|
232
|
+
|
|
166
233
|
Auth lifecycle:
|
|
167
234
|
|
|
168
235
|
```typescript
|
package/dist/browser/index.js
CHANGED
|
@@ -2407,6 +2407,7 @@ class WSTransport extends EventCore {
|
|
|
2407
2407
|
return `${this.id}:${event}`;
|
|
2408
2408
|
}
|
|
2409
2409
|
url;
|
|
2410
|
+
urlProvider;
|
|
2410
2411
|
socket = null;
|
|
2411
2412
|
_isConnected = false;
|
|
2412
2413
|
_isConnecting = false;
|
|
@@ -2432,6 +2433,7 @@ class WSTransport extends EventCore {
|
|
|
2432
2433
|
throw new Error("WSTransport: `url` is required");
|
|
2433
2434
|
}
|
|
2434
2435
|
this.url = opts.url;
|
|
2436
|
+
this.urlProvider = opts.urlProvider;
|
|
2435
2437
|
this.id = opts.id ?? `ws${++__wsInstanceSeq}`;
|
|
2436
2438
|
this.autoReconnect = opts.autoReconnect ?? true;
|
|
2437
2439
|
this.reconnectDelay = opts.reconnectDelay ?? 3000;
|
|
@@ -2448,6 +2450,18 @@ class WSTransport extends EventCore {
|
|
|
2448
2450
|
throw new Error("WSTransport: already connected or connecting");
|
|
2449
2451
|
}
|
|
2450
2452
|
this._isConnecting = true;
|
|
2453
|
+
// On a RECONNECT, refresh the URL (the baked-in single-use ticket is spent).
|
|
2454
|
+
// The first connect uses `url` as-is. If the provider throws (e.g. can't
|
|
2455
|
+
// mint a ticket), surface it and let scheduleReconnect retry.
|
|
2456
|
+
if (this.urlProvider && this.reconnectAttempts > 0) {
|
|
2457
|
+
try {
|
|
2458
|
+
this.url = await this.urlProvider();
|
|
2459
|
+
}
|
|
2460
|
+
catch (err) {
|
|
2461
|
+
this._isConnecting = false;
|
|
2462
|
+
throw err;
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2451
2465
|
return new Promise((resolve, reject) => {
|
|
2452
2466
|
try {
|
|
2453
2467
|
const socket = new WebSocket(this.url);
|
|
@@ -2620,7 +2634,12 @@ class WSTransport extends EventCore {
|
|
|
2620
2634
|
await this.connect();
|
|
2621
2635
|
}
|
|
2622
2636
|
catch {
|
|
2623
|
-
//
|
|
2637
|
+
// A failed reconnect never opened the socket, so its `onclose` won't
|
|
2638
|
+
// re-schedule (that path is guarded by `wasConnected`). Re-schedule here
|
|
2639
|
+
// so we keep retrying (with a fresh ticket each time) until we succeed
|
|
2640
|
+
// or hit the attempt cap.
|
|
2641
|
+
if (this.autoReconnect)
|
|
2642
|
+
this.scheduleReconnect();
|
|
2624
2643
|
}
|
|
2625
2644
|
}, this.reconnectDelay);
|
|
2626
2645
|
}
|
|
@@ -3936,6 +3955,8 @@ class FileStorage {
|
|
|
3936
3955
|
const fileId = input.fileId ?? generateId();
|
|
3937
3956
|
const createdAt = Date.now();
|
|
3938
3957
|
const chunks = [];
|
|
3958
|
+
const totalChunks = Math.max(1, Math.ceil(data.length / this.chunkSize));
|
|
3959
|
+
input.onProgress?.(0, totalChunks);
|
|
3939
3960
|
// Slicing + encrypting + encoding + uploading is done per-chunk so
|
|
3940
3961
|
// the peak memory footprint stays at ~chunkSize even for huge files.
|
|
3941
3962
|
// Within a chunk, the shard uploads are parallelized up to
|
|
@@ -3945,6 +3966,7 @@ class FileStorage {
|
|
|
3945
3966
|
const end = Math.min(start + this.chunkSize, data.length);
|
|
3946
3967
|
const plaintext = data.subarray(start, end);
|
|
3947
3968
|
chunks.push(await this.writeChunk(plaintext, chunkIndex));
|
|
3969
|
+
input.onProgress?.(chunks.length, totalChunks);
|
|
3948
3970
|
// Stop after one iteration if the file is empty — we still emit a
|
|
3949
3971
|
// single zero-length chunk so the manifest has a stable shape.
|
|
3950
3972
|
if (data.length === 0)
|
|
@@ -4691,6 +4713,9 @@ class HttpClient {
|
|
|
4691
4713
|
post(path, body) {
|
|
4692
4714
|
return this.request("POST", path, body);
|
|
4693
4715
|
}
|
|
4716
|
+
patch(path, body) {
|
|
4717
|
+
return this.request("PATCH", path, body);
|
|
4718
|
+
}
|
|
4694
4719
|
del(path, body) {
|
|
4695
4720
|
return this.request("DELETE", path, body);
|
|
4696
4721
|
}
|
|
@@ -5659,12 +5684,27 @@ class Space {
|
|
|
5659
5684
|
/** Open the space websocket (resolves the ticket + identity lazily). */
|
|
5660
5685
|
async connect() {
|
|
5661
5686
|
if (!this.channel) {
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5687
|
+
// The WS upgrade ticket is single-use + short-TTL, so a reconnect
|
|
5688
|
+
// must mint a FRESH one — otherwise the socket drops (idle timeout,
|
|
5689
|
+
// network blip) and never comes back. `buildUrl` is handed to the
|
|
5690
|
+
// transport as a `urlProvider` it calls before each reconnect.
|
|
5691
|
+
const buildUrl = async () => {
|
|
5692
|
+
const ticket = await this.deps.fetchTicket();
|
|
5693
|
+
return (`${this.deps.wsBaseUrl}/api/spaces/${encodeURIComponent(this.name)}/websocket` +
|
|
5694
|
+
(ticket ? `?ticket=${encodeURIComponent(ticket)}` : ""));
|
|
5695
|
+
};
|
|
5696
|
+
const url = await buildUrl();
|
|
5665
5697
|
this.channel = this.deps.createChannel
|
|
5666
5698
|
? this.deps.createChannel(url, this.deps.myId())
|
|
5667
|
-
: new BroadcastChannel({
|
|
5699
|
+
: new BroadcastChannel({
|
|
5700
|
+
url,
|
|
5701
|
+
myId: this.deps.myId(),
|
|
5702
|
+
autoAnnounce: false,
|
|
5703
|
+
urlProvider: buildUrl,
|
|
5704
|
+
// Keep recovering across network flaps (each attempt mints a
|
|
5705
|
+
// fresh ticket); teardown calls disconnect() which stops it.
|
|
5706
|
+
maxReconnectAttempts: 0,
|
|
5707
|
+
});
|
|
5668
5708
|
for (const [event, handler] of this.pending)
|
|
5669
5709
|
this.channel.on(event, handler);
|
|
5670
5710
|
this.pending.length = 0;
|
|
@@ -5868,8 +5908,8 @@ class Space {
|
|
|
5868
5908
|
return this.requireKeyring().rotate(roster);
|
|
5869
5909
|
}
|
|
5870
5910
|
// -- room-scoped files (no websocket needed) -------------------------------
|
|
5871
|
-
async putFile(file, metadata) {
|
|
5872
|
-
return this.fileStorage().writeFileToShards({ data: file, metadata });
|
|
5911
|
+
async putFile(file, metadata, opts = {}) {
|
|
5912
|
+
return this.fileStorage().writeFileToShards({ data: file, metadata, onProgress: opts.onProgress });
|
|
5873
5913
|
}
|
|
5874
5914
|
async getFile(manifest) {
|
|
5875
5915
|
return this.fileStorage().readFileFromShards(manifest);
|
|
@@ -6026,6 +6066,12 @@ class Space {
|
|
|
6026
6066
|
* nothing to verify against → reject.
|
|
6027
6067
|
*/
|
|
6028
6068
|
async verifySender(packet) {
|
|
6069
|
+
// Trust our OWN messages — we sent them, so there's nothing to prove
|
|
6070
|
+
// against the roster. This also keeps a returning member (who loaded a
|
|
6071
|
+
// cached key and never re-published a join-request) from dropping the
|
|
6072
|
+
// echo of their own messages when their `ecdsaPub` isn't in the roster.
|
|
6073
|
+
if (packet.source === this.deps.myId())
|
|
6074
|
+
return true;
|
|
6029
6075
|
const keyring = this.deps.keyring;
|
|
6030
6076
|
const sig = packet.headers?.sig;
|
|
6031
6077
|
if (!keyring || typeof sig !== "string")
|
|
@@ -6522,6 +6568,34 @@ class KeyringClient {
|
|
|
6522
6568
|
async invite(username) {
|
|
6523
6569
|
await this.post("/keyring/invite", { username });
|
|
6524
6570
|
}
|
|
6571
|
+
// -- Shareable invite links ----------------------------------------------
|
|
6572
|
+
// A capability token tied to this space. Redeeming it allowlists the holder
|
|
6573
|
+
// so the keeper admits them — the link IS the invitation. Mint/list/revoke
|
|
6574
|
+
// require the caller to be the creator or an existing member.
|
|
6575
|
+
async createInviteLink(opts = {}) {
|
|
6576
|
+
return this.post("/keyring/link", opts);
|
|
6577
|
+
}
|
|
6578
|
+
async listInviteLinks() {
|
|
6579
|
+
const body = await this.get("/keyring/links");
|
|
6580
|
+
return body.links ?? [];
|
|
6581
|
+
}
|
|
6582
|
+
/** Set a member's role ("viewer" | "editor"). Owner-only on the server. */
|
|
6583
|
+
async setMemberRole(username, role) {
|
|
6584
|
+
await this.post("/keyring/role", { username, role });
|
|
6585
|
+
}
|
|
6586
|
+
/** Roster with roles (owner/members only). */
|
|
6587
|
+
async members() {
|
|
6588
|
+
const body = await this.get("/keyring/members");
|
|
6589
|
+
return body.members ?? [];
|
|
6590
|
+
}
|
|
6591
|
+
async revokeInviteLink(token) {
|
|
6592
|
+
await this.post("/keyring/link-revoke", { token });
|
|
6593
|
+
}
|
|
6594
|
+
/** Redeem a link: the server allowlists this member and asks the keeper to
|
|
6595
|
+
* wrap the group key for their identity. Caller must be authenticated. */
|
|
6596
|
+
async redeemInvite(req) {
|
|
6597
|
+
await this.post("/keyring/redeem", req);
|
|
6598
|
+
}
|
|
6525
6599
|
async fetchMetadata() {
|
|
6526
6600
|
try {
|
|
6527
6601
|
return await this.get("/metadata");
|
|
@@ -6659,6 +6733,56 @@ class SpaceNamespace {
|
|
|
6659
6733
|
return this.joinSpace(spaceId, opts);
|
|
6660
6734
|
}
|
|
6661
6735
|
// -------------------------------------------------------------------------
|
|
6736
|
+
// Invite links — shareable, keeper-gated invitations to a space. The token
|
|
6737
|
+
// is a capability: anyone signed in who redeems it is admitted by the
|
|
6738
|
+
// keeper. Mint/list/revoke require the caller to be the space's creator or
|
|
6739
|
+
// an existing member.
|
|
6740
|
+
// -------------------------------------------------------------------------
|
|
6741
|
+
/** Mint a shareable invite link for a space. `opts.expiresInSec` / `maxUses`
|
|
6742
|
+
* bound it (0/undefined = unlimited); `opts.role` ("viewer"|"editor") is the
|
|
6743
|
+
* access granted to redeemers (default viewer). Returns the link. */
|
|
6744
|
+
async createInviteLink(spaceId, opts = {}) {
|
|
6745
|
+
return this.keyringFor(spaceId).createInviteLink(opts);
|
|
6746
|
+
}
|
|
6747
|
+
/** Set a member's role on a space ("viewer" | "editor"). Owner-only. */
|
|
6748
|
+
async setMemberRole(spaceId, username, role) {
|
|
6749
|
+
await this.keyringFor(spaceId).setMemberRole(username, role);
|
|
6750
|
+
}
|
|
6751
|
+
/** Roster with roles (owner/members only). */
|
|
6752
|
+
async members(spaceId) {
|
|
6753
|
+
return this.keyringFor(spaceId).members();
|
|
6754
|
+
}
|
|
6755
|
+
/** List a space's active invite links (creator/members only). */
|
|
6756
|
+
async listInviteLinks(spaceId) {
|
|
6757
|
+
return this.keyringFor(spaceId).listInviteLinks();
|
|
6758
|
+
}
|
|
6759
|
+
/** Revoke an invite link by token. */
|
|
6760
|
+
async revokeInviteLink(spaceId, token) {
|
|
6761
|
+
await this.keyringFor(spaceId).revokeInviteLink(token);
|
|
6762
|
+
}
|
|
6763
|
+
/**
|
|
6764
|
+
* Join a space using an invite link: redeem the token (the keeper admits
|
|
6765
|
+
* this member), then connect and resolve once the group key arrives.
|
|
6766
|
+
* Returns the open {@link Space}.
|
|
6767
|
+
*/
|
|
6768
|
+
async joinByInvite(spaceId, token, opts = {}) {
|
|
6769
|
+
const memberId = this.myId();
|
|
6770
|
+
const { ecdhPub, ecdsaPub } = await this.ensureIdentity(memberId);
|
|
6771
|
+
await this.keyringFor(spaceId).redeemInvite({ token, identityEcdhPub: ecdhPub, identityEcdsaPub: ecdsaPub });
|
|
6772
|
+
return this.joinSpace(spaceId, opts);
|
|
6773
|
+
}
|
|
6774
|
+
/** The current member roster of a space (member id → identity public keys). */
|
|
6775
|
+
async roster(spaceId) {
|
|
6776
|
+
return this.keyringFor(spaceId).fetchRoster();
|
|
6777
|
+
}
|
|
6778
|
+
keyringFor(spaceId) {
|
|
6779
|
+
return new KeyringClient({
|
|
6780
|
+
spaceId,
|
|
6781
|
+
httpBaseUrl: this.deps.http.baseUrl,
|
|
6782
|
+
fetch: this.deps.http.fetch,
|
|
6783
|
+
});
|
|
6784
|
+
}
|
|
6785
|
+
// -------------------------------------------------------------------------
|
|
6662
6786
|
// Internals
|
|
6663
6787
|
// -------------------------------------------------------------------------
|
|
6664
6788
|
async build(spaceId, historyPolicy) {
|
|
@@ -6725,6 +6849,133 @@ class SpaceNamespace {
|
|
|
6725
6849
|
}
|
|
6726
6850
|
}
|
|
6727
6851
|
|
|
6852
|
+
/**
|
|
6853
|
+
* `client.agents` — manage an app's **Programmable Agents** (server-side
|
|
6854
|
+
* "virtual users" backed by Workers AI).
|
|
6855
|
+
*
|
|
6856
|
+
* const { config } = await client.agents.create(appId, {
|
|
6857
|
+
* handle: '@assistant', displayName: 'Assistant',
|
|
6858
|
+
* systemPrompt: 'You are a helpful teammate in this Space.',
|
|
6859
|
+
* triggers: [{ type: 'mention' }],
|
|
6860
|
+
* });
|
|
6861
|
+
* await client.agents.enable(appId, config.agentId, spaceId); // per-Space opt-in
|
|
6862
|
+
*
|
|
6863
|
+
* These are MANAGEMENT calls — the agent itself runs on the accelerator. Only an
|
|
6864
|
+
* app's owner or an editor of its shared Space may create/update/enable an agent
|
|
6865
|
+
* (viewers can read). Authorization rides the user session token; non-owner
|
|
6866
|
+
* editors pass their management Space via `opts.space`.
|
|
6867
|
+
*
|
|
6868
|
+
* v1 is "prompts + persona": a system prompt, a model, named prompt `skills`,
|
|
6869
|
+
* and `triggers` that decide when the agent speaks. No tools, no code.
|
|
6870
|
+
*/
|
|
6871
|
+
class AgentsNamespace {
|
|
6872
|
+
deps;
|
|
6873
|
+
constructor(deps) {
|
|
6874
|
+
this.deps = deps;
|
|
6875
|
+
}
|
|
6876
|
+
/** List the app's agents. */
|
|
6877
|
+
async list(appId, opts = {}) {
|
|
6878
|
+
const res = await this.deps.http.get(this.path(appId, "", opts));
|
|
6879
|
+
return res.agents ?? [];
|
|
6880
|
+
}
|
|
6881
|
+
/** Read one agent (config + identity keys). */
|
|
6882
|
+
async get(appId, agentId, opts = {}) {
|
|
6883
|
+
return this.deps.http.get(this.path(appId, `/${encodeURIComponent(agentId)}`, opts));
|
|
6884
|
+
}
|
|
6885
|
+
/** Create an agent (owner/editor only). */
|
|
6886
|
+
async create(appId, input, opts = {}) {
|
|
6887
|
+
return this.deps.http.post(this.path(appId, "", {}), { ...input, space: opts.space });
|
|
6888
|
+
}
|
|
6889
|
+
/** Update an agent's editor-settable fields (owner/editor only). */
|
|
6890
|
+
async update(appId, agentId, patch, opts = {}) {
|
|
6891
|
+
return this.deps.http.patch(this.path(appId, `/${encodeURIComponent(agentId)}`, {}), { ...patch, space: opts.space });
|
|
6892
|
+
}
|
|
6893
|
+
/** Delete an agent (owner/editor only). */
|
|
6894
|
+
async delete(appId, agentId, opts = {}) {
|
|
6895
|
+
return this.deps.http.del(this.path(appId, `/${encodeURIComponent(agentId)}`, {}), { space: opts.space });
|
|
6896
|
+
}
|
|
6897
|
+
/** Enable the agent on a Space (per-Space opt-in). */
|
|
6898
|
+
async enable(appId, agentId, spaceId, opts = {}) {
|
|
6899
|
+
return this.deps.http.post(this.path(appId, `/${encodeURIComponent(agentId)}/enable`, {}), { targetSpaceId: spaceId, space: opts.space });
|
|
6900
|
+
}
|
|
6901
|
+
/** Disable the agent on a Space. */
|
|
6902
|
+
async disable(appId, agentId, spaceId, opts = {}) {
|
|
6903
|
+
return this.deps.http.post(this.path(appId, `/${encodeURIComponent(agentId)}/disable`, {}), { targetSpaceId: spaceId, space: opts.space });
|
|
6904
|
+
}
|
|
6905
|
+
path(appId, suffix, opts) {
|
|
6906
|
+
const q = opts.space ? `?space=${encodeURIComponent(opts.space)}` : "";
|
|
6907
|
+
return `/api/apps/${encodeURIComponent(appId)}/agents${suffix}${q}`;
|
|
6908
|
+
}
|
|
6909
|
+
}
|
|
6910
|
+
|
|
6911
|
+
/**
|
|
6912
|
+
* `client.functions` — manage an app's **serverless functions** (developer-
|
|
6913
|
+
* authored code that runs on the accelerator).
|
|
6914
|
+
*
|
|
6915
|
+
* const { config } = await client.functions.deploy(appId, {
|
|
6916
|
+
* name: 'hello', displayName: 'Hello',
|
|
6917
|
+
* code: `export default { fetch: () => new Response('hi') }`,
|
|
6918
|
+
* triggers: { http: { enabled: true } },
|
|
6919
|
+
* });
|
|
6920
|
+
* // HTTP-reachable at hello--<slug>.fns.muhkoo.dev
|
|
6921
|
+
* await client.functions.enable(appId, config.functionId, spaceId); // Space-bound
|
|
6922
|
+
*
|
|
6923
|
+
* These are MANAGEMENT calls — the function itself runs on the accelerator
|
|
6924
|
+
* (uploaded just-in-time, decrypted only at invocation). Only an app's owner or
|
|
6925
|
+
* an editor of its shared Space may deploy/update/enable (viewers read).
|
|
6926
|
+
* Authorization rides the user session token; non-owner editors pass their
|
|
6927
|
+
* management Space via `opts.space`.
|
|
6928
|
+
*
|
|
6929
|
+
* v1 is "compute-only": a function is an untrusted single-module ES worker
|
|
6930
|
+
* (`export default { fetch }`) with NO platform bindings — it reaches the
|
|
6931
|
+
* platform as an external client (the end-user session is passed through). Two
|
|
6932
|
+
* triggers: `http` (its own subdomain) and `space` (invoked on Space messages,
|
|
6933
|
+
* like a Programmable Agent).
|
|
6934
|
+
*/
|
|
6935
|
+
class FunctionsNamespace {
|
|
6936
|
+
deps;
|
|
6937
|
+
constructor(deps) {
|
|
6938
|
+
this.deps = deps;
|
|
6939
|
+
}
|
|
6940
|
+
/** List the app's functions. */
|
|
6941
|
+
async list(appId, opts = {}) {
|
|
6942
|
+
const res = await this.deps.http.get(this.path(appId, "", opts));
|
|
6943
|
+
return res.functions ?? [];
|
|
6944
|
+
}
|
|
6945
|
+
/** Read one function's config. */
|
|
6946
|
+
async get(appId, functionId, opts = {}) {
|
|
6947
|
+
return this.deps.http.get(this.path(appId, `/${encodeURIComponent(functionId)}`, opts));
|
|
6948
|
+
}
|
|
6949
|
+
/** Read a function's decrypted source (owner/editor; for the editor UI). */
|
|
6950
|
+
async code(appId, functionId, opts = {}) {
|
|
6951
|
+
return this.deps.http.get(this.path(appId, `/${encodeURIComponent(functionId)}/code`, opts));
|
|
6952
|
+
}
|
|
6953
|
+
/** Deploy (create) a function (owner/editor only). */
|
|
6954
|
+
async deploy(appId, input, opts = {}) {
|
|
6955
|
+
return this.deps.http.post(this.path(appId, "", {}), { ...input, space: opts.space });
|
|
6956
|
+
}
|
|
6957
|
+
/** Update a function's editor-settable fields and/or redeploy its `code`. */
|
|
6958
|
+
async update(appId, functionId, patch, opts = {}) {
|
|
6959
|
+
return this.deps.http.patch(this.path(appId, `/${encodeURIComponent(functionId)}`, {}), { ...patch, space: opts.space });
|
|
6960
|
+
}
|
|
6961
|
+
/** Delete a function (owner/editor only). */
|
|
6962
|
+
async delete(appId, functionId, opts = {}) {
|
|
6963
|
+
return this.deps.http.del(this.path(appId, `/${encodeURIComponent(functionId)}`, {}), { space: opts.space });
|
|
6964
|
+
}
|
|
6965
|
+
/** Enable the function on a Space (per-Space opt-in for the `space` trigger). */
|
|
6966
|
+
async enable(appId, functionId, spaceId, opts = {}) {
|
|
6967
|
+
return this.deps.http.post(this.path(appId, `/${encodeURIComponent(functionId)}/enable`, {}), { targetSpaceId: spaceId, space: opts.space });
|
|
6968
|
+
}
|
|
6969
|
+
/** Disable the function on a Space. */
|
|
6970
|
+
async disable(appId, functionId, spaceId, opts = {}) {
|
|
6971
|
+
return this.deps.http.post(this.path(appId, `/${encodeURIComponent(functionId)}/disable`, {}), { targetSpaceId: spaceId, space: opts.space });
|
|
6972
|
+
}
|
|
6973
|
+
path(appId, suffix, opts) {
|
|
6974
|
+
const q = opts.space ? `?space=${encodeURIComponent(opts.space)}` : "";
|
|
6975
|
+
return `/api/apps/${encodeURIComponent(appId)}/functions${suffix}${q}`;
|
|
6976
|
+
}
|
|
6977
|
+
}
|
|
6978
|
+
|
|
6728
6979
|
/**
|
|
6729
6980
|
* `Client` — the single entry point to the Muhkoo SDK.
|
|
6730
6981
|
*
|
|
@@ -6769,6 +7020,10 @@ class Client {
|
|
|
6769
7020
|
message;
|
|
6770
7021
|
/** Fan-out group spaces — `client.space.createSpace(...)`, etc. */
|
|
6771
7022
|
space;
|
|
7023
|
+
/** Programmable Agents — `client.agents.create(appId, …)`, etc. */
|
|
7024
|
+
agents;
|
|
7025
|
+
/** Serverless functions — `client.functions.deploy(appId, …)`, etc. */
|
|
7026
|
+
functions;
|
|
6772
7027
|
session;
|
|
6773
7028
|
http;
|
|
6774
7029
|
/** Subscribers notified when a session expires and can't be recovered. */
|
|
@@ -6811,6 +7066,8 @@ class Client {
|
|
|
6811
7066
|
wsBaseUrl,
|
|
6812
7067
|
cache: spaceKeyCache,
|
|
6813
7068
|
});
|
|
7069
|
+
this.agents = new AgentsNamespace({ http: this.http });
|
|
7070
|
+
this.functions = new FunctionsNamespace({ http: this.http });
|
|
6814
7071
|
}
|
|
6815
7072
|
/** The currently signed-in user, or `null`. */
|
|
6816
7073
|
get user() {
|
|
@@ -6930,5 +7187,5 @@ var bundledLoader = /*#__PURE__*/Object.freeze({
|
|
|
6930
7187
|
default: loadRsWasm
|
|
6931
7188
|
});
|
|
6932
7189
|
|
|
6933
|
-
export { AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, AuthClient, AuthNamespace, AuthPublicInput, Authenticator, BroadcastChannel, BroadcastChannelEvents, ChannelExistsError, ChannelNotFoundError, ChunkCipher, Client, DEFAULT_BASE_URL, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, HashKnowledge, HttpClient, HttpError, KEEPER_MEMBER_ID, KeyStore, KeyringClient, KvNamespace, LocalStorageSessionStore, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PersonalSpaceClient, Poseidon, PreimagePoK, ReedSolomonCodec, Space as Room, SerializeMessage, SessionState, ShardClient, SharedSpaceClient, Space, SpaceKeyring, SpaceNamespace, SpacePacketCipher, StorageNamespace, WSTransport, ZkAuth, buildCommitment, compilePrograms, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultSessionStore, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, encodeToHex, encryptAesGcm, exportPublicKeyBase64, exportPublicKeyHex, generateAuthProof, getSubtle, initBn128Wasm, initRsWasm, initializeCircuits, manifestToStat, poseidonHash, quickVerify, randomBytes, shardHash, signMessage, toField, unwrapWithPassphrase, verify, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase };
|
|
7190
|
+
export { AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, AgentsNamespace, AuthClient, AuthNamespace, AuthPublicInput, Authenticator, BroadcastChannel, BroadcastChannelEvents, ChannelExistsError, ChannelNotFoundError, ChunkCipher, Client, DEFAULT_BASE_URL, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, FunctionsNamespace, HashKnowledge, HttpClient, HttpError, KEEPER_MEMBER_ID, KeyStore, KeyringClient, KvNamespace, LocalStorageSessionStore, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PersonalSpaceClient, Poseidon, PreimagePoK, ReedSolomonCodec, Space as Room, SerializeMessage, SessionState, ShardClient, SharedSpaceClient, Space, SpaceKeyring, SpaceNamespace, SpacePacketCipher, StorageNamespace, WSTransport, ZkAuth, buildCommitment, compilePrograms, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultSessionStore, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, encodeToHex, encryptAesGcm, exportPublicKeyBase64, exportPublicKeyHex, generateAuthProof, getSubtle, initBn128Wasm, initRsWasm, initializeCircuits, manifestToStat, poseidonHash, quickVerify, randomBytes, shardHash, signMessage, toField, unwrapWithPassphrase, verify, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase };
|
|
6934
7191
|
//# sourceMappingURL=index.js.map
|