@fungi.computer/agar 0.1.0 → 0.3.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/README.md +34 -18
- package/dist/agent-message-meta.d.ts +21 -16
- package/dist/agent-message-meta.js +17 -6
- package/dist/agents.d.ts +86 -13
- package/dist/agents.js +48 -25
- package/dist/approvals.d.ts +5 -0
- package/dist/approvals.js +36 -0
- package/dist/apps.js +4 -4
- package/dist/backend-definition.d.ts +41 -4
- package/dist/backend-definition.js +70 -10
- package/dist/backend-refusal.d.ts +1 -0
- package/dist/backend-refusal.js +1 -0
- package/dist/backend.js +4 -4
- package/dist/channels.js +4 -4
- package/dist/computer-contracts.d.ts +11 -28
- package/dist/computer-contracts.js +10 -29
- package/dist/computer.js +16 -16
- package/dist/connect.d.ts +3 -3
- package/dist/connect.js +7 -17
- package/dist/errors.d.ts +39 -1
- package/dist/errors.js +62 -5
- package/dist/game-servers.js +2 -2
- package/dist/git-rpc.d.ts +23 -23
- package/dist/git-rpc.js +14 -16
- package/dist/git.d.ts +9 -9
- package/dist/git.js +8 -8
- package/dist/host.d.ts +3 -1
- package/dist/host.js +5 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.js +5 -2
- package/dist/inference.js +2 -1
- package/dist/internal/app-session.js +37 -1
- package/dist/internal/media-diagnostic.d.ts +11 -0
- package/dist/internal/media-diagnostic.js +21 -0
- package/dist/internal/protocol.d.ts +4 -4
- package/dist/internal/schemas.d.ts +40 -7
- package/dist/internal/schemas.js +122 -35
- package/dist/marketplace.js +7 -5
- package/dist/media-json.d.ts +14 -0
- package/dist/media-json.js +1 -0
- package/dist/media-request-id.d.ts +1 -1
- package/dist/media-request-id.js +2 -2
- package/dist/media.d.ts +11 -0
- package/dist/media.js +8 -2
- package/dist/method-presentation.d.ts +7 -0
- package/dist/method-presentation.js +14 -0
- package/dist/method-refusal.d.ts +5 -5
- package/dist/method-refusal.js +4 -4
- package/dist/query.d.ts +2 -2
- package/dist/react-mount.d.ts +7 -1
- package/dist/react-mount.js +28 -8
- package/dist/react.d.ts +14 -2
- package/dist/react.js +40 -4
- package/dist/read-copy.d.ts +1 -1
- package/dist/read-copy.js +3 -6
- package/dist/storage.js +1 -1
- package/dist/test-kit/git.js +19 -2
- package/dist/window.d.ts +68 -0
- package/dist/window.js +31 -0
- package/docs/api/README.md +15 -15
- package/docs/api/backend-definition.md +83 -9
- package/docs/api/backend.md +45 -38
- package/docs/api/errors.md +104 -0
- package/docs/api/git-rpc.md +35 -35
- package/docs/api/git.md +66 -80
- package/docs/api/index.md +851 -143
- package/docs/api/media-json.md +32 -59
- package/docs/api/media-request-id.md +1 -1
- package/docs/api/method-presentation.md +21 -0
- package/docs/api/modules.md +1 -0
- package/docs/api/query.md +1 -1
- package/docs/api/react.md +23 -10
- package/package.json +16 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
|
-
import type { GillCall, GillReply } from "@fungi.computer/gills";
|
|
2
|
+
import type { GillCall, GillReply, ResultRow } from "@fungi.computer/gills";
|
|
3
3
|
import type { AppBackendGill } from "./gills.js";
|
|
4
4
|
export type { AppBackendGill } from "./gills.js";
|
|
5
5
|
import { type AppMethodCallContext } from "@fungi.computer/puffball/app-method-runtime";
|
|
@@ -10,10 +10,10 @@ export { pending, pendingSchema, type Pending } from "./pending.js";
|
|
|
10
10
|
/** EVENTS.emit acknowledges delivery with native void, not a JSON value. */
|
|
11
11
|
export declare const appEventEmitResultSchema: z.ZodUndefined;
|
|
12
12
|
/** App-originated Session send refusal; retry advice is a duration, not a timestamp. */
|
|
13
|
-
export declare const appSendBudgetRefusalSchema: z.ZodObject<{
|
|
13
|
+
export declare const appSendBudgetRefusalSchema: z.ZodLazy<z.ZodObject<{
|
|
14
14
|
error: z.ZodLiteral<"send_budget_exceeded">;
|
|
15
15
|
retryAfterMs: z.ZodNumber;
|
|
16
|
-
}, z.core.$strict
|
|
16
|
+
}, z.core.$strict>>;
|
|
17
17
|
/** A declined Session send, with how long to wait before retrying. */
|
|
18
18
|
export type AppSendBudgetRefusal = z.output<typeof appSendBudgetRefusalSchema>;
|
|
19
19
|
/** A method's schemas own both its TypeScript contract and JSON boundary. */
|
|
@@ -27,19 +27,56 @@ export type MethodPolicy = Readonly<{
|
|
|
27
27
|
anonymous?: true;
|
|
28
28
|
/** Allow the shared Pending result while this method is still working. */
|
|
29
29
|
pending?: true;
|
|
30
|
+
/** Past tense for timelines ("made", "wrote"); the row itself comes from {@link MethodPresentation}. */
|
|
31
|
+
present?: Readonly<{
|
|
32
|
+
verb: string;
|
|
33
|
+
}>;
|
|
34
|
+
}>;
|
|
35
|
+
/** Decodes and bounds result rows; see `resultRows` in `@fungi.computer/agar/method-presentation`. */
|
|
36
|
+
export type ResultRowCodec = Readonly<{
|
|
37
|
+
/** Check one projected row against the shared row's bounds; throws when it breaks them. */
|
|
38
|
+
decode: (row: ResultRow) => ResultRow;
|
|
39
|
+
/** The row's JSON boundary, as the generated `present` method's output. */
|
|
40
|
+
schema: z.ZodType;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* How a finished call reads to a person: a pure projection run in the App
|
|
44
|
+
* backend. `rows` is `resultRows` from
|
|
45
|
+
* `@fungi.computer/agar/method-presentation`; only a backend that presents
|
|
46
|
+
* imports it, so one without presentations carries no row decoder.
|
|
47
|
+
*/
|
|
48
|
+
export type MethodPresentation<I, O> = Readonly<{
|
|
49
|
+
/** Past tense for timelines: "made", "wrote", "pushed". */
|
|
50
|
+
verb: string;
|
|
51
|
+
/** Pure: one finished call to the row a person reads. */
|
|
52
|
+
row: (call: Readonly<{
|
|
53
|
+
input: I;
|
|
54
|
+
output: O;
|
|
55
|
+
}>) => ResultRow;
|
|
56
|
+
/** The shared row codec, `resultRows`. */
|
|
57
|
+
rows: ResultRowCodec;
|
|
30
58
|
}>;
|
|
59
|
+
/** Name of the generated, hidden read method that projects a finished call into its row. */
|
|
60
|
+
export declare const presentMethodName = "present";
|
|
31
61
|
/** Prepared method export consumed by the backend and its build. */
|
|
32
62
|
export type AppBackendMethod<Context = void> = Readonly<{
|
|
33
63
|
policy: MethodPolicy;
|
|
34
64
|
input: z.ZodType;
|
|
35
65
|
output: z.ZodType;
|
|
66
|
+
/** Parse a recorded call against this method's schemas and project its row. */
|
|
67
|
+
present?: (input: WhistleJsonValue, output: WhistleJsonValue) => ResultRow;
|
|
68
|
+
/** The row's JSON boundary, present exactly when `present` is. */
|
|
69
|
+
presentRow?: z.ZodType;
|
|
36
70
|
/** Parse the raw input, run the handler, and return its JSON result. */
|
|
37
71
|
call: (input: WhistleJsonValue, context: Context, caller: AppMethodCallContext) => Promise<WhistleJsonValue>;
|
|
38
72
|
}>;
|
|
73
|
+
/** The prepared methods of one backend, keyed by their public names. */
|
|
74
|
+
export type AppBackendMethods<Context = void> = Readonly<Record<string, AppBackendMethod<Context>>>;
|
|
39
75
|
/** Define a typed method once; neither discovery nor builds execute its handler. */
|
|
40
|
-
export declare function defineAppMethod<I extends z.ZodType, O extends z.ZodType, Context = void>(method: MethodPolicy & {
|
|
76
|
+
export declare function defineAppMethod<I extends z.ZodType, O extends z.ZodType, Context = void>(method: Omit<MethodPolicy, "present"> & {
|
|
41
77
|
input: I;
|
|
42
78
|
output: O;
|
|
79
|
+
present?: MethodPresentation<z.output<I>, z.output<O>>;
|
|
43
80
|
} & ({
|
|
44
81
|
pending: true;
|
|
45
82
|
handler: (input: z.output<I>, context: Context, caller: AppMethodCallContext) => z.input<O> | Pending | Promise<z.input<O> | Pending>;
|
|
@@ -6,10 +6,12 @@ export { pending, pendingSchema } from "./pending.js";
|
|
|
6
6
|
/** EVENTS.emit acknowledges delivery with native void, not a JSON value. */
|
|
7
7
|
export const appEventEmitResultSchema = z.undefined();
|
|
8
8
|
/** App-originated Session send refusal; retry advice is a duration, not a timestamp. */
|
|
9
|
-
export const appSendBudgetRefusalSchema = z.strictObject({
|
|
9
|
+
export const appSendBudgetRefusalSchema = z.lazy(() => z.strictObject({
|
|
10
10
|
error: z.literal("send_budget_exceeded"),
|
|
11
11
|
retryAfterMs: z.number().int().min(1).max(60_000),
|
|
12
|
-
});
|
|
12
|
+
}));
|
|
13
|
+
/** Name of the generated, hidden read method that projects a finished call into its row. */
|
|
14
|
+
export const presentMethodName = "present";
|
|
13
15
|
/** Define a typed method once; neither discovery nor builds execute its handler. */
|
|
14
16
|
export function defineAppMethod(method) {
|
|
15
17
|
const output = method.pending === true
|
|
@@ -26,16 +28,31 @@ export function defineAppMethod(method) {
|
|
|
26
28
|
policy = { ...policy, hidden: true };
|
|
27
29
|
if (method.anonymous === true)
|
|
28
30
|
policy = { ...policy, anonymous: true };
|
|
31
|
+
const call = async (rawInput, context, rawCaller) => {
|
|
32
|
+
const caller = parseAppMethodCallContext(rawCaller);
|
|
33
|
+
const parsed = method.input.parse(rawInput);
|
|
34
|
+
const result = await method.handler(parsed, context, caller);
|
|
35
|
+
return z.json().parse(output.parse(result));
|
|
36
|
+
};
|
|
37
|
+
const presentation = method.present;
|
|
38
|
+
if (presentation === undefined)
|
|
39
|
+
return Object.freeze({
|
|
40
|
+
policy: Object.freeze(policy),
|
|
41
|
+
input: method.input,
|
|
42
|
+
output,
|
|
43
|
+
call,
|
|
44
|
+
});
|
|
45
|
+
policy = { ...policy, present: Object.freeze({ verb: presentation.verb }) };
|
|
29
46
|
return Object.freeze({
|
|
30
47
|
policy: Object.freeze(policy),
|
|
31
48
|
input: method.input,
|
|
32
49
|
output,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
50
|
+
present: (rawInput, rawOutput) => presentation.rows.decode(presentation.row({
|
|
51
|
+
input: method.input.parse(rawInput),
|
|
52
|
+
output: method.output.parse(rawOutput),
|
|
53
|
+
})),
|
|
54
|
+
presentRow: presentation.rows.schema,
|
|
55
|
+
call,
|
|
39
56
|
});
|
|
40
57
|
}
|
|
41
58
|
/** The generated method RPC answers with a typed reply: a handler's
|
|
@@ -54,9 +71,52 @@ async function methodReply(run) {
|
|
|
54
71
|
});
|
|
55
72
|
}
|
|
56
73
|
}
|
|
74
|
+
const presentInput = z.strictObject({
|
|
75
|
+
method: z.string().min(1).max(80),
|
|
76
|
+
input: z.json(),
|
|
77
|
+
output: z.json(),
|
|
78
|
+
});
|
|
79
|
+
const generatedPresentMethods = new WeakSet();
|
|
80
|
+
/**
|
|
81
|
+
* Methods that declare a presentation share one generated, hidden read method
|
|
82
|
+
* that projects a recorded call into its row, so hosts reach it through the
|
|
83
|
+
* ordinary method admission instead of a second invocation path.
|
|
84
|
+
*/
|
|
85
|
+
function withPresentation(all) {
|
|
86
|
+
// A backend rebuilt from another definition's methods regenerates its own.
|
|
87
|
+
const methods = Object.fromEntries(Object.entries(all).filter(([name, method]) => name !== presentMethodName || !generatedPresentMethods.has(method)));
|
|
88
|
+
const presenting = new Map(Object.entries(methods).filter(([, method]) => method.present !== undefined));
|
|
89
|
+
const presentRow = [...presenting.values()][0]?.presentRow;
|
|
90
|
+
if (presentRow === undefined)
|
|
91
|
+
return methods;
|
|
92
|
+
if (Object.hasOwn(methods, presentMethodName))
|
|
93
|
+
throw new Error(`App backend method is reserved: ${presentMethodName}`);
|
|
94
|
+
const present = Object.freeze({
|
|
95
|
+
policy: Object.freeze({
|
|
96
|
+
description: "Project one finished call of a presenting method into its result row.",
|
|
97
|
+
effect: "read",
|
|
98
|
+
requires: Object.freeze([]),
|
|
99
|
+
hidden: true,
|
|
100
|
+
}),
|
|
101
|
+
input: presentInput,
|
|
102
|
+
output: presentRow,
|
|
103
|
+
call: (rawInput) => Promise.resolve().then(() => {
|
|
104
|
+
const request = presentInput.parse(rawInput);
|
|
105
|
+
const row = presenting
|
|
106
|
+
.get(request.method)
|
|
107
|
+
?.present?.(request.input, request.output);
|
|
108
|
+
if (row === undefined)
|
|
109
|
+
throw new Error(`Undeclared App presentation: ${request.method}`);
|
|
110
|
+
return z.json().parse(presentRow.parse(row));
|
|
111
|
+
}),
|
|
112
|
+
});
|
|
113
|
+
generatedPresentMethods.add(present);
|
|
114
|
+
return { ...methods, [presentMethodName]: present };
|
|
115
|
+
}
|
|
57
116
|
/** Generate the API from the method exports; callers cannot supply a second catalog. */
|
|
58
117
|
export function defineAppBackend(options) {
|
|
59
|
-
const
|
|
118
|
+
const declared = withPresentation(options.methods);
|
|
119
|
+
const methods = new Map(Object.entries(declared));
|
|
60
120
|
for (const name of methods.keys())
|
|
61
121
|
if (!isAppApiMethodName(name))
|
|
62
122
|
throw new Error(`Invalid App backend method name: ${name}`);
|
|
@@ -97,7 +157,7 @@ export function defineAppBackend(options) {
|
|
|
97
157
|
});
|
|
98
158
|
};
|
|
99
159
|
let definition = {
|
|
100
|
-
methods: Object.freeze({ ...
|
|
160
|
+
methods: Object.freeze({ ...declared }),
|
|
101
161
|
implement: (implementation) => {
|
|
102
162
|
const members = new Map();
|
|
103
163
|
let prototype = implementation.prototype;
|
|
@@ -3,6 +3,7 @@ import * as z from "zod";
|
|
|
3
3
|
export declare const appBackendRefusalSchema: z.ZodObject<{
|
|
4
4
|
error: z.ZodLiteral<"App execution unavailable">;
|
|
5
5
|
reason: z.ZodEnum<{
|
|
6
|
+
disabled: "disabled";
|
|
6
7
|
exec_isolation_required: "exec_isolation_required";
|
|
7
8
|
execution_not_admitted: "execution_not_admitted";
|
|
8
9
|
artifact_unavailable: "artifact_unavailable";
|
package/dist/backend-refusal.js
CHANGED
package/dist/backend.js
CHANGED
|
@@ -3,18 +3,18 @@ import { defineCapability } from "./capabilities.js";
|
|
|
3
3
|
import { parseJsonValue } from "@fungi.computer/whistle/wire";
|
|
4
4
|
import { rpcDataSchema, rpcHandleSchema, rpcMethod, } from "./internal/rpc-contract.js";
|
|
5
5
|
/** Invoke this App's admitted backend. The App defines its JSON request/result. */
|
|
6
|
-
export const appBackend = defineCapability("app.backend", 1, rpcHandleSchema({
|
|
6
|
+
export const appBackend = defineCapability("app.backend", 1, z.lazy(() => rpcHandleSchema({
|
|
7
7
|
request: rpcMethod(rpcDataSchema(z.unknown().transform((value) => parseJsonValue(value)))),
|
|
8
|
-
}));
|
|
8
|
+
})));
|
|
9
9
|
/**
|
|
10
10
|
* Call one of this App's declared, typed methods through its host. Unlike
|
|
11
11
|
* `appBackend`, the host knows which method is called, so it can apply the
|
|
12
12
|
* method's declared effect (for example, allow only `read` methods to a
|
|
13
13
|
* visitor of a public App).
|
|
14
14
|
*/
|
|
15
|
-
export const appMethods = defineCapability("app.methods", 1, rpcHandleSchema({
|
|
15
|
+
export const appMethods = defineCapability("app.methods", 1, z.lazy(() => rpcHandleSchema({
|
|
16
16
|
call: rpcMethod(rpcDataSchema(z.unknown().transform((value) => parseJsonValue(value)))),
|
|
17
|
-
}));
|
|
17
|
+
})));
|
|
18
18
|
/** The admitting host overwrites this header from its authenticated principal.
|
|
19
19
|
* App request bodies and incoming browser headers never supply this identity. */
|
|
20
20
|
export const appBackendActorHeader = "X-Agar-Actor-Id";
|
package/dist/channels.js
CHANGED
|
@@ -84,14 +84,14 @@ export const appWebsiteReplyResponseSchema = z.strictObject({
|
|
|
84
84
|
status: z.literal("sent"),
|
|
85
85
|
});
|
|
86
86
|
/** Reading website conversations is never an automatic default-App grant. */
|
|
87
|
-
export const channelsRead = defineCapability("channels.read", 1, rpcHandleSchema({
|
|
87
|
+
export const channelsRead = defineCapability("channels.read", 1, z.lazy(() => rpcHandleSchema({
|
|
88
88
|
list: rpcMethod(rpcDataSchema(appWebsitePageSchema)),
|
|
89
89
|
conversations: rpcMethod(rpcDataSchema(appWebsiteConversationPageSchema)),
|
|
90
|
-
}));
|
|
90
|
+
})));
|
|
91
91
|
/** Member-only takeover; separate consent from read-only channel access. */
|
|
92
|
-
export const channelsPersonalManage = defineCapability("channels.personal.manage", 1, rpcHandleSchema({
|
|
92
|
+
export const channelsPersonalManage = defineCapability("channels.personal.manage", 1, z.lazy(() => rpcHandleSchema({
|
|
93
93
|
hold: rpcMethod(rpcDataSchema(appWebsiteConversationSchema)),
|
|
94
94
|
release: rpcMethod(rpcDataSchema(appWebsiteConversationSchema)),
|
|
95
95
|
dismiss: rpcMethod(rpcDataSchema(appWebsiteConversationSchema)),
|
|
96
96
|
reply: rpcMethod(rpcDataSchema(appWebsiteReplyResponseSchema)),
|
|
97
|
-
}));
|
|
97
|
+
})));
|
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
|
-
/** Public metadata for one host-admitted Workflow Git pass; parsing never grants authority. */
|
|
3
|
-
export declare const workflowGitPassResultSchema: z.ZodObject<{
|
|
4
|
-
passId: z.ZodString;
|
|
5
|
-
remote: z.ZodURL;
|
|
6
|
-
expiresAt: z.ZodNumber;
|
|
7
|
-
bearer: z.ZodExactOptional<z.ZodString>;
|
|
8
|
-
}, z.core.$strict>;
|
|
9
|
-
/** The complete remote stays opaque; the host owns admission, renewal and revocation. */
|
|
10
|
-
export type WorkflowGitPassResult = z.output<typeof workflowGitPassResultSchema>;
|
|
11
2
|
/** Existing attachment budget operation kinds shared by Computer and Media. */
|
|
12
3
|
export declare const connectorOperationBudgetKindSchema: z.ZodEnum<{
|
|
13
4
|
write: "write";
|
|
@@ -15,7 +6,7 @@ export declare const connectorOperationBudgetKindSchema: z.ZodEnum<{
|
|
|
15
6
|
exec: "exec";
|
|
16
7
|
}>;
|
|
17
8
|
/** Existing owner budget refusal; it remains a domain result, not a wire envelope. */
|
|
18
|
-
export declare const connectorOperationBudgetRefusalSchema: z.ZodObject<{
|
|
9
|
+
export declare const connectorOperationBudgetRefusalSchema: z.ZodLazy<z.ZodObject<{
|
|
19
10
|
error: z.ZodLiteral<"attachment_budget_exceeded">;
|
|
20
11
|
kind: z.ZodEnum<{
|
|
21
12
|
write: "write";
|
|
@@ -23,16 +14,16 @@ export declare const connectorOperationBudgetRefusalSchema: z.ZodObject<{
|
|
|
23
14
|
exec: "exec";
|
|
24
15
|
}>;
|
|
25
16
|
retryAfterMs: z.ZodNumber;
|
|
26
|
-
}, z.core.$strict
|
|
17
|
+
}, z.core.$strict>>;
|
|
27
18
|
/** Computer admission refusals returned before native dispatch. */
|
|
28
|
-
export declare const appComputerAdmissionRefusalSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
19
|
+
export declare const appComputerAdmissionRefusalSchema: z.ZodLazy<z.ZodUnion<readonly [z.ZodObject<{
|
|
29
20
|
error: z.ZodEnum<{
|
|
30
21
|
unavailable: "unavailable";
|
|
31
22
|
revoked: "revoked";
|
|
32
23
|
not_granted: "not_granted";
|
|
33
24
|
grantor_lost_authority: "grantor_lost_authority";
|
|
34
25
|
}>;
|
|
35
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
26
|
+
}, z.core.$strict>, z.ZodLazy<z.ZodObject<{
|
|
36
27
|
error: z.ZodLiteral<"attachment_budget_exceeded">;
|
|
37
28
|
kind: z.ZodEnum<{
|
|
38
29
|
write: "write";
|
|
@@ -40,34 +31,26 @@ export declare const appComputerAdmissionRefusalSchema: z.ZodUnion<readonly [z.Z
|
|
|
40
31
|
exec: "exec";
|
|
41
32
|
}>;
|
|
42
33
|
retryAfterMs: z.ZodNumber;
|
|
43
|
-
}, z.core.$strict
|
|
34
|
+
}, z.core.$strict>>]>>;
|
|
44
35
|
/** Existing Computer broker argument refusals. */
|
|
45
|
-
export declare const appComputerInputRefusalSchema: z.ZodObject<{
|
|
36
|
+
export declare const appComputerInputRefusalSchema: z.ZodLazy<z.ZodObject<{
|
|
46
37
|
error: z.ZodEnum<{
|
|
47
38
|
invalid: "invalid";
|
|
48
39
|
too_large: "too_large";
|
|
49
40
|
invalid_path: "invalid_path";
|
|
50
41
|
}>;
|
|
51
|
-
}, z.core.$strict
|
|
42
|
+
}, z.core.$strict>>;
|
|
52
43
|
/** Existing Computer dispatch status/decoding refusals. */
|
|
53
|
-
export declare const appComputerDispatchRefusalSchema: z.ZodObject<{
|
|
44
|
+
export declare const appComputerDispatchRefusalSchema: z.ZodLazy<z.ZodObject<{
|
|
54
45
|
error: z.ZodEnum<{
|
|
55
46
|
unavailable: "unavailable";
|
|
56
47
|
conflict: "conflict";
|
|
57
48
|
not_granted: "not_granted";
|
|
58
49
|
too_large: "too_large";
|
|
59
50
|
}>;
|
|
60
|
-
}, z.core.$strict
|
|
51
|
+
}, z.core.$strict>>;
|
|
61
52
|
/** An execution wait can time out while its durable request remains identifiable. */
|
|
62
|
-
export declare const appComputerExecTimeoutSchema: z.ZodObject<{
|
|
53
|
+
export declare const appComputerExecTimeoutSchema: z.ZodLazy<z.ZodObject<{
|
|
63
54
|
error: z.ZodLiteral<"timeout">;
|
|
64
55
|
requestId: z.ZodString;
|
|
65
|
-
}, z.core.$strict
|
|
66
|
-
/** The Workflow Git-pass owner has exactly these existing domain refusals. */
|
|
67
|
-
export declare const workflowGitPassRefusalSchema: z.ZodObject<{
|
|
68
|
-
error: z.ZodEnum<{
|
|
69
|
-
unavailable: "unavailable";
|
|
70
|
-
revoked: "revoked";
|
|
71
|
-
not_granted: "not_granted";
|
|
72
|
-
}>;
|
|
73
|
-
}, z.core.$strict>;
|
|
56
|
+
}, z.core.$strict>>;
|
|
@@ -1,19 +1,4 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
|
-
/** Public metadata for one host-admitted Workflow Git pass; parsing never grants authority. */
|
|
3
|
-
export const workflowGitPassResultSchema = z.strictObject({
|
|
4
|
-
passId: z.string().min(1),
|
|
5
|
-
remote: z.url({ protocol: /^https$/, abort: true }).refine((value) => {
|
|
6
|
-
const remote = new URL(value);
|
|
7
|
-
return remote.username === "" && remote.password === "";
|
|
8
|
-
}, "Workflow Git remotes must not contain credentials"),
|
|
9
|
-
expiresAt: z.number().int().positive(),
|
|
10
|
-
/**
|
|
11
|
-
* Present when the host admits this pass by `Authorization: Bearer` on the
|
|
12
|
-
* remote. It is never part of the remote; a command receives it only as
|
|
13
|
-
* command-local Git config, never written to a repository config.
|
|
14
|
-
*/
|
|
15
|
-
bearer: z.string().min(1).exactOptional(),
|
|
16
|
-
});
|
|
17
2
|
/** Existing attachment budget operation kinds shared by Computer and Media. */
|
|
18
3
|
export const connectorOperationBudgetKindSchema = z.enum([
|
|
19
4
|
"exec",
|
|
@@ -21,13 +6,13 @@ export const connectorOperationBudgetKindSchema = z.enum([
|
|
|
21
6
|
"media",
|
|
22
7
|
]);
|
|
23
8
|
/** Existing owner budget refusal; it remains a domain result, not a wire envelope. */
|
|
24
|
-
export const connectorOperationBudgetRefusalSchema = z.strictObject({
|
|
9
|
+
export const connectorOperationBudgetRefusalSchema = z.lazy(() => z.strictObject({
|
|
25
10
|
error: z.literal("attachment_budget_exceeded"),
|
|
26
11
|
kind: connectorOperationBudgetKindSchema,
|
|
27
12
|
retryAfterMs: z.number(),
|
|
28
|
-
});
|
|
13
|
+
}));
|
|
29
14
|
/** Computer admission refusals returned before native dispatch. */
|
|
30
|
-
export const appComputerAdmissionRefusalSchema = z.union([
|
|
15
|
+
export const appComputerAdmissionRefusalSchema = z.lazy(() => z.union([
|
|
31
16
|
z.strictObject({
|
|
32
17
|
error: z.enum([
|
|
33
18
|
"revoked",
|
|
@@ -37,21 +22,17 @@ export const appComputerAdmissionRefusalSchema = z.union([
|
|
|
37
22
|
]),
|
|
38
23
|
}),
|
|
39
24
|
connectorOperationBudgetRefusalSchema,
|
|
40
|
-
]);
|
|
25
|
+
]));
|
|
41
26
|
/** Existing Computer broker argument refusals. */
|
|
42
|
-
export const appComputerInputRefusalSchema = z.strictObject({
|
|
27
|
+
export const appComputerInputRefusalSchema = z.lazy(() => z.strictObject({
|
|
43
28
|
error: z.enum(["too_large", "invalid_path", "invalid"]),
|
|
44
|
-
});
|
|
29
|
+
}));
|
|
45
30
|
/** Existing Computer dispatch status/decoding refusals. */
|
|
46
|
-
export const appComputerDispatchRefusalSchema = z.strictObject({
|
|
31
|
+
export const appComputerDispatchRefusalSchema = z.lazy(() => z.strictObject({
|
|
47
32
|
error: z.enum(["not_granted", "too_large", "conflict", "unavailable"]),
|
|
48
|
-
});
|
|
33
|
+
}));
|
|
49
34
|
/** An execution wait can time out while its durable request remains identifiable. */
|
|
50
|
-
export const appComputerExecTimeoutSchema = z.strictObject({
|
|
35
|
+
export const appComputerExecTimeoutSchema = z.lazy(() => z.strictObject({
|
|
51
36
|
error: z.literal("timeout"),
|
|
52
37
|
requestId: z.string(),
|
|
53
|
-
});
|
|
54
|
-
/** The Workflow Git-pass owner has exactly these existing domain refusals. */
|
|
55
|
-
export const workflowGitPassRefusalSchema = z.strictObject({
|
|
56
|
-
error: z.enum(["revoked", "not_granted", "unavailable"]),
|
|
57
|
-
});
|
|
38
|
+
}));
|
package/dist/computer.js
CHANGED
|
@@ -15,11 +15,11 @@ export const computerListingSchema = Schema.Struct({
|
|
|
15
15
|
})),
|
|
16
16
|
nextOffset: Schema.NullOr(Schema.Number),
|
|
17
17
|
});
|
|
18
|
-
export const computerList = defineCapability("computer.list", 1, rpcHandleSchema({
|
|
18
|
+
export const computerList = defineCapability("computer.list", 1, z.lazy(() => rpcHandleSchema({
|
|
19
19
|
list: rpcMethod(rpcDataSchema(z
|
|
20
20
|
.unknown()
|
|
21
21
|
.transform((value) => Schema.decodeUnknownSync(computerListingSchema)(value)))),
|
|
22
|
-
}));
|
|
22
|
+
})));
|
|
23
23
|
export const computerRuntimeStateSchema = Schema.Union([
|
|
24
24
|
Schema.Struct({
|
|
25
25
|
state: Schema.Literals(["running", "sleeping", "starting"]),
|
|
@@ -50,12 +50,12 @@ export const computerInstrumentSnapshotSchema = Schema.Struct({
|
|
|
50
50
|
})),
|
|
51
51
|
internet: computerInternetAccessSchema,
|
|
52
52
|
});
|
|
53
|
-
export const computerInstrumentRead = defineCapability("computer.instrument.read", 1, rpcHandleSchema({
|
|
53
|
+
export const computerInstrumentRead = defineCapability("computer.instrument.read", 1, z.lazy(() => rpcHandleSchema({
|
|
54
54
|
read: rpcMethod(rpcDataSchema(z
|
|
55
55
|
.unknown()
|
|
56
56
|
.transform((value) => Schema.decodeUnknownSync(computerInstrumentSnapshotSchema)(value)))),
|
|
57
|
-
}));
|
|
58
|
-
export const computerInstrumentControl = defineCapability("computer.instrument.control", 1, rpcHandleSchema({
|
|
57
|
+
})));
|
|
58
|
+
export const computerInstrumentControl = defineCapability("computer.instrument.control", 1, z.lazy(() => rpcHandleSchema({
|
|
59
59
|
start: rpcMethod(rpcDataSchema(z
|
|
60
60
|
.unknown()
|
|
61
61
|
.transform((value) => Schema.decodeUnknownSync(computerRuntimeStateSchema)(value)))),
|
|
@@ -65,7 +65,7 @@ export const computerInstrumentControl = defineCapability("computer.instrument.c
|
|
|
65
65
|
detachLocal: rpcMethod(rpcDataSchema(z
|
|
66
66
|
.unknown()
|
|
67
67
|
.transform((value) => Schema.decodeUnknownSync(Schema.Struct({ detached: Schema.Boolean }))(value)))),
|
|
68
|
-
}));
|
|
68
|
+
})));
|
|
69
69
|
export async function readComputerInstrument(capabilities) {
|
|
70
70
|
return Schema.decodeUnknownSync(computerInstrumentSnapshotSchema)(await capabilities.require(computerInstrumentRead).read());
|
|
71
71
|
}
|
|
@@ -106,7 +106,7 @@ export const computerPortRemovalSchema = Schema.Struct({
|
|
|
106
106
|
removed: Schema.Boolean,
|
|
107
107
|
});
|
|
108
108
|
/** The selected Computer's admitted preview URLs and port mutations. */
|
|
109
|
-
export const computerPorts = defineCapability("computer.ports", 2, rpcHandleSchema({
|
|
109
|
+
export const computerPorts = defineCapability("computer.ports", 2, z.lazy(() => rpcHandleSchema({
|
|
110
110
|
list: rpcMethod(rpcDataSchema(z
|
|
111
111
|
.unknown()
|
|
112
112
|
.transform((value) => Schema.decodeUnknownSync(computerPortsResultSchema)(value)))),
|
|
@@ -116,7 +116,7 @@ export const computerPorts = defineCapability("computer.ports", 2, rpcHandleSche
|
|
|
116
116
|
unexpose: rpcMethod(rpcDataSchema(z
|
|
117
117
|
.unknown()
|
|
118
118
|
.transform((value) => Schema.decodeUnknownSync(computerPortRemovalSchema)(value)))),
|
|
119
|
-
}));
|
|
119
|
+
})));
|
|
120
120
|
/** Decode exposed ports at the SDK boundary. */
|
|
121
121
|
export async function listComputerPorts(capabilities) {
|
|
122
122
|
const result = Schema.decodeUnknownSync(computerPortsResultSchema)(await capabilities.require(computerPorts).list());
|
|
@@ -178,7 +178,7 @@ export const computerProcessStartSchema = Schema.Struct({
|
|
|
178
178
|
port: Schema.optionalKey(computerPortExposureSchema.fields.port),
|
|
179
179
|
});
|
|
180
180
|
/** Read the authoritative process inventory from the selected Computer. */
|
|
181
|
-
export const computerProcesses = defineCapability("computer.processes", 1, rpcHandleSchema({
|
|
181
|
+
export const computerProcesses = defineCapability("computer.processes", 1, z.lazy(() => rpcHandleSchema({
|
|
182
182
|
start: rpcMethod(rpcDataSchema(z
|
|
183
183
|
.unknown()
|
|
184
184
|
.transform((value) => Schema.decodeUnknownSync(computerProcessSchema)(value)))),
|
|
@@ -188,7 +188,7 @@ export const computerProcesses = defineCapability("computer.processes", 1, rpcHa
|
|
|
188
188
|
kill: rpcMethod(rpcDataSchema(z
|
|
189
189
|
.unknown()
|
|
190
190
|
.transform((value) => Schema.decodeUnknownSync(computerProcessKillSchema)(value)))),
|
|
191
|
-
}));
|
|
191
|
+
})));
|
|
192
192
|
/** Decode the Computer owner's process inventory at the App boundary. */
|
|
193
193
|
export async function listComputerProcesses(capabilities) {
|
|
194
194
|
return Schema.decodeUnknownSync(computerProcessesSchema)(await capabilities.require(computerProcesses).list());
|
|
@@ -197,12 +197,12 @@ export async function listComputerProcesses(capabilities) {
|
|
|
197
197
|
export async function killComputerProcess(capabilities, id) {
|
|
198
198
|
return Schema.decodeUnknownSync(computerProcessKillSchema)(await capabilities.require(computerProcesses).kill(id));
|
|
199
199
|
}
|
|
200
|
-
export const computerReadFile = defineCapability("computer.readFile", 1, rpcHandleSchema({
|
|
200
|
+
export const computerReadFile = defineCapability("computer.readFile", 1, z.lazy(() => rpcHandleSchema({
|
|
201
201
|
open: rpcMethod(rpcHandleSchema({
|
|
202
202
|
response: rpcMethod(z.instanceof(Response)),
|
|
203
203
|
close: rpcVoid,
|
|
204
204
|
})),
|
|
205
|
-
}));
|
|
205
|
+
})));
|
|
206
206
|
/** The caller receives native file bytes; HTTP details stay in the SDK adapter. */
|
|
207
207
|
export async function readComputerFile(capabilities, input, options) {
|
|
208
208
|
return await readAppFile(await capabilities.require(computerReadFile).open(input), { ...options, name: input.path.split("/").at(-1) ?? "file" });
|
|
@@ -211,13 +211,13 @@ export async function readComputerFile(capabilities, input, options) {
|
|
|
211
211
|
export async function listComputerFiles(capabilities, input) {
|
|
212
212
|
return Schema.decodeUnknownSync(computerListingSchema)(await capabilities.require(computerList).list(input));
|
|
213
213
|
}
|
|
214
|
-
export const computerWriteFile = defineCapability("computer.writeFile", 3, rpcHandleSchema({
|
|
214
|
+
export const computerWriteFile = defineCapability("computer.writeFile", 3, z.lazy(() => rpcHandleSchema({
|
|
215
215
|
open: rpcMethod(rpcHandleSchema({ write: rpcVoid, close: rpcVoid })),
|
|
216
216
|
mkdir: rpcVoid,
|
|
217
217
|
replace: rpcMethod(rpcDataSchema(z
|
|
218
218
|
.unknown()
|
|
219
219
|
.transform((value) => Schema.decodeUnknownSync(computerFileReplacementOutcomeSchema)(value)))),
|
|
220
|
-
}));
|
|
220
|
+
})));
|
|
221
221
|
const computerFileReplacementOutcomeSchema = Schema.Union([
|
|
222
222
|
Schema.Struct({
|
|
223
223
|
kind: Schema.Literal("replaced"),
|
|
@@ -244,7 +244,7 @@ export const computerTerminalInformationSchema = Schema.Struct({
|
|
|
244
244
|
uptimeSeconds: Schema.Number.pipe(Schema.check(Schema.isInt()), Schema.check(Schema.isGreaterThanOrEqualTo(0))),
|
|
245
245
|
version: Schema.NonEmptyString.pipe(Schema.check(Schema.isMaxLength(64))),
|
|
246
246
|
});
|
|
247
|
-
export const computerTerminal = defineCapability("computer.terminal", 2, rpcHandleSchema({
|
|
247
|
+
export const computerTerminal = defineCapability("computer.terminal", 2, z.lazy(() => rpcHandleSchema({
|
|
248
248
|
open: rpcMethod(rpcHandleSchema({
|
|
249
249
|
information: rpcMethod(rpcDataSchema(z
|
|
250
250
|
.unknown()
|
|
@@ -258,7 +258,7 @@ export const computerTerminal = defineCapability("computer.terminal", 2, rpcHand
|
|
|
258
258
|
resize: rpcVoid,
|
|
259
259
|
close: rpcVoid,
|
|
260
260
|
})),
|
|
261
|
-
}));
|
|
261
|
+
})));
|
|
262
262
|
/** Open one host-owned terminal and bind its lifetime to the mounted App. */
|
|
263
263
|
export async function openComputerTerminal(capabilities, size, options = {}) {
|
|
264
264
|
options.signal?.throwIfAborted();
|
package/dist/connect.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { AppRoot } from "./root.js";
|
|
2
2
|
/** Options for connecting this iframe page to its authenticated host. */
|
|
3
3
|
export type ConnectAppOptions = {
|
|
4
|
-
/** The
|
|
5
|
-
*
|
|
6
|
-
readonly
|
|
4
|
+
/** The parent is the host. Serve the App with CSP `frame-ancestors` pinned
|
|
5
|
+
* to its allowed host so an unrelated site cannot become its parent. */
|
|
6
|
+
readonly host: "parent";
|
|
7
7
|
/** Cancels a pending connection attempt. */
|
|
8
8
|
readonly signal?: AbortSignal;
|
|
9
9
|
};
|
package/dist/connect.js
CHANGED
|
@@ -14,28 +14,23 @@ let liveConnection = false;
|
|
|
14
14
|
class PendingAppConnection {
|
|
15
15
|
options;
|
|
16
16
|
#appWindow;
|
|
17
|
-
#origin;
|
|
18
17
|
#port;
|
|
19
18
|
#settled = false;
|
|
20
19
|
#readyRetry;
|
|
21
20
|
#deadline;
|
|
22
21
|
constructor(options) {
|
|
23
22
|
this.options = options;
|
|
24
|
-
this.#origin = expectedOrigin(options.expectedHostOrigin);
|
|
25
23
|
this.#appWindow = globalThis.window;
|
|
26
24
|
}
|
|
27
25
|
start() {
|
|
28
26
|
performance.mark("fungi.app-open.agar-connect-start");
|
|
29
|
-
const origin = this.#origin;
|
|
30
|
-
if (!origin)
|
|
31
|
-
return Promise.reject(invalidOrigin());
|
|
32
27
|
const error = this.#startError();
|
|
33
28
|
if (error)
|
|
34
29
|
return Promise.reject(error);
|
|
35
30
|
liveConnection = true;
|
|
36
|
-
return this.#awaitConnection(
|
|
31
|
+
return this.#awaitConnection();
|
|
37
32
|
}
|
|
38
|
-
#awaitConnection(
|
|
33
|
+
#awaitConnection() {
|
|
39
34
|
return new Promise((resolve, reject) => {
|
|
40
35
|
const finish = (error, root) => {
|
|
41
36
|
this.#finish(error, root, onMessage, onAbort, resolve, reject);
|
|
@@ -44,14 +39,14 @@ class PendingAppConnection {
|
|
|
44
39
|
finish(cancelled());
|
|
45
40
|
};
|
|
46
41
|
const onMessage = (event) => {
|
|
47
|
-
this.#onMessage(event,
|
|
42
|
+
this.#onMessage(event, finish, onMessage);
|
|
48
43
|
};
|
|
49
44
|
this.#appWindow.addEventListener("message", onMessage);
|
|
50
45
|
this.options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
51
46
|
const notify = () => {
|
|
52
47
|
try {
|
|
53
48
|
performance.mark("fungi.app-open.agar-ready-sent");
|
|
54
|
-
this.#appWindow.parent.postMessage({ protocolVersion: APP_PROTOCOL_VERSION, type: APP_READY_TYPE },
|
|
49
|
+
this.#appWindow.parent.postMessage({ protocolVersion: APP_PROTOCOL_VERSION, type: APP_READY_TYPE }, "*");
|
|
55
50
|
}
|
|
56
51
|
catch (cause) {
|
|
57
52
|
reportCatchFailure("connect.notify-host", cause);
|
|
@@ -82,11 +77,12 @@ class PendingAppConnection {
|
|
|
82
77
|
}
|
|
83
78
|
resolve(root);
|
|
84
79
|
}
|
|
85
|
-
#onMessage(event,
|
|
80
|
+
#onMessage(event, finish, onMessage) {
|
|
86
81
|
if (!isConnectMessage(event.data))
|
|
87
82
|
return;
|
|
88
83
|
performance.mark("fungi.app-open.agar-connect-received");
|
|
89
|
-
if (event.source !== this.#appWindow.parent ||
|
|
84
|
+
if (event.source !== this.#appWindow.parent ||
|
|
85
|
+
expectedOrigin(event.origin) === undefined) {
|
|
90
86
|
finish(identityError());
|
|
91
87
|
return;
|
|
92
88
|
}
|
|
@@ -176,12 +172,6 @@ function identityError() {
|
|
|
176
172
|
retryable: false,
|
|
177
173
|
});
|
|
178
174
|
}
|
|
179
|
-
function invalidOrigin() {
|
|
180
|
-
return new AppError("expectedHostOrigin must be an origin", {
|
|
181
|
-
code: "INVALID_ARGUMENT",
|
|
182
|
-
retryable: false,
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
175
|
const isConnectMessage = Schema.is(Schema.Struct({ type: Schema.Literal(APP_CONNECT_TYPE) }));
|
|
186
176
|
function notifyError(cause) {
|
|
187
177
|
return new AppError("Unable to notify the app host", {
|