summer-engine 4.6.0 → 4.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 +1 -1
- package/.claude-plugin/plugin.json +3 -2
- package/.codex-plugin/plugin.json +5 -4
- package/.cursor-plugin/plugin.json +4 -3
- package/.factory-plugin/plugin.json +4 -3
- package/CHANGELOG.md +37 -0
- package/README.md +3 -3
- package/commands/start.md +1 -1
- package/dist/cli/commands/api.d.ts +43 -0
- package/dist/cli/commands/api.js +191 -0
- package/dist/cli/commands/login.d.ts +2 -5
- package/dist/cli/commands/login.js +10 -81
- package/dist/cli/commands/token.d.ts +17 -0
- package/dist/cli/commands/token.js +25 -0
- package/dist/cli/index.js +4 -0
- package/dist/core/api-client.d.ts +5 -1
- package/dist/core/api-client.js +21 -0
- package/dist/core/capabilities/doctor.d.ts +1 -1
- package/dist/core/capabilities/doctor.js +1 -1
- package/dist/core/capabilities/game-task-plan.d.ts +2 -2
- package/dist/core/capabilities/navigation/open.d.ts +3 -0
- package/dist/core/capabilities/navigation/open.js +53 -0
- package/dist/core/capabilities/projects-local.js +2 -1
- package/dist/core/capabilities/runtime-control.d.ts +2 -2
- package/dist/core/capabilities/seeing/args.d.ts +4 -4
- package/dist/core/capabilities/tool-dispatch.js +37 -0
- package/dist/core/capabilities/ui-control.d.ts +2 -2
- package/dist/core/capabilities/view-set.d.ts +11 -0
- package/dist/core/capabilities/view-set.js +15 -0
- package/dist/core/capability-skew.d.ts +8 -0
- package/dist/core/capability-skew.js +18 -0
- package/dist/core/cli-session.d.ts +25 -0
- package/dist/core/cli-session.js +97 -0
- package/dist/core/engine-self-start.d.ts +20 -0
- package/dist/core/engine-self-start.js +108 -5
- package/dist/core/oauth.d.ts +15 -2
- package/dist/core/oauth.js +26 -3
- package/dist/core/ops/areas.d.ts +1 -1
- package/dist/core/ops/areas.js +5 -3
- package/dist/core/ops/engine-answer.js +5 -2
- package/dist/core/ops/engine.js +6 -0
- package/dist/core/ops/save-files.d.ts +2 -0
- package/dist/core/ops/save-files.js +13 -0
- package/dist/core/ops/studio-page.d.ts +22 -0
- package/dist/core/ops/studio-page.js +63 -0
- package/dist/core/sign-in-link.d.ts +38 -0
- package/dist/core/sign-in-link.js +106 -0
- package/dist/core/store.js +19 -1
- package/dist/core/summer-api.d.ts +114 -0
- package/dist/core/summer-api.js +281 -0
- package/dist/mcp/ops-surface.d.ts +3 -0
- package/dist/mcp/ops-surface.js +6 -0
- package/dist/mcp/server.js +16 -4
- package/dist/mcp/tool-results.d.ts +9 -0
- package/dist/mcp/tool-results.js +64 -0
- package/dist/mcp/tool-surface.js +7 -1
- package/dist/mcp/tools/api-tools.d.ts +50 -0
- package/dist/mcp/tools/api-tools.js +65 -0
- package/dist/mcp/tools/local-project-tools.d.ts +14 -0
- package/dist/mcp/tools/local-project-tools.js +13 -2
- package/dist/mcp/tools/navigation-tools.js +1 -0
- package/dist/mcp/tools/scene-tools.js +7 -0
- package/gemini-extension.json +3 -2
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +15 -2
- package/library/references/mcp-tools-reference/resource.yaml +1 -1
- package/library/skills/README.md +6 -5
- package/library/skills/design-npc/SKILL.md +1 -1
- package/library/skills/design-npc/resource.yaml +2 -1
- package/library/skills/navigate-summer/SKILL.md +23 -2
- package/library/skills/navigate-summer/resource.yaml +5 -3
- package/library/skills/summer-api/SKILL.md +198 -0
- package/library/skills/summer-api/resource.yaml +38 -0
- package/library/skills/summer-assets/SKILL.md +1 -1
- package/library/skills/summer-assets/resource.yaml +3 -2
- package/library/skills/summer-router/SKILL.md +251 -0
- package/library/skills/summer-router/resource.yaml +35 -0
- package/library/tools/api-batch/resource.yaml +82 -0
- package/library/tools/api-estimate/resource.yaml +52 -0
- package/library/tools/api-job/resource.yaml +58 -0
- package/library/tools/api-models/resource.yaml +57 -0
- package/library/tools/api-run/resource.yaml +70 -0
- package/library/tools/view-set/GUIDE.md +3 -0
- package/library/tools/view-set/resource.yaml +78 -0
- package/package.json +3 -3
- package/registry/generated/aliases.json +7 -0
- package/registry/generated/counts.json +3 -3
- package/registry/generated/gemini-extension.json +3 -2
- package/registry/generated/index.json +316 -51
- package/registry/generated/marketplace.claude.json +1 -1
- package/registry/generated/plugin.claude-directory.json +4 -3
- package/registry/generated/plugin.claude.json +3 -2
- package/registry/generated/plugin.codex.json +5 -4
- package/registry/generated/plugin.cursor.json +4 -3
- package/registry/generated/plugin.factory.json +4 -3
- package/registry/generated/server.json +2 -2
- package/registry/generated/skills-index.md +6 -5
- package/registry/generated/skills-registry.json +29 -15
- package/registry/operations/legacy-names.json +30 -0
- package/registry/operations/public-catalog.json +9169 -4926
- package/library/skills/ai-characters-in-games/SKILL.md +0 -235
- package/library/skills/ai-characters-in-games/resource.yaml +0 -30
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
/**
|
|
3
|
+
* `summer token`: print the Summer sign-in's current access token (Supabase
|
|
4
|
+
* OAuth, about one hour; refreshed first when it is about to expire), for
|
|
5
|
+
* curl against the Summer API:
|
|
6
|
+
*
|
|
7
|
+
* curl -H "Authorization: Bearer $(summer token)" https://www.summerengine.com/v1/models
|
|
8
|
+
*
|
|
9
|
+
* Only the token goes to stdout; a missing sign-in says so on stderr and exits 1.
|
|
10
|
+
*/
|
|
11
|
+
export interface TokenCommandDependencies {
|
|
12
|
+
token: () => Promise<string>;
|
|
13
|
+
print: (line: string) => void;
|
|
14
|
+
printError: (line: string) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function runTokenCommand(overrides?: Partial<TokenCommandDependencies>): Promise<number>;
|
|
17
|
+
export declare const tokenCommand: Command;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { getStoreAccessToken, OAuthError } from "../../core/oauth.js";
|
|
3
|
+
const defaults = () => ({
|
|
4
|
+
token: () => getStoreAccessToken(),
|
|
5
|
+
print: (line) => process.stdout.write(`${line}\n`),
|
|
6
|
+
printError: (line) => console.error(line),
|
|
7
|
+
});
|
|
8
|
+
export async function runTokenCommand(overrides = {}) {
|
|
9
|
+
const deps = { ...defaults(), ...overrides };
|
|
10
|
+
try {
|
|
11
|
+
deps.print(await deps.token());
|
|
12
|
+
return 0;
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
deps.printError(error instanceof OAuthError ? 'This computer is not signed in to Summer. Run "summer login", then retry.' : error instanceof Error ? error.message : String(error));
|
|
16
|
+
return 1;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export const tokenCommand = new Command("token")
|
|
20
|
+
.description("Print a one-hour Summer API access token from this computer's Summer sign-in (summer login), for curl: Authorization: Bearer $(summer token)")
|
|
21
|
+
.action(async () => {
|
|
22
|
+
const code = await runTokenCommand();
|
|
23
|
+
if (code)
|
|
24
|
+
process.exitCode = code;
|
|
25
|
+
});
|
package/dist/cli/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { loginCommand } from "./commands/login.js";
|
|
3
3
|
import { logoutCommand } from "./commands/logout.js";
|
|
4
|
+
import { tokenCommand } from "./commands/token.js";
|
|
4
5
|
import { statusCommand } from "./commands/status.js";
|
|
5
6
|
import { runCommand } from "./commands/run.js";
|
|
6
7
|
import { openCommand } from "./commands/open.js";
|
|
@@ -18,6 +19,7 @@ import { configCommand } from "./commands/config.js";
|
|
|
18
19
|
import { eventsCommand } from "./commands/events.js";
|
|
19
20
|
import { previewCommand } from "./commands/preview.js";
|
|
20
21
|
import { toolCommand } from "./commands/tool.js";
|
|
22
|
+
import { apiCommand } from "./commands/api.js";
|
|
21
23
|
import { opsCliCommands } from "./commands/ops.js";
|
|
22
24
|
import { getBanner } from "./banner.js";
|
|
23
25
|
import { runRootAction } from "./root-action.js";
|
|
@@ -42,6 +44,7 @@ export function runCli(mcpCommand) {
|
|
|
42
44
|
program.addCommand(installCommand);
|
|
43
45
|
program.addCommand(loginCommand);
|
|
44
46
|
program.addCommand(logoutCommand);
|
|
47
|
+
program.addCommand(tokenCommand);
|
|
45
48
|
program.addCommand(statusCommand);
|
|
46
49
|
program.addCommand(runCommand);
|
|
47
50
|
program.addCommand(openCommand);
|
|
@@ -58,6 +61,7 @@ export function runCli(mcpCommand) {
|
|
|
58
61
|
program.addCommand(eventsCommand);
|
|
59
62
|
program.addCommand(previewCommand);
|
|
60
63
|
program.addCommand(toolCommand);
|
|
64
|
+
program.addCommand(apiCommand);
|
|
61
65
|
// The 4.0 operation catalog (`summer <area> <action>`); SUMMER_SURFACE=v3 leaves it out.
|
|
62
66
|
if (resolveSurface(undefined) === "v4") {
|
|
63
67
|
for (const command of opsCliCommands())
|
|
@@ -74,7 +74,6 @@ export type EventsPollParams = {
|
|
|
74
74
|
* truncated:true when the page was full. */
|
|
75
75
|
limit?: number;
|
|
76
76
|
};
|
|
77
|
-
/** rebind() could not re-read engine health; the session identity is unchanged. */
|
|
78
77
|
export declare class EngineRebindError extends Error {
|
|
79
78
|
constructor(message: string);
|
|
80
79
|
}
|
|
@@ -152,6 +151,11 @@ export declare class EngineApiClient {
|
|
|
152
151
|
* feed its ONE response in here instead of issuing a second request.
|
|
153
152
|
*/
|
|
154
153
|
private _resolveQueuedResponse;
|
|
154
|
+
/**
|
|
155
|
+
* One Studio page op in the app's own Studio tab (capabilities.studioPage):
|
|
156
|
+
* POST /api/studio {op, input} -> 202, then its one terminal envelope.
|
|
157
|
+
*/
|
|
158
|
+
studioPage(op: string, input: Record<string, unknown>, timeoutMs?: number): Promise<unknown>;
|
|
155
159
|
health(): Promise<unknown>;
|
|
156
160
|
/**
|
|
157
161
|
* One long-poll of GET /api/events/poll (the engine's events channel). The
|
package/dist/core/api-client.js
CHANGED
|
@@ -3,6 +3,7 @@ import { tmpdir } from "os";
|
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { checkEngineHealth, discoverRegistryConnection, engineNotRunningError, engineSelectionFromEnv, getApiPort, getApiToken, resolveEngineConnection, } from "./engine.js";
|
|
6
|
+
import { AGENT_HOST_HEADER, resolveAgentHost } from "./agent-host.js";
|
|
6
7
|
import { asRecord, boolFrom, numberFrom, stringFrom } from "./util/json.js";
|
|
7
8
|
import { classifyOpsResponse, pollOpToTerminal, } from "./async-op-lifecycle.js";
|
|
8
9
|
import { pickPlayDeterminism } from "./capabilities/play-determinism.js";
|
|
@@ -56,6 +57,16 @@ function withoutImageData(payload) {
|
|
|
56
57
|
return metadata;
|
|
57
58
|
}
|
|
58
59
|
/** rebind() could not re-read engine health; the session identity is unchanged. */
|
|
60
|
+
/**
|
|
61
|
+
* The agent app driving the engine (Claude Code, Codex, ...), so the person's
|
|
62
|
+
* editor can say who is working ("Claude Code · Placing 12 trees"). Local
|
|
63
|
+
* only: the engine on this computer reads it for its activity line; it never
|
|
64
|
+
* leaves the machine from here, so the telemetry opt-out does not apply.
|
|
65
|
+
*/
|
|
66
|
+
function engineAgentHeader() {
|
|
67
|
+
const host = resolveAgentHost().id;
|
|
68
|
+
return host && host !== "unknown" ? { [AGENT_HOST_HEADER]: host } : {};
|
|
69
|
+
}
|
|
59
70
|
export class EngineRebindError extends Error {
|
|
60
71
|
constructor(message) {
|
|
61
72
|
super(message);
|
|
@@ -254,6 +265,7 @@ export class EngineApiClient {
|
|
|
254
265
|
const headers = {
|
|
255
266
|
Authorization: `Bearer ${this.token}`,
|
|
256
267
|
"Content-Type": "application/json",
|
|
268
|
+
...engineAgentHeader(),
|
|
257
269
|
};
|
|
258
270
|
const res = await fetch(url, {
|
|
259
271
|
method,
|
|
@@ -282,6 +294,7 @@ export class EngineApiClient {
|
|
|
282
294
|
headers: {
|
|
283
295
|
Authorization: `Bearer ${this.token}`,
|
|
284
296
|
"Content-Type": "application/json",
|
|
297
|
+
...engineAgentHeader(),
|
|
285
298
|
},
|
|
286
299
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
287
300
|
signal: AbortSignal.timeout(timeoutMs),
|
|
@@ -404,6 +417,14 @@ export class EngineApiClient {
|
|
|
404
417
|
};
|
|
405
418
|
}
|
|
406
419
|
}
|
|
420
|
+
/**
|
|
421
|
+
* One Studio page op in the app's own Studio tab (capabilities.studioPage):
|
|
422
|
+
* POST /api/studio {op, input} -> 202, then its one terminal envelope.
|
|
423
|
+
*/
|
|
424
|
+
async studioPage(op, input, timeoutMs = 90_000) {
|
|
425
|
+
const path = this.getEngineCapabilities()?.studioPage?.path ?? "/api/studio";
|
|
426
|
+
return this._requestQueued("POST", path, { op, input }, timeoutMs);
|
|
427
|
+
}
|
|
407
428
|
async health() {
|
|
408
429
|
return this.request("GET", "/api/health");
|
|
409
430
|
}
|
|
@@ -23,7 +23,7 @@ interface DoctorOptions {
|
|
|
23
23
|
}
|
|
24
24
|
export declare function runDoctor(options?: DoctorOptions): Promise<DoctorResult>;
|
|
25
25
|
/** The oldest Node major the package supports: `engines.node` in package.json. */
|
|
26
|
-
export declare const MIN_NODE_MAJOR =
|
|
26
|
+
export declare const MIN_NODE_MAJOR = 22;
|
|
27
27
|
export declare function checkNodeVersion(nodeVersion?: string): DoctorCheck;
|
|
28
28
|
export declare function checkLogin(): Promise<DoctorCheck>;
|
|
29
29
|
/**
|
|
@@ -49,7 +49,7 @@ export async function runDoctor(options = {}) {
|
|
|
49
49
|
return result;
|
|
50
50
|
}
|
|
51
51
|
/** The oldest Node major the package supports: `engines.node` in package.json. */
|
|
52
|
-
export const MIN_NODE_MAJOR =
|
|
52
|
+
export const MIN_NODE_MAJOR = 22;
|
|
53
53
|
export function checkNodeVersion(nodeVersion = process.versions.node) {
|
|
54
54
|
const major = Number(nodeVersion.split(".")[0]);
|
|
55
55
|
const shown = `v${nodeVersion.replace(/^v/, "")}`;
|
|
@@ -16,14 +16,14 @@ export declare const gameTaskPlanInputSchema: z.ZodObject<{
|
|
|
16
16
|
verification: z.ZodDefault<z.ZodEnum<["none", "fast", "full"]>>;
|
|
17
17
|
}, "strip", z.ZodTypeAny, {
|
|
18
18
|
mode: "auto" | "ship" | "debug" | "asset" | "new-game" | "feature" | "playtest" | "polish";
|
|
19
|
-
target: "auto" | "audio" | "level" | "npc" | "ui" | "
|
|
19
|
+
target: "auto" | "audio" | "level" | "npc" | "ui" | "3d" | "2d" | "animation" | "multiplayer";
|
|
20
20
|
goal: string;
|
|
21
21
|
assetPolicy: "reuse-first" | "ask-before-paid-generation" | "no-paid-generation" | "generate-when-clearly-needed";
|
|
22
22
|
verification: "none" | "full" | "fast";
|
|
23
23
|
}, {
|
|
24
24
|
goal: string;
|
|
25
25
|
mode?: "auto" | "ship" | "debug" | "asset" | "new-game" | "feature" | "playtest" | "polish" | undefined;
|
|
26
|
-
target?: "auto" | "audio" | "level" | "npc" | "ui" | "
|
|
26
|
+
target?: "auto" | "audio" | "level" | "npc" | "ui" | "3d" | "2d" | "animation" | "multiplayer" | undefined;
|
|
27
27
|
assetPolicy?: "reuse-first" | "ask-before-paid-generation" | "no-paid-generation" | "generate-when-clearly-needed" | undefined;
|
|
28
28
|
verification?: "none" | "full" | "fast" | undefined;
|
|
29
29
|
}>;
|
|
@@ -126,6 +126,9 @@ export declare function isAllowedNavigationOrigin(origin: string): boolean;
|
|
|
126
126
|
* escapes, nothing that could name a file outside the project. Returns the
|
|
127
127
|
* problem, or null when the path is acceptable. */
|
|
128
128
|
export declare function resPathProblem(value: string): string | null;
|
|
129
|
+
/** A summerengine.com Studio path (`/studio`, `/studio/games`, `/studio?tab=assets`), the
|
|
130
|
+
* only paths the engine's `studio` Navigate row opens. */
|
|
131
|
+
export declare function isStudioPath(path: string): boolean;
|
|
129
132
|
export declare function tokenize(text: string): string[];
|
|
130
133
|
/** Rank every target for a free-text query. Exported for tests. */
|
|
131
134
|
export declare function rankTargets(query: string, surface?: OpenSurface): OpenMatch[];
|
|
@@ -87,6 +87,50 @@ export function resPathProblem(value) {
|
|
|
87
87
|
return "must not contain control characters";
|
|
88
88
|
return null;
|
|
89
89
|
}
|
|
90
|
+
/** A summerengine.com Studio path (`/studio`, `/studio/games`, `/studio?tab=assets`), the
|
|
91
|
+
* only paths the engine's `studio` Navigate row opens. */
|
|
92
|
+
export function isStudioPath(path) {
|
|
93
|
+
return /^\/studio(?:[/?#]|$)/.test(path);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* A Studio path opens in the Summer app's own Studio tab when the running
|
|
97
|
+
* engine advertises the `studio` Navigate destination (it loads the tab when
|
|
98
|
+
* the person has not opened it yet). Otherwise, the engine off, an older
|
|
99
|
+
* engine or a refused Navigate, null: the caller opens the browser as before.
|
|
100
|
+
*/
|
|
101
|
+
async function openInAppStudio(deps, path, url, extra) {
|
|
102
|
+
if (!isStudioPath(path))
|
|
103
|
+
return null;
|
|
104
|
+
let client;
|
|
105
|
+
try {
|
|
106
|
+
client = await deps.engine();
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
if (!advertisedNavigation(client)?.has("studio"))
|
|
112
|
+
return null;
|
|
113
|
+
const op = { op: "Navigate", target: "studio", path };
|
|
114
|
+
let receipt;
|
|
115
|
+
try {
|
|
116
|
+
receipt = await client.executeOps([op]);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
if (extractOpError(receipt))
|
|
122
|
+
return null;
|
|
123
|
+
return {
|
|
124
|
+
ok: true,
|
|
125
|
+
action: "opened",
|
|
126
|
+
url,
|
|
127
|
+
...extra,
|
|
128
|
+
op,
|
|
129
|
+
engine: { running: true, version: client.getEngineVersion?.(), navigation: true },
|
|
130
|
+
receipt,
|
|
131
|
+
hint: "Opened in the Studio tab of the Summer app, not a browser.",
|
|
132
|
+
};
|
|
133
|
+
}
|
|
90
134
|
/** Launch the browser; a failed launch is a result, not a crash. */
|
|
91
135
|
async function launch(deps, url) {
|
|
92
136
|
try {
|
|
@@ -444,6 +488,9 @@ export async function runOpen(args, deps) {
|
|
|
444
488
|
};
|
|
445
489
|
if (print)
|
|
446
490
|
return base;
|
|
491
|
+
const inApp = await openInAppStudio(deps, resolution.url, url, { unmapped: true });
|
|
492
|
+
if (inApp)
|
|
493
|
+
return inApp;
|
|
447
494
|
const failure = await launch(deps, url);
|
|
448
495
|
if (failure)
|
|
449
496
|
return openFailed(url, failure, { unmapped: true });
|
|
@@ -479,6 +526,12 @@ export async function runOpen(args, deps) {
|
|
|
479
526
|
};
|
|
480
527
|
if (print)
|
|
481
528
|
return result;
|
|
529
|
+
if (target.web.origin !== "docs") {
|
|
530
|
+
// The app's Studio tab is signed in with the app: no login detour.
|
|
531
|
+
const inApp = await openInAppStudio(deps, path, url, { target: summary });
|
|
532
|
+
if (inApp)
|
|
533
|
+
return inApp;
|
|
534
|
+
}
|
|
482
535
|
const toOpen = needsLogin && loggedIn === false ? loginUrl : url;
|
|
483
536
|
const failure = await launch(deps, toOpen);
|
|
484
537
|
if (failure)
|
|
@@ -143,7 +143,8 @@ sessionFolder = process.cwd()) {
|
|
|
143
143
|
}
|
|
144
144
|
let client;
|
|
145
145
|
try {
|
|
146
|
-
|
|
146
|
+
// The agent asked for this game: an editor with another one switches (J16.1).
|
|
147
|
+
client = await openProjectInEditor(root, { cwd: root }, { install: input.install === true, switchOpen: true }, deps);
|
|
147
148
|
}
|
|
148
149
|
catch (error) {
|
|
149
150
|
if (error instanceof EngineInstallError)
|
|
@@ -141,18 +141,18 @@ export declare const runtimeSpawnArgsSchema: z.ZodObject<{
|
|
|
141
141
|
action: "free" | "spawn";
|
|
142
142
|
mode?: "free" | "queue_free" | undefined;
|
|
143
143
|
name?: string | undefined;
|
|
144
|
+
path?: string | undefined;
|
|
144
145
|
instance?: string | undefined;
|
|
145
146
|
scene?: string | undefined;
|
|
146
|
-
path?: string | undefined;
|
|
147
147
|
parent?: string | undefined;
|
|
148
148
|
props?: Record<string, string | number | boolean> | undefined;
|
|
149
149
|
}, {
|
|
150
150
|
action: "free" | "spawn";
|
|
151
151
|
mode?: "free" | "queue_free" | undefined;
|
|
152
152
|
name?: string | undefined;
|
|
153
|
+
path?: string | undefined;
|
|
153
154
|
instance?: string | undefined;
|
|
154
155
|
scene?: string | undefined;
|
|
155
|
-
path?: string | undefined;
|
|
156
156
|
parent?: string | undefined;
|
|
157
157
|
props?: Record<string, string | number | boolean> | undefined;
|
|
158
158
|
}>;
|
|
@@ -177,10 +177,10 @@ export declare const shotSheetArgsSchema: z.ZodObject<{
|
|
|
177
177
|
bookmark_name?: string | undefined;
|
|
178
178
|
}[];
|
|
179
179
|
scenePath?: string | undefined;
|
|
180
|
+
view?: "lighting" | "beauty" | "unshaded" | "normals" | "overdraw" | "wireframe" | undefined;
|
|
180
181
|
compare_previous?: boolean | undefined;
|
|
181
182
|
max_size?: number | undefined;
|
|
182
183
|
save_to?: string | undefined;
|
|
183
|
-
view?: "lighting" | "beauty" | "unshaded" | "normals" | "overdraw" | "wireframe" | undefined;
|
|
184
184
|
update_previous?: boolean | undefined;
|
|
185
185
|
aspect?: number | undefined;
|
|
186
186
|
}, {
|
|
@@ -192,10 +192,10 @@ export declare const shotSheetArgsSchema: z.ZodObject<{
|
|
|
192
192
|
bookmark_name?: string | undefined;
|
|
193
193
|
}[];
|
|
194
194
|
scenePath?: string | undefined;
|
|
195
|
+
view?: "lighting" | "beauty" | "unshaded" | "normals" | "overdraw" | "wireframe" | undefined;
|
|
195
196
|
compare_previous?: boolean | undefined;
|
|
196
197
|
max_size?: number | undefined;
|
|
197
198
|
save_to?: string | undefined;
|
|
198
|
-
view?: "lighting" | "beauty" | "unshaded" | "normals" | "overdraw" | "wireframe" | undefined;
|
|
199
199
|
update_previous?: boolean | undefined;
|
|
200
200
|
aspect?: number | undefined;
|
|
201
201
|
}>;
|
|
@@ -251,11 +251,11 @@ export declare const zoomArgsSchema: z.ZodObject<{
|
|
|
251
251
|
camera_position?: string | undefined;
|
|
252
252
|
camera_look_at?: string | undefined;
|
|
253
253
|
fov?: number | undefined;
|
|
254
|
+
view?: "lighting" | "beauty" | "unshaded" | "normals" | "overdraw" | "wireframe" | undefined;
|
|
254
255
|
bookmark_name?: string | undefined;
|
|
255
256
|
region?: number[] | undefined;
|
|
256
257
|
max_size?: number | undefined;
|
|
257
258
|
save_to?: string | undefined;
|
|
258
|
-
view?: "lighting" | "beauty" | "unshaded" | "normals" | "overdraw" | "wireframe" | undefined;
|
|
259
259
|
mark?: number | undefined;
|
|
260
260
|
reference_size?: number[] | undefined;
|
|
261
261
|
pad?: number | undefined;
|
|
@@ -264,11 +264,11 @@ export declare const zoomArgsSchema: z.ZodObject<{
|
|
|
264
264
|
camera_position?: string | undefined;
|
|
265
265
|
camera_look_at?: string | undefined;
|
|
266
266
|
fov?: number | undefined;
|
|
267
|
+
view?: "lighting" | "beauty" | "unshaded" | "normals" | "overdraw" | "wireframe" | undefined;
|
|
267
268
|
bookmark_name?: string | undefined;
|
|
268
269
|
region?: number[] | undefined;
|
|
269
270
|
max_size?: number | undefined;
|
|
270
271
|
save_to?: string | undefined;
|
|
271
|
-
view?: "lighting" | "beauty" | "unshaded" | "normals" | "overdraw" | "wireframe" | undefined;
|
|
272
272
|
mark?: number | undefined;
|
|
273
273
|
reference_size?: number[] | undefined;
|
|
274
274
|
pad?: number | undefined;
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* as the single per-tool dispatch table so the handler bodies stop being two
|
|
17
17
|
* mirrors as well.
|
|
18
18
|
*/
|
|
19
|
+
import { viewSetOp } from "./view-set.js";
|
|
19
20
|
import { tmpdir } from "node:os";
|
|
20
21
|
import { join } from "node:path";
|
|
21
22
|
import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
|
|
@@ -79,6 +80,7 @@ import { captureGame, captureScene, captureViewport } from "./capture.js";
|
|
|
79
80
|
// the scripting ones come with their op builders from ./scene-script.js.
|
|
80
81
|
import { ALIGN_DISTRIBUTE_FALLBACK, GREP_FALLBACK, NAVIGATION_PROBE_FALLBACK, RUNTIME_NODE_FALLBACK, RUNTIME_TREE_FALLBACK, SNAP_TO_SURFACE_FALLBACK, SNAPSHOT_DIFF_FALLBACK, STARCAST_FALLBACK, TEST_PLACEMENT_FALLBACK, WORLD_SNAPSHOT_FALLBACK, } from "./engine-fallbacks.js";
|
|
81
82
|
import { sendLibraryFeedback, } from "../feedback/client.js";
|
|
83
|
+
import { batchToolResult, cancelJob, checkBatch, createBatch, estimateJob, getBatch, getJob, jobToolResult, listModels, runJob, summerApiToolAnswer } from "../summer-api.js";
|
|
82
84
|
// One copy of the batch op classification and the instantiate/batch engine
|
|
83
85
|
// path, shared with the MCP face.
|
|
84
86
|
import { PLACEMENT_FIELDS, SCENE_MUTATION_OPS, SCENE_QUERY_OPS, instantiateScene, runBatch, } from "./placement-batch.js";
|
|
@@ -1296,6 +1298,11 @@ export const TOOL_DISPATCH = [
|
|
|
1296
1298
|
op.scenePath = args.scenePath;
|
|
1297
1299
|
return requireEngineSuccess(await (await ctx.engine()).executeOps([op]));
|
|
1298
1300
|
}),
|
|
1301
|
+
entry("summer_view_set", "Show the person a view, rail and focus in the editor", true, async (args, ctx) => {
|
|
1302
|
+
const frame = Array.isArray(args.frame) ? args.frame.filter((entry) => typeof entry === "string") : undefined;
|
|
1303
|
+
const op = viewSetOp({ view: str(args, "view"), rail: optStr(args, "rail"), focus: optStr(args, "focus"), frame });
|
|
1304
|
+
return requireEngineSuccess(await (await ctx.engine()).executeOps([op]));
|
|
1305
|
+
}),
|
|
1299
1306
|
entry("summer_replace_node", "Replace a node with a different scene or type and verify the saved scene", true, async (args, ctx) => {
|
|
1300
1307
|
// ONE implementation for both faces (core/capabilities/replace-node.ts):
|
|
1301
1308
|
// persisted ops + read-back of the saved .tscn.
|
|
@@ -1767,6 +1774,36 @@ export const TOOL_DISPATCH = [
|
|
|
1767
1774
|
throw new ToolResultError(result, result.hint);
|
|
1768
1775
|
return result;
|
|
1769
1776
|
}),
|
|
1777
|
+
// --- account ---
|
|
1778
|
+
entry("summer_api_models", "The Summer API's models with input and price", false, async (args) => summerApiToolAnswer(() => listModels({ ...(optStr(args, "model") ? { model: optStr(args, "model") } : {}), ...(optStr(args, "capability") ? { capability: optStr(args, "capability") } : {}) }))),
|
|
1779
|
+
entry("summer_api_estimate", "The exact price of one Summer API job; charges nothing", false, async (args) => summerApiToolAnswer(() => estimateJob(str(args, "model"), args.input ?? {}))),
|
|
1780
|
+
entry("summer_api_run", "Run one Summer API job with the Summer sign-in on this computer", false, async (args) => summerApiToolAnswer(async () => {
|
|
1781
|
+
const out = optStr(args, "out");
|
|
1782
|
+
const follow = args.wait === true || Boolean(out);
|
|
1783
|
+
const job = await runJob(str(args, "model"), args.input ?? {}, {
|
|
1784
|
+
...(optStr(args, "quoteId") ? { quoteId: optStr(args, "quoteId") } : {}),
|
|
1785
|
+
...(optStr(args, "idempotencyKey") ? { idempotencyKey: optStr(args, "idempotencyKey") } : {}),
|
|
1786
|
+
...(follow ? { waitSeconds: 30 } : {}),
|
|
1787
|
+
});
|
|
1788
|
+
return jobToolResult(job, { wait: args.wait === true, ...(out ? { out } : {}) });
|
|
1789
|
+
})),
|
|
1790
|
+
entry("summer_api_batch", "Run up to 500 Summer API jobs at once (all or none), or read a batch", false, async (args) => summerApiToolAnswer(async () => {
|
|
1791
|
+
const out = optStr(args, "out");
|
|
1792
|
+
const follow = { wait: args.wait === true, ...(out ? { out } : {}) };
|
|
1793
|
+
const batchId = optStr(args, "batchId");
|
|
1794
|
+
if (batchId)
|
|
1795
|
+
return batchToolResult(await getBatch(batchId), follow);
|
|
1796
|
+
const key = optStr(args, "idempotencyKey");
|
|
1797
|
+
const batch = await createBatch(checkBatch(Array.isArray(args.jobs) ? args.jobs : []), key ? { idempotencyKey: key } : {});
|
|
1798
|
+
return batchToolResult(batch, follow);
|
|
1799
|
+
})),
|
|
1800
|
+
entry("summer_api_job", "Read, follow, download or cancel a Summer API job", false, async (args) => summerApiToolAnswer(async () => {
|
|
1801
|
+
const jobId = str(args, "jobId");
|
|
1802
|
+
if (args.cancel === true)
|
|
1803
|
+
return { job: await cancelJob(jobId) };
|
|
1804
|
+
const out = optStr(args, "out");
|
|
1805
|
+
return jobToolResult(await getJob(jobId), { wait: args.wait === true, ...(out ? { out } : {}) });
|
|
1806
|
+
})),
|
|
1770
1807
|
// --- feedback ---
|
|
1771
1808
|
entry("summer_library_feedback", "Report library entry outcomes so Summer can fix and re-rank them", false, async (args) => {
|
|
1772
1809
|
if (!Array.isArray(args.reports) || args.reports.length === 0) {
|
|
@@ -88,16 +88,16 @@ export declare const uiActivateArgsSchema: z.ZodObject<{
|
|
|
88
88
|
button: z.ZodOptional<z.ZodString>;
|
|
89
89
|
}, "strip", z.ZodTypeAny, {
|
|
90
90
|
value?: string | number | undefined;
|
|
91
|
-
title?: string | undefined;
|
|
92
91
|
path?: string | undefined;
|
|
92
|
+
title?: string | undefined;
|
|
93
93
|
action?: "focus" | "press" | "select_tab" | "toggle" | "set_text" | "set_value" | "dismiss_dialog" | undefined;
|
|
94
94
|
button?: string | undefined;
|
|
95
95
|
index?: number | undefined;
|
|
96
96
|
submit?: boolean | undefined;
|
|
97
97
|
}, {
|
|
98
98
|
value?: string | number | undefined;
|
|
99
|
-
title?: string | undefined;
|
|
100
99
|
path?: string | undefined;
|
|
100
|
+
title?: string | undefined;
|
|
101
101
|
action?: "focus" | "press" | "select_tab" | "toggle" | "set_text" | "set_value" | "dismiss_dialog" | undefined;
|
|
102
102
|
button?: string | undefined;
|
|
103
103
|
index?: number | undefined;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* summer_view_set / engine.view_set (PRODUCT-SPEC J17): an outside agent shows
|
|
3
|
+
* the person what it works on, through the engine's SetView op: the view, the
|
|
4
|
+
* right rail, what to focus, and the nodes to frame the camera on.
|
|
5
|
+
*/
|
|
6
|
+
export declare function viewSetOp(args: {
|
|
7
|
+
view: string;
|
|
8
|
+
rail?: string;
|
|
9
|
+
focus?: string;
|
|
10
|
+
frame?: string[];
|
|
11
|
+
}): Record<string, unknown>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* summer_view_set / engine.view_set (PRODUCT-SPEC J17): an outside agent shows
|
|
3
|
+
* the person what it works on, through the engine's SetView op: the view, the
|
|
4
|
+
* right rail, what to focus, and the nodes to frame the camera on.
|
|
5
|
+
*/
|
|
6
|
+
export function viewSetOp(args) {
|
|
7
|
+
const op = { op: "SetView", view: args.view };
|
|
8
|
+
if (args.rail)
|
|
9
|
+
op.rail = args.rail;
|
|
10
|
+
if (args.focus)
|
|
11
|
+
op.focus = args.focus;
|
|
12
|
+
if (args.frame?.length)
|
|
13
|
+
op.frame = args.frame;
|
|
14
|
+
return op;
|
|
15
|
+
}
|
|
@@ -81,6 +81,9 @@ export interface EngineCapabilities {
|
|
|
81
81
|
* destination ids this build can open. Absent = engine predates the op;
|
|
82
82
|
* summer_open then falls back to the legacy per-surface ops it can map. */
|
|
83
83
|
navigation?: EngineNavigationCapabilities;
|
|
84
|
+
/** The Studio page ops this app answers itself through its own Studio tab
|
|
85
|
+
* (POST /api/studio). Absent = send them to Summer's server. */
|
|
86
|
+
studioPage?: EngineStudioPageCapabilities;
|
|
84
87
|
/** Launch postures this build ENFORCES: "focus" (always), "background"
|
|
85
88
|
* (`--summer-background`), "offscreen" (`--summer-offscreen` /
|
|
86
89
|
* `--summer-verify`). Non-macOS builds advertise ["focus"] only. Absent =
|
|
@@ -89,6 +92,11 @@ export interface EngineCapabilities {
|
|
|
89
92
|
* snake_case spelling too. */
|
|
90
93
|
launchPostures?: string[];
|
|
91
94
|
}
|
|
95
|
+
export interface EngineStudioPageCapabilities {
|
|
96
|
+
version?: number;
|
|
97
|
+
path: string;
|
|
98
|
+
ops: string[];
|
|
99
|
+
}
|
|
92
100
|
export interface EngineNavigationCapabilities {
|
|
93
101
|
version?: number;
|
|
94
102
|
targets: EngineNavigationTarget[];
|
|
@@ -49,6 +49,8 @@ export const CLI_KNOWN_OP_NEEDS = [
|
|
|
49
49
|
// engine's one-table op; the other three are the legacy
|
|
50
50
|
// fallbacks on engines that predate it.
|
|
51
51
|
"Navigate", "OpenResource", "FocusDock", "RevealInFileSystem",
|
|
52
|
+
// engine.view_set: the frame's view, rail, focus and frame (core/capabilities/view-set.ts)
|
|
53
|
+
"SetView",
|
|
52
54
|
// Summer Apps editor docks (summer_app_dock: core/capabilities/apps.ts)
|
|
53
55
|
"OpenAppDock", "CloseAppDock", "ListAppDocks",
|
|
54
56
|
// summer_enable_plugin (core/capabilities/enable-plugin.ts)
|
|
@@ -188,11 +190,27 @@ export function parseEngineCapabilities(raw) {
|
|
|
188
190
|
const navigation = parseNavigation(record.navigation);
|
|
189
191
|
if (navigation)
|
|
190
192
|
out.navigation = navigation;
|
|
193
|
+
const studioPage = parseStudioPage(record.studioPage);
|
|
194
|
+
if (studioPage)
|
|
195
|
+
out.studioPage = studioPage;
|
|
191
196
|
const launchPostures = stringList(record.launchPostures) ?? stringList(record.launch_postures);
|
|
192
197
|
if (launchPostures)
|
|
193
198
|
out.launchPostures = launchPostures;
|
|
194
199
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
195
200
|
}
|
|
201
|
+
function parseStudioPage(raw) {
|
|
202
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
203
|
+
return undefined;
|
|
204
|
+
const record = raw;
|
|
205
|
+
const ops = stringList(record.ops);
|
|
206
|
+
if (!ops?.length)
|
|
207
|
+
return undefined;
|
|
208
|
+
return {
|
|
209
|
+
...(typeof record.version === "number" ? { version: record.version } : {}),
|
|
210
|
+
path: typeof record.path === "string" && record.path.startsWith("/api/") ? record.path : "/api/studio",
|
|
211
|
+
ops,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
196
214
|
function parseNavigation(raw) {
|
|
197
215
|
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
198
216
|
return undefined;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The browser sign-in for the Summer account (the `cli_session` flow): the
|
|
3
|
+
* person opens `{gateway}/login?cli_session=<id>`, signs in, and approves;
|
|
4
|
+
* this polls `{gateway}/api/auth/cli-login?session=<id>` until the gateway
|
|
5
|
+
* hands over the CLI token, then saves it to ~/.summer (the files the desktop
|
|
6
|
+
* engine and every tool read). `summer login` and the MCP's sign-in link
|
|
7
|
+
* (core/sign-in-link.ts) share it.
|
|
8
|
+
*/
|
|
9
|
+
import { type LoginSession } from "./auth.js";
|
|
10
|
+
export declare const POLL_TIMEOUT_MS = 900000;
|
|
11
|
+
export interface CliSessionPollDependencies {
|
|
12
|
+
fetch: typeof fetch;
|
|
13
|
+
sleep: (ms: number) => Promise<void>;
|
|
14
|
+
now: () => number;
|
|
15
|
+
log: (message: string) => void;
|
|
16
|
+
/** Stop waiting (another sign-in already saved a token, the server is closing). */
|
|
17
|
+
cancelled?: () => boolean | Promise<boolean>;
|
|
18
|
+
}
|
|
19
|
+
/** The page the person opens to sign in. */
|
|
20
|
+
export declare function cliSessionLoginUrl(gatewayUrl: string, sessionId: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Wait for the browser sign-in of `sessionId`, save it, and return it.
|
|
23
|
+
* Throws on a rejected request, an invalid token, a timeout, or `cancelled`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function pollCliSession(gatewayUrl: string, sessionId: string, deps: CliSessionPollDependencies): Promise<LoginSession>;
|