gamesteel 0.4.0 → 0.4.1

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/dist/gamesteel.js CHANGED
@@ -64163,12 +64163,52 @@ function write(file, before2, after, paste) {
64163
64163
  return { kind: "refused", why: `${file} could not be written: ${error2.message}`, text: paste };
64164
64164
  }
64165
64165
  }
64166
- function tomlBody(agent, launch) {
64166
+ var CODEX_SCHEMA_BYTES = 131072;
64167
+ var CODEX_SETTINGS = [
64168
+ "JAVA_HOME",
64169
+ "ANDROID_HOME",
64170
+ "ANDROID_SDK_ROOT",
64171
+ "BLENDER",
64172
+ "GAMESTEEL_KEY",
64173
+ "GAMESTEEL_SITE",
64174
+ "GAMESTEEL_ANDROID_TEMPLATE",
64175
+ "HTTP_PROXY",
64176
+ "HTTPS_PROXY",
64177
+ "NO_PROXY",
64178
+ "http_proxy",
64179
+ "https_proxy",
64180
+ "no_proxy"
64181
+ ];
64182
+ var isTable = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
64183
+ var OWN_TOML_KEYS = /* @__PURE__ */ new Set([
64184
+ "command",
64185
+ "args",
64186
+ "env",
64187
+ "startup_timeout_sec",
64188
+ "tool_timeout_sec",
64189
+ "default_tools_approval_mode",
64190
+ "tool_input_schema_max_bytes",
64191
+ "omit_tools_from",
64192
+ "env_vars"
64193
+ ]);
64194
+ var tomlKey = (key) => /^[A-Za-z0-9_-]+$/.test(key) ? key : tomlString(key);
64195
+ function tomlBody(agent, launch, before2 = {}) {
64196
+ const theirs = Object.entries(isTable(before2["env"]) ? before2["env"] : {}).filter(([name2]) => !name2.startsWith("GAMESTEEL_"));
64197
+ const env = theirs.length === 0 ? launch.env : { ...Object.fromEntries(theirs), ...launch.env };
64198
+ const approval = typeof before2["default_tools_approval_mode"] === "string" ? before2["default_tools_approval_mode"] : "approve";
64167
64199
  return [
64168
64200
  `command = ${tomlString(launch.command)}`,
64169
64201
  `args = [${launch.args.map(tomlString).join(", ")}]`,
64170
- ...launch.env === void 0 ? [] : [`env = ${tomlValue(launch.env)}`],
64171
- ...agent.id === "codex" ? [`startup_timeout_sec = ${String(START_SECONDS)}`, `tool_timeout_sec = ${String(CALL_SECONDS)}`] : []
64202
+ ...env === void 0 ? [] : [`env = ${tomlValue(env)}`],
64203
+ ...agent.id === "codex" ? [
64204
+ `startup_timeout_sec = ${String(START_SECONDS)}`,
64205
+ `tool_timeout_sec = ${String(CALL_SECONDS)}`,
64206
+ `default_tools_approval_mode = ${tomlString(approval)}`,
64207
+ `tool_input_schema_max_bytes = ${String(CODEX_SCHEMA_BYTES)}`,
64208
+ 'omit_tools_from = ["deferred"]',
64209
+ `env_vars = [${CODEX_SETTINGS.map(tomlString).join(", ")}]`
64210
+ ] : [],
64211
+ ...Object.entries(before2).filter(([key]) => !OWN_TOML_KEYS.has(key)).map(([key, value]) => `${tomlKey(key)} = ${tomlValue(value)}`)
64172
64212
  ];
64173
64213
  }
64174
64214
  function jsonEntry(agent, launch) {
@@ -64356,7 +64396,7 @@ function launchOrNull(agent, env) {
64356
64396
  return null;
64357
64397
  }
64358
64398
  }
64359
- var tomlFor = (agent, form) => tomlBody(agent, form.launch);
64399
+ var tomlFor = (agent, form, before2) => tomlBody(agent, form.launch, before2);
64360
64400
  var tomlListFor = (form) => [
64361
64401
  `name = ${tomlString(NAME)}`,
64362
64402
  'transport = "stdio"',
@@ -64420,7 +64460,8 @@ function connectWith(agent, env, print, launch) {
64420
64460
  }
64421
64461
  }
64422
64462
  if (agent.way === "toml") {
64423
- const body3 = tomlFor(agent, form);
64463
+ const there2 = before2 === null ? void 0 : tomlTable(before2, NAME);
64464
+ const body3 = tomlFor(agent, form, isTable(there2) ? there2 : void 0);
64424
64465
  const paste2 = [`[mcp_servers.${NAME}]`, ...body3].join("\n");
64425
64466
  try {
64426
64467
  return viaFile(file, before2, setTomlServer(withoutOld(agent, env, before2) ?? "", NAME, body3), print, paste2);
@@ -82098,6 +82139,10 @@ function admit(bound, value, at5) {
82098
82139
  return refuse2("unknown-argument", `${at5}.${key}`, raw[key], known2.join(", "), null, `${at5} does not take '${key}'; it takes ${known2.join(", ")}. unknown arguments are refused rather than ignored, because an ignored one is how a call comes to look stronger than it was`);
82099
82140
  }
82100
82141
  }
82142
+ const missing2 = known2.filter((key) => raw[key] === void 0 && bound.required.includes(key));
82143
+ if (missing2.length > 1) {
82144
+ return refuse2("missing-argument", `${at5}.${missing2[0] ?? ""}`, void 0, "required", null, `${at5} needs ${missing2.slice(0, -1).join(", ")} and ${missing2[missing2.length - 1] ?? ""}`);
82145
+ }
82101
82146
  const out2 = {};
82102
82147
  for (const key of known2.sort()) {
82103
82148
  const field2 = bound.fields[key];
@@ -95653,7 +95698,7 @@ var START = [
95653
95698
  "## Read as you go",
95654
95699
  "",
95655
95700
  "- game: the game text. Read it before writing one.",
95656
- "- scripts, then api: how scripts run, and the exact TypeScript they are checked against. Most of a good game",
95701
+ "- scripts, then api and api-game: how scripts run, and the exact TypeScript they are checked against. Most of a good game",
95657
95702
  " lives in its scripts.",
95658
95703
  "- drawing: art drawn in code: shapes, gradients, soft light, curves, panels, every option.",
95659
95704
  "- style: craft -- palette, light, texture, depth, UI and type, and the tells that make art look generated.",
@@ -95692,6 +95737,9 @@ var START = [
95692
95737
  " the title, a menu, a hit, the ending. Words are cheap; pictures cost more, so look on purpose.",
95693
95738
  "6. Polish until it is a game you would show someone.",
95694
95739
  "",
95740
+ "games new and games open answer with the game's link, where the person sees and plays it in the editor: give it to",
95741
+ "the person whenever there is something to see, the first time above all. A look with shaders is drawn there too.",
95742
+ "",
95695
95743
  "## Made for this game, never repeated",
95696
95744
  "",
95697
95745
  "Before a character, a creature, a vehicle, a building, or any song:",
@@ -96028,16 +96076,27 @@ var MODEL = [
96028
96076
  ];
96029
96077
  var SCRIPTS = [
96030
96078
  ...SCRIPTS_PRIMER,
96031
- "The exact declarations every script is checked against are the api topic."
96079
+ "The exact declarations every script is checked against are the api and api-game topics."
96032
96080
  ];
96033
- function agentApi() {
96081
+ function agentApi(part) {
96034
96082
  const own3 = (what, name2) => `/** ${what}: one of the game's own, own:<name>, made for it. */
96035
96083
  type ${name2} = \`own:\${string}\`
96036
96084
  `;
96037
- return [
96038
- "// The script API, as the type checker holds scripts to it. A sound and a song are the game's own (own:<name>); the",
96039
- "// stock names older games still use are left out here.",
96040
- SCRIPT_API.trimEnd().replace(/(?:\/\*\*[^\n]*\*\/\n)?type SoundName = [^\n]*\n/, () => own3("A sound", "SoundName")).replace(/(?:\/\*\*[^\n]*\*\/\n)?type MusicName = [^\n]*\n/, () => own3("A song", "MusicName"))
96085
+ const whole3 = SCRIPT_API.trimEnd().replace(/(?:\/\*\*[^\n]*\*\/\n)?type SoundName = [^\n]*\n/, () => own3("A sound", "SoundName")).replace(/(?:\/\*\*[^\n]*\*\/\n)?type MusicName = [^\n]*\n/, () => own3("A song", "MusicName"));
96086
+ const at5 = whole3.indexOf("\ninterface Game {");
96087
+ if (at5 < 0)
96088
+ throw new Error("the script API has no interface Game to split it at");
96089
+ return part === "objects" ? [
96090
+ "// The script API, part one, as the type checker holds scripts to it: the shared types, tweens and sequences, Thing,",
96091
+ "// sounds, songs and the Mixer, Turns, the UI, spawning, Behaviour, Bot, Check, GameScript, input, rays, the camera",
96092
+ "// and bursts. A sound and a song are the game's own (own:<name>); the stock names older games still use are left",
96093
+ "// out here. this.game, drawing, noise and console are part two, the api-game topic.",
96094
+ whole3.slice(0, at5).trimEnd()
96095
+ ].join("\n") : [
96096
+ "// The script API, part two, as the type checker holds scripts to it: this.game (interface Game), drawing (Draw,",
96097
+ "// its layers, paints and shaders), noise and console. The types it names, Thing, Point and Colour among them, are",
96098
+ "// part one, the api topic.",
96099
+ whole3.slice(at5 + 1)
96041
96100
  ].join("\n");
96042
96101
  }
96043
96102
  var TEAM = [
@@ -96291,18 +96350,19 @@ var GUIDE_TOPICS = [
96291
96350
  { name: "start", about: "how to work, and the bar for a finished game -- read it first", text: () => START.join("\n") },
96292
96351
  { name: "game", about: "the game text: settings, types, bodies, levels and tiles, the camera, rules, controls", text: () => [...quickWay(), ...gameText()].join("\n") },
96293
96352
  { name: "scripts", about: "how scripts run: hooks, state, timers, tweens, screens, sound, with ready-made UI", text: () => SCRIPTS.join("\n") },
96294
- { name: "api", about: "the script API, exactly as the type checker holds scripts to it", text: () => agentApi() },
96353
+ { name: "api", about: "the script API, part one: Thing, scripts, sound, UI, input", text: () => agentApi("objects") },
96354
+ { name: "api-game", about: "the script API, part two: this.game and drawing", text: () => agentApi("game") },
96295
96355
  { name: "drawing", about: "art drawn in code: layers and units, shapes, gradients, soft light, curves, panels, push and pop, screens that scale", text: () => DRAWING.join("\n") },
96296
- { name: "shaders", about: "drawing with the graphics card: boxes painted by a shader, shaders over the picture, passes, lists and paintings as inputs", text: () => SHADERS.join("\n") },
96356
+ { name: "shaders", about: "drawing with the graphics card: shaded boxes, shaders over the picture, passes, inputs", text: () => SHADERS.join("\n") },
96297
96357
  { name: "style", about: "craft: palette, light, texture, depth, UI and type, and the tells that make art look generated", text: () => STYLE.join("\n") },
96298
96358
  { name: "feel", about: "screens, transitions, juice, sound and music", text: () => FEEL.join("\n") },
96299
96359
  { name: "sound", about: "designed sound effects and composed music: layers, variants, instruments, loudness, and listening", text: () => SOUND_TOPIC.join("\n") },
96300
96360
  { name: "playtest", about: "playing it yourself with tracks and bots, and looking at it", text: () => playtestGuide().join("\n") },
96301
96361
  { name: "edit", about: "ids, the edit operations, and write_code against edit", text: () => howToEdit().join("\n") },
96302
96362
  { name: "art", about: "art paint: pictures, animations and tilesets painted against the 2D canvas", text: () => ART.join("\n") },
96303
- { name: "model", about: "art model: 3D art Blender builds from a short script against the kit, when the person asks for 3D", text: () => MODEL.join("\n") },
96304
- { name: "team", about: "working beside teammates and their agents: the board, taking with parts, claims, notes", text: () => TEAM.join("\n") },
96305
- { name: "film", about: "films: levels that play themselves, cutscenes, chapters, endFilm, and film render, which makes them MP4 video", text: () => FILM.join("\n") }
96363
+ { name: "model", about: "art model: 3D art Blender builds from a short script, when the person asks for 3D", text: () => MODEL.join("\n") },
96364
+ { name: "team", about: "working beside teammates and their agents: the board, parts, claims, notes", text: () => TEAM.join("\n") },
96365
+ { name: "film", about: "films: levels that play themselves, cutscenes, chapters, endFilm, and film render to MP4", text: () => FILM.join("\n") }
96306
96366
  ];
96307
96367
  function guideTopic(name2) {
96308
96368
  return GUIDE_TOPICS.find((topic) => topic.name === name2);
@@ -96398,9 +96458,11 @@ function admitArgs(schema, value, at5) {
96398
96458
  }
96399
96459
  }
96400
96460
  const required2 = Array.isArray(schema["required"]) ? schema["required"] : [];
96401
- for (const key of required2)
96402
- if (raw[key] === void 0)
96403
- return `${at5}.${key} is required`;
96461
+ const missing2 = [...Object.keys(properties), ...required2.filter((key) => !(key in properties))].filter((key) => required2.includes(key) && raw[key] === void 0);
96462
+ if (missing2.length === 1)
96463
+ return `${at5}.${missing2[0] ?? ""} is required`;
96464
+ if (missing2.length > 1)
96465
+ return `${at5} needs ${missing2.slice(0, -1).join(", ")} and ${missing2[missing2.length - 1] ?? ""}`;
96404
96466
  for (const key of known2) {
96405
96467
  const property = properties[key];
96406
96468
  if (raw[key] === void 0 || property === void 0)
@@ -107566,13 +107628,15 @@ function guideAnswer(raw) {
107566
107628
  }
107567
107629
  return { content: [{ type: "text", text: topic.text() }] };
107568
107630
  }
107631
+ var NAMED_PEOPLE = 4;
107569
107632
  function teamInstructions(who) {
107570
- const people = who.people.length === 0 ? "" : ` (${who.people.join(", ")})`;
107633
+ const more2 = who.people.length > NAMED_PEOPLE ? ` and ${String(who.people.length - NAMED_PEOPLE)} more` : "";
107634
+ const people = who.people.length === 0 ? "" : ` (${who.people.slice(0, NAMED_PEOPLE).join(", ")}${more2})`;
107571
107635
  return [
107572
- `You are ${who.agent}, in the workspace '${who.workspace}'${people}. Others may be working on the same games at the`,
107573
- "same time. Every answer about a game tells you what your teammates are doing. Before you start something, take it",
107574
- "on the game's board (team), with the parts it touches. Leave a note when you finish. The guide's team topic has",
107575
- "the rest."
107636
+ `You are ${who.agent}, in the workspace '${who.workspace}'${people}. Teammates may work on the same games at once:`,
107637
+ "every answer about a game says what they are doing. Before you start something, take it",
107638
+ "on the game's board (team), with the parts it touches, and leave a note when you finish. The guide's team topic",
107639
+ "has the rest."
107576
107640
  ].join("\n");
107577
107641
  }
107578
107642
  function createServer(more2) {
@@ -107700,9 +107764,13 @@ function makesGame(name2, args2, exists) {
107700
107764
  return false;
107701
107765
  }
107702
107766
  function servedTools(options = DEFAULT_SERVE, leaveOut = /* @__PURE__ */ new Set()) {
107703
- const families = familyListing(underlyingTools(options, leaveOut));
107767
+ const families = familyListing(underlyingTools(options, leaveOut)).map((tool) => ({ ...tool, annotations: marksOf(tool.name) }));
107704
107768
  return options.schemas === "portable" ? families.map((tool) => ({ ...tool, inputSchema: portableSchema(tool.inputSchema) })) : families;
107705
107769
  }
107770
+ var READING_FAMILIES = /* @__PURE__ */ new Set(["guide", "read", "search", "library"]);
107771
+ function marksOf(name2) {
107772
+ return READING_FAMILIES.has(name2) ? { readOnlyHint: true, idempotentHint: true, openWorldHint: false } : { readOnlyHint: false, openWorldHint: name2 === "app" };
107773
+ }
107706
107774
  function underlyingTools(_options, leaveOut) {
107707
107775
  return toolList().filter((tool) => !leaveOut.has(tool.name));
107708
107776
  }
@@ -111801,7 +111869,7 @@ var FONT_FILES = {
111801
111869
  };
111802
111870
 
111803
111871
  // ../design/dist/plans.js
111804
- var line = (text3, withAccounts = false) => ({ text: text3, withAccounts });
111872
+ var line = (text3) => ({ text: text3 });
111805
111873
  var PLANS = [
111806
111874
  {
111807
111875
  id: "starter",
@@ -111826,7 +111894,7 @@ var PLANS = [
111826
111894
  extraSeat: 1e3,
111827
111895
  everythingIn: "starter",
111828
111896
  pick: true,
111829
- includes: [line("An organization with shared games and roles", true)]
111897
+ includes: [line("An organization with shared games and roles")]
111830
111898
  },
111831
111899
  {
111832
111900
  id: "enterprise",
@@ -111836,10 +111904,29 @@ var PLANS = [
111836
111904
  extraSeat: 1e3,
111837
111905
  everythingIn: "pro",
111838
111906
  pick: false,
111839
- includes: [line("Invoices instead of cards", true), line("Priority support", true)]
111907
+ includes: [line("Invoices instead of cards"), line("Priority support")]
111840
111908
  }
111841
111909
  ];
111842
111910
 
111911
+ // ../i18n/dist/index.js
111912
+ function chooseLanguage(header2) {
111913
+ const best = { en: { q: 0, at: Infinity }, es: { q: 0, at: Infinity } };
111914
+ for (const [at5, part] of (header2 ?? "").split(",").entries()) {
111915
+ const [tag = "", ...params] = part.trim().split(";");
111916
+ const primary = tag.trim().toLowerCase().split("-")[0] ?? "";
111917
+ if (primary !== "en" && primary !== "es")
111918
+ continue;
111919
+ const qParam = params.map((param) => param.trim()).find((param) => param.startsWith("q="));
111920
+ const q2 = qParam === void 0 ? 1 : Number(qParam.slice(2));
111921
+ if (!Number.isFinite(q2) || q2 <= 0)
111922
+ continue;
111923
+ if (q2 > best[primary].q || q2 === best[primary].q && at5 < best[primary].at)
111924
+ best[primary] = { q: q2, at: at5 };
111925
+ }
111926
+ const { en: en2, es } = best;
111927
+ return es.q > en2.q || es.q > 0 && es.q === en2.q && es.at < en2.at ? "es" : "en";
111928
+ }
111929
+
111843
111930
  // ../studio/dist/server/static.js
111844
111931
  function rangeOf(header2, size2) {
111845
111932
  if (header2 === void 0)
@@ -114817,7 +114904,7 @@ function keepSettings(store, doc, slug, prefix, settings) {
114817
114904
  var isObject5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
114818
114905
  var TARGETS2 = "android-debug, android-release or ios-project";
114819
114906
  var KEY_ROLES = "the upload key is for the workspace's owner and admins";
114820
- var NO_EXPORT = "this workspace's plan does not export, so it builds no apps: the workspace's owner can choose a plan on the Plan page";
114907
+ var NO_EXPORT = (plans) => `this workspace's plan does not export, so it builds no apps: the workspace's owner can choose a plan ${plans === void 0 ? "on the Plan page" : `at ${plans}`}`;
114821
114908
  var sizeOf4 = (bytes2) => bytes2 < 1024 * 1024 ? `${String(Math.max(1, Math.round(bytes2 / 1024)))} KB` : `${(bytes2 / (1024 * 1024)).toFixed(1)} MB`;
114822
114909
  function stepNow(job) {
114823
114910
  return (job.steps.find((step2) => step2.state === "running") ?? job.steps.find((step2) => step2.state === "failed") ?? job.steps.at(-1))?.name ?? "";
@@ -115009,7 +115096,7 @@ function hostedAppRoutes(options) {
115009
115096
  }
115010
115097
  if (call.action === "build") {
115011
115098
  if (!await mayExport(req2))
115012
- throw new AppRefused(402, NO_EXPORT);
115099
+ throw new AppRefused(402, NO_EXPORT(options.plans));
115013
115100
  const job = await builds.build(workspaceId, slug, call.target, source);
115014
115101
  return { text: `${job.target} started (build ${job.id}): ${builds.started}. app status says how it goes.` };
115015
115102
  }
@@ -123019,16 +123106,35 @@ Open it, allow Gamesteel, then ask again.`);
123019
123106
  };
123020
123107
  var EARLY_MS = 6e4;
123021
123108
  var STALE_LOCK_MS = 12e4;
123109
+ function browserCommand(platform, address) {
123110
+ return platform === "darwin" ? ["open", [address]] : platform === "win32" ? ["rundll32", ["url.dll,FileProtocolHandler", address]] : ["xdg-open", [address]];
123111
+ }
123022
123112
  function openInBrowser(address) {
123023
- const [command2, ...args2] = process.platform === "darwin" ? ["open", address] : process.platform === "win32" ? ["cmd", "/c", "start", "", address] : ["xdg-open", address];
123113
+ const [command2, args2] = browserCommand(process.platform, address);
123024
123114
  try {
123025
- const child = spawn4(command2 ?? "open", args2, { stdio: "ignore", detached: true });
123115
+ const child = spawn4(command2, args2, { stdio: "ignore", detached: true });
123026
123116
  child.on("error", () => void 0);
123027
123117
  child.unref();
123028
123118
  } catch {
123029
123119
  }
123030
123120
  }
123031
- var page = (words3) => `<!doctype html><meta charset="utf-8"><title>Gamesteel</title><p style="font:16px system-ui;margin:3em">${words3}</p>`;
123121
+ var SAID = {
123122
+ en: {
123123
+ done: "Gamesteel is signed in. Go back to your agent and ask it again, for example: \u201Ctry again\u201D. You can close this tab.",
123124
+ denied: "You did not allow Gamesteel, so your agent cannot use it yet. To sign in, ask your agent again.",
123125
+ stranger: "This sign-in is not the one Gamesteel asked for. Ask your agent again.",
123126
+ failed: "Gamesteel could not finish signing in. Ask your agent again."
123127
+ },
123128
+ es: {
123129
+ done: "Gamesteel inici\xF3 sesi\xF3n. Vuelve a tu agente y p\xEDdeselo de nuevo, por ejemplo: \xABint\xE9ntalo de nuevo\xBB. Puedes cerrar esta pesta\xF1a.",
123130
+ denied: "No le diste permiso a Gamesteel, as\xED que tu agente a\xFAn no puede usarlo. Para iniciar sesi\xF3n, p\xEDdeselo de nuevo a tu agente.",
123131
+ stranger: "Este inicio de sesi\xF3n no es el que pidi\xF3 Gamesteel. P\xEDdeselo de nuevo a tu agente.",
123132
+ failed: "Gamesteel no pudo terminar de iniciar sesi\xF3n. P\xEDdeselo de nuevo a tu agente."
123133
+ }
123134
+ };
123135
+ var languageOf3 = (req2) => chooseLanguage(req2.headers["accept-language"]);
123136
+ var page = (lang, said4) => `<!doctype html><html lang="${lang}"><meta charset="utf-8"><title>Gamesteel</title><p style="font:16px system-ui;margin:3em">${SAID[lang][said4]}</p></html>`;
123137
+ var REOPEN_MS = 3e4;
123032
123138
  var SignIn = class {
123033
123139
  constructor(options) {
123034
123140
  this.options = options;
@@ -123040,6 +123146,8 @@ var SignIn = class {
123040
123146
  clock;
123041
123147
  flow = null;
123042
123148
  server = null;
123149
+ /** When the sign-in's page was last opened in the browser. */
123150
+ opened = 0;
123043
123151
  get file() {
123044
123152
  return join33(this.options.home, "credentials.json");
123045
123153
  }
@@ -123078,13 +123186,22 @@ var SignIn = class {
123078
123186
  /** The address the person opens to sign in: the website's own sign-in, opened in their browser once. */
123079
123187
  address() {
123080
123188
  if (this.flow === null) {
123081
- const flow = this.begin();
123082
- this.flow = flow;
123083
- flow.catch(() => {
123084
- if (this.flow === flow) this.flow = null;
123189
+ const flow2 = this.begin();
123190
+ this.flow = flow2;
123191
+ flow2.catch(() => {
123192
+ if (this.flow === flow2) this.flow = null;
123085
123193
  });
123194
+ return flow2;
123086
123195
  }
123087
- return this.flow;
123196
+ const flow = this.flow;
123197
+ void flow.then((address) => {
123198
+ if (this.flow === flow && this.clock() - this.opened >= REOPEN_MS) this.open(address);
123199
+ }, () => void 0);
123200
+ return flow;
123201
+ }
123202
+ open(address) {
123203
+ this.opened = this.clock();
123204
+ (this.options.openBrowser ?? openInBrowser)(address);
123088
123205
  }
123089
123206
  /** Ends the sign-in under way: the next one, if the person must sign in again, begins afresh. */
123090
123207
  close() {
@@ -123135,17 +123252,26 @@ var SignIn = class {
123135
123252
  return;
123136
123253
  }
123137
123254
  const code = url.searchParams.get("code");
123255
+ const lang = languageOf3(req2);
123256
+ const error2 = url.searchParams.get("error");
123257
+ if (url.searchParams.get("state") === state && error2 !== null) {
123258
+ const denied = error2 === "access_denied";
123259
+ res.writeHead(denied ? 200 : 502, { "content-type": "text/html; charset=utf-8" }).end(page(lang, denied ? "denied" : "failed"));
123260
+ if (denied) this.opened = 0;
123261
+ else this.close();
123262
+ return;
123263
+ }
123138
123264
  if (url.searchParams.get("state") !== state || code === null) {
123139
- res.writeHead(400, { "content-type": "text/html" }).end(page("This sign-in is not the one Gamesteel asked for. Ask your agent again."));
123265
+ res.writeHead(400, { "content-type": "text/html; charset=utf-8" }).end(page(lang, "stranger"));
123140
123266
  return;
123141
123267
  }
123142
123268
  void this.form("/oauth/token", { grant_type: "authorization_code", code, redirect_uri: redirectUri, code_verifier: verifier, client_id: clientId, resource }).then(async (answer3) => {
123143
123269
  if (answer3.status !== 200) throw new Error(String(answer3.status));
123144
123270
  await this.keepLocked(clientId, await answer3.json());
123145
- res.writeHead(200, { "content-type": "text/html" }).end(page("Gamesteel is signed in. You can close this tab and go back to your agent."));
123271
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8" }).end(page(lang, "done"));
123146
123272
  this.close();
123147
123273
  }).catch(() => {
123148
- res.writeHead(502, { "content-type": "text/html" }).end(page("Gamesteel could not finish signing in. Ask your agent again."));
123274
+ res.writeHead(502, { "content-type": "text/html; charset=utf-8" }).end(page(lang, "failed"));
123149
123275
  });
123150
123276
  });
123151
123277
  this.server = server;
@@ -123164,7 +123290,7 @@ var SignIn = class {
123164
123290
  code_challenge_method: "S256",
123165
123291
  resource
123166
123292
  }).toString()}`;
123167
- (this.options.openBrowser ?? openInBrowser)(address);
123293
+ this.open(address);
123168
123294
  return address;
123169
123295
  }
123170
123296
  /** A new token, made by one program on the machine at a time: the others find it made. */
@@ -123695,7 +123821,7 @@ async function startProgram(options) {
123695
123821
  models: machineModels(options.models),
123696
123822
  // The plan's rule for an app build is the website's, read here; the key is downloaded only from the App tab.
123697
123823
  apps,
123698
- appOptions: { mayExport: async () => (await plan()).limits.export, mayKeepKeys: () => false },
123824
+ appOptions: { mayExport: async () => (await plan()).limits.export, mayKeepKeys: () => false, plans: `${settings.site}/editor#plan` },
123699
123825
  // An agent's film render and a look with shaders, drawn by the editors with the game open on the website.
123700
123826
  relays: { renders: remoteRenders(site), looks: remoteLooks(site) }
123701
123827
  });
@@ -123805,7 +123931,9 @@ async function startProgram(options) {
123805
123931
  const listed5 = await site.games();
123806
123932
  const names2 = new Set(listed5.map((game) => game.slug));
123807
123933
  if (makesGame(name2, args2, (one2) => names2.has(one2) || copy.has(one2)) && listed5.length >= now.limits.games) {
123808
- return refusal3("the free week makes one game; a plan makes more");
123934
+ const slugs = listed5.map((game) => game.slug);
123935
+ const has = slugs.length === 1 ? `${slugs[0] ?? ""}: work on it` : `${slugs.slice(0, -1).join(", ")} and ${slugs.at(-1) ?? ""}: work on one of them`;
123936
+ return refusal3(`the free week makes one game, and this workspace has ${has}, or its owner can choose a plan at ${settings.site}/editor#plan`);
123809
123937
  }
123810
123938
  }
123811
123939
  const named3 = typeof args2["game"] === "string" ? args2["game"] : name2 === "save_game" && typeof args2["name"] === "string" ? args2["name"] : null;
@@ -123840,8 +123968,9 @@ async function startProgram(options) {
123840
123968
  const answered2 = await call();
123841
123969
  const said4 = team2 !== null && slug !== null && here ? await team2.notes(slug, author) : "";
123842
123970
  let answer3 = said4 === "" ? answered2 : { ...answered2, content: [...answered2.content, { type: "text", text: said4 }] };
123971
+ let made2 = [];
123843
123972
  if (!reading) {
123844
- const made2 = [...gamesHere(copy.root)].filter((one2) => !before2.has(one2));
123973
+ made2 = [...gamesHere(copy.root)].filter((one2) => !before2.has(one2));
123845
123974
  pending.push(...(await uploader.send([...slug === null ? [] : [slug], ...made2])).pending);
123846
123975
  if (slug !== null) moves2.wrote(slug);
123847
123976
  }
@@ -123850,6 +123979,13 @@ async function startProgram(options) {
123850
123979
  if (report !== null) answer3 = { ...answer3, content: [...answer3.content, { type: "text", text: `
123851
123980
 
123852
123981
  ${report}` }] };
123982
+ }
123983
+ const shown3 = (answer3.isError === true ? [] : name2 === "open_game" && slug !== null ? [slug] : made2).filter((one2) => copy.onWebsite(one2));
123984
+ for (const one2 of shown3) {
123985
+ answer3 = { ...answer3, content: [...answer3.content, { type: "text", text: `
123986
+
123987
+ Give the person this link to see and play it: ${settings.site}/editor?game=${one2}&workspace=${now.workspaceId}
123988
+ ` }] };
123853
123989
  }
123854
123990
  if (slug !== null) moves2.settled(slug);
123855
123991
  const recorded = slug === null ? null : moves2.said(slug);
@@ -124299,6 +124435,9 @@ ${lines.join("\n")}` }] } };
124299
124435
  toolNotices.set("playtest", "The Gamesteel program updated itself: playtests started over at the first tick, and their saved moments were forgotten.");
124300
124436
  if (old.tools === null) await old.listing?.catch(() => void 0);
124301
124437
  if (initialized && (old.tools === null || old.tools !== next2.tools)) toClient({ jsonrpc: "2.0", method: "notifications/tools/list_changed" });
124438
+ if (old.tools !== null && old.tools !== next2.tools) {
124439
+ say(`Gamesteel updated itself to ${next2.version}, and its tools changed: if your agent's client does not show the new ones, restart it.`);
124440
+ }
124302
124441
  try {
124303
124442
  options.versions.sweep([next2.version, old.version]);
124304
124443
  } catch (error2) {
@@ -76534,7 +76534,7 @@ var START = [
76534
76534
  "## Read as you go",
76535
76535
  "",
76536
76536
  "- game: the game text. Read it before writing one.",
76537
- "- scripts, then api: how scripts run, and the exact TypeScript they are checked against. Most of a good game",
76537
+ "- scripts, then api and api-game: how scripts run, and the exact TypeScript they are checked against. Most of a good game",
76538
76538
  " lives in its scripts.",
76539
76539
  "- drawing: art drawn in code: shapes, gradients, soft light, curves, panels, every option.",
76540
76540
  "- style: craft -- palette, light, texture, depth, UI and type, and the tells that make art look generated.",
@@ -76573,6 +76573,9 @@ var START = [
76573
76573
  " the title, a menu, a hit, the ending. Words are cheap; pictures cost more, so look on purpose.",
76574
76574
  "6. Polish until it is a game you would show someone.",
76575
76575
  "",
76576
+ "games new and games open answer with the game's link, where the person sees and plays it in the editor: give it to",
76577
+ "the person whenever there is something to see, the first time above all. A look with shaders is drawn there too.",
76578
+ "",
76576
76579
  "## Made for this game, never repeated",
76577
76580
  "",
76578
76581
  "Before a character, a creature, a vehicle, a building, or any song:",
@@ -76909,16 +76912,27 @@ var MODEL = [
76909
76912
  ];
76910
76913
  var SCRIPTS = [
76911
76914
  ...SCRIPTS_PRIMER,
76912
- "The exact declarations every script is checked against are the api topic."
76915
+ "The exact declarations every script is checked against are the api and api-game topics."
76913
76916
  ];
76914
- function agentApi() {
76917
+ function agentApi(part) {
76915
76918
  const own3 = (what, name) => `/** ${what}: one of the game's own, own:<name>, made for it. */
76916
76919
  type ${name} = \`own:\${string}\`
76917
76920
  `;
76918
- return [
76919
- "// The script API, as the type checker holds scripts to it. A sound and a song are the game's own (own:<name>); the",
76920
- "// stock names older games still use are left out here.",
76921
- SCRIPT_API.trimEnd().replace(/(?:\/\*\*[^\n]*\*\/\n)?type SoundName = [^\n]*\n/, () => own3("A sound", "SoundName")).replace(/(?:\/\*\*[^\n]*\*\/\n)?type MusicName = [^\n]*\n/, () => own3("A song", "MusicName"))
76921
+ const whole = SCRIPT_API.trimEnd().replace(/(?:\/\*\*[^\n]*\*\/\n)?type SoundName = [^\n]*\n/, () => own3("A sound", "SoundName")).replace(/(?:\/\*\*[^\n]*\*\/\n)?type MusicName = [^\n]*\n/, () => own3("A song", "MusicName"));
76922
+ const at2 = whole.indexOf("\ninterface Game {");
76923
+ if (at2 < 0)
76924
+ throw new Error("the script API has no interface Game to split it at");
76925
+ return part === "objects" ? [
76926
+ "// The script API, part one, as the type checker holds scripts to it: the shared types, tweens and sequences, Thing,",
76927
+ "// sounds, songs and the Mixer, Turns, the UI, spawning, Behaviour, Bot, Check, GameScript, input, rays, the camera",
76928
+ "// and bursts. A sound and a song are the game's own (own:<name>); the stock names older games still use are left",
76929
+ "// out here. this.game, drawing, noise and console are part two, the api-game topic.",
76930
+ whole.slice(0, at2).trimEnd()
76931
+ ].join("\n") : [
76932
+ "// The script API, part two, as the type checker holds scripts to it: this.game (interface Game), drawing (Draw,",
76933
+ "// its layers, paints and shaders), noise and console. The types it names, Thing, Point and Colour among them, are",
76934
+ "// part one, the api topic.",
76935
+ whole.slice(at2 + 1)
76922
76936
  ].join("\n");
76923
76937
  }
76924
76938
  var TEAM = [
@@ -77172,18 +77186,19 @@ var GUIDE_TOPICS = [
77172
77186
  { name: "start", about: "how to work, and the bar for a finished game -- read it first", text: () => START.join("\n") },
77173
77187
  { name: "game", about: "the game text: settings, types, bodies, levels and tiles, the camera, rules, controls", text: () => [...quickWay(), ...gameText()].join("\n") },
77174
77188
  { name: "scripts", about: "how scripts run: hooks, state, timers, tweens, screens, sound, with ready-made UI", text: () => SCRIPTS.join("\n") },
77175
- { name: "api", about: "the script API, exactly as the type checker holds scripts to it", text: () => agentApi() },
77189
+ { name: "api", about: "the script API, part one: Thing, scripts, sound, UI, input", text: () => agentApi("objects") },
77190
+ { name: "api-game", about: "the script API, part two: this.game and drawing", text: () => agentApi("game") },
77176
77191
  { name: "drawing", about: "art drawn in code: layers and units, shapes, gradients, soft light, curves, panels, push and pop, screens that scale", text: () => DRAWING.join("\n") },
77177
- { name: "shaders", about: "drawing with the graphics card: boxes painted by a shader, shaders over the picture, passes, lists and paintings as inputs", text: () => SHADERS.join("\n") },
77192
+ { name: "shaders", about: "drawing with the graphics card: shaded boxes, shaders over the picture, passes, inputs", text: () => SHADERS.join("\n") },
77178
77193
  { name: "style", about: "craft: palette, light, texture, depth, UI and type, and the tells that make art look generated", text: () => STYLE.join("\n") },
77179
77194
  { name: "feel", about: "screens, transitions, juice, sound and music", text: () => FEEL.join("\n") },
77180
77195
  { name: "sound", about: "designed sound effects and composed music: layers, variants, instruments, loudness, and listening", text: () => SOUND_TOPIC.join("\n") },
77181
77196
  { name: "playtest", about: "playing it yourself with tracks and bots, and looking at it", text: () => playtestGuide().join("\n") },
77182
77197
  { name: "edit", about: "ids, the edit operations, and write_code against edit", text: () => howToEdit().join("\n") },
77183
77198
  { name: "art", about: "art paint: pictures, animations and tilesets painted against the 2D canvas", text: () => ART.join("\n") },
77184
- { name: "model", about: "art model: 3D art Blender builds from a short script against the kit, when the person asks for 3D", text: () => MODEL.join("\n") },
77185
- { name: "team", about: "working beside teammates and their agents: the board, taking with parts, claims, notes", text: () => TEAM.join("\n") },
77186
- { name: "film", about: "films: levels that play themselves, cutscenes, chapters, endFilm, and film render, which makes them MP4 video", text: () => FILM.join("\n") }
77199
+ { name: "model", about: "art model: 3D art Blender builds from a short script, when the person asks for 3D", text: () => MODEL.join("\n") },
77200
+ { name: "team", about: "working beside teammates and their agents: the board, parts, claims, notes", text: () => TEAM.join("\n") },
77201
+ { name: "film", about: "films: levels that play themselves, cutscenes, chapters, endFilm, and film render to MP4", text: () => FILM.join("\n") }
77187
77202
  ];
77188
77203
 
77189
77204
  // ../agent/dist/artgen-tools.js
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gamesteel",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "The Gamesteel program your AI agent starts, and the command that sets it up in Claude Code, Codex, Gemini CLI, Cursor, VS Code and 26 more. The work runs on your machine; the website keeps your games.",
5
5
  "keywords": [
6
6
  "gamesteel",
@@ -37,18 +37,19 @@
37
37
  "devDependencies": {
38
38
  "@modelcontextprotocol/sdk": "^1.0.0",
39
39
  "esbuild": "0.28.2",
40
+ "@gamesteel/billing": "0.1.0",
41
+ "@gamesteel/agent": "0.1.0",
42
+ "@gamesteel/connect": "0.1.0",
40
43
  "@gamesteel/builder": "0.1.0",
41
- "@gamesteel/accounts": "0.1.0",
42
- "@gamesteel/design": "0.1.0",
43
44
  "@gamesteel/cloud": "0.1.0",
44
- "@gamesteel/edit": "0.1.0",
45
45
  "@gamesteel/core": "0.1.0",
46
- "@gamesteel/store": "0.1.0",
47
- "@gamesteel/billing": "0.1.0",
48
- "@gamesteel/connect": "0.1.0",
46
+ "@gamesteel/edit": "0.1.0",
47
+ "@gamesteel/accounts": "0.1.0",
49
48
  "@gamesteel/service": "0.1.0",
49
+ "@gamesteel/i18n": "0.1.0",
50
50
  "@gamesteel/studio": "0.1.0",
51
- "@gamesteel/agent": "0.1.0",
51
+ "@gamesteel/store": "0.1.0",
52
+ "@gamesteel/design": "0.1.0",
52
53
  "@gamesteel/model": "0.1.0"
53
54
  },
54
55
  "scripts": {