summer-engine 3.6.0 → 3.8.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +4 -2
- package/.codex-plugin/plugin.json +5 -3
- package/.cursor-plugin/plugin.json +4 -2
- package/.factory-plugin/plugin.json +4 -2
- package/CHANGELOG.md +24 -0
- package/README.md +11 -5
- package/commands/summer.md +2 -1
- package/dist/cli/commands/login.d.ts +0 -10
- package/dist/cli/commands/login.js +2 -82
- package/dist/cli/index.js +0 -4
- package/dist/core/auth.d.ts +0 -8
- package/dist/core/auth.js +5 -35
- package/dist/core/capabilities/apps.d.ts +318 -0
- package/dist/core/capabilities/apps.js +340 -0
- package/dist/core/capabilities/enable-plugin.d.ts +27 -0
- package/dist/core/capabilities/enable-plugin.js +75 -0
- package/dist/core/capabilities/seeing/args.d.ts +2 -2
- package/dist/core/capabilities/seeing/seeing.d.ts +1 -1
- package/dist/core/capabilities/sfx-choices.d.ts +2 -15
- package/dist/core/capabilities/sfx-choices.js +26 -64
- package/dist/core/capabilities/tool-dispatch.js +51 -54
- package/dist/core/capability-skew.js +4 -0
- package/dist/core/config.d.ts +1 -7
- package/dist/core/config.js +5 -75
- package/dist/mcp/hosted-mount.js +13 -1
- package/dist/mcp/server.d.ts +2 -1
- package/dist/mcp/server.js +8 -5
- package/dist/mcp/tools/app-tools.d.ts +2 -0
- package/dist/mcp/tools/app-tools.js +64 -0
- package/dist/mcp/tools/creator-tools.js +0 -71
- package/dist/mcp/tools/generate-tools.js +6 -7
- package/gemini-extension.json +3 -1
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +24 -5
- package/library/skills/README.md +10 -2
- package/library/skills/publish-your-game/SKILL.md +2 -0
- package/library/skills/sound-effect/SKILL.md +2 -0
- package/library/skills/summer-apps/SKILL.md +152 -0
- package/library/skills/summer-apps/manifest.md +57 -0
- package/library/skills/summer-apps/minimal-app.md +183 -0
- package/library/skills/summer-apps/resource.yaml +36 -0
- package/library/skills/summer-apps/sdk-reference.md +135 -0
- package/library/skills/summer-apps-design/SKILL.md +197 -0
- package/library/skills/summer-apps-design/resource.yaml +23 -0
- package/library/skills/using-summer/SKILL.md +1 -0
- package/library/tools/app-call/resource.yaml +59 -0
- package/library/tools/app-create/resource.yaml +67 -0
- package/library/tools/app-data/resource.yaml +74 -0
- package/library/tools/app-dock/resource.yaml +59 -0
- package/library/tools/app-events/resource.yaml +68 -0
- package/library/tools/app-get/resource.yaml +46 -0
- package/library/tools/app-guide/resource.yaml +55 -0
- package/library/tools/app-list/resource.yaml +48 -0
- package/library/tools/app-page/resource.yaml +74 -0
- package/library/tools/app-publish/resource.yaml +90 -0
- package/library/tools/app-report-gap/resource.yaml +58 -0
- package/library/tools/app-screenshot/resource.yaml +85 -0
- package/library/tools/app-tools/resource.yaml +46 -0
- package/library/tools/app-update/resource.yaml +68 -0
- package/library/tools/enable-plugin/resource.yaml +53 -0
- package/library/tools/generate-sfx-choices/resource.yaml +2 -2
- package/package.json +1 -1
- package/registry/generated/aliases.json +15 -3
- package/registry/generated/counts.json +3 -3
- package/registry/generated/gemini-extension.json +3 -1
- package/registry/generated/index.json +759 -209
- package/registry/generated/marketplace.claude.json +2 -2
- package/registry/generated/plugin.claude.json +4 -2
- package/registry/generated/plugin.codex.json +5 -3
- package/registry/generated/plugin.cursor.json +4 -2
- package/registry/generated/plugin.factory.json +4 -2
- package/registry/generated/skills-index.md +10 -2
- package/registry/generated/skills-registry.json +27 -0
- package/registry/schemas/allowed-hosts.json +4 -2
- package/dist/cli/commands/publish.d.ts +0 -2
- package/dist/cli/commands/publish.js +0 -21
- package/dist/cli/commands/releases.d.ts +0 -2
- package/dist/cli/commands/releases.js +0 -20
- package/dist/core/capabilities/creator.d.ts +0 -65
- package/dist/core/capabilities/creator.js +0 -374
- package/library/tools/creator-config/resource.yaml +0 -63
- package/library/tools/creator-publish/resource.yaml +0 -74
- package/library/tools/creator-releases/resource.yaml +0 -53
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Summer Apps (summer_app_*): tools a game team uses inside Summer Studio,
|
|
4
|
+
* built by agents. Every cloud tool here calls the Summer web API under
|
|
5
|
+
* /api/mcp/apps with the signed-in Summer account; summer_app_dock asks the
|
|
6
|
+
* running editor to dock an app (engine ops OpenAppDock / CloseAppDock /
|
|
7
|
+
* ListAppDocks).
|
|
8
|
+
*
|
|
9
|
+
* One implementation for both faces: the MCP tools (src/mcp/tools/app-tools.ts)
|
|
10
|
+
* and `summer tool app-*` (tool-dispatch.ts) share the schemas and functions
|
|
11
|
+
* here and pass their own client surface.
|
|
12
|
+
*/
|
|
13
|
+
export declare const APPS_ENDPOINT = "/api/mcp/apps";
|
|
14
|
+
/** The server's long-poll ceiling for summer_app_events. */
|
|
15
|
+
export declare const MAX_APP_EVENTS_WAIT_SECONDS = 50;
|
|
16
|
+
export declare const APP_GUIDE_SECTIONS: readonly ["all", "overview", "manifest", "sdk", "kit", "layout", "design", "examples", "types"];
|
|
17
|
+
export declare const APP_DOCK_SLOTS: readonly ["left", "right", "bottom"];
|
|
18
|
+
export declare const appGuideArgsSchema: z.ZodObject<{
|
|
19
|
+
section: z.ZodDefault<z.ZodEnum<["all", "overview", "manifest", "sdk", "kit", "layout", "design", "examples", "types"]>>;
|
|
20
|
+
}, "strip", z.ZodTypeAny, {
|
|
21
|
+
section: "all" | "kit" | "layout" | "examples" | "manifest" | "overview" | "sdk" | "design" | "types";
|
|
22
|
+
}, {
|
|
23
|
+
section?: "all" | "kit" | "layout" | "examples" | "manifest" | "overview" | "sdk" | "design" | "types" | undefined;
|
|
24
|
+
}>;
|
|
25
|
+
export declare const appListArgsSchema: z.ZodObject<{
|
|
26
|
+
workspaceId: z.ZodOptional<z.ZodString>;
|
|
27
|
+
q: z.ZodOptional<z.ZodString>;
|
|
28
|
+
}, "strip", z.ZodTypeAny, {
|
|
29
|
+
workspaceId?: string | undefined;
|
|
30
|
+
q?: string | undefined;
|
|
31
|
+
}, {
|
|
32
|
+
workspaceId?: string | undefined;
|
|
33
|
+
q?: string | undefined;
|
|
34
|
+
}>;
|
|
35
|
+
export declare const appCreateArgsSchema: z.ZodObject<{
|
|
36
|
+
name: z.ZodString;
|
|
37
|
+
purpose: z.ZodOptional<z.ZodString>;
|
|
38
|
+
format: z.ZodDefault<z.ZodEnum<["code", "layout"]>>;
|
|
39
|
+
workspaceId: z.ZodOptional<z.ZodString>;
|
|
40
|
+
folder: z.ZodOptional<z.ZodString>;
|
|
41
|
+
}, "strip", z.ZodTypeAny, {
|
|
42
|
+
name: string;
|
|
43
|
+
format: "code" | "layout";
|
|
44
|
+
folder?: string | undefined;
|
|
45
|
+
workspaceId?: string | undefined;
|
|
46
|
+
purpose?: string | undefined;
|
|
47
|
+
}, {
|
|
48
|
+
name: string;
|
|
49
|
+
format?: "code" | "layout" | undefined;
|
|
50
|
+
folder?: string | undefined;
|
|
51
|
+
workspaceId?: string | undefined;
|
|
52
|
+
purpose?: string | undefined;
|
|
53
|
+
}>;
|
|
54
|
+
export declare const appPublishArgsSchema: z.ZodObject<{
|
|
55
|
+
appId: z.ZodString;
|
|
56
|
+
dir: z.ZodOptional<z.ZodString>;
|
|
57
|
+
files: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
58
|
+
path: z.ZodString;
|
|
59
|
+
content: z.ZodString;
|
|
60
|
+
encoding: z.ZodDefault<z.ZodEnum<["utf8", "base64"]>>;
|
|
61
|
+
}, "strip", z.ZodTypeAny, {
|
|
62
|
+
encoding: "utf8" | "base64";
|
|
63
|
+
path: string;
|
|
64
|
+
content: string;
|
|
65
|
+
}, {
|
|
66
|
+
path: string;
|
|
67
|
+
content: string;
|
|
68
|
+
encoding?: "utf8" | "base64" | undefined;
|
|
69
|
+
}>, "many">>;
|
|
70
|
+
layout: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
71
|
+
manifest: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
72
|
+
note: z.ZodOptional<z.ZodString>;
|
|
73
|
+
idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
74
|
+
}, "strip", z.ZodTypeAny, {
|
|
75
|
+
manifest: Record<string, unknown>;
|
|
76
|
+
appId: string;
|
|
77
|
+
note?: string | undefined;
|
|
78
|
+
files?: {
|
|
79
|
+
encoding: "utf8" | "base64";
|
|
80
|
+
path: string;
|
|
81
|
+
content: string;
|
|
82
|
+
}[] | undefined;
|
|
83
|
+
idempotencyKey?: string | undefined;
|
|
84
|
+
dir?: string | undefined;
|
|
85
|
+
layout?: Record<string, unknown> | undefined;
|
|
86
|
+
}, {
|
|
87
|
+
manifest: Record<string, unknown>;
|
|
88
|
+
appId: string;
|
|
89
|
+
note?: string | undefined;
|
|
90
|
+
files?: {
|
|
91
|
+
path: string;
|
|
92
|
+
content: string;
|
|
93
|
+
encoding?: "utf8" | "base64" | undefined;
|
|
94
|
+
}[] | undefined;
|
|
95
|
+
idempotencyKey?: string | undefined;
|
|
96
|
+
dir?: string | undefined;
|
|
97
|
+
layout?: Record<string, unknown> | undefined;
|
|
98
|
+
}>;
|
|
99
|
+
export declare const appGetArgsSchema: z.ZodObject<{
|
|
100
|
+
appId: z.ZodString;
|
|
101
|
+
}, "strip", z.ZodTypeAny, {
|
|
102
|
+
appId: string;
|
|
103
|
+
}, {
|
|
104
|
+
appId: string;
|
|
105
|
+
}>;
|
|
106
|
+
export declare const appEventsArgsSchema: z.ZodObject<{
|
|
107
|
+
appId: z.ZodString;
|
|
108
|
+
after: z.ZodDefault<z.ZodNumber>;
|
|
109
|
+
timeoutSeconds: z.ZodDefault<z.ZodNumber>;
|
|
110
|
+
kinds: z.ZodOptional<z.ZodArray<z.ZodEnum<["agent", "error", "feedback", "tool"]>, "many">>;
|
|
111
|
+
}, "strip", z.ZodTypeAny, {
|
|
112
|
+
timeoutSeconds: number;
|
|
113
|
+
after: number;
|
|
114
|
+
appId: string;
|
|
115
|
+
kinds?: ("error" | "agent" | "tool" | "feedback")[] | undefined;
|
|
116
|
+
}, {
|
|
117
|
+
appId: string;
|
|
118
|
+
kinds?: ("error" | "agent" | "tool" | "feedback")[] | undefined;
|
|
119
|
+
timeoutSeconds?: number | undefined;
|
|
120
|
+
after?: number | undefined;
|
|
121
|
+
}>;
|
|
122
|
+
export declare const appPageArgsSchema: z.ZodObject<{
|
|
123
|
+
appId: z.ZodString;
|
|
124
|
+
fill: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
125
|
+
field: z.ZodString;
|
|
126
|
+
value: z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]>;
|
|
127
|
+
}, "strip", z.ZodTypeAny, {
|
|
128
|
+
value: string | number | boolean;
|
|
129
|
+
field: string;
|
|
130
|
+
}, {
|
|
131
|
+
value: string | number | boolean;
|
|
132
|
+
field: string;
|
|
133
|
+
}>, "many">>;
|
|
134
|
+
press: z.ZodOptional<z.ZodString>;
|
|
135
|
+
}, "strip", z.ZodTypeAny, {
|
|
136
|
+
appId: string;
|
|
137
|
+
fill?: {
|
|
138
|
+
value: string | number | boolean;
|
|
139
|
+
field: string;
|
|
140
|
+
}[] | undefined;
|
|
141
|
+
press?: string | undefined;
|
|
142
|
+
}, {
|
|
143
|
+
appId: string;
|
|
144
|
+
fill?: {
|
|
145
|
+
value: string | number | boolean;
|
|
146
|
+
field: string;
|
|
147
|
+
}[] | undefined;
|
|
148
|
+
press?: string | undefined;
|
|
149
|
+
}>;
|
|
150
|
+
export declare const appUpdateArgsSchema: z.ZodObject<{
|
|
151
|
+
appId: z.ZodString;
|
|
152
|
+
name: z.ZodOptional<z.ZodString>;
|
|
153
|
+
purpose: z.ZodOptional<z.ZodString>;
|
|
154
|
+
status: z.ZodOptional<z.ZodEnum<["scratch", "kept", "archived"]>>;
|
|
155
|
+
folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
156
|
+
}, "strip", z.ZodTypeAny, {
|
|
157
|
+
appId: string;
|
|
158
|
+
status?: "scratch" | "kept" | "archived" | undefined;
|
|
159
|
+
name?: string | undefined;
|
|
160
|
+
folder?: string | null | undefined;
|
|
161
|
+
purpose?: string | undefined;
|
|
162
|
+
}, {
|
|
163
|
+
appId: string;
|
|
164
|
+
status?: "scratch" | "kept" | "archived" | undefined;
|
|
165
|
+
name?: string | undefined;
|
|
166
|
+
folder?: string | null | undefined;
|
|
167
|
+
purpose?: string | undefined;
|
|
168
|
+
}>;
|
|
169
|
+
export declare const appScreenshotArgsSchema: z.ZodObject<{
|
|
170
|
+
appId: z.ZodString;
|
|
171
|
+
version: z.ZodOptional<z.ZodNumber>;
|
|
172
|
+
width: z.ZodDefault<z.ZodNumber>;
|
|
173
|
+
height: z.ZodDefault<z.ZodNumber>;
|
|
174
|
+
theme: z.ZodDefault<z.ZodEnum<["light", "dark"]>>;
|
|
175
|
+
fullPage: z.ZodDefault<z.ZodBoolean>;
|
|
176
|
+
params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
177
|
+
out: z.ZodOptional<z.ZodString>;
|
|
178
|
+
}, "strip", z.ZodTypeAny, {
|
|
179
|
+
width: number;
|
|
180
|
+
height: number;
|
|
181
|
+
theme: "light" | "dark";
|
|
182
|
+
appId: string;
|
|
183
|
+
fullPage: boolean;
|
|
184
|
+
version?: number | undefined;
|
|
185
|
+
params?: Record<string, string> | undefined;
|
|
186
|
+
out?: string | undefined;
|
|
187
|
+
}, {
|
|
188
|
+
appId: string;
|
|
189
|
+
version?: number | undefined;
|
|
190
|
+
width?: number | undefined;
|
|
191
|
+
height?: number | undefined;
|
|
192
|
+
params?: Record<string, string> | undefined;
|
|
193
|
+
out?: string | undefined;
|
|
194
|
+
theme?: "light" | "dark" | undefined;
|
|
195
|
+
fullPage?: boolean | undefined;
|
|
196
|
+
}>;
|
|
197
|
+
export declare const appDataArgsSchema: z.ZodObject<{
|
|
198
|
+
appId: z.ZodString;
|
|
199
|
+
collection: z.ZodOptional<z.ZodString>;
|
|
200
|
+
after: z.ZodOptional<z.ZodString>;
|
|
201
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
202
|
+
ops: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">>;
|
|
203
|
+
as: z.ZodOptional<z.ZodEnum<["agent", "seed"]>>;
|
|
204
|
+
}, "strip", z.ZodTypeAny, {
|
|
205
|
+
limit: number;
|
|
206
|
+
appId: string;
|
|
207
|
+
ops?: Record<string, unknown>[] | undefined;
|
|
208
|
+
after?: string | undefined;
|
|
209
|
+
as?: "seed" | "agent" | undefined;
|
|
210
|
+
collection?: string | undefined;
|
|
211
|
+
}, {
|
|
212
|
+
appId: string;
|
|
213
|
+
ops?: Record<string, unknown>[] | undefined;
|
|
214
|
+
limit?: number | undefined;
|
|
215
|
+
after?: string | undefined;
|
|
216
|
+
as?: "seed" | "agent" | undefined;
|
|
217
|
+
collection?: string | undefined;
|
|
218
|
+
}>;
|
|
219
|
+
export declare const appToolsArgsSchema: z.ZodObject<{
|
|
220
|
+
appId: z.ZodString;
|
|
221
|
+
}, "strip", z.ZodTypeAny, {
|
|
222
|
+
appId: string;
|
|
223
|
+
}, {
|
|
224
|
+
appId: string;
|
|
225
|
+
}>;
|
|
226
|
+
export declare const appCallArgsSchema: z.ZodObject<{
|
|
227
|
+
appId: z.ZodString;
|
|
228
|
+
tool: z.ZodString;
|
|
229
|
+
input: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
230
|
+
}, "strip", z.ZodTypeAny, {
|
|
231
|
+
input: Record<string, unknown>;
|
|
232
|
+
tool: string;
|
|
233
|
+
appId: string;
|
|
234
|
+
}, {
|
|
235
|
+
tool: string;
|
|
236
|
+
appId: string;
|
|
237
|
+
input?: Record<string, unknown> | undefined;
|
|
238
|
+
}>;
|
|
239
|
+
export declare const appReportGapArgsSchema: z.ZodObject<{
|
|
240
|
+
capability: z.ZodString;
|
|
241
|
+
detail: z.ZodOptional<z.ZodString>;
|
|
242
|
+
method: z.ZodOptional<z.ZodString>;
|
|
243
|
+
appId: z.ZodOptional<z.ZodString>;
|
|
244
|
+
}, "strip", z.ZodTypeAny, {
|
|
245
|
+
capability: string;
|
|
246
|
+
method?: string | undefined;
|
|
247
|
+
detail?: string | undefined;
|
|
248
|
+
appId?: string | undefined;
|
|
249
|
+
}, {
|
|
250
|
+
capability: string;
|
|
251
|
+
method?: string | undefined;
|
|
252
|
+
detail?: string | undefined;
|
|
253
|
+
appId?: string | undefined;
|
|
254
|
+
}>;
|
|
255
|
+
export declare const appDockArgsSchema: z.ZodObject<{
|
|
256
|
+
action: z.ZodDefault<z.ZodEnum<["open", "close", "list"]>>;
|
|
257
|
+
appId: z.ZodOptional<z.ZodString>;
|
|
258
|
+
slot: z.ZodDefault<z.ZodEnum<["left", "right", "bottom"]>>;
|
|
259
|
+
}, "strip", z.ZodTypeAny, {
|
|
260
|
+
action: "close" | "open" | "list";
|
|
261
|
+
slot: "left" | "right" | "bottom";
|
|
262
|
+
appId?: string | undefined;
|
|
263
|
+
}, {
|
|
264
|
+
action?: "close" | "open" | "list" | undefined;
|
|
265
|
+
appId?: string | undefined;
|
|
266
|
+
slot?: "left" | "right" | "bottom" | undefined;
|
|
267
|
+
}>;
|
|
268
|
+
export interface AppsReply {
|
|
269
|
+
ok: boolean;
|
|
270
|
+
status: number;
|
|
271
|
+
data: Record<string, unknown>;
|
|
272
|
+
}
|
|
273
|
+
export interface AppsDeps {
|
|
274
|
+
request(method: "GET" | "POST", endpoint: string, init: {
|
|
275
|
+
query?: URLSearchParams;
|
|
276
|
+
body?: unknown;
|
|
277
|
+
timeoutMs: number;
|
|
278
|
+
}): Promise<AppsReply>;
|
|
279
|
+
readDir?: (dir: string) => Promise<Array<{
|
|
280
|
+
path: string;
|
|
281
|
+
content: string;
|
|
282
|
+
encoding: "utf8" | "base64";
|
|
283
|
+
}>>;
|
|
284
|
+
}
|
|
285
|
+
/** Fetch with the Summer account token; errors come back as replies, never throws. */
|
|
286
|
+
export declare function appsGateway(surface: "mcp" | "cli", tool: string): AppsDeps["request"];
|
|
287
|
+
export declare function appGuide(args: z.output<typeof appGuideArgsSchema>, deps: AppsDeps): Promise<Record<string, unknown>>;
|
|
288
|
+
export declare function appList(args: z.output<typeof appListArgsSchema>, deps: AppsDeps): Promise<Record<string, unknown>>;
|
|
289
|
+
export declare function appCreate(args: z.output<typeof appCreateArgsSchema>, deps: AppsDeps): Promise<Record<string, unknown>>;
|
|
290
|
+
/** Every file under dir (dotfiles, node_modules and .d.ts type files skipped), as publish files. */
|
|
291
|
+
export declare function readAppDir(dir: string): Promise<Array<{
|
|
292
|
+
path: string;
|
|
293
|
+
content: string;
|
|
294
|
+
encoding: "utf8" | "base64";
|
|
295
|
+
}>>;
|
|
296
|
+
export declare function appPublish(args: z.output<typeof appPublishArgsSchema>, deps: AppsDeps): Promise<Record<string, unknown>>;
|
|
297
|
+
export declare function appGet(args: z.output<typeof appGetArgsSchema>, deps: AppsDeps): Promise<Record<string, unknown>>;
|
|
298
|
+
export declare function appEvents(args: z.output<typeof appEventsArgsSchema>, deps: AppsDeps): Promise<Record<string, unknown>>;
|
|
299
|
+
export declare function appPage(args: z.output<typeof appPageArgsSchema>, deps: AppsDeps): Promise<Record<string, unknown>>;
|
|
300
|
+
export declare function appUpdate(args: z.output<typeof appUpdateArgsSchema>, deps: AppsDeps): Promise<Record<string, unknown>>;
|
|
301
|
+
/**
|
|
302
|
+
* summer_app_screenshot: the server renders the app headless and read-only as the
|
|
303
|
+
* person and answers the PNG (base64) with its console, errors and declared page.
|
|
304
|
+
* The image moves to `image`; `out` also writes it to disk.
|
|
305
|
+
*/
|
|
306
|
+
export declare function appScreenshot(args: z.output<typeof appScreenshotArgsSchema>, deps: AppsDeps & {
|
|
307
|
+
writeFile?: (file: string, data: Buffer) => Promise<void>;
|
|
308
|
+
}): Promise<Record<string, unknown>>;
|
|
309
|
+
export declare function appData(args: z.output<typeof appDataArgsSchema>, deps: AppsDeps): Promise<Record<string, unknown>>;
|
|
310
|
+
export declare function appTools(args: z.output<typeof appToolsArgsSchema>, deps: AppsDeps): Promise<Record<string, unknown>>;
|
|
311
|
+
export declare function appCall(args: z.output<typeof appCallArgsSchema>, deps: AppsDeps): Promise<Record<string, unknown>>;
|
|
312
|
+
export declare function appReportGap(args: z.output<typeof appReportGapArgsSchema>, deps: AppsDeps): Promise<Record<string, unknown>>;
|
|
313
|
+
export declare const APP_DOCK_FALLBACK = "This engine build cannot dock apps yet. Open the app in the Summer Studio tab instead (summer_studio_open with /studio/apps/<appId>), or update Summer Engine.";
|
|
314
|
+
/** The engine op for one dock action. */
|
|
315
|
+
export declare function appDockOp(args: z.output<typeof appDockArgsSchema>): {
|
|
316
|
+
op: string;
|
|
317
|
+
[key: string]: unknown;
|
|
318
|
+
};
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import { readdir, readFile, lstat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { getAuthToken } from "../auth.js";
|
|
5
|
+
import { resolveGatewayUrl } from "../config.js";
|
|
6
|
+
import { TOOLKIT_VERSION } from "../version.js";
|
|
7
|
+
/**
|
|
8
|
+
* Summer Apps (summer_app_*): tools a game team uses inside Summer Studio,
|
|
9
|
+
* built by agents. Every cloud tool here calls the Summer web API under
|
|
10
|
+
* /api/mcp/apps with the signed-in Summer account; summer_app_dock asks the
|
|
11
|
+
* running editor to dock an app (engine ops OpenAppDock / CloseAppDock /
|
|
12
|
+
* ListAppDocks).
|
|
13
|
+
*
|
|
14
|
+
* One implementation for both faces: the MCP tools (src/mcp/tools/app-tools.ts)
|
|
15
|
+
* and `summer tool app-*` (tool-dispatch.ts) share the schemas and functions
|
|
16
|
+
* here and pass their own client surface.
|
|
17
|
+
*/
|
|
18
|
+
export const APPS_ENDPOINT = "/api/mcp/apps";
|
|
19
|
+
/** The server's long-poll ceiling for summer_app_events. */
|
|
20
|
+
export const MAX_APP_EVENTS_WAIT_SECONDS = 50;
|
|
21
|
+
export const APP_GUIDE_SECTIONS = ["all", "overview", "manifest", "sdk", "kit", "layout", "design", "examples", "types"];
|
|
22
|
+
export const APP_DOCK_SLOTS = ["left", "right", "bottom"];
|
|
23
|
+
/** Bundle limits the server enforces (checked here first for a clear local error). */
|
|
24
|
+
const MAX_FILES = 300;
|
|
25
|
+
const MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
26
|
+
const MAX_BUNDLE_BYTES = 10 * 1024 * 1024;
|
|
27
|
+
const TEXT_EXTENSIONS = new Set([".html", ".js", ".mjs", ".css", ".json", ".txt", ".md", ".svg", ".gltf"]);
|
|
28
|
+
const appId = z.string().uuid().describe("The app id from summer_app_create or summer_app_list");
|
|
29
|
+
const jsonObject = z.record(z.unknown());
|
|
30
|
+
export const appGuideArgsSchema = z.object({
|
|
31
|
+
section: z
|
|
32
|
+
.enum(APP_GUIDE_SECTIONS)
|
|
33
|
+
.default("all")
|
|
34
|
+
.describe("One part of the guide: overview, manifest, sdk, kit, layout, design, examples, or types (the SDK's exact TypeScript types, not in all). Default all"),
|
|
35
|
+
});
|
|
36
|
+
export const appListArgsSchema = z.object({
|
|
37
|
+
workspaceId: z.string().uuid().optional().describe("Only this workspace (team). Default: every workspace the person is in"),
|
|
38
|
+
q: z.string().max(200).optional().describe("Search app names and purposes"),
|
|
39
|
+
});
|
|
40
|
+
export const appCreateArgsSchema = z.object({
|
|
41
|
+
name: z.string().min(1).max(80),
|
|
42
|
+
purpose: z.string().max(280).optional().describe("One line: what the tool does and where its data goes"),
|
|
43
|
+
format: z.enum(["code", "layout"]).default("code").describe("code: index.html + JS/CSS (default). layout: a JSON layout of kit nodes, for quick decision UIs"),
|
|
44
|
+
workspaceId: z.string().uuid().optional().describe("A workspace id. Default: the person's team (the one their Studio tab is on, else their latest team, else personal); the result's workspace says which"),
|
|
45
|
+
folder: z.string().min(1).max(80).optional(),
|
|
46
|
+
});
|
|
47
|
+
export const appPublishArgsSchema = z.object({
|
|
48
|
+
appId,
|
|
49
|
+
dir: z
|
|
50
|
+
.string()
|
|
51
|
+
.min(1)
|
|
52
|
+
.optional()
|
|
53
|
+
.describe("A local folder holding the app (index.html at its root). Every file under it is published; dotfiles, node_modules and .d.ts files are skipped"),
|
|
54
|
+
files: z
|
|
55
|
+
.array(z.object({
|
|
56
|
+
path: z.string().min(1).max(200),
|
|
57
|
+
content: z.string(),
|
|
58
|
+
encoding: z.enum(["utf8", "base64"]).default("utf8"),
|
|
59
|
+
}))
|
|
60
|
+
.max(MAX_FILES)
|
|
61
|
+
.optional()
|
|
62
|
+
.describe("The files instead of dir: [{path, content, encoding}]"),
|
|
63
|
+
layout: jsonObject.optional().describe("Layout apps: { version: 1, title, root }"),
|
|
64
|
+
manifest: jsonObject.describe("The manifest {name, purpose, permissions, engineFiles, agentEvents, agentTools} (summer_app_guide section manifest)"),
|
|
65
|
+
note: z.string().max(500).optional().describe("What changed, shown in the version list"),
|
|
66
|
+
idempotencyKey: z.string().min(8).max(128).optional(),
|
|
67
|
+
});
|
|
68
|
+
export const appGetArgsSchema = z.object({ appId });
|
|
69
|
+
export const appEventsArgsSchema = z.object({
|
|
70
|
+
appId,
|
|
71
|
+
after: z.number().int().min(0).default(0).describe("Return events after this cursor (0: from the start). Pass the returned cursor next time"),
|
|
72
|
+
timeoutSeconds: z.number().int().min(0).max(MAX_APP_EVENTS_WAIT_SECONDS).default(25).describe("Wait up to this long (max 50) for the first new event"),
|
|
73
|
+
kinds: z.array(z.enum(["agent", "error", "feedback", "tool"])).max(4).optional().describe("Default: agent, error, feedback"),
|
|
74
|
+
});
|
|
75
|
+
export const appPageArgsSchema = z.object({
|
|
76
|
+
appId,
|
|
77
|
+
fill: z
|
|
78
|
+
.array(z.object({ field: z.string().min(1).max(200), value: z.union([z.string().max(10_000), z.number(), z.boolean()]) }))
|
|
79
|
+
.max(50)
|
|
80
|
+
.optional()
|
|
81
|
+
.describe("Values to set: [{field, value}] with field ids from a read"),
|
|
82
|
+
press: z.string().min(1).max(200).optional().describe("An action id to press after filling"),
|
|
83
|
+
});
|
|
84
|
+
export const appUpdateArgsSchema = z.object({
|
|
85
|
+
appId,
|
|
86
|
+
name: z.string().min(1).max(80).optional(),
|
|
87
|
+
purpose: z.string().max(280).optional(),
|
|
88
|
+
status: z.enum(["scratch", "kept", "archived"]).optional().describe("kept: the team keeps it. archived: hidden from the app list (restore with scratch or kept)"),
|
|
89
|
+
folder: z.string().min(1).max(80).nullable().optional().describe("null takes it out of its folder"),
|
|
90
|
+
});
|
|
91
|
+
export const appScreenshotArgsSchema = z.object({
|
|
92
|
+
appId,
|
|
93
|
+
version: z.number().int().positive().optional().describe("A published version. Default: the current one"),
|
|
94
|
+
width: z.number().int().min(320).max(1920).default(1280).describe("Viewport width in px: 390 for a phone"),
|
|
95
|
+
height: z.number().int().min(320).max(1600).default(800),
|
|
96
|
+
theme: z.enum(["light", "dark"]).default("light"),
|
|
97
|
+
fullPage: z.boolean().default(false).describe("Capture the whole scroll height (up to 4000 px)"),
|
|
98
|
+
params: z.record(z.string().max(500)).optional().describe("Launch params, as in /studio/apps/<id>?key=value"),
|
|
99
|
+
out: z.string().min(1).optional().describe("Also save the PNG to this local path"),
|
|
100
|
+
});
|
|
101
|
+
export const appDataArgsSchema = z.object({
|
|
102
|
+
appId,
|
|
103
|
+
collection: z.string().max(48).optional().describe("Read: one collection. Default: all"),
|
|
104
|
+
after: z.string().max(200).optional().describe("Read: the next cursor from the previous page"),
|
|
105
|
+
limit: z.number().int().min(1).max(500).default(200),
|
|
106
|
+
ops: z
|
|
107
|
+
.array(jsonObject)
|
|
108
|
+
.min(1)
|
|
109
|
+
.max(100)
|
|
110
|
+
.optional()
|
|
111
|
+
.describe("Write: [{op: put, collection, recordId, data}, {op: patch, collection, recordId, patch}, {op: remove, collection, recordId}]; ifRevision on an op applies it only at that record revision (0: must not exist)"),
|
|
112
|
+
as: z.enum(["agent", "seed"]).optional().describe("Write: seed for starter data that belongs to the team (no author). Default: agent"),
|
|
113
|
+
});
|
|
114
|
+
export const appToolsArgsSchema = z.object({ appId });
|
|
115
|
+
export const appCallArgsSchema = z.object({
|
|
116
|
+
appId,
|
|
117
|
+
tool: z.string().min(1).max(64).describe("The agent tool name from summer_app_tools"),
|
|
118
|
+
input: jsonObject.default({}).describe("Input matching the tool's input schema"),
|
|
119
|
+
});
|
|
120
|
+
export const appReportGapArgsSchema = z.object({
|
|
121
|
+
capability: z.string().min(1).max(200).describe('What the app needed, e.g. "read Steam reviews"'),
|
|
122
|
+
detail: z.string().max(2000).optional(),
|
|
123
|
+
method: z.string().max(100).optional().describe("The SDK method name you wished existed"),
|
|
124
|
+
appId: appId.optional(),
|
|
125
|
+
});
|
|
126
|
+
export const appDockArgsSchema = z.object({
|
|
127
|
+
action: z.enum(["open", "close", "list"]).default("open"),
|
|
128
|
+
appId: appId.optional().describe("open, close: the app"),
|
|
129
|
+
slot: z.enum(APP_DOCK_SLOTS).default("right").describe("open: left, right or bottom"),
|
|
130
|
+
});
|
|
131
|
+
/** Fetch with the Summer account token; errors come back as replies, never throws. */
|
|
132
|
+
export function appsGateway(surface, tool) {
|
|
133
|
+
return async (method, endpoint, { query, body, timeoutMs }) => {
|
|
134
|
+
const token = await getAuthToken();
|
|
135
|
+
if (!token)
|
|
136
|
+
return {
|
|
137
|
+
ok: false,
|
|
138
|
+
status: 401,
|
|
139
|
+
data: { error: { code: "unauthorized", message: "Not signed in. Run: npx -y summer-engine@latest login" } },
|
|
140
|
+
};
|
|
141
|
+
const gateway = await resolveGatewayUrl();
|
|
142
|
+
const suffix = query?.toString() ? `?${query.toString()}` : "";
|
|
143
|
+
let response;
|
|
144
|
+
try {
|
|
145
|
+
response = await fetch(`${gateway}${endpoint}${suffix}`, {
|
|
146
|
+
method,
|
|
147
|
+
headers: {
|
|
148
|
+
Authorization: `Bearer ${token}`,
|
|
149
|
+
"X-Summer-Client": "summer-cli",
|
|
150
|
+
"X-Summer-Client-Version": TOOLKIT_VERSION,
|
|
151
|
+
"X-Summer-Client-Surface": surface,
|
|
152
|
+
"X-Summer-MCP-Endpoint": endpoint,
|
|
153
|
+
"X-Summer-MCP-Tool": tool,
|
|
154
|
+
...(body !== undefined ? { "Content-Type": "application/json" } : {}),
|
|
155
|
+
},
|
|
156
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
157
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
return { ok: false, status: 0, data: { error: { code: "unavailable", message: `Request failed: ${error instanceof Error ? error.message : String(error)}` } } };
|
|
162
|
+
}
|
|
163
|
+
const text = await response.text().catch(() => "");
|
|
164
|
+
let data = {};
|
|
165
|
+
try {
|
|
166
|
+
data = text ? JSON.parse(text) : {};
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
data = { message: text.slice(0, 1000) };
|
|
170
|
+
}
|
|
171
|
+
return { ok: response.ok, status: response.status, data };
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/** The server's `{error: {code, message}}` as one flat result an agent can act on. */
|
|
175
|
+
function failure(reply) {
|
|
176
|
+
const error = reply.data.error;
|
|
177
|
+
const record = error && typeof error === "object" ? error : {};
|
|
178
|
+
const code = typeof record.code === "string" ? record.code : reply.status === 404 ? "not_found" : "unavailable";
|
|
179
|
+
const message = typeof record.message === "string" ? record.message : typeof reply.data.message === "string" ? reply.data.message : `Request failed (${reply.status})`;
|
|
180
|
+
const hints = {
|
|
181
|
+
unauthorized: "Run: npx -y summer-engine@latest login",
|
|
182
|
+
not_found: "Summer Apps is not available for this account yet, or the app id is wrong: summer_app_list shows the person's apps.",
|
|
183
|
+
app_not_open: "Use summer_app_data, or ask the person to open the app in Summer Studio.",
|
|
184
|
+
invalid: "Fix what the message names and try again (summer_app_guide has the rules).",
|
|
185
|
+
too_large: "Limits: 10 MB per app, 300 files, 5 MB per file.",
|
|
186
|
+
rate_limited: "Wait a minute and try again.",
|
|
187
|
+
};
|
|
188
|
+
const { error: _error, message: _message, ...rest } = reply.data;
|
|
189
|
+
return { ok: false, code, error: message, status: reply.status, ...(hints[code] ? { hint: hints[code] } : {}), ...rest, ...(record.details ? { details: record.details } : {}) };
|
|
190
|
+
}
|
|
191
|
+
const done = (reply) => (reply.ok ? { ok: true, ...reply.data } : failure(reply));
|
|
192
|
+
const params = (values) => {
|
|
193
|
+
const query = new URLSearchParams();
|
|
194
|
+
for (const [key, value] of Object.entries(values))
|
|
195
|
+
if (value !== undefined && value !== "")
|
|
196
|
+
query.set(key, value);
|
|
197
|
+
return query;
|
|
198
|
+
};
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
// Tools
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
export async function appGuide(args, deps) {
|
|
203
|
+
return done(await deps.request("GET", `${APPS_ENDPOINT}/guide`, { query: params({ section: args.section }), timeoutMs: 30_000 }));
|
|
204
|
+
}
|
|
205
|
+
export async function appList(args, deps) {
|
|
206
|
+
return done(await deps.request("GET", APPS_ENDPOINT, { query: params({ workspaceId: args.workspaceId, q: args.q }), timeoutMs: 30_000 }));
|
|
207
|
+
}
|
|
208
|
+
export async function appCreate(args, deps) {
|
|
209
|
+
const reply = await deps.request("POST", APPS_ENDPOINT, { body: args, timeoutMs: 30_000 });
|
|
210
|
+
if (!reply.ok)
|
|
211
|
+
return failure(reply);
|
|
212
|
+
const studio = reply.data.studio;
|
|
213
|
+
const workspace = reply.data.workspace;
|
|
214
|
+
const where = workspace?.name ? `It is in ${workspace.kind === "personal" ? "the person's personal workspace" : `the team ${workspace.name}`}: tell them. ` : "";
|
|
215
|
+
return {
|
|
216
|
+
ok: true,
|
|
217
|
+
...reply.data,
|
|
218
|
+
next: `${where}${studio?.queued ? "The person's Summer Studio tab is opening the app. " : ""}Write the app (summer_app_guide first), then summer_app_publish with appId and the files or dir.`,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/** Every file under dir (dotfiles, node_modules and .d.ts type files skipped), as publish files. */
|
|
222
|
+
export async function readAppDir(dir) {
|
|
223
|
+
const root = path.resolve(dir);
|
|
224
|
+
const files = [];
|
|
225
|
+
let total = 0;
|
|
226
|
+
const walk = async (current) => {
|
|
227
|
+
for (const entry of (await readdir(current, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
228
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name.endsWith(".d.ts"))
|
|
229
|
+
continue;
|
|
230
|
+
const full = path.join(current, entry.name);
|
|
231
|
+
if (entry.isDirectory()) {
|
|
232
|
+
await walk(full);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (!entry.isFile() || (await lstat(full)).isSymbolicLink())
|
|
236
|
+
continue;
|
|
237
|
+
const bytes = await readFile(full);
|
|
238
|
+
if (bytes.length > MAX_FILE_BYTES)
|
|
239
|
+
throw new Error(`${path.relative(root, full)} is over 5 MB`);
|
|
240
|
+
total += bytes.length;
|
|
241
|
+
if (total > MAX_BUNDLE_BYTES)
|
|
242
|
+
throw new Error(`${dir} is over the 10 MB app limit`);
|
|
243
|
+
const relative = path.relative(root, full).split(path.sep).join("/");
|
|
244
|
+
const text = TEXT_EXTENSIONS.has(path.extname(entry.name).toLowerCase());
|
|
245
|
+
files.push({ path: relative, content: bytes.toString(text ? "utf8" : "base64"), encoding: text ? "utf8" : "base64" });
|
|
246
|
+
if (files.length > MAX_FILES)
|
|
247
|
+
throw new Error(`${dir} has more than ${MAX_FILES} files`);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
await walk(root);
|
|
251
|
+
return files;
|
|
252
|
+
}
|
|
253
|
+
export async function appPublish(args, deps) {
|
|
254
|
+
const sources = [args.dir, args.files, args.layout].filter((value) => value !== undefined).length;
|
|
255
|
+
if (sources !== 1)
|
|
256
|
+
return { ok: false, code: "invalid", error: "Send exactly one of dir, files or layout." };
|
|
257
|
+
let files = args.files;
|
|
258
|
+
if (args.dir) {
|
|
259
|
+
try {
|
|
260
|
+
files = await (deps.readDir ?? readAppDir)(args.dir);
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
return { ok: false, code: "invalid", error: error instanceof Error ? error.message : String(error) };
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const body = { ...(files ? { files } : { layout: args.layout }), manifest: args.manifest, ...(args.note ? { note: args.note } : {}), ...(args.idempotencyKey ? { idempotencyKey: args.idempotencyKey } : {}) };
|
|
267
|
+
const reply = await deps.request("POST", `${APPS_ENDPOINT}/${args.appId}/publish`, { body, timeoutMs: 120_000 });
|
|
268
|
+
if (!reply.ok)
|
|
269
|
+
return failure(reply);
|
|
270
|
+
const version = reply.data.version;
|
|
271
|
+
return {
|
|
272
|
+
ok: true,
|
|
273
|
+
...reply.data,
|
|
274
|
+
next: `Version ${version?.version ?? "?"} is live in the person's Studio. Wait for what the app sends with summer_app_events, or publish again to change it.`,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
export async function appGet(args, deps) {
|
|
278
|
+
return done(await deps.request("GET", `${APPS_ENDPOINT}/${args.appId}`, { timeoutMs: 60_000 }));
|
|
279
|
+
}
|
|
280
|
+
export async function appEvents(args, deps) {
|
|
281
|
+
return done(await deps.request("GET", `${APPS_ENDPOINT}/${args.appId}/events`, {
|
|
282
|
+
query: params({ after: String(args.after), timeoutMs: String(args.timeoutSeconds * 1000), kinds: args.kinds?.join(",") }),
|
|
283
|
+
timeoutMs: (args.timeoutSeconds + 20) * 1000,
|
|
284
|
+
}));
|
|
285
|
+
}
|
|
286
|
+
export async function appPage(args, deps) {
|
|
287
|
+
const { appId: id, ...body } = args;
|
|
288
|
+
return done(await deps.request("POST", `${APPS_ENDPOINT}/${id}/page`, { body, timeoutMs: 60_000 }));
|
|
289
|
+
}
|
|
290
|
+
export async function appUpdate(args, deps) {
|
|
291
|
+
const { appId: id, ...body } = args;
|
|
292
|
+
return done(await deps.request("POST", `${APPS_ENDPOINT}/${id}/update`, { body, timeoutMs: 30_000 }));
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* summer_app_screenshot: the server renders the app headless and read-only as the
|
|
296
|
+
* person and answers the PNG (base64) with its console, errors and declared page.
|
|
297
|
+
* The image moves to `image`; `out` also writes it to disk.
|
|
298
|
+
*/
|
|
299
|
+
export async function appScreenshot(args, deps) {
|
|
300
|
+
const { appId: id, out, ...body } = args;
|
|
301
|
+
const result = done(await deps.request("POST", `${APPS_ENDPOINT}/${id}/screenshot`, { body, timeoutMs: 90_000 }));
|
|
302
|
+
if (result.ok !== true)
|
|
303
|
+
return result;
|
|
304
|
+
const { image, ...report } = result;
|
|
305
|
+
if (!image)
|
|
306
|
+
return { ...report, ok: false, code: "invalid", error: "The server answered without an image" };
|
|
307
|
+
if (out)
|
|
308
|
+
await (deps.writeFile ?? ((file, data) => import("node:fs/promises").then((fs) => fs.writeFile(file, data))))(path.resolve(out), Buffer.from(image.base64, "base64"));
|
|
309
|
+
return { ...report, image, ...(out ? { savedTo: path.resolve(out) } : {}) };
|
|
310
|
+
}
|
|
311
|
+
export async function appData(args, deps) {
|
|
312
|
+
if (args.ops)
|
|
313
|
+
return done(await deps.request("POST", `${APPS_ENDPOINT}/${args.appId}/data`, { body: { ops: args.ops, ...(args.as ? { as: args.as } : {}) }, timeoutMs: 30_000 }));
|
|
314
|
+
return done(await deps.request("GET", `${APPS_ENDPOINT}/${args.appId}/data`, {
|
|
315
|
+
query: params({ collection: args.collection, after: args.after, limit: String(args.limit) }),
|
|
316
|
+
timeoutMs: 30_000,
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
319
|
+
export async function appTools(args, deps) {
|
|
320
|
+
return done(await deps.request("GET", `${APPS_ENDPOINT}/${args.appId}/tools`, { timeoutMs: 30_000 }));
|
|
321
|
+
}
|
|
322
|
+
export async function appCall(args, deps) {
|
|
323
|
+
const { appId: id, ...body } = args;
|
|
324
|
+
return done(await deps.request("POST", `${APPS_ENDPOINT}/${id}/call`, { body, timeoutMs: 90_000 }));
|
|
325
|
+
}
|
|
326
|
+
export async function appReportGap(args, deps) {
|
|
327
|
+
return done(await deps.request("POST", `${APPS_ENDPOINT}/report-gap`, { body: args, timeoutMs: 30_000 }));
|
|
328
|
+
}
|
|
329
|
+
// ---------------------------------------------------------------------------
|
|
330
|
+
// summer_app_dock (engine)
|
|
331
|
+
// ---------------------------------------------------------------------------
|
|
332
|
+
export const APP_DOCK_FALLBACK = "This engine build cannot dock apps yet. Open the app in the Summer Studio tab instead (summer_studio_open with /studio/apps/<appId>), or update Summer Engine.";
|
|
333
|
+
/** The engine op for one dock action. */
|
|
334
|
+
export function appDockOp(args) {
|
|
335
|
+
if (args.action === "list")
|
|
336
|
+
return { op: "ListAppDocks" };
|
|
337
|
+
if (!args.appId)
|
|
338
|
+
throw new Error(`${args.action} needs appId`);
|
|
339
|
+
return args.action === "open" ? { op: "OpenAppDock", appId: args.appId, slot: args.slot } : { op: "CloseAppDock", appId: args.appId };
|
|
340
|
+
}
|