summer-engine 3.5.2 → 3.7.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 +16 -2
- package/README.md +0 -4
- 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/seeing/args.d.ts +2 -2
- package/dist/core/capabilities/seeing/seeing.d.ts +1 -1
- package/dist/core/capabilities/sfx-choices.d.ts +78 -0
- package/dist/core/capabilities/sfx-choices.js +253 -0
- package/dist/core/capabilities/tool-dispatch.js +35 -52
- package/dist/core/config.d.ts +1 -7
- package/dist/core/config.js +5 -75
- package/dist/mcp/server.d.ts +2 -1
- package/dist/mcp/server.js +6 -5
- package/dist/mcp/tools/creator-tools.js +0 -71
- package/dist/mcp/tools/generate-tools.js +69 -3
- package/gemini-extension.json +1 -1
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +6 -7
- package/library/references/mcp-tools-reference/resource.yaml +1 -1
- package/library/skills/README.md +2 -2
- package/library/skills/ambient-bed/SKILL.md +3 -1
- package/library/skills/ambient-bed/resource.yaml +1 -1
- package/library/skills/audio-direction/SKILL.md +5 -5
- package/library/skills/audio-direction/resource.yaml +1 -1
- package/library/skills/export-and-ship/SKILL.md +2 -0
- package/library/skills/multiplayer-publish/SKILL.md +8 -4
- package/library/skills/publish-your-game/SKILL.md +2 -0
- package/library/skills/release-readiness/SKILL.md +141 -2
- package/library/skills/release-readiness/resource.yaml +4 -1
- package/library/skills/sound-effect/SKILL.md +83 -158
- package/library/skills/sound-effect/resource.yaml +8 -2
- package/library/skills/ui-basics/SKILL.md +1 -0
- package/library/tools/generate-audio/resource.yaml +6 -2
- package/library/tools/generate-sfx-choices/resource.yaml +69 -0
- package/library/tools/get-choices/resource.yaml +50 -0
- package/package.json +1 -1
- package/registry/generated/aliases.json +2 -3
- package/registry/generated/counts.json +2 -2
- package/registry/generated/gemini-extension.json +1 -1
- package/registry/generated/index.json +105 -121
- 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
- package/registry/generated/skills-index.md +2 -2
- package/registry/generated/skills-registry.json +2 -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
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { createReadStream } from "node:fs";
|
|
3
|
-
import { lstat, readFile } from "node:fs/promises";
|
|
4
|
-
import { resolve } from "node:path";
|
|
5
|
-
import { getCredentialMetadata, getCreatorToken, } from "../auth.js";
|
|
6
|
-
import { getCreatorApiUrl, readSummerConfig, } from "../config.js";
|
|
7
|
-
import { appendStoreJsonLine } from "../store.js";
|
|
8
|
-
import { readJsonResponse } from "../util/http.js";
|
|
9
|
-
export const CREATOR_API_CONTRACT = "summer.creator.v1";
|
|
10
|
-
const CREATOR_PUBLISH_PATH = "/api/creator/v1/publish";
|
|
11
|
-
const CREATOR_RELEASES_PATH = "/api/creator/v1/releases";
|
|
12
|
-
const REQUIRED_SCOPE = "publish";
|
|
13
|
-
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
14
|
-
const VERSION = /^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$/;
|
|
15
|
-
export class CreatorOperationError extends Error {
|
|
16
|
-
code;
|
|
17
|
-
operation;
|
|
18
|
-
recovery;
|
|
19
|
-
status;
|
|
20
|
-
constructor(code, operation, message, recovery, status) {
|
|
21
|
-
super(`${message} ${recovery}`);
|
|
22
|
-
this.code = code;
|
|
23
|
-
this.operation = operation;
|
|
24
|
-
this.recovery = recovery;
|
|
25
|
-
this.status = status;
|
|
26
|
-
this.name = "CreatorOperationError";
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
const defaultDependencies = {
|
|
30
|
-
fetch: (input, init) => globalThis.fetch(input, init),
|
|
31
|
-
now: () => new Date(),
|
|
32
|
-
};
|
|
33
|
-
async function resolveProjectId(explicit) {
|
|
34
|
-
const projectId = explicit?.trim() || (await readSummerConfig()).creator?.projectId;
|
|
35
|
-
if (!projectId) {
|
|
36
|
-
throw new Error('No creator project is selected. Recovery: pass --project-id <uuid>, or run "summer config set creator.projectId <uuid>".');
|
|
37
|
-
}
|
|
38
|
-
if (!UUID.test(projectId)) {
|
|
39
|
-
throw new Error(`Creator project ID "${projectId}" is not a UUID. Recovery: copy the Summer game ID from the creator dashboard and configure it again.`);
|
|
40
|
-
}
|
|
41
|
-
return projectId;
|
|
42
|
-
}
|
|
43
|
-
async function requireCreatorCredential(operation) {
|
|
44
|
-
const token = await getCreatorToken();
|
|
45
|
-
if (!token) {
|
|
46
|
-
throw new CreatorOperationError("creator_token_required", operation, "Creator publishing needs a separate Summer Platform creator API token.", 'Recovery: run "summer login --creator", mint a token with the exact "publish" scope in the opened browser page, and paste its one-time sc_ value. Your core Summer login will not be changed.');
|
|
47
|
-
}
|
|
48
|
-
const metadata = await getCredentialMetadata();
|
|
49
|
-
if (!metadata.creator?.scopes.includes(REQUIRED_SCOPE)) {
|
|
50
|
-
throw new CreatorOperationError("creator_scope_missing", operation, `The locally recorded creator credential does not declare the exact "${REQUIRED_SCOPE}" scope.`, 'Recovery: run "summer login --creator --force", mint a new publish-scoped token, and retry. The server will still independently verify the scope.');
|
|
51
|
-
}
|
|
52
|
-
return token;
|
|
53
|
-
}
|
|
54
|
-
function recoveryForStatus(operation, status) {
|
|
55
|
-
if (status === 401 || status === 403) {
|
|
56
|
-
return 'Recovery: run "summer login --creator --force", mint a non-revoked token with the exact "publish" scope, and retry.';
|
|
57
|
-
}
|
|
58
|
-
if (status === 404) {
|
|
59
|
-
return 'Recovery: check creator.projectId against the Summer creator dashboard and make sure that game belongs to the token owner.';
|
|
60
|
-
}
|
|
61
|
-
if (status === 409 && operation === "publish") {
|
|
62
|
-
return "Recovery: read the server detail, bump an immutable version when required, then repeat the unconfirmed command to review the new exact target.";
|
|
63
|
-
}
|
|
64
|
-
return `Recovery: retry once; if it repeats, check creator.apiUrl and the Summer Platform creator API health.`;
|
|
65
|
-
}
|
|
66
|
-
async function readCreatorResponse(response, operation) {
|
|
67
|
-
const { json, parsed } = await readJsonResponse(response);
|
|
68
|
-
const payload = json;
|
|
69
|
-
if (!parsed) {
|
|
70
|
-
throw new CreatorOperationError("creator_invalid_response", operation, `The creator API returned a non-JSON response (${response.status}).`, recoveryForStatus(operation, response.status), response.status);
|
|
71
|
-
}
|
|
72
|
-
if (!response.ok) {
|
|
73
|
-
const code = typeof payload.error === "string" ? payload.error : "creator_request_failed";
|
|
74
|
-
const detail = typeof payload.detail === "string"
|
|
75
|
-
? payload.detail
|
|
76
|
-
: `The creator API refused the request (${response.status}).`;
|
|
77
|
-
const recovery = typeof payload.recovery === "string"
|
|
78
|
-
? payload.recovery
|
|
79
|
-
: recoveryForStatus(operation, response.status);
|
|
80
|
-
throw new CreatorOperationError(code, operation, detail, recovery, response.status);
|
|
81
|
-
}
|
|
82
|
-
if (payload.contract !== CREATOR_API_CONTRACT) {
|
|
83
|
-
throw new CreatorOperationError("creator_contract_mismatch", operation, `The creator API did not return contract ${CREATOR_API_CONTRACT}.`, "Recovery: upgrade the Summer CLI and retry; if it repeats, the configured creator API is incompatible.", response.status);
|
|
84
|
-
}
|
|
85
|
-
return payload;
|
|
86
|
-
}
|
|
87
|
-
async function creatorRequest(operation, path, token, deps, init = {}) {
|
|
88
|
-
let response;
|
|
89
|
-
try {
|
|
90
|
-
response = await deps.fetch(`${await getCreatorApiUrl()}${path}`, {
|
|
91
|
-
...init,
|
|
92
|
-
headers: {
|
|
93
|
-
accept: "application/json",
|
|
94
|
-
authorization: `Bearer ${token}`,
|
|
95
|
-
...(init.body ? { "content-type": "application/json" } : {}),
|
|
96
|
-
...init.headers,
|
|
97
|
-
},
|
|
98
|
-
signal: AbortSignal.timeout(operation === "publish" ? 300_000 : 30_000),
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
catch (error) {
|
|
102
|
-
throw new CreatorOperationError("creator_network_failed", operation, `The creator API request failed: ${error instanceof Error ? error.message : String(error)}.`, "Recovery: check your network and creator.apiUrl, then retry. No successful release is being claimed.");
|
|
103
|
-
}
|
|
104
|
-
return readCreatorResponse(response, operation);
|
|
105
|
-
}
|
|
106
|
-
async function inspectArtifact(path) {
|
|
107
|
-
const artifactPath = resolve(path);
|
|
108
|
-
const info = await lstat(artifactPath).catch(() => null);
|
|
109
|
-
if (!info || !info.isFile() || info.isSymbolicLink()) {
|
|
110
|
-
throw new Error(`Release artifact ${artifactPath} must be a real regular file, not a missing path, directory, or symlink.`);
|
|
111
|
-
}
|
|
112
|
-
if (!artifactPath.toLowerCase().endsWith(".pck")) {
|
|
113
|
-
throw new Error(`Release artifact ${artifactPath} is not a .pck. Recovery: export a Summer PCK and pass its exact path with --artifact.`);
|
|
114
|
-
}
|
|
115
|
-
if (info.size <= 0) {
|
|
116
|
-
throw new Error(`Release artifact ${artifactPath} is empty. Recovery: export the Summer PCK again and verify it locally before publishing.`);
|
|
117
|
-
}
|
|
118
|
-
const hash = createHash("sha256");
|
|
119
|
-
for await (const chunk of createReadStream(artifactPath))
|
|
120
|
-
hash.update(chunk);
|
|
121
|
-
return {
|
|
122
|
-
path: artifactPath,
|
|
123
|
-
sizeBytes: info.size,
|
|
124
|
-
sha256: hash.digest("hex"),
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
async function readManifest(path) {
|
|
128
|
-
if (!path)
|
|
129
|
-
return undefined;
|
|
130
|
-
const manifestPath = resolve(path);
|
|
131
|
-
try {
|
|
132
|
-
return JSON.parse(await readFile(manifestPath, "utf8"));
|
|
133
|
-
}
|
|
134
|
-
catch (error) {
|
|
135
|
-
throw new Error(`Cannot read release manifest ${manifestPath}: ${error instanceof Error ? error.message : String(error)}. Recovery: pass a valid JSON manifest or omit --manifest.`);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
function assertUploadUrl(value) {
|
|
139
|
-
if (typeof value !== "string") {
|
|
140
|
-
throw new CreatorOperationError("creator_invalid_response", "publish", "The prepare response did not contain an uploadUrl.", "Recovery: retry once; if it repeats, report the Summer Platform creator API as unhealthy.");
|
|
141
|
-
}
|
|
142
|
-
let url;
|
|
143
|
-
try {
|
|
144
|
-
url = new URL(value);
|
|
145
|
-
}
|
|
146
|
-
catch {
|
|
147
|
-
throw new CreatorOperationError("creator_unsafe_upload_url", "publish", "The creator API returned a malformed upload URL.", "Recovery: do not upload the artifact; report the creator API response as a security issue.");
|
|
148
|
-
}
|
|
149
|
-
const local = url.hostname === "localhost" ||
|
|
150
|
-
url.hostname === "127.0.0.1" ||
|
|
151
|
-
url.hostname === "[::1]";
|
|
152
|
-
if (url.username ||
|
|
153
|
-
url.password ||
|
|
154
|
-
(url.protocol !== "https:" && !(local && url.protocol === "http:"))) {
|
|
155
|
-
throw new CreatorOperationError("creator_unsafe_upload_url", "publish", "The creator API returned an unsafe upload URL.", "Recovery: do not upload the artifact; report the creator API response as a security issue.");
|
|
156
|
-
}
|
|
157
|
-
return url.toString();
|
|
158
|
-
}
|
|
159
|
-
function assertUploadHeaders(value) {
|
|
160
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
161
|
-
throw new CreatorOperationError("creator_invalid_response", "publish", "The prepare response did not contain signed upload headers.", "Recovery: retry once; if it repeats, report the Summer Platform creator API as unhealthy.");
|
|
162
|
-
}
|
|
163
|
-
const headers = Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
|
|
164
|
-
if (headers["content-type"] !== "application/octet-stream" ||
|
|
165
|
-
headers["if-none-match"] !== "*") {
|
|
166
|
-
throw new CreatorOperationError("creator_unsafe_upload_headers", "publish", "The prepare response did not bind the expected content type and write-once header.", "Recovery: do not upload the artifact; report the creator API response as a security issue.");
|
|
167
|
-
}
|
|
168
|
-
return headers;
|
|
169
|
-
}
|
|
170
|
-
async function uploadArtifact(uploadUrl, headers, artifactPath, deps) {
|
|
171
|
-
let response;
|
|
172
|
-
try {
|
|
173
|
-
response = await deps.fetch(uploadUrl, {
|
|
174
|
-
method: "PUT",
|
|
175
|
-
headers,
|
|
176
|
-
body: createReadStream(artifactPath),
|
|
177
|
-
// Node fetch requires this for a streaming request body. It is a Node
|
|
178
|
-
// extension and therefore absent from the DOM RequestInit declaration.
|
|
179
|
-
duplex: "half",
|
|
180
|
-
signal: AbortSignal.timeout(600_000),
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
catch (error) {
|
|
184
|
-
throw new CreatorOperationError("creator_upload_failed", "publish", `The artifact upload failed: ${error instanceof Error ? error.message : String(error)}.`, "Recovery: rerun the unconfirmed publish command to recompute and review the exact artifact, then retry.");
|
|
185
|
-
}
|
|
186
|
-
if (!response.ok) {
|
|
187
|
-
throw new CreatorOperationError("creator_upload_refused", "publish", `The immutable artifact upload was refused (${response.status}).`, response.status === 412
|
|
188
|
-
? "Recovery: the content-addressed key is already occupied. Do not overwrite it; verify the project/version and retry with a new immutable version."
|
|
189
|
-
: "Recovery: repeat prepare and reproduce every returned signed header exactly, then retry.", response.status);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
export async function publishCreator(input, overrides = {}) {
|
|
193
|
-
const deps = { ...defaultDependencies, ...overrides };
|
|
194
|
-
const project = resolve(input.project ?? process.cwd());
|
|
195
|
-
const projectId = await resolveProjectId(input.projectId);
|
|
196
|
-
const channel = input.channel?.trim() ||
|
|
197
|
-
(await readSummerConfig()).creator?.channel ||
|
|
198
|
-
"production";
|
|
199
|
-
if (channel !== "production") {
|
|
200
|
-
throw new CreatorOperationError("creator_channel_unsupported", "publish", `The Summer Platform creator API has no "${channel}" release channel.`, 'Recovery: use --channel production or run "summer config set creator.channel production". No release was created.');
|
|
201
|
-
}
|
|
202
|
-
const version = input.version?.trim();
|
|
203
|
-
if (!version || !VERSION.test(version)) {
|
|
204
|
-
throw new Error("A release version is required and must be 1–32 characters of letters, numbers, dots, underscores, or hyphens. Recovery: pass --version <value>.");
|
|
205
|
-
}
|
|
206
|
-
if (!input.artifact) {
|
|
207
|
-
throw new Error("A release artifact is required. Recovery: export a Summer .pck and pass --artifact <path>; the CLI never guesses an export layout.");
|
|
208
|
-
}
|
|
209
|
-
const artifact = await inspectArtifact(input.artifact);
|
|
210
|
-
const manifest = await readManifest(input.manifest);
|
|
211
|
-
const auditBase = {
|
|
212
|
-
at: deps.now().toISOString(),
|
|
213
|
-
operation: "publish",
|
|
214
|
-
face: input.face,
|
|
215
|
-
project,
|
|
216
|
-
projectId,
|
|
217
|
-
channel,
|
|
218
|
-
artifact: artifact.path,
|
|
219
|
-
version,
|
|
220
|
-
sha256: artifact.sha256,
|
|
221
|
-
sizeBytes: artifact.sizeBytes,
|
|
222
|
-
notes: input.notes?.trim() || null,
|
|
223
|
-
};
|
|
224
|
-
if (!input.confirm) {
|
|
225
|
-
await appendStoreJsonLine("creator-audit.jsonl", {
|
|
226
|
-
...auditBase,
|
|
227
|
-
outcome: "confirmation_required",
|
|
228
|
-
});
|
|
229
|
-
throw new CreatorOperationError("publish_confirmation_required", "publish", `Publishing changes project ${projectId}: version ${version}, sha256 ${artifact.sha256}, ${artifact.sizeBytes} bytes from ${artifact.path}.`, "Recovery: show this exact project, version, digest, size, and artifact path to the user. Only after approval, rerun with --confirm (or confirm=true). No release was created.");
|
|
230
|
-
}
|
|
231
|
-
let token;
|
|
232
|
-
try {
|
|
233
|
-
token = await requireCreatorCredential("publish");
|
|
234
|
-
await appendStoreJsonLine("creator-audit.jsonl", {
|
|
235
|
-
...auditBase,
|
|
236
|
-
outcome: "started",
|
|
237
|
-
tokenAudience: "summercraft-creator",
|
|
238
|
-
advisoryTokenScopes: [REQUIRED_SCOPE],
|
|
239
|
-
});
|
|
240
|
-
const confirmation = {
|
|
241
|
-
gameId: projectId,
|
|
242
|
-
version,
|
|
243
|
-
sha256: artifact.sha256,
|
|
244
|
-
};
|
|
245
|
-
const prepareBody = {
|
|
246
|
-
contract: CREATOR_API_CONTRACT,
|
|
247
|
-
operation: "prepare",
|
|
248
|
-
...confirmation,
|
|
249
|
-
sizeBytes: artifact.sizeBytes,
|
|
250
|
-
contentType: "application/octet-stream",
|
|
251
|
-
...(input.notes?.trim() ? { changelog: input.notes.trim() } : {}),
|
|
252
|
-
...(manifest !== undefined ? { manifest } : {}),
|
|
253
|
-
confirmation,
|
|
254
|
-
};
|
|
255
|
-
const prepared = await creatorRequest("publish", CREATOR_PUBLISH_PATH, token, deps, {
|
|
256
|
-
method: "POST",
|
|
257
|
-
body: JSON.stringify(prepareBody),
|
|
258
|
-
});
|
|
259
|
-
if (prepared.operation !== "prepare") {
|
|
260
|
-
throw new CreatorOperationError("creator_invalid_response", "publish", "The prepare response named the wrong operation.", "Recovery: do not upload the artifact; upgrade the Summer CLI or report the creator API as incompatible.");
|
|
261
|
-
}
|
|
262
|
-
if (prepared.method !== "PUT" ||
|
|
263
|
-
prepared.finalizeUrl !== CREATOR_PUBLISH_PATH) {
|
|
264
|
-
throw new CreatorOperationError("creator_invalid_response", "publish", "The prepare response did not bind the expected PUT and versioned finalize route.", "Recovery: do not upload the artifact; upgrade the Summer CLI or report the creator API as incompatible.");
|
|
265
|
-
}
|
|
266
|
-
await uploadArtifact(assertUploadUrl(prepared.uploadUrl), assertUploadHeaders(prepared.headers), artifact.path, deps);
|
|
267
|
-
const finalized = await creatorRequest("publish", CREATOR_PUBLISH_PATH, token, deps, {
|
|
268
|
-
method: "POST",
|
|
269
|
-
body: JSON.stringify({
|
|
270
|
-
...prepareBody,
|
|
271
|
-
operation: "finalize",
|
|
272
|
-
}),
|
|
273
|
-
});
|
|
274
|
-
if (finalized.operation !== "finalize" ||
|
|
275
|
-
typeof finalized.releaseId !== "string" ||
|
|
276
|
-
!UUID.test(finalized.releaseId) ||
|
|
277
|
-
finalized.status !== "pending_review" ||
|
|
278
|
-
finalized.gameId !== projectId ||
|
|
279
|
-
finalized.version !== version ||
|
|
280
|
-
finalized.sha256 !== artifact.sha256 ||
|
|
281
|
-
finalized.sizeBytes !== artifact.sizeBytes) {
|
|
282
|
-
throw new CreatorOperationError("creator_invalid_response", "publish", "The finalize response did not echo the exact project, version, digest, size, release ID, and status.", "Recovery: query summer releases before retrying; the server may have recorded the release even though its response was incomplete.");
|
|
283
|
-
}
|
|
284
|
-
const result = {
|
|
285
|
-
ok: true,
|
|
286
|
-
contract: CREATOR_API_CONTRACT,
|
|
287
|
-
operation: "publish",
|
|
288
|
-
projectId,
|
|
289
|
-
releaseId: finalized.releaseId,
|
|
290
|
-
version,
|
|
291
|
-
sha256: artifact.sha256,
|
|
292
|
-
sizeBytes: artifact.sizeBytes,
|
|
293
|
-
status: finalized.status,
|
|
294
|
-
detail: typeof finalized.detail === "string" ? finalized.detail : null,
|
|
295
|
-
};
|
|
296
|
-
await appendStoreJsonLine("creator-audit.jsonl", {
|
|
297
|
-
...auditBase,
|
|
298
|
-
outcome: "succeeded",
|
|
299
|
-
releaseId: result.releaseId,
|
|
300
|
-
releaseStatus: result.status,
|
|
301
|
-
});
|
|
302
|
-
return result;
|
|
303
|
-
}
|
|
304
|
-
catch (error) {
|
|
305
|
-
await appendStoreJsonLine("creator-audit.jsonl", {
|
|
306
|
-
...auditBase,
|
|
307
|
-
outcome: "failed",
|
|
308
|
-
code: error instanceof CreatorOperationError
|
|
309
|
-
? error.code
|
|
310
|
-
: "creator_request_invalid",
|
|
311
|
-
status: error instanceof CreatorOperationError ? error.status ?? null : null,
|
|
312
|
-
});
|
|
313
|
-
throw error;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
function readReleases(value, projectId) {
|
|
317
|
-
if (!Array.isArray(value)) {
|
|
318
|
-
throw new CreatorOperationError("creator_invalid_response", "releases", "The creator API response did not contain a releases array.", "Recovery: upgrade the Summer CLI and retry; if it repeats, report the creator API as incompatible.");
|
|
319
|
-
}
|
|
320
|
-
const releases = [];
|
|
321
|
-
for (const item of value) {
|
|
322
|
-
const row = item;
|
|
323
|
-
if (!row ||
|
|
324
|
-
typeof row !== "object" ||
|
|
325
|
-
typeof row.releaseId !== "string" ||
|
|
326
|
-
!UUID.test(row.releaseId) ||
|
|
327
|
-
row.gameId !== projectId ||
|
|
328
|
-
typeof row.version !== "string" ||
|
|
329
|
-
!["published", "pending_review", "retired"].includes(String(row.state)) ||
|
|
330
|
-
typeof row.gameStatus !== "string" ||
|
|
331
|
-
(row.sha256 !== null &&
|
|
332
|
-
(typeof row.sha256 !== "string" ||
|
|
333
|
-
!/^[0-9a-f]{64}$/.test(row.sha256))) ||
|
|
334
|
-
(row.sizeBytes !== null &&
|
|
335
|
-
(typeof row.sizeBytes !== "number" ||
|
|
336
|
-
!Number.isSafeInteger(row.sizeBytes) ||
|
|
337
|
-
row.sizeBytes < 0)) ||
|
|
338
|
-
(row.changelog !== null && typeof row.changelog !== "string") ||
|
|
339
|
-
typeof row.submittedAt !== "string" ||
|
|
340
|
-
!Number.isFinite(Date.parse(row.submittedAt)) ||
|
|
341
|
-
(row.artifactPlane !== "r2" && row.artifactPlane !== "legacy")) {
|
|
342
|
-
throw new CreatorOperationError("creator_invalid_response", "releases", "The creator API returned a malformed or cross-project release item.", "Recovery: do not trust this release list; upgrade the Summer CLI or report the creator API as incompatible.");
|
|
343
|
-
}
|
|
344
|
-
releases.push(row);
|
|
345
|
-
}
|
|
346
|
-
return releases;
|
|
347
|
-
}
|
|
348
|
-
export async function listCreatorReleases(input, overrides = {}) {
|
|
349
|
-
const deps = { ...defaultDependencies, ...overrides };
|
|
350
|
-
const projectId = await resolveProjectId(input.projectId);
|
|
351
|
-
const token = await requireCreatorCredential("releases");
|
|
352
|
-
const params = new URLSearchParams({
|
|
353
|
-
gameId: projectId,
|
|
354
|
-
limit: String(input.limit ?? 20),
|
|
355
|
-
});
|
|
356
|
-
if (input.cursor)
|
|
357
|
-
params.set("cursor", input.cursor);
|
|
358
|
-
const payload = await creatorRequest("releases", `${CREATOR_RELEASES_PATH}?${params}`, token, deps);
|
|
359
|
-
if (payload.gameId !== projectId) {
|
|
360
|
-
throw new CreatorOperationError("creator_invalid_response", "releases", "The creator API returned release history for a different project.", "Recovery: do not trust this release list; check creator.apiUrl and report the response as a security issue.");
|
|
361
|
-
}
|
|
362
|
-
if (payload.nextCursor !== null &&
|
|
363
|
-
typeof payload.nextCursor !== "string") {
|
|
364
|
-
throw new CreatorOperationError("creator_invalid_response", "releases", "The creator API returned a malformed release cursor.", "Recovery: omit the cursor and retry from the newest release; report the response if it repeats.");
|
|
365
|
-
}
|
|
366
|
-
return {
|
|
367
|
-
ok: true,
|
|
368
|
-
contract: CREATOR_API_CONTRACT,
|
|
369
|
-
operation: "releases",
|
|
370
|
-
projectId,
|
|
371
|
-
releases: readReleases(payload.releases, projectId),
|
|
372
|
-
nextCursor: payload.nextCursor,
|
|
373
|
-
};
|
|
374
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
id: tool/creator-config
|
|
2
|
-
kind: tool
|
|
3
|
-
version: 1.0.2
|
|
4
|
-
summary: Read or update the shared non-secret Summer configuration; mutations require confirm, and tokens are never returned or accepted.
|
|
5
|
-
use_when:
|
|
6
|
-
- inspecting or changing shared CLI and MCP configuration values
|
|
7
|
-
- '"which creator project id / channel is this CLI pointed at?"'
|
|
8
|
-
- switching the default publish channel
|
|
9
|
-
do_not_use_when:
|
|
10
|
-
- reading or setting auth tokens — they never pass through this tool; use summer login / logout
|
|
11
|
-
facets:
|
|
12
|
-
lifecycle:
|
|
13
|
-
- launch
|
|
14
|
-
domains:
|
|
15
|
-
- creator
|
|
16
|
-
- deployment
|
|
17
|
-
compatibility:
|
|
18
|
-
engine: ">=4.6"
|
|
19
|
-
source: official
|
|
20
|
-
license: MIT
|
|
21
|
-
status: stable
|
|
22
|
-
aliases:
|
|
23
|
-
- summer_creator_config
|
|
24
|
-
implementation:
|
|
25
|
-
module: src/mcp/tools/creator-tools.ts
|
|
26
|
-
export: registerCreatorTools
|
|
27
|
-
surfaces:
|
|
28
|
-
mcp:
|
|
29
|
-
tool_name: summer_creator_config
|
|
30
|
-
remote: false
|
|
31
|
-
cli:
|
|
32
|
-
command: summer config
|
|
33
|
-
input_schema:
|
|
34
|
-
type: object
|
|
35
|
-
properties:
|
|
36
|
-
action:
|
|
37
|
-
type: string
|
|
38
|
-
enum:
|
|
39
|
-
- list
|
|
40
|
-
- get
|
|
41
|
-
- set
|
|
42
|
-
- unset
|
|
43
|
-
key:
|
|
44
|
-
type: string
|
|
45
|
-
enum:
|
|
46
|
-
- gateway.url
|
|
47
|
-
- creator.apiUrl
|
|
48
|
-
- creator.projectId
|
|
49
|
-
- creator.channel
|
|
50
|
-
value:
|
|
51
|
-
type: string
|
|
52
|
-
confirm:
|
|
53
|
-
type: boolean
|
|
54
|
-
default: false
|
|
55
|
-
required:
|
|
56
|
-
- action
|
|
57
|
-
additionalProperties: false
|
|
58
|
-
authority:
|
|
59
|
-
filesystem: true
|
|
60
|
-
editor_mutation: false
|
|
61
|
-
network: false
|
|
62
|
-
credentials: false
|
|
63
|
-
publish: false
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
id: tool/creator-publish
|
|
2
|
-
kind: tool
|
|
3
|
-
version: 1.1.0
|
|
4
|
-
summary: "Deprecated: publishes a .pck to the legacy creator API. Use summer_export_game, then summer_publish_build, to upload to summer.games."
|
|
5
|
-
use_when:
|
|
6
|
-
- an old project that still publishes a .pck to the legacy Summercraft creator channel with an sc_ creator token
|
|
7
|
-
- a ~/.summer/config.json that names a legacy creator projectId and channel
|
|
8
|
-
do_not_use_when:
|
|
9
|
-
- releasing a version on summer.games — summer_publish_build, then summer_store_submit (the owner approves)
|
|
10
|
-
- uploading to summer.games — summer_export_game, then summer_publish_build
|
|
11
|
-
- the user has not approved the exact version, digest, and channel
|
|
12
|
-
- the game is not exported yet — export the .pck first (skill/export-and-ship)
|
|
13
|
-
facets:
|
|
14
|
-
lifecycle:
|
|
15
|
-
- launch
|
|
16
|
-
domains:
|
|
17
|
-
- creator
|
|
18
|
-
- deployment
|
|
19
|
-
compatibility:
|
|
20
|
-
engine: ">=4.6"
|
|
21
|
-
source: official
|
|
22
|
-
license: MIT
|
|
23
|
-
status: deprecated
|
|
24
|
-
aliases:
|
|
25
|
-
- summer_creator_publish
|
|
26
|
-
implementation:
|
|
27
|
-
module: src/core/capabilities/creator.ts
|
|
28
|
-
export: publishCreator
|
|
29
|
-
surfaces:
|
|
30
|
-
mcp:
|
|
31
|
-
tool_name: summer_creator_publish
|
|
32
|
-
remote: false
|
|
33
|
-
cli:
|
|
34
|
-
command: summer publish
|
|
35
|
-
input_schema:
|
|
36
|
-
type: object
|
|
37
|
-
properties:
|
|
38
|
-
project:
|
|
39
|
-
type: string
|
|
40
|
-
description: Project root. Defaults to the current working directory.
|
|
41
|
-
artifact:
|
|
42
|
-
type: string
|
|
43
|
-
description: Exact path to the exported Summer .pck artifact.
|
|
44
|
-
version:
|
|
45
|
-
type: string
|
|
46
|
-
description: Immutable release version.
|
|
47
|
-
manifest:
|
|
48
|
-
type: string
|
|
49
|
-
description: Optional path to a JSON release manifest.
|
|
50
|
-
projectId:
|
|
51
|
-
type: string
|
|
52
|
-
description: Creator project ID. Defaults to ~/.summer/config.json.
|
|
53
|
-
channel:
|
|
54
|
-
type: string
|
|
55
|
-
description: Release channel. Defaults to the configured channel or production.
|
|
56
|
-
notes:
|
|
57
|
-
type: string
|
|
58
|
-
description: Release notes shown during confirmation and recorded in the local audit.
|
|
59
|
-
confirm:
|
|
60
|
-
type: boolean
|
|
61
|
-
default: false
|
|
62
|
-
description: Set true only after the user approves the exact release target.
|
|
63
|
-
required:
|
|
64
|
-
- artifact
|
|
65
|
-
- version
|
|
66
|
-
additionalProperties: false
|
|
67
|
-
authority:
|
|
68
|
-
filesystem: true
|
|
69
|
-
editor_mutation: false
|
|
70
|
-
network: true
|
|
71
|
-
credentials: false
|
|
72
|
-
publish: true
|
|
73
|
-
evidence_checks:
|
|
74
|
-
- release-recorded
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
id: tool/creator-releases
|
|
2
|
-
kind: tool
|
|
3
|
-
version: 1.0.2
|
|
4
|
-
summary: "Deprecated: lists releases on the legacy creator API. For summer.games use summer_store_releases."
|
|
5
|
-
use_when:
|
|
6
|
-
- an old project that published a .pck to the legacy Summercraft creator channel with an sc_ creator token
|
|
7
|
-
- paging legacy creator releases by projectId and cursor
|
|
8
|
-
do_not_use_when:
|
|
9
|
-
- what players get on summer.games, or whether a release landed — summer_store_releases
|
|
10
|
-
- publishing a new release — summer_publish_build
|
|
11
|
-
facets:
|
|
12
|
-
lifecycle:
|
|
13
|
-
- launch
|
|
14
|
-
domains:
|
|
15
|
-
- creator
|
|
16
|
-
- deployment
|
|
17
|
-
compatibility:
|
|
18
|
-
engine: ">=4.6"
|
|
19
|
-
source: official
|
|
20
|
-
license: MIT
|
|
21
|
-
status: deprecated
|
|
22
|
-
aliases:
|
|
23
|
-
- summer_creator_releases
|
|
24
|
-
implementation:
|
|
25
|
-
module: src/core/capabilities/creator.ts
|
|
26
|
-
export: listCreatorReleases
|
|
27
|
-
surfaces:
|
|
28
|
-
mcp:
|
|
29
|
-
tool_name: summer_creator_releases
|
|
30
|
-
remote: true
|
|
31
|
-
cli:
|
|
32
|
-
command: summer releases
|
|
33
|
-
input_schema:
|
|
34
|
-
type: object
|
|
35
|
-
properties:
|
|
36
|
-
projectId:
|
|
37
|
-
type: string
|
|
38
|
-
description: Creator project ID. Defaults to ~/.summer/config.json.
|
|
39
|
-
limit:
|
|
40
|
-
type: integer
|
|
41
|
-
minimum: 1
|
|
42
|
-
maximum: 100
|
|
43
|
-
default: 20
|
|
44
|
-
cursor:
|
|
45
|
-
type: string
|
|
46
|
-
description: Opaque nextCursor from the previous page.
|
|
47
|
-
additionalProperties: false
|
|
48
|
-
authority:
|
|
49
|
-
filesystem: false
|
|
50
|
-
editor_mutation: false
|
|
51
|
-
network: true
|
|
52
|
-
credentials: false
|
|
53
|
-
publish: false
|