summer-engine 4.3.0 → 4.5.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.
Files changed (218) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +3 -1
  3. package/.codex-plugin/plugin.json +5 -3
  4. package/.cursor-plugin/plugin.json +4 -2
  5. package/.factory-plugin/plugin.json +4 -2
  6. package/AGENTS.md +2 -2
  7. package/CHANGELOG.md +87 -0
  8. package/README.md +67 -27
  9. package/commands/start.md +19 -15
  10. package/commands/summer.md +1 -0
  11. package/dist/cli/command-usage.d.ts +3 -1
  12. package/dist/cli/command-usage.js +10 -4
  13. package/dist/cli/commands/create.d.ts +1 -25
  14. package/dist/cli/commands/create.js +10 -138
  15. package/dist/cli/commands/dev.d.ts +66 -0
  16. package/dist/cli/commands/dev.js +234 -0
  17. package/dist/cli/commands/install.d.ts +15 -58
  18. package/dist/cli/commands/install.js +31 -481
  19. package/dist/cli/commands/login.js +1 -1
  20. package/dist/cli/commands/ops.d.ts +17 -1
  21. package/dist/cli/commands/ops.js +97 -9
  22. package/dist/cli/commands/preview.d.ts +58 -0
  23. package/dist/cli/commands/preview.js +181 -0
  24. package/dist/cli/commands/run.d.ts +1 -17
  25. package/dist/cli/commands/run.js +17 -206
  26. package/dist/cli/index.js +5 -0
  27. package/dist/core/agent-host.d.ts +3 -0
  28. package/dist/core/agent-host.js +1 -1
  29. package/dist/core/api-client.d.ts +10 -2
  30. package/dist/core/api-client.js +13 -8
  31. package/dist/core/capabilities/agent-playbook.js +1 -0
  32. package/dist/core/capabilities/apps.d.ts +9 -9
  33. package/dist/core/capabilities/asset-import.d.ts +4 -1
  34. package/dist/core/capabilities/asset-import.js +15 -6
  35. package/dist/core/capabilities/capture-gameplay.js +1 -1
  36. package/dist/core/capabilities/connect-signal.d.ts +2 -2
  37. package/dist/core/capabilities/content-make/index.d.ts +81 -0
  38. package/dist/core/capabilities/content-make/index.js +706 -0
  39. package/dist/core/capabilities/content-make/media.d.ts +57 -0
  40. package/dist/core/capabilities/content-make/media.js +172 -0
  41. package/dist/core/capabilities/content-make/plan.d.ts +130 -0
  42. package/dist/core/capabilities/content-make/plan.js +433 -0
  43. package/dist/core/capabilities/content-make/render.d.ts +64 -0
  44. package/dist/core/capabilities/content-make/render.js +370 -0
  45. package/dist/core/capabilities/content-make/study.d.ts +13 -0
  46. package/dist/core/capabilities/content-make/study.js +165 -0
  47. package/dist/core/capabilities/content-make/types.d.ts +173 -0
  48. package/dist/core/capabilities/content-make/types.js +6 -0
  49. package/dist/core/capabilities/content-make/voice.d.ts +7 -0
  50. package/dist/core/capabilities/content-make/voice.js +27 -0
  51. package/dist/core/capabilities/events.d.ts +2 -2
  52. package/dist/core/capabilities/export-game.d.ts +4 -1
  53. package/dist/core/capabilities/export-game.js +4 -3
  54. package/dist/core/capabilities/export-presets.d.ts +2 -1
  55. package/dist/core/capabilities/export-presets.js +2 -1
  56. package/dist/core/capabilities/go-live.d.ts +14 -0
  57. package/dist/core/capabilities/go-live.js +408 -0
  58. package/dist/core/capabilities/image-generation.d.ts +6 -6
  59. package/dist/core/capabilities/item-manifest.d.ts +92 -0
  60. package/dist/core/capabilities/item-manifest.js +247 -0
  61. package/dist/core/capabilities/placement.d.ts +7 -7
  62. package/dist/core/capabilities/projects-local.d.ts +68 -0
  63. package/dist/core/capabilities/projects-local.js +172 -0
  64. package/dist/core/capabilities/publish-build.d.ts +13 -2
  65. package/dist/core/capabilities/publish-build.js +87 -21
  66. package/dist/core/capabilities/publish-download.js +1 -1
  67. package/dist/core/capabilities/runtime-control.d.ts +7 -2
  68. package/dist/core/capabilities/runtime-control.js +27 -1
  69. package/dist/core/capabilities/seeing/args.d.ts +2 -2
  70. package/dist/core/capabilities/store-checks.d.ts +45 -0
  71. package/dist/core/capabilities/store-checks.js +85 -0
  72. package/dist/core/capabilities/store-details.d.ts +37 -0
  73. package/dist/core/capabilities/store-details.js +94 -0
  74. package/dist/core/capabilities/summer-bundle.d.ts +21 -2
  75. package/dist/core/capabilities/summer-bundle.js +54 -7
  76. package/dist/core/capabilities/sync-items.d.ts +89 -0
  77. package/dist/core/capabilities/sync-items.js +352 -0
  78. package/dist/core/capabilities/tool-dispatch.js +66 -6
  79. package/dist/core/capabilities/video-ogv.d.ts +49 -0
  80. package/dist/core/capabilities/video-ogv.js +260 -0
  81. package/dist/core/capability-skew.js +2 -2
  82. package/dist/core/cli-usage-beacon.js +26 -2
  83. package/dist/core/cli-usage.d.ts +5 -2
  84. package/dist/core/cli-usage.js +8 -2
  85. package/dist/core/engine-install.d.ts +53 -0
  86. package/dist/core/engine-install.js +93 -0
  87. package/dist/core/engine-installer.d.ts +134 -0
  88. package/dist/core/engine-installer.js +760 -0
  89. package/dist/core/engine-launch.d.ts +126 -0
  90. package/dist/core/engine-launch.js +398 -0
  91. package/dist/core/engine-self-start.d.ts +63 -0
  92. package/dist/core/engine-self-start.js +282 -0
  93. package/dist/core/engine.d.ts +7 -1
  94. package/dist/core/engine.js +9 -2
  95. package/dist/core/error-codes.d.ts +4 -1
  96. package/dist/core/error-codes.js +7 -3
  97. package/dist/core/library-read.d.ts +2 -2
  98. package/dist/core/library-search.d.ts +2 -2
  99. package/dist/core/local-project.d.ts +50 -0
  100. package/dist/core/local-project.js +234 -0
  101. package/dist/core/ops/areas.js +9 -3
  102. package/dist/core/ops/catalog.d.ts +9 -0
  103. package/dist/core/ops/catalog.js +56 -4
  104. package/dist/core/ops/engine-answer.d.ts +4 -1
  105. package/dist/core/ops/engine-answer.js +16 -3
  106. package/dist/core/ops/engine.js +4 -4
  107. package/dist/core/ops/save-files.d.ts +6 -1
  108. package/dist/core/ops/save-files.js +67 -50
  109. package/dist/core/ops/search.js +9 -2
  110. package/dist/core/preview/input.d.ts +35 -0
  111. package/dist/core/preview/input.js +105 -0
  112. package/dist/core/preview/launch-config.d.ts +52 -0
  113. package/dist/core/preview/launch-config.js +137 -0
  114. package/dist/core/preview/page.d.ts +22 -0
  115. package/dist/core/preview/page.js +232 -0
  116. package/dist/core/preview/preview-source.d.ts +179 -0
  117. package/dist/core/preview/preview-source.js +350 -0
  118. package/dist/core/preview/server.d.ts +26 -0
  119. package/dist/core/preview/server.js +218 -0
  120. package/dist/core/telemetry.d.ts +20 -16
  121. package/dist/core/telemetry.js +92 -16
  122. package/dist/core/templates.d.ts +20 -0
  123. package/dist/core/templates.js +300 -3
  124. package/dist/core/tool-profiles.d.ts +2 -2
  125. package/dist/core/tool-profiles.js +1 -0
  126. package/dist/core/usage-fields.d.ts +41 -0
  127. package/dist/core/usage-fields.js +150 -0
  128. package/dist/core/util/sleep.d.ts +3 -0
  129. package/dist/core/util/sleep.js +12 -0
  130. package/dist/mcp/ops-surface.js +28 -2
  131. package/dist/mcp/server.d.ts +14 -4
  132. package/dist/mcp/server.js +42 -8
  133. package/dist/mcp/tool-surface.d.ts +8 -0
  134. package/dist/mcp/tool-surface.js +19 -2
  135. package/dist/mcp/tools/creator-tools.js +92 -14
  136. package/dist/mcp/tools/debug-tools.js +2 -0
  137. package/dist/mcp/tools/local-project-tools.d.ts +6 -0
  138. package/dist/mcp/tools/local-project-tools.js +33 -0
  139. package/dist/mcp/tools/navigation-tools.js +2 -1
  140. package/dist/mcp/tools/project-tools.js +5 -8
  141. package/dist/mcp/tools/seeing-tools.js +1 -1
  142. package/dist/mcp/tools/visual-tools.js +1 -1
  143. package/dist/mcp/tools/with-engine.js +1 -1
  144. package/gemini-extension.json +3 -1
  145. package/library/references/mcp-tools-reference/mcp-tools-reference.md +18 -5
  146. package/library/skills/README.md +12 -8
  147. package/library/skills/animated-loop/SKILL.md +10 -16
  148. package/library/skills/cinematic-cutscene/SKILL.md +7 -18
  149. package/library/skills/headless-scripting/SKILL.md +7 -4
  150. package/library/skills/in-game-purchases/SKILL.md +25 -8
  151. package/library/skills/in-game-purchases/resource.yaml +2 -1
  152. package/library/skills/launch-ready/SKILL.md +351 -0
  153. package/library/skills/launch-ready/resource.yaml +45 -0
  154. package/library/skills/make-content/SKILL.md +202 -0
  155. package/library/skills/make-content/resource.yaml +32 -0
  156. package/library/skills/make-game/SKILL.md +2 -0
  157. package/library/skills/make-game/resource.yaml +2 -1
  158. package/library/skills/navigate-summer/SKILL.md +4 -2
  159. package/library/skills/new-project/SKILL.md +15 -22
  160. package/library/skills/new-project/resource.yaml +2 -1
  161. package/library/skills/painted-25d-world/SKILL.md +366 -0
  162. package/library/skills/painted-25d-world/_building-blocks/blender-route.md +33 -0
  163. package/library/skills/painted-25d-world/_building-blocks/blockout_builder.gd +149 -0
  164. package/library/skills/painted-25d-world/_building-blocks/capture_paint_view.gd +32 -0
  165. package/library/skills/painted-25d-world/_building-blocks/paint_projection.gdshader +23 -0
  166. package/library/skills/painted-25d-world/_building-blocks/paintcam_check.gd +25 -0
  167. package/library/skills/painted-25d-world/_building-blocks/painted_door.gd +17 -0
  168. package/library/skills/painted-25d-world/_building-blocks/painted_location.gd +56 -0
  169. package/library/skills/painted-25d-world/_building-blocks/painted_player.gd +125 -0
  170. package/library/skills/painted-25d-world/_building-blocks/painted_world.gd +111 -0
  171. package/library/skills/painted-25d-world/_building-blocks/tools/cam.py +57 -0
  172. package/library/skills/painted-25d-world/_building-blocks/tools/detail4k.py +81 -0
  173. package/library/skills/painted-25d-world/_building-blocks/tools/overlay.py +17 -0
  174. package/library/skills/painted-25d-world/_building-blocks/tools/preview.py +60 -0
  175. package/library/skills/painted-25d-world/resource.yaml +39 -0
  176. package/library/skills/publish-your-game/SKILL.md +13 -14
  177. package/library/skills/publish-your-game/resource.yaml +4 -3
  178. package/library/skills/release-checklist/SKILL.md +188 -0
  179. package/library/skills/release-checklist/resource.yaml +40 -0
  180. package/library/skills/release-readiness/SKILL.md +1 -1
  181. package/library/skills/release-readiness/resource.yaml +1 -1
  182. package/library/skills/store-art/SKILL.md +1 -1
  183. package/library/skills/store-art/resource.yaml +1 -1
  184. package/library/skills/store-listing/SKILL.md +1 -1
  185. package/library/skills/store-listing/resource.yaml +1 -1
  186. package/library/skills/summer-cli/SKILL.md +2 -0
  187. package/library/skills/summer-store/SKILL.md +295 -39
  188. package/library/skills/summer-store/resource.yaml +9 -3
  189. package/library/skills/trailer-shot/SKILL.md +3 -3
  190. package/library/templates/README.md +5 -0
  191. package/library/tools/content-make/resource.yaml +132 -0
  192. package/library/tools/frame-shot/GUIDE.md +1 -1
  193. package/library/tools/go-live/resource.yaml +67 -0
  194. package/library/tools/import-from-url/GUIDE.md +1 -0
  195. package/library/tools/import-from-url-batch/GUIDE.md +1 -1
  196. package/library/tools/play/GUIDE.md +2 -0
  197. package/library/tools/projects-create-local/resource.yaml +55 -0
  198. package/library/tools/projects-open-local/resource.yaml +51 -0
  199. package/library/tools/publish-build/resource.yaml +3 -3
  200. package/library/tools/screenshot/GUIDE.md +1 -1
  201. package/library/tools/store-set-details/resource.yaml +114 -0
  202. package/library/tools/sync-items/resource.yaml +72 -0
  203. package/package.json +1 -1
  204. package/registry/generated/aliases.json +6 -0
  205. package/registry/generated/counts.json +3 -3
  206. package/registry/generated/gemini-extension.json +3 -1
  207. package/registry/generated/index.json +463 -37
  208. package/registry/generated/marketplace.claude.json +1 -1
  209. package/registry/generated/plugin.claude-directory.json +4 -2
  210. package/registry/generated/plugin.claude.json +3 -1
  211. package/registry/generated/plugin.codex.json +5 -3
  212. package/registry/generated/plugin.cursor.json +4 -2
  213. package/registry/generated/plugin.factory.json +4 -2
  214. package/registry/generated/server.json +2 -2
  215. package/registry/generated/skills-index.md +12 -8
  216. package/registry/generated/skills-registry.json +62 -5
  217. package/registry/operations/legacy-names.json +40 -0
  218. package/registry/operations/public-catalog.json +10144 -387
@@ -0,0 +1,408 @@
1
+ import { createHash } from "node:crypto";
2
+ import { resolveGatewayUrl } from "../config.js";
3
+ import { appendStoreJsonLine } from "../store.js";
4
+ import { createStore, defaultDependencies, listGamesOrThrow, storeToken } from "./publish-build.js";
5
+ import { describeFailure, summarizeChecks } from "./store-checks.js";
6
+ import { BuildToolError } from "./summer-bundle.js";
7
+ /**
8
+ * summer_go_live: take a store game live for players over the same creator
9
+ * store routes Studio uses (PSE /api/creator-store/* -> /v1/management/*),
10
+ * then watch until players get it or a check fails.
11
+ *
12
+ * GET games/{g}, games/{g}/revisions, games/{g}/approval the page and Summer's decision
13
+ * GET games/{g}/build-publications, store/versions the uploads and their checks
14
+ * GET games/{g}/environments/env_production (+ releases/{id}) what players get now
15
+ * POST build-publications/{p}:publish a preview Build, made ready for players
16
+ * POST environments/env_production/release-drafts (+ :publish) release the Build to players
17
+ * POST revisions/{r}:publish (If-Match) publish the store page
18
+ *
19
+ * An agent credential may call the three :publish actions without an owner
20
+ * approval while the person's Agent permissions allow Publish (the platform
21
+ * default; summer-platform internal/management/agent_gate.go). The platform
22
+ * still runs every automated check, and today it holds a game's first launch
23
+ * for Summer's review; that hold is reported as Summer's, never as a step the
24
+ * creator waits on or fixes, because staff review is becoming a step after
25
+ * launch. Calling again continues: the same idempotency keys replay what was
26
+ * already asked, so nothing is requested twice.
27
+ */
28
+ const ENVIRONMENT = "env_production";
29
+ const DEFAULT_WAIT_SECONDS = 600;
30
+ const POLL_MS = 10_000;
31
+ /** Client platforms a bundle Build is released to (web ships as a store version). */
32
+ const RELEASE_PLATFORMS = ["ios", "android", "macos", "windows"];
33
+ const INTAKE_DONE = ["preview_ready", "published", "ready", "succeeded"];
34
+ const SENT_BACK = ["CHANGES_REQUESTED", "REJECTED"];
35
+ const PUBLISHABLE = ["DRAFT", "SUBMITTED", "VERIFYING", "APPROVED"];
36
+ const seg = encodeURIComponent;
37
+ /** The creator store proxy sends this header on as If-Match. */
38
+ const REVISION_HEADER = "x-summer-revision";
39
+ /** Text the store page must have before it can be published (summer-platform internal/creatorcatalog). */
40
+ const PAGE_FIXES = {
41
+ name: "Give the page a title: store.update_listing title.",
42
+ description: "Write the short description: store.update_listing desktop.description.",
43
+ supportedPlatforms: "List the platforms players can play on: store.update_listing targets.",
44
+ contentRating: "Pick a content rating (everyone, everyone10Plus or teen): store.update_listing contentRating.",
45
+ contentDescriptors: "Answer the content descriptors, an empty list when none apply: store.update_listing contentDescriptors.",
46
+ mediaSlots: "An art slot holds the wrong kind or shape of image: fix it with store.set_art (the store-art skill has each slot's size).",
47
+ media: "An image or video is still being checked or was refused: wait a minute, then read store.get_listing and replace a refused one with store.set_art.",
48
+ };
49
+ const text = (value) => (typeof value === "string" && value.trim() ? value : null);
50
+ function keyOf(gameId, ...parts) {
51
+ return createHash("sha256").update(["go-live", gameId, ...parts].join(":")).digest("hex");
52
+ }
53
+ async function optional(read) {
54
+ try {
55
+ return await read();
56
+ }
57
+ catch (error) {
58
+ if (error instanceof BuildToolError && (error.status === 404 || error.code === "store_not_found"))
59
+ return null;
60
+ throw error;
61
+ }
62
+ }
63
+ function pageOf(revision) {
64
+ if (!revision || typeof revision.revisionId !== "string")
65
+ return null;
66
+ const name = text(revision.presentation?.name) ?? text(revision.name);
67
+ const description = text(revision.presentation?.description) ?? text(revision.description);
68
+ const platforms = Array.isArray(revision.supportedPlatforms) ? revision.supportedPlatforms.filter((value) => typeof value === "string") : [];
69
+ const problems = [];
70
+ if (!name)
71
+ problems.push({ field: "name", fix: PAGE_FIXES.name });
72
+ if (!description)
73
+ problems.push({ field: "description", fix: PAGE_FIXES.description });
74
+ if (!platforms.length)
75
+ problems.push({ field: "supportedPlatforms", fix: PAGE_FIXES.supportedPlatforms });
76
+ if (!text(revision.contentRating))
77
+ problems.push({ field: "contentRating", fix: PAGE_FIXES.contentRating });
78
+ if (revision.contentDescriptors == null)
79
+ problems.push({ field: "contentDescriptors", fix: PAGE_FIXES.contentDescriptors });
80
+ return { revisionId: revision.revisionId, state: String(revision.state ?? ""), etag: Number(revision.etagRevision ?? 0), name, platforms, problems };
81
+ }
82
+ function newest(items, order) {
83
+ return items.reduce((best, item) => (!best || order(item) > order(best) ? item : best), null);
84
+ }
85
+ const time = (value) => (typeof value === "string" ? Date.parse(value) || 0 : 0);
86
+ /** The players' side of a Build: on which platforms the active release serves it. */
87
+ function livePlatformsOf(release, buildId) {
88
+ if (!release || !buildId || release.status !== "active" || !Array.isArray(release.targets))
89
+ return [];
90
+ return release.targets.filter((target) => target?.buildId === buildId).map((target) => String(target.target));
91
+ }
92
+ async function read(store, gameId, publicationId) {
93
+ const game = await store.call("GET", `games/${seg(gameId)}`);
94
+ const [revisions, approval, publications, versions, environment] = await Promise.all([
95
+ store.call("GET", `games/${seg(gameId)}/revisions?limit=20`),
96
+ optional(() => store.call("GET", `games/${seg(gameId)}/approval`)),
97
+ store.call("GET", `games/${seg(gameId)}/build-publications`),
98
+ optional(() => store.call("GET", `games/${seg(gameId)}/store/versions`)),
99
+ optional(() => store.call("GET", `games/${seg(gameId)}/environments/${ENVIRONMENT}`)),
100
+ ]);
101
+ const page = pageOf(newest(revisions.items ?? [], (item) => Number(item.sequence ?? 0)));
102
+ const release = environment?.activeReleaseId
103
+ ? await optional(() => store.call("GET", `games/${seg(gameId)}/environments/${ENVIRONMENT}/releases/${seg(String(environment.activeReleaseId))}`))
104
+ : null;
105
+ const exported = (publications.items ?? []).filter((item) => item?.source?.kind === "exported-game" || item?.source?.kind === undefined);
106
+ let chosen = null;
107
+ if (publicationId) {
108
+ chosen = await store.call("GET", `games/${seg(gameId)}/build-publications/${seg(publicationId)}`);
109
+ }
110
+ else {
111
+ const candidate = newest(exported.filter((item) => !["failed", "cancelled"].includes(item.state)), (item) => time(item.createdAt ?? item.updatedAt));
112
+ // The list may leave the client pack out: read the chosen one in full.
113
+ chosen = candidate ? await store.call("GET", `games/${seg(gameId)}/build-publications/${seg(String(candidate.id ?? candidate.publicationId))}`) : null;
114
+ }
115
+ let build = null;
116
+ if (chosen) {
117
+ const buildId = text(chosen.buildId);
118
+ const failed = chosen.state === "failed" || chosen.state === "cancelled";
119
+ build = {
120
+ publicationId: String(chosen.id ?? chosen.publicationId ?? publicationId),
121
+ buildId,
122
+ version: text(chosen.clientVersion) ?? text(chosen.version),
123
+ state: String(chosen.state ?? ""),
124
+ hosted: chosen.build?.executionMode ? chosen.build.executionMode === "hosted" : null,
125
+ checks: summarizeChecks(chosen),
126
+ livePlatforms: livePlatformsOf(release, buildId),
127
+ ...(failed ? { failure: describeFailure(text(chosen.errorCode), text(chosen.errorMessage)) } : {}),
128
+ };
129
+ }
130
+ const versionRows = (versions?.items ?? []).map((item) => ({
131
+ platform: String(item.platform ?? ""),
132
+ status: String(item.status ?? ""),
133
+ ...(text(item.rejectionReason) ? { reason: String(item.rejectionReason) } : {}),
134
+ }));
135
+ return { game, page, approval, build, environment, release, versions: versionRows };
136
+ }
137
+ /** What the platform is waiting on, said so an agent never tells a creator to wait for a review. */
138
+ function summerHold(approval) {
139
+ const state = text(approval?.state) ?? "unknown";
140
+ return {
141
+ by: "summer",
142
+ approvalState: state,
143
+ message: "Every automated check passed. The platform still holds a game's first launch on Summer's side; there is nothing to fix and nothing to ask for. " +
144
+ "This request stays in place and goes live by itself once the platform releases it (Summer's review is becoming a step after launch).",
145
+ };
146
+ }
147
+ function sentBack(snapshot) {
148
+ const review = snapshot.approval?.latestReview;
149
+ if (!snapshot.page || !SENT_BACK.includes(snapshot.page.state))
150
+ return null;
151
+ return {
152
+ ok: true,
153
+ status: "needs_changes",
154
+ gameId: snapshot.game.id ?? null,
155
+ page: { revisionId: snapshot.page.revisionId, state: snapshot.page.state },
156
+ decision: review ? { decision: review.decision ?? null, decidedBy: review.decidedBy ?? null, feedback: text(review.feedback) } : null,
157
+ next: text(review?.feedback)
158
+ ? "Fix every point in decision.feedback with store.update_listing (it starts a new draft), then call summer_go_live again."
159
+ : "Summer sent the page back without written feedback: tell the owner and ask what to change; then edit with store.update_listing and call summer_go_live again.",
160
+ };
161
+ }
162
+ /** Which client platforms of the Build passed and may be released, and which did not. */
163
+ function releasable(build) {
164
+ const passed = build.checks.checks.filter((check) => check.state === "passed" && RELEASE_PLATFORMS.includes(check.platform)).map((check) => check.platform);
165
+ return { targets: passed, held: build.checks.checks.filter((check) => check.state === "failed") };
166
+ }
167
+ function view(snapshot) {
168
+ const { page, build } = snapshot;
169
+ return {
170
+ game: { gameId: snapshot.game.id ?? null, name: text(snapshot.game.name) },
171
+ page: page ? { revisionId: page.revisionId, state: page.state, name: page.name, platforms: page.platforms, live: page.state === "PUBLISHED" } : null,
172
+ build: build
173
+ ? {
174
+ publicationId: build.publicationId,
175
+ version: build.version,
176
+ state: build.state,
177
+ checksState: build.failure ? "failed" : build.checks.state,
178
+ checks: build.checks.checks,
179
+ livePlatforms: build.livePlatforms,
180
+ ...(build.failure ? { failure: build.failure } : {}),
181
+ }
182
+ : null,
183
+ downloads: snapshot.versions,
184
+ approvalState: text(snapshot.approval?.state),
185
+ };
186
+ }
187
+ function isLive(snapshot) {
188
+ if (snapshot.page?.state !== "PUBLISHED")
189
+ return false;
190
+ if (!snapshot.build)
191
+ return true;
192
+ const { targets } = releasable(snapshot.build);
193
+ return targets.length > 0 && targets.every((platform) => snapshot.build.livePlatforms.includes(platform));
194
+ }
195
+ /** Refusals no publish call can get past: each is a fix for the creator. */
196
+ function blockers(snapshot) {
197
+ const out = [];
198
+ if (!snapshot.page)
199
+ out.push({ field: "store page", fix: "This game has no store page yet: create it with games.create, or fill it with store.update_listing." });
200
+ for (const problem of snapshot.page?.problems ?? [])
201
+ out.push(problem);
202
+ const build = snapshot.build;
203
+ if (build?.failure)
204
+ out.push({ field: "build", ...build.failure });
205
+ if (build?.checks.packFailure)
206
+ out.push({ field: "build", ...build.checks.packFailure });
207
+ if (build && !build.failure && build.checks.state !== "checking" && releasable(build).targets.length === 0) {
208
+ out.push({ field: "build", code: "no_platform_passed", fix: "No phone or desktop platform of this Build passed its checks. Apply each check's fix, export again and upload with summer_publish_build." });
209
+ for (const check of releasable(build).held)
210
+ out.push({ field: `build ${check.platform}`, ...check });
211
+ }
212
+ for (const version of snapshot.versions.filter((row) => row.status === "rejected")) {
213
+ out.push({ field: `download ${version.platform}`, code: "store_version_rejected", ...(version.reason ? { message: version.reason } : {}), fix: "Fix what the reason names, export the download again and upload it with summer_publish_build." });
214
+ }
215
+ return out;
216
+ }
217
+ function permissionRefused() {
218
+ return {
219
+ ok: true,
220
+ status: "needs_permission",
221
+ next: "Nothing went live: the person's Agent permissions do not let this agent publish. Ask them to turn on Publish (Studio > Agent permissions) and call summer_go_live again, " +
222
+ "or send them the approval link store.publish returns.",
223
+ };
224
+ }
225
+ /** Make the Build ready for players and ask for its release; returns a hold when the platform declines. */
226
+ async function releaseBuild(store, gameId, snapshot, deps, deadline) {
227
+ const build = snapshot.build;
228
+ const { targets } = releasable(build);
229
+ if (!build.buildId || !targets.length || targets.every((platform) => build.livePlatforms.includes(platform)))
230
+ return null;
231
+ const publicationPath = `games/${seg(gameId)}/build-publications/${seg(build.publicationId)}`;
232
+ let detail = await store.call("GET", `games/${seg(gameId)}/builds/${seg(build.buildId)}`);
233
+ if (detail.status === "preview_ready") {
234
+ try {
235
+ await store.call("POST", `${publicationPath}:publish`, { body: {}, key: keyOf(gameId, "prepare", build.publicationId) });
236
+ }
237
+ catch (error) {
238
+ if (error instanceof BuildToolError && error.status === 409 && error.code === "deployment_not_actionable")
239
+ return summerHold(snapshot.approval);
240
+ throw error;
241
+ }
242
+ while (detail.status !== "ready" && deps.now() < deadline) {
243
+ await deps.sleep(POLL_MS);
244
+ detail = await store.call("GET", `games/${seg(gameId)}/builds/${seg(build.buildId)}`);
245
+ if (detail.status === "failed")
246
+ throw new BuildToolError("build_failed", "Summer could not prepare this Build for players.", "Recovery: export again and upload with a new clientVersion.");
247
+ }
248
+ if (detail.status !== "ready")
249
+ return { by: "summer", message: "Summer is preparing the Build for players." };
250
+ }
251
+ const environment = snapshot.environment;
252
+ if (environment?.desiredReleaseId && environment.desiredReleaseId !== environment.activeReleaseId)
253
+ return null; // a release is already on its way
254
+ const pack = (await store.call("GET", publicationPath)).clientPackage ?? {};
255
+ const selection = {};
256
+ const hosted = (detail.executionMode ?? (build.hosted ? "hosted" : "standalone")) === "hosted";
257
+ if (hosted)
258
+ selection.server = { buildId: build.buildId, qualificationId: "" };
259
+ for (const platform of targets) {
260
+ const check = (pack.verifications ?? []).find((row) => row.platform === platform && row.state === "ready" && row.qualificationId);
261
+ if (check)
262
+ selection[platform] = { buildId: build.buildId, uploadId: String(pack.id), qualificationId: String(check.qualificationId) };
263
+ }
264
+ const base = `${environment?.revision ?? 0}:${environment?.activeReleaseId ?? ""}`;
265
+ const draft = await store.call("POST", `games/${seg(gameId)}/environments/${ENVIRONMENT}/release-drafts`, {
266
+ body: { title: build.version ?? "Release", notes: "", targets: selection },
267
+ key: keyOf(gameId, "draft", build.publicationId, base),
268
+ });
269
+ await store.call("POST", `games/${seg(gameId)}/environments/${ENVIRONMENT}/release-drafts/${seg(String(draft.id))}:publish`, {
270
+ body: { targets: Object.keys(selection), replaceTargets: true },
271
+ key: keyOf(gameId, "release", build.publicationId, base),
272
+ // The proxy forwards x-summer-revision as If-Match (it passes no other header but idempotency-key).
273
+ headers: { [REVISION_HEADER]: `"${draft.revision}"` },
274
+ });
275
+ return null;
276
+ }
277
+ /** Publish the store page revision; returns a result to answer with when the platform refuses it. */
278
+ async function publishPage(store, gameId, snapshot) {
279
+ const page = snapshot.page;
280
+ if (!PUBLISHABLE.includes(page.state))
281
+ return null;
282
+ try {
283
+ await store.call("POST", `games/${seg(gameId)}/revisions/${seg(page.revisionId)}:publish`, {
284
+ body: {},
285
+ key: keyOf(gameId, "page", page.revisionId),
286
+ headers: { [REVISION_HEADER]: `"game-revision:${page.revisionId}:${page.etag}"` },
287
+ });
288
+ }
289
+ catch (error) {
290
+ if (!(error instanceof BuildToolError))
291
+ throw error;
292
+ // Already asked (a replay, or a request in flight): keep watching it.
293
+ if (error.status === 409 && ["publication_in_progress", "idempotency_conflict"].includes(error.code))
294
+ return null;
295
+ if (error.code === "owner_approval_required")
296
+ return permissionRefused();
297
+ if (error.code === "store_page_incomplete") {
298
+ const missing = Array.isArray(error.detail?.missing) ? error.detail.missing : [];
299
+ return {
300
+ ok: true,
301
+ status: "blocked",
302
+ gameId,
303
+ failures: missing.map((field) => ({ field, fix: PAGE_FIXES[field] ?? "Finish this field of the store page with store.update_listing." })),
304
+ next: "Fix each failure, then call summer_go_live again.",
305
+ };
306
+ }
307
+ if (error.code === "playable_release_required") {
308
+ return {
309
+ ok: true,
310
+ status: "blocked",
311
+ gameId,
312
+ failures: [{ field: "build", code: error.code, fix: "A released page needs a build players can play on a platform it lists: upload one with summer_publish_build (or set release stage coming_soon with store.update_listing), then call summer_go_live again." }],
313
+ };
314
+ }
315
+ throw error;
316
+ }
317
+ return null;
318
+ }
319
+ export async function goLive(input, overrides = {}) {
320
+ const deps = { ...defaultDependencies, ...overrides };
321
+ const gameId = input.gameId?.trim();
322
+ if (!gameId)
323
+ return listGamesOrThrow(deps);
324
+ const store = createStore(await resolveGatewayUrl(), await storeToken(deps), deps);
325
+ const deadline = deps.now() + Math.min(Math.max(input.waitSeconds ?? DEFAULT_WAIT_SECONDS, 0), 1800) * 1000;
326
+ let snapshot = await read(store, gameId, input.publicationId);
327
+ const back = sentBack(snapshot);
328
+ if (back)
329
+ return back;
330
+ if (isLive(snapshot))
331
+ return { ok: true, status: "live", ...view(snapshot), next: "Players get it now. Confirm it as a player on summer.games and in the Summer Games apps." };
332
+ const stops = blockers(snapshot);
333
+ if (stops.length)
334
+ return { ok: true, status: "blocked", ...view(snapshot), failures: stops, next: "Fix each failure, then call summer_go_live again." };
335
+ if (input.confirm !== true) {
336
+ return {
337
+ ok: true,
338
+ status: "confirmation_required",
339
+ ...view(snapshot),
340
+ next: "Show the user this exact game, store page and build (version and platforms). Only after they approve going live, call again with confirm=true. Nothing was published.",
341
+ };
342
+ }
343
+ const audit = { at: new Date(deps.now()).toISOString(), operation: "go_live", face: input.face, gameId, revisionId: snapshot.page.revisionId, publicationId: snapshot.build?.publicationId ?? null };
344
+ await appendStoreJsonLine("creator-audit.jsonl", { ...audit, outcome: "started" });
345
+ let hold = null;
346
+ try {
347
+ // The Build first: a released page needs a playable release (or one on its way).
348
+ while (snapshot.build && snapshot.build.checks.state === "checking" && deps.now() < deadline) {
349
+ await deps.sleep(POLL_MS);
350
+ snapshot = await read(store, gameId, snapshot.build.publicationId);
351
+ }
352
+ if (snapshot.build?.checks.state === "checking") {
353
+ return { ok: true, status: "waiting", waitingOn: "automated_checks", ...view(snapshot), next: "Summer's automated checks are still running. Call summer_go_live again with confirm=true to keep going; nothing is asked twice." };
354
+ }
355
+ const stopped = blockers(snapshot);
356
+ if (stopped.length)
357
+ return { ok: true, status: "blocked", ...view(snapshot), failures: stopped, next: "Fix each failure, then call summer_go_live again." };
358
+ if (snapshot.build)
359
+ hold = await releaseBuild(store, gameId, snapshot, deps, deadline);
360
+ const refused = await publishPage(store, gameId, snapshot);
361
+ if (refused)
362
+ return { ...view(snapshot), ...refused };
363
+ }
364
+ catch (error) {
365
+ if (error instanceof BuildToolError && error.code === "owner_approval_required")
366
+ return { ...view(snapshot), ...permissionRefused() };
367
+ await appendStoreJsonLine("creator-audit.jsonl", { ...audit, outcome: "failed", code: error instanceof BuildToolError ? error.code : "go_live_failed" });
368
+ throw error;
369
+ }
370
+ await appendStoreJsonLine("creator-audit.jsonl", { ...audit, outcome: "requested" });
371
+ // Watch until players get it, a check fails or the wait ends.
372
+ for (;;) {
373
+ snapshot = await read(store, gameId, snapshot.build?.publicationId);
374
+ const again = sentBack(snapshot);
375
+ if (again)
376
+ return again;
377
+ if (isLive(snapshot)) {
378
+ await appendStoreJsonLine("creator-audit.jsonl", { ...audit, outcome: "live" });
379
+ const held = snapshot.build ? releasable(snapshot.build).held : [];
380
+ return {
381
+ ok: true,
382
+ status: "live",
383
+ ...view(snapshot),
384
+ ...(held.length ? { notReleased: held } : {}),
385
+ next: held.length
386
+ ? `Live. ${held.map((check) => check.platform).join(", ")} did not pass its checks and is not released: apply each fix and upload a new version.`
387
+ : "Live: players get it now. Confirm it as a player on summer.games and in the Summer Games apps.",
388
+ };
389
+ }
390
+ const failed = blockers(snapshot);
391
+ if (failed.length)
392
+ return { ok: true, status: "blocked", ...view(snapshot), failures: failed, next: "Fix each failure, then call summer_go_live again." };
393
+ if (deps.now() >= deadline)
394
+ break;
395
+ await deps.sleep(POLL_MS);
396
+ }
397
+ const approved = text(snapshot.approval?.state) === "APPROVED";
398
+ return {
399
+ ok: true,
400
+ status: "waiting",
401
+ waitingOn: approved ? "activation" : "summer",
402
+ ...view(snapshot),
403
+ ...(approved ? {} : { hold: hold ?? summerHold(snapshot.approval) }),
404
+ next: approved
405
+ ? "The platform is activating it. Call summer_go_live again with confirm=true to keep watching; nothing is asked twice."
406
+ : "Nothing to fix. Call summer_go_live again with confirm=true to keep watching; it reports the moment players get it.",
407
+ };
408
+ }
@@ -16,21 +16,21 @@ export declare const imageGenerationArgsSchema: z.ZodObject<{
16
16
  }, "strip", z.ZodTypeAny, {
17
17
  prompt: string;
18
18
  style: string;
19
- width?: number | undefined;
20
- height?: number | undefined;
21
- options?: Record<string, any> | undefined;
22
19
  out?: string | undefined;
23
20
  model?: string | undefined;
21
+ options?: Record<string, any> | undefined;
22
+ width?: number | undefined;
23
+ height?: number | undefined;
24
24
  referenceImageUrl?: string | undefined;
25
25
  removeBackground?: boolean | undefined;
26
26
  aspectRatio?: "1:1" | "16:9" | "9:16" | "4:3" | "3:4" | "3:2" | "2:3" | "5:4" | "4:5" | "21:9" | undefined;
27
27
  }, {
28
28
  prompt: string;
29
- width?: number | undefined;
30
- height?: number | undefined;
31
- options?: Record<string, any> | undefined;
32
29
  out?: string | undefined;
33
30
  model?: string | undefined;
31
+ options?: Record<string, any> | undefined;
32
+ width?: number | undefined;
33
+ height?: number | undefined;
34
34
  style?: string | undefined;
35
35
  referenceImageUrl?: string | undefined;
36
36
  removeBackground?: boolean | undefined;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * summer.items.json: the Game's items, keyed by creator slug, next to
3
+ * summer.build.json in the project root and committed with the game.
4
+ *
5
+ * It names no Game, environment or platform id: the same file syncs to the
6
+ * staging Game and to the production Game, and the slug (the creator key the
7
+ * game's code uses, e.g. "emote_ponder") is what identifies an item in both.
8
+ * The gameId comes with each sync call, as with summer_publish_build.
9
+ *
10
+ * {
11
+ * "schema": "summer.items/v1",
12
+ * "items": [
13
+ * {
14
+ * "slug": "emote_ponder",
15
+ * "name": "Ponder",
16
+ * "description": "Strike a thoughtful pose.",
17
+ * "class": "COSMETIC",
18
+ * "cosmetic": { "familyId": "emote", "variantKey": "ponder" },
19
+ * "tags": ["emote"],
20
+ * "ownership": "OWNED",
21
+ * "priceSparks": 150,
22
+ * "media": ["art/items/emote_ponder.png"]
23
+ * }
24
+ * ]
25
+ * }
26
+ *
27
+ * The checks here are the platform's own create rules (management API
28
+ * item-definitions), so a file that passes is not refused for its shape.
29
+ */
30
+ export declare const ITEMS_MANIFEST_FILE = "summer.items.json";
31
+ export declare const ITEMS_MANIFEST_SCHEMA = "summer.items/v1";
32
+ /** The platform's slug rule: 1-64 characters, lowercase, words joined by one ".", "_" or "-". */
33
+ export declare const ITEM_SLUG: RegExp;
34
+ export declare const ITEM_CLASSES: readonly ["STANDARD", "COSMETIC"];
35
+ export declare const OWNERSHIP_MODES: readonly ["INSTANCE", "QUANTITY"];
36
+ export declare const OWNERSHIP_POLICIES: readonly ["SOULBOUND", "OWNED", "MARKETABLE"];
37
+ export declare const MEDIA_TYPES: Record<string, {
38
+ kind: "IMAGE" | "MODEL_3D";
39
+ contentType: string;
40
+ maxBytes: number;
41
+ }>;
42
+ export interface ManifestItem {
43
+ slug: string;
44
+ name: string;
45
+ description: string;
46
+ class: (typeof ITEM_CLASSES)[number];
47
+ cosmetic?: {
48
+ familyId: string;
49
+ variantKey: string;
50
+ };
51
+ tags: string[];
52
+ ownershipMode: (typeof OWNERSHIP_MODES)[number];
53
+ maxOwned?: number;
54
+ ownership: (typeof OWNERSHIP_POLICIES)[number];
55
+ consumable: boolean;
56
+ /** Lifetime mint limit; absent is unlimited. */
57
+ supply?: number;
58
+ /** Base price in Sparks. Absent: this file sets no price (a new item is not sold; an existing price stays). */
59
+ priceSparks?: number;
60
+ backgroundColor?: string;
61
+ /** Project-relative paths, in store order. */
62
+ media: string[];
63
+ }
64
+ export interface ItemsManifest {
65
+ path: string;
66
+ project: string;
67
+ items: ManifestItem[];
68
+ }
69
+ /** The platform behavior a manifest entry declares (item-definitions create body). */
70
+ export declare function behaviorOf(item: ManifestItem): {
71
+ cosmetic?: {
72
+ familyId: string;
73
+ variantKey: string;
74
+ } | undefined;
75
+ rights: {
76
+ policy: "SOULBOUND" | "OWNED" | "MARKETABLE";
77
+ consumable: boolean;
78
+ directTransferAllowed: boolean;
79
+ giftingAfterOwnershipAllowed: boolean;
80
+ publicResalePolicyEligible: boolean;
81
+ };
82
+ issuance: {
83
+ serverMintAllowed: boolean;
84
+ supportGrantAllowed: boolean;
85
+ };
86
+ maxOwned?: number | undefined;
87
+ class: "STANDARD" | "COSMETIC";
88
+ ownershipMode: "INSTANCE" | "QUANTITY";
89
+ };
90
+ /** Parse and check a manifest document; every problem is reported at once. */
91
+ export declare function parseItemsManifest(project: string, document: unknown, path?: string): ItemsManifest;
92
+ export declare function readItemsManifest(project: string, file?: string): Promise<ItemsManifest>;