summer-engine 3.2.0 → 3.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +3 -3
- package/.cursor-plugin/plugin.json +2 -2
- package/.factory-plugin/plugin.json +2 -2
- package/CHANGELOG.md +30 -0
- package/README.md +27 -12
- package/dist/cli/commands/login.js +12 -0
- package/dist/cli/commands/publish.js +2 -1
- package/dist/core/auth.js +3 -0
- package/dist/core/capabilities/audit/args.d.ts +14 -14
- package/dist/core/capabilities/engine-run.d.ts +8 -0
- package/dist/core/capabilities/engine-run.js +33 -0
- package/dist/core/capabilities/export-game.d.ts +99 -0
- package/dist/core/capabilities/export-game.js +369 -0
- package/dist/core/capabilities/export-presets.d.ts +50 -0
- package/dist/core/capabilities/export-presets.js +177 -0
- package/dist/core/capabilities/export-templates.d.ts +118 -0
- package/dist/core/capabilities/export-templates.js +291 -0
- package/dist/core/capabilities/publish-build.d.ts +64 -0
- package/dist/core/capabilities/publish-build.js +448 -0
- package/dist/core/capabilities/publish-download.d.ts +37 -0
- package/dist/core/capabilities/publish-download.js +168 -0
- package/dist/core/capabilities/runtime-control.d.ts +10 -10
- package/dist/core/capabilities/summer-bundle.d.ts +49 -0
- package/dist/core/capabilities/summer-bundle.js +156 -0
- package/dist/core/capabilities/tool-dispatch.js +33 -1
- package/dist/core/capabilities/ui-control.d.ts +2 -2
- package/dist/core/library-read.d.ts +2 -2
- package/dist/core/oauth.d.ts +78 -0
- package/dist/core/oauth.js +309 -0
- package/dist/core/util/zip-write.d.ts +18 -0
- package/dist/core/util/zip-write.js +112 -0
- package/dist/core/util/zip.d.ts +22 -0
- package/dist/core/util/zip.js +140 -0
- package/dist/mcp/hosted-mount.d.ts +96 -0
- package/dist/mcp/hosted-mount.js +206 -0
- package/dist/mcp/server.d.ts +2 -1
- package/dist/mcp/server.js +13 -6
- package/dist/mcp/tools/creator-tools.js +54 -1
- package/gemini-extension.json +1 -1
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +6 -3
- package/library/skills/export-and-ship/SKILL.md +2 -0
- package/library/skills/multiplayer-publish/SKILL.md +5 -1
- package/library/skills/navigate-summer/SKILL.md +1 -1
- package/library/tools/creator-publish/resource.yaml +4 -3
- package/library/tools/creator-releases/resource.yaml +1 -1
- package/library/tools/export-game/resource.yaml +72 -0
- package/library/tools/export-templates/resource.yaml +63 -0
- package/library/tools/open/resource.yaml +1 -1
- package/library/tools/publish-build/resource.yaml +78 -0
- package/package.json +1 -1
- package/registry/generated/aliases.json +3 -0
- package/registry/generated/counts.json +2 -2
- package/registry/generated/gemini-extension.json +1 -1
- package/registry/generated/index.json +119 -10
- package/registry/generated/marketplace.claude.json +2 -2
- package/registry/generated/plugin.claude.json +2 -2
- package/registry/generated/plugin.codex.json +3 -3
- package/registry/generated/plugin.cursor.json +2 -2
- package/registry/generated/plugin.factory.json +2 -2
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { type EngineRun } from "./engine-run.js";
|
|
2
|
+
/**
|
|
3
|
+
* summer_export_templates: list, download and install Summer Engine export
|
|
4
|
+
* templates from Summer's CDN.
|
|
5
|
+
*
|
|
6
|
+
* The Summer Games store's own builds (summer.games bundles for iOS, Android,
|
|
7
|
+
* macOS and Windows) need no template. Templates are for the store's web
|
|
8
|
+
* build (the Summer WebGPU/JSPI Forward+ template, web_summer_jspi_*.zip) and
|
|
9
|
+
* for native downloads (macos.zip, windows_*_x86_64.exe). The Mac app bundles
|
|
10
|
+
* only macos.zip, the Windows app only the Windows exes, both bundle the web
|
|
11
|
+
* pair when packaged with it, and Linux editors bundle nothing; the editor's
|
|
12
|
+
* own downloader reads the stock Godot mirrors, which never carry Summer's
|
|
13
|
+
* binaries. So this tool fetches them from
|
|
14
|
+
*
|
|
15
|
+
* <base>/<summerVersion>/manifest.json (schema summer.export-templates.v1)
|
|
16
|
+
*
|
|
17
|
+
* verifies each file's sha256 and installs it where the engine looks:
|
|
18
|
+
* <user data>/export_templates/<FULL_CONFIG>/ (Godot's folder; Summer has not
|
|
19
|
+
* renamed it), FULL_CONFIG being the Godot version the engine prints with
|
|
20
|
+
* --version (4.7.2.stable, plus .mono for the .NET editor).
|
|
21
|
+
*/
|
|
22
|
+
export declare const TEMPLATES_URL_ENV = "SUMMER_TEMPLATES_URL";
|
|
23
|
+
export declare const DEFAULT_TEMPLATES_URL = "https://downloads.summer.games/engine-templates";
|
|
24
|
+
export declare const TEMPLATES_MANIFEST_SCHEMA = "summer.export-templates.v1";
|
|
25
|
+
export declare const TEMPLATE_PLATFORMS: readonly ["web", "macos", "windows", "linux", "ios", "android"];
|
|
26
|
+
export type TemplatePlatform = (typeof TEMPLATE_PLATFORMS)[number];
|
|
27
|
+
export interface TemplateEntry {
|
|
28
|
+
platform: TemplatePlatform;
|
|
29
|
+
/** File name the engine looks for (macos.zip, web_summer_jspi_release.zip, ...). */
|
|
30
|
+
file: string;
|
|
31
|
+
/** Path of the file relative to the manifest. */
|
|
32
|
+
path: string;
|
|
33
|
+
sha256: string;
|
|
34
|
+
size: number;
|
|
35
|
+
debug: boolean;
|
|
36
|
+
/** true: only the .mono folder; false: only the standard folder; null: both. */
|
|
37
|
+
mono: boolean | null;
|
|
38
|
+
}
|
|
39
|
+
export interface TemplatesManifest {
|
|
40
|
+
schema: typeof TEMPLATES_MANIFEST_SCHEMA;
|
|
41
|
+
summerVersion: string;
|
|
42
|
+
/** Godot FULL_CONFIG without .mono, e.g. 4.7.2.stable: the template folder name. */
|
|
43
|
+
godotVersion: string;
|
|
44
|
+
templates: TemplateEntry[];
|
|
45
|
+
}
|
|
46
|
+
export interface EngineTemplateInfo {
|
|
47
|
+
engine: string;
|
|
48
|
+
/** Template folder name: Godot FULL_CONFIG, e.g. 4.7.2.stable.mono. */
|
|
49
|
+
folder: string;
|
|
50
|
+
mono: boolean;
|
|
51
|
+
summerVersion: string | null;
|
|
52
|
+
/** Folders the engine reads, first match wins: inside the app, then the user folder. */
|
|
53
|
+
searchDirs: string[];
|
|
54
|
+
/** Where downloads go. */
|
|
55
|
+
userDir: string;
|
|
56
|
+
}
|
|
57
|
+
export interface TemplatesDependencies {
|
|
58
|
+
findBinary: () => string | null;
|
|
59
|
+
run: (binary: string, args: string[], timeoutMs: number) => Promise<EngineRun>;
|
|
60
|
+
fetch: typeof globalThis.fetch;
|
|
61
|
+
os: NodeJS.Platform;
|
|
62
|
+
env: NodeJS.ProcessEnv;
|
|
63
|
+
home: string;
|
|
64
|
+
}
|
|
65
|
+
/** Godot's user data folder + export_templates (editor_paths.cpp; get_godot_dir_name is "Godot", "godot" on Linux). */
|
|
66
|
+
export declare function userTemplatesRoot(os: NodeJS.Platform, env: NodeJS.ProcessEnv, home: string): string;
|
|
67
|
+
/** Templates inside the installed app (editor_export_platform.cpp find_export_template). */
|
|
68
|
+
export declare function bundledTemplatesRoot(os: NodeJS.Platform, engine: string): string | null;
|
|
69
|
+
/** FULL_CONFIG from `--version` output ("4.7.2.stable.mono.custom_build.abc" -> "4.7.2.stable.mono"). */
|
|
70
|
+
export declare function parseTemplateFolder(output: string): string | null;
|
|
71
|
+
export declare function engineTemplateInfo(overrides?: Partial<TemplatesDependencies>, summerVersion?: string): Promise<EngineTemplateInfo>;
|
|
72
|
+
/** Where the engine would find this template file, or null. */
|
|
73
|
+
export declare function findInstalledTemplate(info: EngineTemplateInfo, file: string): string | null;
|
|
74
|
+
/** Template file the engine needs for a download export (export plugins' template names). */
|
|
75
|
+
export declare function templateFileFor(target: "web" | "macos" | "windows", debug: boolean): string;
|
|
76
|
+
export declare function templatesBaseUrl(env?: NodeJS.ProcessEnv): string;
|
|
77
|
+
export declare function fetchTemplatesManifest(summerVersion: string, overrides?: Partial<TemplatesDependencies>): Promise<{
|
|
78
|
+
manifest: TemplatesManifest;
|
|
79
|
+
url: string;
|
|
80
|
+
}>;
|
|
81
|
+
export interface TemplatesInput {
|
|
82
|
+
action: "list" | "install";
|
|
83
|
+
/** Template platforms to install (default: web). */
|
|
84
|
+
platforms?: string[];
|
|
85
|
+
/** Also install debug templates. */
|
|
86
|
+
includeDebug?: boolean;
|
|
87
|
+
/** Summer version of the installed engine, when the tool cannot read it (Windows, Linux). */
|
|
88
|
+
summerVersion?: string;
|
|
89
|
+
}
|
|
90
|
+
export declare function exportTemplates(input: TemplatesInput, overrides?: Partial<TemplatesDependencies>): Promise<{
|
|
91
|
+
installed: {
|
|
92
|
+
file: string;
|
|
93
|
+
dir: string;
|
|
94
|
+
}[];
|
|
95
|
+
available: (TemplateEntry & {
|
|
96
|
+
installed: boolean;
|
|
97
|
+
})[] | null;
|
|
98
|
+
availability: string;
|
|
99
|
+
note: string;
|
|
100
|
+
engine: string;
|
|
101
|
+
folder: string;
|
|
102
|
+
summerVersion: string | null;
|
|
103
|
+
searchDirs: string[];
|
|
104
|
+
ok: true;
|
|
105
|
+
} | {
|
|
106
|
+
manifest: string;
|
|
107
|
+
templates: {
|
|
108
|
+
file: string;
|
|
109
|
+
path: string;
|
|
110
|
+
status: "installed" | "already_installed";
|
|
111
|
+
}[];
|
|
112
|
+
next: string;
|
|
113
|
+
engine: string;
|
|
114
|
+
folder: string;
|
|
115
|
+
summerVersion: string | null;
|
|
116
|
+
searchDirs: string[];
|
|
117
|
+
ok: true;
|
|
118
|
+
}>;
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, readdirSync, readFileSync, createWriteStream } from "node:fs";
|
|
3
|
+
import { mkdir, rename, rm } from "node:fs/promises";
|
|
4
|
+
import { homedir, platform as osPlatform } from "node:os";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { Readable } from "node:stream";
|
|
7
|
+
import { pipeline } from "node:stream/promises";
|
|
8
|
+
import { findEngineBinary } from "../engine-install.js";
|
|
9
|
+
import { runEngine } from "./engine-run.js";
|
|
10
|
+
import { hashFile, BuildToolError } from "./summer-bundle.js";
|
|
11
|
+
/**
|
|
12
|
+
* summer_export_templates: list, download and install Summer Engine export
|
|
13
|
+
* templates from Summer's CDN.
|
|
14
|
+
*
|
|
15
|
+
* The Summer Games store's own builds (summer.games bundles for iOS, Android,
|
|
16
|
+
* macOS and Windows) need no template. Templates are for the store's web
|
|
17
|
+
* build (the Summer WebGPU/JSPI Forward+ template, web_summer_jspi_*.zip) and
|
|
18
|
+
* for native downloads (macos.zip, windows_*_x86_64.exe). The Mac app bundles
|
|
19
|
+
* only macos.zip, the Windows app only the Windows exes, both bundle the web
|
|
20
|
+
* pair when packaged with it, and Linux editors bundle nothing; the editor's
|
|
21
|
+
* own downloader reads the stock Godot mirrors, which never carry Summer's
|
|
22
|
+
* binaries. So this tool fetches them from
|
|
23
|
+
*
|
|
24
|
+
* <base>/<summerVersion>/manifest.json (schema summer.export-templates.v1)
|
|
25
|
+
*
|
|
26
|
+
* verifies each file's sha256 and installs it where the engine looks:
|
|
27
|
+
* <user data>/export_templates/<FULL_CONFIG>/ (Godot's folder; Summer has not
|
|
28
|
+
* renamed it), FULL_CONFIG being the Godot version the engine prints with
|
|
29
|
+
* --version (4.7.2.stable, plus .mono for the .NET editor).
|
|
30
|
+
*/
|
|
31
|
+
export const TEMPLATES_URL_ENV = "SUMMER_TEMPLATES_URL";
|
|
32
|
+
export const DEFAULT_TEMPLATES_URL = "https://downloads.summer.games/engine-templates";
|
|
33
|
+
export const TEMPLATES_MANIFEST_SCHEMA = "summer.export-templates.v1";
|
|
34
|
+
export const TEMPLATE_PLATFORMS = ["web", "macos", "windows", "linux", "ios", "android"];
|
|
35
|
+
const SHA256 = /^sha256:[0-9a-f]{64}$/;
|
|
36
|
+
const SAFE_FILE = /^[A-Za-z0-9._-]+$/;
|
|
37
|
+
const VERSION_TIMEOUT_MS = 20_000;
|
|
38
|
+
const defaultDependencies = () => ({
|
|
39
|
+
findBinary: () => findEngineBinary(osPlatform()),
|
|
40
|
+
run: runEngine,
|
|
41
|
+
fetch: (input, init) => globalThis.fetch(input, init),
|
|
42
|
+
os: osPlatform(),
|
|
43
|
+
env: process.env,
|
|
44
|
+
home: homedir(),
|
|
45
|
+
});
|
|
46
|
+
/** Godot's user data folder + export_templates (editor_paths.cpp; get_godot_dir_name is "Godot", "godot" on Linux). */
|
|
47
|
+
export function userTemplatesRoot(os, env, home) {
|
|
48
|
+
if (os === "darwin")
|
|
49
|
+
return join(home, "Library", "Application Support", "Godot", "export_templates");
|
|
50
|
+
if (os === "win32")
|
|
51
|
+
return join(env.APPDATA || join(home, "AppData", "Roaming"), "Godot", "export_templates");
|
|
52
|
+
return join(env.XDG_DATA_HOME || join(home, ".local", "share"), "godot", "export_templates");
|
|
53
|
+
}
|
|
54
|
+
/** Templates inside the installed app (editor_export_platform.cpp find_export_template). */
|
|
55
|
+
export function bundledTemplatesRoot(os, engine) {
|
|
56
|
+
if (os === "darwin")
|
|
57
|
+
return join(dirname(dirname(engine)), "Resources", "export_templates");
|
|
58
|
+
if (os === "win32")
|
|
59
|
+
return join(dirname(engine), "export_templates");
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
/** FULL_CONFIG from `--version` output ("4.7.2.stable.mono.custom_build.abc" -> "4.7.2.stable.mono"). */
|
|
63
|
+
export function parseTemplateFolder(output) {
|
|
64
|
+
const match = /(?<![\d.])(\d+\.\d+(?:\.\d+)?\.(?:stable|dev\d*|alpha\d*|beta\d*|rc\d*)(?:\.mono)?)\b/.exec(output);
|
|
65
|
+
return match ? match[1] : null;
|
|
66
|
+
}
|
|
67
|
+
/** The Mac app's CFBundleShortVersionString is the Summer version. */
|
|
68
|
+
function macSummerVersion(engine) {
|
|
69
|
+
const plist = join(dirname(dirname(engine)), "Info.plist");
|
|
70
|
+
if (!existsSync(plist))
|
|
71
|
+
return null;
|
|
72
|
+
const match = /<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(readFileSync(plist, "utf8"));
|
|
73
|
+
return match ? match[1].trim() : null;
|
|
74
|
+
}
|
|
75
|
+
export async function engineTemplateInfo(overrides = {}, summerVersion) {
|
|
76
|
+
const deps = { ...defaultDependencies(), ...overrides };
|
|
77
|
+
const engine = deps.findBinary();
|
|
78
|
+
if (!engine) {
|
|
79
|
+
throw new BuildToolError("engine_not_installed", "Summer Engine is not installed on this machine.", 'Recovery: run "summer install", or set SUMMER_BIN to the engine executable.');
|
|
80
|
+
}
|
|
81
|
+
let run;
|
|
82
|
+
try {
|
|
83
|
+
run = await deps.run(engine, ["--headless", "--version"], VERSION_TIMEOUT_MS);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
throw new BuildToolError("engine_launch_failed", `Summer Engine could not start: ${error instanceof Error ? error.message : String(error)}.`, 'Recovery: run "summer doctor" and check the engine install.');
|
|
87
|
+
}
|
|
88
|
+
const folder = parseTemplateFolder(run.output);
|
|
89
|
+
if (!folder) {
|
|
90
|
+
throw new BuildToolError("engine_version_unknown", "Summer Engine did not print a version this tool can read.", 'Recovery: run "summer doctor"; update Summer Engine if it is very old.', undefined, { output: run.output.trim().slice(-400) });
|
|
91
|
+
}
|
|
92
|
+
const bundled = bundledTemplatesRoot(deps.os, engine);
|
|
93
|
+
const userRoot = userTemplatesRoot(deps.os, deps.env, deps.home);
|
|
94
|
+
const version = summerVersion?.trim() || (deps.os === "darwin" ? macSummerVersion(engine) : null);
|
|
95
|
+
return {
|
|
96
|
+
engine,
|
|
97
|
+
folder,
|
|
98
|
+
mono: folder.endsWith(".mono"),
|
|
99
|
+
summerVersion: version,
|
|
100
|
+
searchDirs: [...(bundled ? [join(bundled, folder)] : []), join(userRoot, folder)],
|
|
101
|
+
userDir: join(userRoot, folder),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/** Where the engine would find this template file, or null. */
|
|
105
|
+
export function findInstalledTemplate(info, file) {
|
|
106
|
+
for (const dir of info.searchDirs) {
|
|
107
|
+
const path = join(dir, file);
|
|
108
|
+
if (existsSync(path))
|
|
109
|
+
return path;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
/** Template file the engine needs for a download export (export plugins' template names). */
|
|
114
|
+
export function templateFileFor(target, debug) {
|
|
115
|
+
if (target === "web")
|
|
116
|
+
return `web_summer_jspi_${debug ? "debug" : "release"}.zip`;
|
|
117
|
+
if (target === "macos")
|
|
118
|
+
return "macos.zip";
|
|
119
|
+
return `windows_${debug ? "debug" : "release"}_x86_64.exe`;
|
|
120
|
+
}
|
|
121
|
+
export function templatesBaseUrl(env = process.env) {
|
|
122
|
+
return (env[TEMPLATES_URL_ENV]?.trim() || DEFAULT_TEMPLATES_URL).replace(/\/+$/, "");
|
|
123
|
+
}
|
|
124
|
+
function notPublished(summerVersion, url) {
|
|
125
|
+
return new BuildToolError("templates_not_published", `Summer has not published export templates for Summer ${summerVersion} (${url}).`, "Recovery: store builds for iOS, Android, macOS and Windows need no template (summer_export_game with format bundle). " +
|
|
126
|
+
"For web or native downloads use the Summer Engine Mac or Windows app, which bundles them, or update Summer Engine and retry.", 404);
|
|
127
|
+
}
|
|
128
|
+
function parseManifest(raw, url) {
|
|
129
|
+
const bad = (why) => new BuildToolError("templates_manifest_invalid", `The templates manifest at ${url} is not valid: ${why}.`, "Recovery: retry later; report it if it persists.");
|
|
130
|
+
const value = raw;
|
|
131
|
+
if (!value || value.schema !== TEMPLATES_MANIFEST_SCHEMA)
|
|
132
|
+
throw bad(`schema is not ${TEMPLATES_MANIFEST_SCHEMA}`);
|
|
133
|
+
if (typeof value.summerVersion !== "string" || typeof value.godotVersion !== "string" || !Array.isArray(value.templates)) {
|
|
134
|
+
throw bad("summerVersion, godotVersion or templates is missing");
|
|
135
|
+
}
|
|
136
|
+
const templates = value.templates.map((item, index) => {
|
|
137
|
+
if (!TEMPLATE_PLATFORMS.includes(item?.platform) ||
|
|
138
|
+
typeof item.file !== "string" ||
|
|
139
|
+
!SAFE_FILE.test(item.file) ||
|
|
140
|
+
typeof item.path !== "string" ||
|
|
141
|
+
item.path.split("/").some((part) => !part || part === "." || part === "..") ||
|
|
142
|
+
typeof item.sha256 !== "string" ||
|
|
143
|
+
!SHA256.test(item.sha256) ||
|
|
144
|
+
!Number.isSafeInteger(item.size) ||
|
|
145
|
+
item.size <= 0 ||
|
|
146
|
+
typeof item.debug !== "boolean" ||
|
|
147
|
+
!(item.mono === null || typeof item.mono === "boolean")) {
|
|
148
|
+
throw bad(`templates[${index}] is malformed`);
|
|
149
|
+
}
|
|
150
|
+
return { platform: item.platform, file: item.file, path: item.path, sha256: item.sha256, size: item.size, debug: item.debug, mono: item.mono };
|
|
151
|
+
});
|
|
152
|
+
return { schema: TEMPLATES_MANIFEST_SCHEMA, summerVersion: value.summerVersion, godotVersion: value.godotVersion, templates };
|
|
153
|
+
}
|
|
154
|
+
export async function fetchTemplatesManifest(summerVersion, overrides = {}) {
|
|
155
|
+
const deps = { ...defaultDependencies(), ...overrides };
|
|
156
|
+
const url = `${templatesBaseUrl(deps.env)}/${encodeURIComponent(summerVersion)}/manifest.json`;
|
|
157
|
+
let response;
|
|
158
|
+
try {
|
|
159
|
+
response = await deps.fetch(url, { headers: { accept: "application/json" } });
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
throw new BuildToolError("templates_unreachable", `Could not reach ${url}: ${error instanceof Error ? error.message : String(error)}.`, "Recovery: check the network and retry.");
|
|
163
|
+
}
|
|
164
|
+
if (response.status === 404 || response.status === 403)
|
|
165
|
+
throw notPublished(summerVersion, url);
|
|
166
|
+
if (!response.ok) {
|
|
167
|
+
throw new BuildToolError("templates_unreachable", `${url} answered ${response.status}.`, "Recovery: retry later.", response.status);
|
|
168
|
+
}
|
|
169
|
+
let body;
|
|
170
|
+
try {
|
|
171
|
+
body = await response.json();
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
body = null;
|
|
175
|
+
}
|
|
176
|
+
return { manifest: parseManifest(body, url), url };
|
|
177
|
+
}
|
|
178
|
+
function servesFolder(entry, mono) {
|
|
179
|
+
return entry.mono === null || entry.mono === mono;
|
|
180
|
+
}
|
|
181
|
+
function wantedPlatforms(platforms) {
|
|
182
|
+
const list = platforms?.length ? platforms : ["web"];
|
|
183
|
+
const unknown = list.filter((item) => !TEMPLATE_PLATFORMS.includes(item));
|
|
184
|
+
if (unknown.length) {
|
|
185
|
+
throw new BuildToolError("template_platform_unknown", `Unknown template platform ${unknown.join(", ")}.`, `Recovery: use ${TEMPLATE_PLATFORMS.join(", ")}.`);
|
|
186
|
+
}
|
|
187
|
+
return TEMPLATE_PLATFORMS.filter((item) => list.includes(item));
|
|
188
|
+
}
|
|
189
|
+
function needVersion(info) {
|
|
190
|
+
if (info.summerVersion)
|
|
191
|
+
return info.summerVersion;
|
|
192
|
+
throw new BuildToolError("summer_version_unknown", "This tool cannot read the installed Summer version on this system.", "Recovery: pass summerVersion (shown in Summer Engine, Help > About, e.g. 0.7.1).");
|
|
193
|
+
}
|
|
194
|
+
async function download(deps, url, entry, target) {
|
|
195
|
+
let response;
|
|
196
|
+
try {
|
|
197
|
+
response = await deps.fetch(url);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
throw new BuildToolError("templates_unreachable", `Could not download ${url}: ${error instanceof Error ? error.message : String(error)}.`, "Recovery: retry.");
|
|
201
|
+
}
|
|
202
|
+
if (!response.ok || !response.body) {
|
|
203
|
+
throw new BuildToolError("templates_unreachable", `${url} answered ${response.status}.`, "Recovery: retry later.", response.status);
|
|
204
|
+
}
|
|
205
|
+
await mkdir(dirname(target), { recursive: true });
|
|
206
|
+
const part = `${target}.part`;
|
|
207
|
+
const hash = createHash("sha256");
|
|
208
|
+
let size = 0;
|
|
209
|
+
try {
|
|
210
|
+
await pipeline(Readable.fromWeb(response.body), async function* (source) {
|
|
211
|
+
for await (const chunk of source) {
|
|
212
|
+
hash.update(chunk);
|
|
213
|
+
size += chunk.length;
|
|
214
|
+
yield chunk;
|
|
215
|
+
}
|
|
216
|
+
}, createWriteStream(part));
|
|
217
|
+
const sha256 = `sha256:${hash.digest("hex")}`;
|
|
218
|
+
if (sha256 !== entry.sha256 || size !== entry.size) {
|
|
219
|
+
throw new BuildToolError("template_checksum_mismatch", `${entry.file} did not match the manifest (got ${sha256}, ${size} bytes).`, "Recovery: retry; nothing was installed.");
|
|
220
|
+
}
|
|
221
|
+
await rename(part, target);
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
await rm(part, { force: true });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
export async function exportTemplates(input, overrides = {}) {
|
|
228
|
+
const deps = { ...defaultDependencies(), ...overrides };
|
|
229
|
+
const info = await engineTemplateInfo(deps, input.summerVersion);
|
|
230
|
+
const installedFiles = () => info.searchDirs.flatMap((dir) => existsSync(dir) ? readdirSync(dir).filter((name) => !name.endsWith(".part")).map((file) => ({ file, dir })) : []);
|
|
231
|
+
const engine = { engine: info.engine, folder: info.folder, summerVersion: info.summerVersion, searchDirs: info.searchDirs };
|
|
232
|
+
if (input.action === "list") {
|
|
233
|
+
let available = null;
|
|
234
|
+
let availability;
|
|
235
|
+
if (info.summerVersion) {
|
|
236
|
+
try {
|
|
237
|
+
const { manifest, url } = await fetchTemplatesManifest(info.summerVersion, deps);
|
|
238
|
+
available = manifest.templates
|
|
239
|
+
.filter((entry) => servesFolder(entry, info.mono))
|
|
240
|
+
.map((entry) => ({ ...entry, installed: findInstalledTemplate(info, entry.file) !== null }));
|
|
241
|
+
availability = url;
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
if (!(error instanceof BuildToolError))
|
|
245
|
+
throw error;
|
|
246
|
+
availability = `${error.code}: ${error.message.replace(` ${error.recovery}`, "")}`;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
availability = "summer_version_unknown: pass summerVersion to see what Summer publishes.";
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
ok: true,
|
|
254
|
+
...engine,
|
|
255
|
+
installed: installedFiles(),
|
|
256
|
+
available,
|
|
257
|
+
availability,
|
|
258
|
+
note: "Store builds (summer_export_game, format bundle) need no template. Web and native downloads need web_summer_jspi_release.zip, macos.zip or windows_release_x86_64.exe.",
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
const platforms = wantedPlatforms(input.platforms);
|
|
262
|
+
const summerVersion = needVersion(info);
|
|
263
|
+
const { manifest, url } = await fetchTemplatesManifest(summerVersion, deps);
|
|
264
|
+
const godotFolder = info.folder.replace(/\.mono$/, "");
|
|
265
|
+
if (manifest.godotVersion !== godotFolder) {
|
|
266
|
+
throw new BuildToolError("templates_version_mismatch", `Summer ${summerVersion} templates are for Godot base ${manifest.godotVersion}, but this engine reads ${info.folder}.`, "Recovery: pass the summerVersion of the installed Summer Engine (Help > About), or update Summer Engine.");
|
|
267
|
+
}
|
|
268
|
+
const entries = manifest.templates.filter((entry) => platforms.includes(entry.platform) && (input.includeDebug || !entry.debug) && servesFolder(entry, info.mono));
|
|
269
|
+
const missing = platforms.filter((item) => !entries.some((entry) => entry.platform === item));
|
|
270
|
+
if (missing.length) {
|
|
271
|
+
throw new BuildToolError("template_not_published", `Summer ${summerVersion} publishes no ${missing.join(", ")} template${info.mono ? " for the .NET editor" : ""}.`, `Recovery: install from ${manifest.templates.map((entry) => entry.platform).filter((v, i, a) => a.indexOf(v) === i).join(", ") || "nothing yet"}.`);
|
|
272
|
+
}
|
|
273
|
+
const base = url.slice(0, url.lastIndexOf("/") + 1);
|
|
274
|
+
const results = [];
|
|
275
|
+
for (const entry of entries) {
|
|
276
|
+
const target = join(info.userDir, entry.file);
|
|
277
|
+
if (existsSync(target) && (await hashFile(target)).sha256 === entry.sha256) {
|
|
278
|
+
results.push({ file: entry.file, path: target, status: "already_installed" });
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
await download(deps, base + entry.path.split("/").map(encodeURIComponent).join("/"), entry, target);
|
|
282
|
+
results.push({ file: entry.file, path: target, status: "installed" });
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
ok: true,
|
|
286
|
+
...engine,
|
|
287
|
+
manifest: url,
|
|
288
|
+
templates: results,
|
|
289
|
+
next: "Export with summer_export_game (format download) for web, macos or windows.",
|
|
290
|
+
};
|
|
291
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { BuildToolError } from "./summer-bundle.js";
|
|
2
|
+
/** summer-platform client versions: vMAJOR.MINOR.PATCH[-rN]. */
|
|
3
|
+
export declare const CLIENT_VERSION: RegExp;
|
|
4
|
+
export interface PublishBuildInput {
|
|
5
|
+
gameId?: string;
|
|
6
|
+
/** Exported .zip. Defaults to the last summer_export_game result. */
|
|
7
|
+
file?: string;
|
|
8
|
+
clientVersion?: string;
|
|
9
|
+
/** Also approve the Build for players (needs a game that passed review). */
|
|
10
|
+
publish?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* A web build or native download (summer_export_game format "download"):
|
|
13
|
+
* the store versions platform (web, macos-universal, windows-x64,
|
|
14
|
+
* linux-x64). Read from the last export when it was a download.
|
|
15
|
+
*/
|
|
16
|
+
platform?: string;
|
|
17
|
+
confirm?: boolean;
|
|
18
|
+
/** How long to wait for Summer to make the Build before returning. */
|
|
19
|
+
waitSeconds?: number;
|
|
20
|
+
face: "cli" | "mcp";
|
|
21
|
+
}
|
|
22
|
+
export interface PublishBuildDependencies {
|
|
23
|
+
fetch: typeof fetch;
|
|
24
|
+
sleep: (ms: number) => Promise<void>;
|
|
25
|
+
now: () => number;
|
|
26
|
+
token: () => Promise<string>;
|
|
27
|
+
}
|
|
28
|
+
export declare const defaultDependencies: PublishBuildDependencies;
|
|
29
|
+
export interface Store {
|
|
30
|
+
call<T = Record<string, unknown>>(method: string, path: string, options?: {
|
|
31
|
+
body?: unknown;
|
|
32
|
+
key?: string;
|
|
33
|
+
}): Promise<T>;
|
|
34
|
+
}
|
|
35
|
+
export declare function storeError(status: number, code: string, message: string | undefined): BuildToolError;
|
|
36
|
+
export declare function createStore(baseUrl: string, token: string, deps: PublishBuildDependencies): Store;
|
|
37
|
+
/** Whole-file and per-part SHA-256 ("sha256:<hex>") in one pass. */
|
|
38
|
+
export declare function hashParts(path: string, partSize: number): Promise<{
|
|
39
|
+
sha256: string;
|
|
40
|
+
sizeBytes: number;
|
|
41
|
+
parts: string[];
|
|
42
|
+
}>;
|
|
43
|
+
export interface Grant {
|
|
44
|
+
partNumber: number;
|
|
45
|
+
url: string;
|
|
46
|
+
headers: Record<string, string>;
|
|
47
|
+
expiresAt: number;
|
|
48
|
+
}
|
|
49
|
+
export declare function readGrants(body: Record<string, any>): Grant[];
|
|
50
|
+
/**
|
|
51
|
+
* Upload the given parts: signed in batches, PUT from disk, re-signed when
|
|
52
|
+
* refused or about to expire. `sign` asks the store for URLs (build
|
|
53
|
+
* publications bind each to its part SHA-256; store versions to its length).
|
|
54
|
+
*/
|
|
55
|
+
export declare function uploadParts(sign: (partNumbers: number[]) => Promise<Grant[]>, file: string, sizeBytes: number, plan: {
|
|
56
|
+
partSizeBytes: number;
|
|
57
|
+
partCount: number;
|
|
58
|
+
}, deps: PublishBuildDependencies, parts?: number[]): Promise<void>;
|
|
59
|
+
export declare function normalizeClientVersion(value?: string): string;
|
|
60
|
+
export type PublishBuildResult = Record<string, unknown> & {
|
|
61
|
+
ok: true;
|
|
62
|
+
status: string;
|
|
63
|
+
};
|
|
64
|
+
export declare function publishBuild(input: PublishBuildInput, overrides?: Partial<PublishBuildDependencies>): Promise<PublishBuildResult>;
|