gamesteel 0.5.2 → 0.5.4
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 +468 -146
- package/dist/pkgs/audio/dist/worker.js +3 -3
- package/dist/pkgs/model/dist/worker.js +3 -3
- package/dist/pkgs/paint/dist/worker.js +3 -3
- package/dist/pkgs/studio/dist/server/playtest-worker.js +205 -46
- package/dist/resolved/@gamesteel/online/worker/runtime.js +10 -10
- package/package.json +11 -11
package/dist/gamesteel.js
CHANGED
|
@@ -25339,7 +25339,7 @@ interface Purchases {
|
|
|
25339
25339
|
restore(): void
|
|
25340
25340
|
}
|
|
25341
25341
|
|
|
25342
|
-
/** The game's own server, for a game whose
|
|
25342
|
+
/** The game's own server, for a game whose Server page turned it on (the guide's online topic). Each ask goes to the
|
|
25343
25343
|
* server through the page; what comes back reaches every script on a later tick as onEvent('online', heard), where
|
|
25344
25344
|
* heard is { kind: 'answer', id, name, result, data?, reason? } (result 'ok', 'error', 'busy', 'resting' or 'refused'),
|
|
25345
25345
|
* { kind: 'message', room, data }, or { kind: 'joined' | 'left' | 'full', room }. A tick never waits on the network. */
|
|
@@ -25696,7 +25696,7 @@ interface Game {
|
|
|
25696
25696
|
/** An event every onEvent hears on the next tick; it also counts as the tick's event. */
|
|
25697
25697
|
emit(name: string, data?: Plain): void
|
|
25698
25698
|
/** The game's seeded random number in [0, 1); Math.random is the same stream. Each
|
|
25699
|
-
* play in the
|
|
25699
|
+
* play in the editor starts from a seed of its own; a playtest starts from seed 1,
|
|
25700
25700
|
* so its runs repeat. */
|
|
25701
25701
|
random(): number
|
|
25702
25702
|
/** A whole number from lo to hi, both included. */
|
|
@@ -25751,7 +25751,7 @@ interface Game {
|
|
|
25751
25751
|
/** Opens the game's privacy policy, as a game on the app stores must from inside itself: call it when the player
|
|
25752
25752
|
* taps the game's own Privacy button. The page knows where the policy is; with none yet, nothing opens. */
|
|
25753
25753
|
openPrivacy(): void
|
|
25754
|
-
/** Kept across restarts and levels, and between plays in the
|
|
25754
|
+
/** Kept across restarts and levels, and between plays in the editor (in that
|
|
25755
25755
|
* browser). A playtest starts with nothing saved. */
|
|
25756
25756
|
save(key: string, value: Plain): void
|
|
25757
25757
|
load(key: string): Plain
|
|
@@ -86206,6 +86206,7 @@ var PLANS = [
|
|
|
86206
86206
|
monthly: 999,
|
|
86207
86207
|
seats: 1,
|
|
86208
86208
|
extraSeat: null,
|
|
86209
|
+
storageBlock: 499,
|
|
86209
86210
|
everythingIn: null,
|
|
86210
86211
|
pick: false,
|
|
86211
86212
|
storage: 10 * GB,
|
|
@@ -86213,7 +86214,7 @@ var PLANS = [
|
|
|
86213
86214
|
includes: [
|
|
86214
86215
|
room(10 * GB, 500 * MB),
|
|
86215
86216
|
line("Everything Gamesteel does, with your own agent"),
|
|
86216
|
-
line("The agent's
|
|
86217
|
+
line("The agent's 16 tools"),
|
|
86217
86218
|
line("Export to the web"),
|
|
86218
86219
|
line("Phone app builds")
|
|
86219
86220
|
]
|
|
@@ -86224,6 +86225,7 @@ var PLANS = [
|
|
|
86224
86225
|
monthly: 1999,
|
|
86225
86226
|
seats: 3,
|
|
86226
86227
|
extraSeat: 1e3,
|
|
86228
|
+
storageBlock: 399,
|
|
86227
86229
|
everythingIn: "starter",
|
|
86228
86230
|
pick: true,
|
|
86229
86231
|
storage: 50 * GB,
|
|
@@ -86231,11 +86233,12 @@ var PLANS = [
|
|
|
86231
86233
|
includes: [room(50 * GB, GB), line("An organization with shared games and roles")]
|
|
86232
86234
|
},
|
|
86233
86235
|
{
|
|
86234
|
-
id: "
|
|
86235
|
-
name: "
|
|
86236
|
+
id: "business",
|
|
86237
|
+
name: "Business",
|
|
86236
86238
|
monthly: 3999,
|
|
86237
86239
|
seats: 20,
|
|
86238
|
-
extraSeat:
|
|
86240
|
+
extraSeat: 800,
|
|
86241
|
+
storageBlock: 299,
|
|
86239
86242
|
everythingIn: "pro",
|
|
86240
86243
|
pick: false,
|
|
86241
86244
|
storage: 250 * GB,
|
|
@@ -86243,6 +86246,12 @@ var PLANS = [
|
|
|
86243
86246
|
includes: [room(250 * GB, 2 * GB), line("Invoices instead of cards"), line("Priority support")]
|
|
86244
86247
|
}
|
|
86245
86248
|
];
|
|
86249
|
+
var ENTERPRISE = {
|
|
86250
|
+
name: "Enterprise",
|
|
86251
|
+
everythingIn: "business",
|
|
86252
|
+
includes: [line("Seats and storage sized for your team"), line("Terms and invoicing agreed with you"), line("A direct line to the team")],
|
|
86253
|
+
email: "business@gamesteel.studio"
|
|
86254
|
+
};
|
|
86246
86255
|
var TRIAL = {
|
|
86247
86256
|
days: 7,
|
|
86248
86257
|
games: 1,
|
|
@@ -86252,11 +86261,17 @@ var TRIAL = {
|
|
|
86252
86261
|
gameBytes: 200 * MB,
|
|
86253
86262
|
rules: ["One week, free", "One game", "No export", "A watermark on the game"]
|
|
86254
86263
|
};
|
|
86255
|
-
var STORAGE_BLOCK = { bytes: 10 * GB
|
|
86264
|
+
var STORAGE_BLOCK = { bytes: 10 * GB };
|
|
86256
86265
|
var STORAGE_FULL = (site, allowed) => `this workspace's ${sizeText(allowed)} are full: delete something it keeps, or its owner can add storage or choose a bigger plan at ${site}/editor#plan`;
|
|
86257
86266
|
var GAME_FULL = (site, allowed) => `a game may hold at most ${sizeText(allowed)} on this plan: delete something in it, or its owner can choose a bigger plan at ${site}/editor#plan`;
|
|
86258
86267
|
var NEARLY_FULL = 0.8;
|
|
86259
86268
|
var UNLIMITED_GAME_BYTES = 2 * GB;
|
|
86269
|
+
function planById(id) {
|
|
86270
|
+
const plan = PLANS.find((one2) => one2.id === id);
|
|
86271
|
+
if (plan === void 0)
|
|
86272
|
+
throw new Error(`no plan called ${id}`);
|
|
86273
|
+
return plan;
|
|
86274
|
+
}
|
|
86260
86275
|
|
|
86261
86276
|
// ../edit/dist/model.js
|
|
86262
86277
|
var TILE_LAYERS = ["back", "front"];
|
|
@@ -91891,7 +91906,7 @@ var SCRIPTS_PRIMER = [
|
|
|
91891
91906
|
" and sets that language and game.languages lists them. The HUD, the end screens and the menus write @hello for a",
|
|
91892
91907
|
" key's text. The person picks the language in the player's menu; the edit operation game.setStrings writes the tables.",
|
|
91893
91908
|
"- The person exports a game from the toolbar's Export: a zip whose page plays it from the disk or any web host and",
|
|
91894
|
-
" installs on a phone, playing as the
|
|
91909
|
+
" installs on a phone, playing as the editor does, saves in the browser and all.",
|
|
91895
91910
|
"- Mix freely: rules and scripts run in the same game, and a script may read what a rule changed.",
|
|
91896
91911
|
"",
|
|
91897
91912
|
"A kind's script:",
|
|
@@ -96164,7 +96179,7 @@ interface GameRequest {
|
|
|
96164
96179
|
json(): Json
|
|
96165
96180
|
}
|
|
96166
96181
|
|
|
96167
|
-
/** Players' files and the game's own, in the game's storage (turned on
|
|
96182
|
+
/** Players' files and the game's own, in the game's storage (turned on, on its Server page, with its database): a
|
|
96168
96183
|
* player's under players/<their id>/, so "Delete my online data" reaches them, the game's under game/. */
|
|
96169
96184
|
interface Files {
|
|
96170
96185
|
/** An address the player's page uploads exactly size bytes to, for 15 minutes or the seconds given: at most the
|
|
@@ -96180,7 +96195,7 @@ interface Files {
|
|
|
96180
96195
|
list(prefix: string): Promise<Array<{ key: string; size: number }>>
|
|
96181
96196
|
}
|
|
96182
96197
|
|
|
96183
|
-
/** Notifications to players' phones, through the game's own Firebase project (turned on
|
|
96198
|
+
/** Notifications to players' phones, through the game's own Firebase project (turned on, on its Server page, with its
|
|
96184
96199
|
* database). A player is reached once their app asked them (game.online.askPush) and they said yes. Each push costs
|
|
96185
96200
|
* one event. Never required to play, never an ad without the player's consent, never a reward for saying yes. */
|
|
96186
96201
|
interface Push {
|
|
@@ -96190,7 +96205,7 @@ interface Push {
|
|
|
96190
96205
|
|
|
96191
96206
|
/** What every function of the server is given. */
|
|
96192
96207
|
interface Ctx {
|
|
96193
|
-
/** The game's own database (Postgres): turned on
|
|
96208
|
+
/** The game's own database (Postgres): turned on, on its Server page. Keep each player's rows with a player_id column,
|
|
96194
96209
|
* so "Delete my online data" reaches them. */
|
|
96195
96210
|
readonly db: { query(sql: string, params?: readonly Json[]): Promise<{ rows: Record<string, Json>[] }> }
|
|
96196
96211
|
/** Live rooms: send to everyone in a room, or to one player in it. Who is in a room is room.players, in its hooks. */
|
|
@@ -96631,7 +96646,7 @@ var SOUND_TOPIC = [
|
|
|
96631
96646
|
"a noise layer, a brighter wave, a filter opened. It is muddy: highpass it, or lower the body. A click at its end: a",
|
|
96632
96647
|
"longer release. A loop jumps at its seam: end on notes that lead back. Compare two of the game's own with listen",
|
|
96633
96648
|
"{ names: ['own:hit', 'own:hit-heavy'] }. A playtest plays no audio, so the readout is how you hear it; the person",
|
|
96634
|
-
"hears it in the
|
|
96649
|
+
"hears it when they play it in the editor."
|
|
96635
96650
|
];
|
|
96636
96651
|
|
|
96637
96652
|
// ../agent/dist/sight.js
|
|
@@ -97043,7 +97058,7 @@ var PLAYTEST_TOOLS = [
|
|
|
97043
97058
|
},
|
|
97044
97059
|
{
|
|
97045
97060
|
name: "playtest_look",
|
|
97046
|
-
description: "a picture of the game now, as the person sees it in the
|
|
97061
|
+
description: "a picture of the game now, as the person sees it when they play it in the editor, and the data of the objects you name; or, with frames, a strip of moments side by side in one picture, each labelled with its time. Pictures cost money: look at the moments that matter, not after every step.",
|
|
97047
97062
|
inputSchema: object2({
|
|
97048
97063
|
game: GAME,
|
|
97049
97064
|
scene: SCENE,
|
|
@@ -97914,7 +97929,7 @@ var FILM = [
|
|
|
97914
97929
|
" second, its sound mixed from the ticks that asked for it. Nobody watches it live as it renders, so a film too",
|
|
97915
97930
|
" heavy to play smoothly live comes out as smooth as a light one.",
|
|
97916
97931
|
"- Each later answer about the game says how the render goes, then how it ended. With no editor open, ask the person",
|
|
97917
|
-
" to open the game and press Render video in
|
|
97932
|
+
" to open the game and press Render video in Upload \u203A Films.",
|
|
97918
97933
|
"- The exported game plays a film's video in the film's place, in the player's language, with pause, chapters and",
|
|
97919
97934
|
" skip; a film with no video in that language plays live. After any edit a video is older than the game: render it",
|
|
97920
97935
|
" again before the person exports.",
|
|
@@ -97927,11 +97942,11 @@ var FILM = [
|
|
|
97927
97942
|
var PUBLISH = [
|
|
97928
97943
|
"# Publishing a game",
|
|
97929
97944
|
"",
|
|
97930
|
-
"A finished game goes out from the editor's Upload
|
|
97945
|
+
"A finished game goes out from the editor's Upload page. Every account it goes to is the person's own: their GitHub,",
|
|
97931
97946
|
"their Google Play and App Store developer accounts, their AdMob. Gamesteel makes the files and walks them through each",
|
|
97932
97947
|
"step; the stores' clicks, checkboxes and agreements are theirs.",
|
|
97933
97948
|
"",
|
|
97934
|
-
"## The Upload
|
|
97949
|
+
"## The Upload page",
|
|
97935
97950
|
"",
|
|
97936
97951
|
"- Export: a zip whose index.html plays the game from the disk, and installs as an app once any website serves it.",
|
|
97937
97952
|
"- GitHub: one repo per game, in the person's own GitHub account, which they connect in the menu; each save can go to it.",
|
|
@@ -97964,7 +97979,7 @@ var PUBLISH = [
|
|
|
97964
97979
|
"",
|
|
97965
97980
|
"A game going to the App Store or Google Play opens its privacy policy from inside itself: add a small Privacy button on",
|
|
97966
97981
|
"its title or pause menu, drawn like its other buttons, that calls this.game.openPrivacy() (the scripts topic shows",
|
|
97967
|
-
"one). The person writes and confirms the policy in the editor's Upload
|
|
97982
|
+
"one). The person writes and confirms the policy in the editor's Upload page, in the App Store's wizard or Google",
|
|
97968
97983
|
"Play's. A game with ads gets an ads part in the policy's draft, and the policy is confirmed again whenever the",
|
|
97969
97984
|
"game's ads change. A game with its own server gets an online part, and its Privacy button offers Delete my online",
|
|
97970
97985
|
"data: the online topic has it. A game that sells gets a purchases part: the purchases topic has it.",
|
|
@@ -97989,7 +98004,7 @@ var PUBLISH = [
|
|
|
97989
98004
|
" It warns of a break while the player is in the middle of play, and of a banner beside one of the game's buttons.",
|
|
97990
98005
|
"- In a store app, the Privacy button also opens Ad choices, where Google's consent asks for them: the player's ad",
|
|
97991
98006
|
" choices, which the law asks they can change.",
|
|
97992
|
-
"- A game whose audience includes children under 13 (
|
|
98007
|
+
"- A game whose audience includes children under 13 (Upload \u203A App asks) is made for children: it shows no ads, every",
|
|
97993
98008
|
" ad is refused, and its builds carry no ad library. Never place ads in a game for children.",
|
|
97994
98009
|
"- An APK to try and the Xcode project download show Google's test ads, whatever ids the person gave; a release shows",
|
|
97995
98010
|
" their real ones. Tell the person never to tap their own real ads, even to test: AdMob can close the account. They",
|
|
@@ -98014,7 +98029,7 @@ var ONLINE = () => [
|
|
|
98014
98029
|
"",
|
|
98015
98030
|
"A game's own server does what one device can't: a leaderboard, a save that follows the player, players together, an",
|
|
98016
98031
|
"outside service with a secret key. It is the person's choice and money: the person turns it on, sizes it, pays for it",
|
|
98017
|
-
"and publishes it
|
|
98032
|
+
"and publishes it on the editor's Server page. You write its code; you never turn it on, resize it, spend or publish.",
|
|
98018
98033
|
"",
|
|
98019
98034
|
"## The code",
|
|
98020
98035
|
"",
|
|
@@ -98025,7 +98040,7 @@ var ONLINE = () => [
|
|
|
98025
98040
|
" is given the player (an anonymous id, checked before the code runs), the data the game sent, and ctx; what it returns",
|
|
98026
98041
|
" is the answer's data, and what it throws the answer's error, in its words.",
|
|
98027
98042
|
"- ctx.db is the game's own Postgres database, once the person turns it on. ctx.secrets holds the game's secrets by",
|
|
98028
|
-
" name, whose values the person sets
|
|
98043
|
+
" name, whose values the person sets on the Server page: never write a key in code. The rest of ctx is below.",
|
|
98029
98044
|
"- Check every field a player sends, its type and its range, as the patterns do.",
|
|
98030
98045
|
"",
|
|
98031
98046
|
"The server API, exactly as server code is checked against it:",
|
|
@@ -98049,16 +98064,16 @@ var ONLINE = () => [
|
|
|
98049
98064
|
"",
|
|
98050
98065
|
"- Every table keeps the player's id in a player_id column. Delete my online data, behind the game's Privacy button,",
|
|
98051
98066
|
" deletes every row whose player_id is the player's, then runs onForget, where the code deletes anything else it keeps",
|
|
98052
|
-
" of them. The
|
|
98067
|
+
" of them. The Server page marks a table with no player_id.",
|
|
98053
98068
|
"- The privacy policy and the store forms follow the game's online, as they follow its ads: the person confirms them",
|
|
98054
|
-
" again in the Upload
|
|
98069
|
+
" again in the Upload page's wizards when its server, database, live rooms, files, push or purchases change (the",
|
|
98055
98070
|
" publish topic).",
|
|
98056
98071
|
"- onPurchase, onRevoke and onReward hear what players buy and the ads they watch for a reward, each checked first",
|
|
98057
98072
|
" with its store or AdMob: the purchases topic has them.",
|
|
98058
98073
|
"",
|
|
98059
98074
|
"## Players' files",
|
|
98060
98075
|
"",
|
|
98061
|
-
"- ctx.files keeps the game's files beside its database, once the person turns them on
|
|
98076
|
+
"- ctx.files keeps the game's files beside its database, once the person turns them on, on the Server page: a player's",
|
|
98062
98077
|
" under players/<their id>/, the game's own under game/; a key is at most 512 bytes, with no '..'.",
|
|
98063
98078
|
"- this.game.online.upload('saveLevel', file, data) sends a file of plain data (at most 1,000,000 bytes as JSON);",
|
|
98064
98079
|
" uploadScreen('saveShot', data) sends the frame as a PNG. The page asks that call with { size, type, data }; the call",
|
|
@@ -98079,15 +98094,15 @@ var ONLINE = () => [
|
|
|
98079
98094
|
"- Ask after a moment that shows why (a match ended, a level shared), never at launch. Push is never needed to play,",
|
|
98080
98095
|
" never an ad the player didn't agree to, never a reward for saying yes (App Store Review Guideline 4.5.4, Google",
|
|
98081
98096
|
" Play's notifications policy).",
|
|
98082
|
-
"- The person's steps, in their own Firebase project, which the
|
|
98097
|
+
"- The person's steps, in their own Firebase project, which the Server page says: add the game's Android and Apple apps;",
|
|
98083
98098
|
" Apple's push key goes into Project settings \u203A Cloud Messaging; the service account and the two app files go into",
|
|
98084
|
-
" the
|
|
98099
|
+
" the Server page, and each app build carries its own.",
|
|
98085
98100
|
"",
|
|
98086
98101
|
"## Test and live",
|
|
98087
98102
|
"",
|
|
98088
|
-
"-
|
|
98103
|
+
"- Playing in the editor runs the game against its test server, and players against live. Each save that changes the",
|
|
98089
98104
|
" server code goes to test a moment later; the person sends test's code to players with Publish to players in the",
|
|
98090
|
-
"
|
|
98105
|
+
" Server page, and can undo it there. Test and live each have their own database and secrets: test's data is never",
|
|
98091
98106
|
" players'.",
|
|
98092
98107
|
"- Old apps keep calling: ctx.version says which, so a change can answer them the old way.",
|
|
98093
98108
|
"",
|
|
@@ -98107,13 +98122,13 @@ var ONLINE = () => [
|
|
|
98107
98122
|
" person turns on, and where.",
|
|
98108
98123
|
"- app online_logs reads the server's logs, stage test or live.",
|
|
98109
98124
|
"- app online_query reads the game's data with one read-only SQL statement: here, where playtests write it, unless",
|
|
98110
|
-
" stage says test, or live once the person allows it
|
|
98125
|
+
" stage says test, or live once the person allows it on the Server page.",
|
|
98111
98126
|
"",
|
|
98112
98127
|
"## What it costs",
|
|
98113
98128
|
"",
|
|
98114
98129
|
`- Online is paid by use from the workspace's wallet, apart from its plan: the person adds credit on the Plan page, and`,
|
|
98115
98130
|
` it turns on with $10 or more there. A small game's month (about 10,000 calls a day, its database awake two hours a`,
|
|
98116
|
-
` day) costs about ${dollars(priceOf(TYPICAL_MONTH))}; the
|
|
98131
|
+
` day) costs about ${dollars(priceOf(TYPICAL_MONTH))}; the Server page and the Plan page give every price.`,
|
|
98117
98132
|
"- Each size holds its ceilings (calls a second, players at once, work a call, the database's compute and data); a call",
|
|
98118
98133
|
" past them answers busy.",
|
|
98119
98134
|
"- When the wallet runs out, the game's server pauses: the game keeps running, its calls answer resting, and nothing",
|
|
@@ -98121,8 +98136,8 @@ var ONLINE = () => [
|
|
|
98121
98136
|
"",
|
|
98122
98137
|
"## Games made for children",
|
|
98123
98138
|
"",
|
|
98124
|
-
"- A game made for children (
|
|
98125
|
-
"
|
|
98139
|
+
"- A game made for children (Upload \u203A App asks) keeps its live rooms, player files and push off until the person confirms,",
|
|
98140
|
+
" on the Server page, that each is designed to be safe. Design them so: no free text between players, only preset",
|
|
98126
98141
|
" phrases, emotes and moves; no name a player types; nothing that says who or where a player is; files only the game",
|
|
98127
98142
|
" makes, like a level; notifications that never sell and never reward.",
|
|
98128
98143
|
"- Keep no more of anyone than the game needs.",
|
|
@@ -98228,10 +98243,10 @@ var PURCHASES = [
|
|
|
98228
98243
|
"- Google's check uses the workspace's Play key file, the one that uploads: its account needs View financial data and",
|
|
98229
98244
|
" Manage orders in Play Console \u203A Users and permissions. Until the workspace's owner adds it under Store access,",
|
|
98230
98245
|
" Google's purchases are checked on the phone.",
|
|
98231
|
-
"- Refunds and renewals reach the game's own addresses, which the
|
|
98246
|
+
"- Refunds and renewals reach the game's own addresses, which the Server page gives. Apple's: the App Store wizard's",
|
|
98232
98247
|
" Purchases step sets it. Google's: the person makes a Pub/Sub topic in their Google Cloud project, names it in Play",
|
|
98233
98248
|
" Console \u203A Monetize \u203A Monetization setup, and adds a push subscription to the game's address, authenticating as the",
|
|
98234
|
-
" service account of the workspace's Play key file; a notice sent as anyone else is refused. The
|
|
98249
|
+
" service account of the workspace's Play key file; a notice sent as anyone else is refused. The Server page says each",
|
|
98235
98250
|
" step.",
|
|
98236
98251
|
"",
|
|
98237
98252
|
"## Checked rewards",
|
|
@@ -98241,7 +98256,7 @@ var PURCHASES = [
|
|
|
98241
98256
|
" reward, ctx) hears it once: { placement, amount, item, transaction }. Give the reward there; the game asks its",
|
|
98242
98257
|
" server for it.",
|
|
98243
98258
|
"- The person pastes that address into each rewarded ad unit in AdMob, under Advanced settings \u203A Server-side",
|
|
98244
|
-
" verification; the
|
|
98259
|
+
" verification; the Server page gives it. Only rewards from the game's own rewarded ad units (Upload \u203A Ads) count: AdMob",
|
|
98245
98260
|
" signs every account's alike. AdMob's call can come a few seconds after the ad ends: ask when the ad says rewarded,",
|
|
98246
98261
|
" and once more a moment later.",
|
|
98247
98262
|
"- The server keeps each reward under the player's id, which Delete my online data deletes too.",
|
|
@@ -98258,7 +98273,7 @@ var PURCHASES = [
|
|
|
98258
98273
|
"",
|
|
98259
98274
|
"## Testing",
|
|
98260
98275
|
"",
|
|
98261
|
-
"-
|
|
98276
|
+
"- When the person plays in the editor, each buy is a card they answer: Bought, Cancelled or Pending. With the test server",
|
|
98262
98277
|
" granting purchases, Bought reaches its onPurchase as a test purchase, and a reward watched reaches its onReward.",
|
|
98263
98278
|
"- A playtest buys by itself, granted as a test purchase, and a restore finds nothing owned; with onPurchase, the buy",
|
|
98264
98279
|
" goes to the server on this machine first. The answer lists each, like purchases: buy coins_100 granted at 1.2 s,",
|
|
@@ -98473,7 +98488,7 @@ var GUIDE_TOPICS = [
|
|
|
98473
98488
|
{ name: "model", about: "art model: 3D art Blender builds from a script, when asked for 3D", text: () => MODEL.join("\n") },
|
|
98474
98489
|
{ name: "team", about: "working beside teammates and their agents: board, parts, claims, notes", text: () => TEAM.join("\n") },
|
|
98475
98490
|
{ name: "film", about: "films: levels that play themselves, cutscenes, chapters, render to MP4", text: () => FILM.join("\n") },
|
|
98476
|
-
{ name: "publish", about: "the Upload
|
|
98491
|
+
{ name: "publish", about: "the Upload page: export, GitHub, app builds, stores, privacy, ads", text: () => PUBLISH.join("\n") },
|
|
98477
98492
|
{ name: "online", about: "a game's own server: code, database, live rooms, test and live, costs", text: () => ONLINE().join("\n") },
|
|
98478
98493
|
{ name: "purchases", about: "selling in store apps, and ad rewards the server verifies", text: () => PURCHASES.join("\n") }
|
|
98479
98494
|
];
|
|
@@ -106781,7 +106796,7 @@ function readArt2(args2, ctx) {
|
|
|
106781
106796
|
|
|
106782
106797
|
${painting}` : modelled !== null ? `${head}, modelled in 3D.${earlier} its model, to change with art model's edit or give whole again, or to render again with art model and its name alone:
|
|
106783
106798
|
|
|
106784
|
-
${modelled}` : by === "code" ? `${head}, made with code in the Art
|
|
106799
|
+
${modelled}` : by === "code" ? `${head}, made with code in the Art view (the person's Make with code).${earlier} paint it with art paint for a finished look` : `${head}, imported, not painted: the person's own, never replaced`;
|
|
106785
106800
|
const shown3 = answer(body3, { name: `own:${name2}`, kind: entry.kind, painted: painting !== null, modelled: modelled !== null });
|
|
106786
106801
|
return { ...shown3, content: [...shown3.content, ...picture5] };
|
|
106787
106802
|
}
|
|
@@ -107239,7 +107254,7 @@ var APP_TOOLS = [
|
|
|
107239
107254
|
},
|
|
107240
107255
|
{
|
|
107241
107256
|
name: "app_online",
|
|
107242
|
-
description: "the game's own server (guide online): sizes, today's calls and errors, cost and wallet, versions in use; what the person turns on
|
|
107257
|
+
description: "the game's own server (guide online): sizes, today's calls and errors, cost and wallet, versions in use; what the person turns on, on the editor's Server page.",
|
|
107243
107258
|
inputSchema: object2({ game: GAME }, ["game"]),
|
|
107244
107259
|
handle: (args2, ctx) => sent(args2, ctx, { action: "online" }, ONLINE2)
|
|
107245
107260
|
},
|
|
@@ -107251,7 +107266,7 @@ var APP_TOOLS = [
|
|
|
107251
107266
|
},
|
|
107252
107267
|
{
|
|
107253
107268
|
name: "app_online_query",
|
|
107254
|
-
description: "one read-only SQL statement on its data: here (playtests'), test, or live once the person allows it in the
|
|
107269
|
+
description: "one read-only SQL statement on its data: here (playtests'), test, or live once the person allows it in the Server page.",
|
|
107255
107270
|
inputSchema: object2({
|
|
107256
107271
|
game: GAME,
|
|
107257
107272
|
sql: { type: "string", minLength: 1, maxLength: 4e3, description: "one statement, like select player_id, score from scores" },
|
|
@@ -107276,7 +107291,8 @@ var NEEDS_PROGRAM6 = "exporting to a file runs in the Gamesteel program on your
|
|
|
107276
107291
|
var EXPORT_TOOLS = [
|
|
107277
107292
|
{
|
|
107278
107293
|
name: "export_game",
|
|
107279
|
-
|
|
107294
|
+
// Its words cut to make room under Cursor's list size for the website's own tool (2026-10-26).
|
|
107295
|
+
description: "write the game's export, a zip that plays anywhere, on this machine, and say where it is: unzipped, its index.html plays in a browser. A plan exports games up to its cap; a large game takes minutes, and memory.",
|
|
107280
107296
|
inputSchema: object2({ game: GAME }, ["game"]),
|
|
107281
107297
|
handle: async (args2, ctx) => {
|
|
107282
107298
|
const slug = textArg(args2, "game");
|
|
@@ -107321,7 +107337,7 @@ var GITHUB_TOOLS = [
|
|
|
107321
107337
|
// ../agent/dist/film-tools.js
|
|
107322
107338
|
var NEEDS_PROGRAM8 = "rendering video runs in the Gamesteel program on your machine, which asks the editor with the game open: add it to your agent with npx gamesteel@latest connect <agent>";
|
|
107323
107339
|
var STARTED = "Rendering started in the editor where the game is open. It renders each film with no video, or an old one, once for each of the game's languages, and keeps the videos with the game; each answer about the game says how it goes, and how it ended.";
|
|
107324
|
-
var NONE = "No editor has this game open: ask the person to open it in the editor and press Render video in
|
|
107340
|
+
var NONE = "No editor has this game open: ask the person to open it in the editor and press Render video in Upload \u203A Films, or to keep it open and ask you again.";
|
|
107325
107341
|
var FILM_TOOLS = [
|
|
107326
107342
|
{
|
|
107327
107343
|
name: "render_video",
|
|
@@ -111289,7 +111305,7 @@ var DEFAULT_SERVE = { schemas: "full" };
|
|
|
111289
111305
|
var SINGLES2 = ["guide", "edit", "write_code", "team"];
|
|
111290
111306
|
var fam = (name2, about, actions) => ({ name: name2, about, actions: Object.entries(actions).map(([action, tool]) => ({ action, tool })) });
|
|
111291
111307
|
var FAMILIES2 = [
|
|
111292
|
-
fam("games", "the games in the library
|
|
111308
|
+
fam("games", "the games in the library, and game text given as source", {
|
|
111293
111309
|
list: "list_games",
|
|
111294
111310
|
open: "open_game",
|
|
111295
111311
|
new: "new_game",
|
|
@@ -111316,7 +111332,7 @@ var FAMILIES2 = [
|
|
|
111316
111332
|
redo: "redo",
|
|
111317
111333
|
art: "undo_art"
|
|
111318
111334
|
}),
|
|
111319
|
-
fam("playtest", "play the game yourself, headless
|
|
111335
|
+
fam("playtest", "play the game yourself, headless, and look at it", {
|
|
111320
111336
|
play: "playtest_play",
|
|
111321
111337
|
until: "playtest_until",
|
|
111322
111338
|
look: "playtest_look",
|
|
@@ -111325,13 +111341,13 @@ var FAMILIES2 = [
|
|
|
111325
111341
|
snapshot: "playtest_snapshot",
|
|
111326
111342
|
restore: "playtest_restore"
|
|
111327
111343
|
}),
|
|
111328
|
-
fam("art", "a game's pictures
|
|
111344
|
+
fam("art", "a game's pictures, painted or modelled in 3D", {
|
|
111329
111345
|
paint: "paint_art",
|
|
111330
111346
|
model: "model_art",
|
|
111331
111347
|
read: "read_art",
|
|
111332
111348
|
list: "list_art"
|
|
111333
111349
|
}),
|
|
111334
|
-
fam("sound", "a game's sound
|
|
111350
|
+
fam("sound", "a game's sound effects and music", {
|
|
111335
111351
|
design: "design_sound",
|
|
111336
111352
|
compose: "compose_music",
|
|
111337
111353
|
listen: "listen",
|
|
@@ -111357,6 +111373,14 @@ var FAMILIES2 = [
|
|
|
111357
111373
|
online: "app_online",
|
|
111358
111374
|
online_logs: "app_online_logs",
|
|
111359
111375
|
online_query: "app_online_query"
|
|
111376
|
+
}),
|
|
111377
|
+
// The 16th (the owner, 2026-10-26): what is the website's, not a game's.
|
|
111378
|
+
fam("website", "your Gamesteel account on the website", {
|
|
111379
|
+
account: "website_account",
|
|
111380
|
+
workspaces: "website_workspaces",
|
|
111381
|
+
members: "website_members",
|
|
111382
|
+
storage: "website_storage",
|
|
111383
|
+
sign_in: "sign_in"
|
|
111360
111384
|
})
|
|
111361
111385
|
];
|
|
111362
111386
|
function nowCalled(tool) {
|
|
@@ -111447,7 +111471,7 @@ function listingOf(family2, tools) {
|
|
|
111447
111471
|
};
|
|
111448
111472
|
for (const [key, { shape, uses }] of options)
|
|
111449
111473
|
properties[key] = { ...shape, description: saidOf(uses, present2.length) };
|
|
111450
|
-
const description = [`${family2.about}.
|
|
111474
|
+
const description = [`${family2.about}. Actions:`, ...present2.map((a4) => `- ${a4.action}: ${a4.def.description}`)].join("\n");
|
|
111451
111475
|
return { name: family2.name, description, inputSchema: { type: "object", additionalProperties: false, required: ["action"], properties } };
|
|
111452
111476
|
}
|
|
111453
111477
|
var ORDER = [
|
|
@@ -111465,7 +111489,8 @@ var ORDER = [
|
|
|
111465
111489
|
"library",
|
|
111466
111490
|
"film",
|
|
111467
111491
|
"team",
|
|
111468
|
-
"app"
|
|
111492
|
+
"app",
|
|
111493
|
+
"website"
|
|
111469
111494
|
];
|
|
111470
111495
|
function familyListing(tools) {
|
|
111471
111496
|
const singles = SINGLES2.flatMap((name2) => tools.filter((t2) => t2.name === name2));
|
|
@@ -111513,6 +111538,20 @@ var WAIT_TOOL = {
|
|
|
111513
111538
|
description: `wait up to ${String(JOB_MS / 1e3)} seconds for a call of yours still going, and answer with what it said`,
|
|
111514
111539
|
inputSchema: { type: "object", additionalProperties: false, properties: {} }
|
|
111515
111540
|
};
|
|
111541
|
+
var WEBSITE_READS = { account: "website_account", workspaces: "website_workspaces", members: "website_members", storage: "website_storage" };
|
|
111542
|
+
var NO_OPTIONS = { type: "object", additionalProperties: false, properties: {} };
|
|
111543
|
+
var WEBSITE_TOOLS = [
|
|
111544
|
+
{ name: WEBSITE_READS.account, description: "which account and workspace this works in, its plan, limits and pages", inputSchema: NO_OPTIONS },
|
|
111545
|
+
{ name: WEBSITE_READS.workspaces, description: "the person's workspaces, their role in each, and which this works in", inputSchema: NO_OPTIONS },
|
|
111546
|
+
{ name: WEBSITE_READS.members, description: "the workspace's people and their roles", inputSchema: NO_OPTIONS },
|
|
111547
|
+
{ name: WEBSITE_READS.storage, description: "what the workspace keeps: by kind, the largest files, what no game uses", inputSchema: NO_OPTIONS }
|
|
111548
|
+
];
|
|
111549
|
+
var WEBSITE_NAMES = new Set(Object.values(WEBSITE_READS));
|
|
111550
|
+
var SIGN_IN_TOOL = {
|
|
111551
|
+
name: "sign_in",
|
|
111552
|
+
description: "sign in again, as another account or workspace",
|
|
111553
|
+
inputSchema: { type: "object", additionalProperties: false, properties: {} }
|
|
111554
|
+
};
|
|
111516
111555
|
var NOTHING_GOING = "no call of yours is going on";
|
|
111517
111556
|
function guideAnswer(raw) {
|
|
111518
111557
|
const name2 = typeof raw["topic"] === "string" ? raw["topic"] : "";
|
|
@@ -111549,6 +111588,10 @@ function schemaFor(tool) {
|
|
|
111549
111588
|
return GUIDE_TOOL.inputSchema;
|
|
111550
111589
|
if (tool === WAIT_TOOL.name)
|
|
111551
111590
|
return WAIT_TOOL.inputSchema;
|
|
111591
|
+
if (tool === SIGN_IN_TOOL.name)
|
|
111592
|
+
return SIGN_IN_TOOL.inputSchema;
|
|
111593
|
+
if (WEBSITE_NAMES.has(tool))
|
|
111594
|
+
return NO_OPTIONS;
|
|
111552
111595
|
const editing = EDITING.get(tool);
|
|
111553
111596
|
if (editing !== void 0)
|
|
111554
111597
|
return editing.inputSchema;
|
|
@@ -111570,6 +111613,8 @@ async function callToolAsync(tool, args2, ctx, label = nowCalled(tool)) {
|
|
|
111570
111613
|
return { content: [{ type: "text", text: refused3 }], isError: true };
|
|
111571
111614
|
return { content: [{ type: "text", text: NOTHING_GOING }] };
|
|
111572
111615
|
}
|
|
111616
|
+
if (tool === SIGN_IN_TOOL.name || WEBSITE_NAMES.has(tool))
|
|
111617
|
+
return { content: [{ type: "text", text: ON_YOUR_MACHINE }], isError: true };
|
|
111573
111618
|
const editing = EDITING.get(tool);
|
|
111574
111619
|
if (editing !== void 0) {
|
|
111575
111620
|
const refused3 = admitArgs(editing.inputSchema, raw, label);
|
|
@@ -111637,13 +111682,18 @@ function toolList() {
|
|
|
111637
111682
|
return [
|
|
111638
111683
|
GUIDE_TOOL,
|
|
111639
111684
|
WAIT_TOOL,
|
|
111685
|
+
...WEBSITE_TOOLS,
|
|
111686
|
+
SIGN_IN_TOOL,
|
|
111640
111687
|
...EDIT_TOOLS.map((tool) => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema })),
|
|
111641
111688
|
...CORE_TOOLS.map((name2) => ({ name: name2, description: CORE_LINES.get(name2) ?? name2, inputSchema: schemaFor(name2) }))
|
|
111642
111689
|
];
|
|
111643
111690
|
}
|
|
111644
111691
|
var READING_TOOLS = /* @__PURE__ */ new Set([
|
|
111692
|
+
// The account read and signed in again too, so an agent in a read-only workspace can still move to another.
|
|
111645
111693
|
"guide",
|
|
111646
111694
|
"wait_for_calls",
|
|
111695
|
+
...Object.values(WEBSITE_READS),
|
|
111696
|
+
"sign_in",
|
|
111647
111697
|
"open_game",
|
|
111648
111698
|
"read_scene",
|
|
111649
111699
|
"read_type",
|
|
@@ -111668,6 +111718,7 @@ var READING_TOOLS = /* @__PURE__ */ new Set([
|
|
|
111668
111718
|
"app_online_logs",
|
|
111669
111719
|
"app_online_query"
|
|
111670
111720
|
]);
|
|
111721
|
+
var ON_YOUR_MACHINE = "This runs on your own machine, not on the website. Add Gamesteel to your agent with `npx gamesteel@latest connect`, or `claude mcp add --scope user gamesteel -- npx -y gamesteel@latest`, then ask again.";
|
|
111671
111722
|
function makesGame(name2, args2, exists) {
|
|
111672
111723
|
if (name2 === "new_game")
|
|
111673
111724
|
return true;
|
|
@@ -112034,6 +112085,110 @@ function studioBackend(run3, session, headers = {}, timings = {}) {
|
|
|
112034
112085
|
|
|
112035
112086
|
// ../studio/dist/server/http.js
|
|
112036
112087
|
import { randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
|
|
112088
|
+
|
|
112089
|
+
// ../design/dist/tokens.js
|
|
112090
|
+
var PALETTE2 = {
|
|
112091
|
+
"bg": "#050607",
|
|
112092
|
+
/** A panel's solid stand-in, and what the scene canvas reads. */
|
|
112093
|
+
"surface": "#101216",
|
|
112094
|
+
/** Raised, solid: banners, and floating glass where blur is missing. */
|
|
112095
|
+
"surface-2": "#181B20",
|
|
112096
|
+
/** The code editor's well. */
|
|
112097
|
+
"field": "#08090B",
|
|
112098
|
+
/** The scene canvas's grid and frame; glass draws its hairlines in white. */
|
|
112099
|
+
"line": "#232833",
|
|
112100
|
+
"line-strong": "#2E3440",
|
|
112101
|
+
"text": "#F3F5F8",
|
|
112102
|
+
"text-2": "#B0B6C0",
|
|
112103
|
+
"text-3": "#999FAA",
|
|
112104
|
+
/** The editor's mint. The renderer's palette and the chrome still agree. */
|
|
112105
|
+
"accent": "#5ED3A0",
|
|
112106
|
+
/** The top of the primary button's gradient, where light catches it. */
|
|
112107
|
+
"accent-hi": "#8EF2C7",
|
|
112108
|
+
/** Words on an accent fill. */
|
|
112109
|
+
"accent-ink": "#06281A",
|
|
112110
|
+
/** Accent chips, solid. */
|
|
112111
|
+
"accent-soft": "#163A2C",
|
|
112112
|
+
/** The AI's colour in the editor: its edits, its outlines, its entries in
|
|
112113
|
+
* the console. Reserved for it, so a person always knows which changes
|
|
112114
|
+
* were not theirs. */
|
|
112115
|
+
"ai": "#8EAEFF",
|
|
112116
|
+
/** The forge's red: the landing page's room glows in it (the owner, 2026-10-12: "put the glow on red"), and it is
|
|
112117
|
+
* the site's accent where the editor has its mint. */
|
|
112118
|
+
"forge": "#EC1B1C",
|
|
112119
|
+
/** The forge's red, light enough to read as words: the site's code and its accent text. */
|
|
112120
|
+
"forge-hi": "#FF6B5E",
|
|
112121
|
+
/** Words on a forge fill. */
|
|
112122
|
+
"forge-ink": "#2B0705",
|
|
112123
|
+
/** The forge's red as the editor's words: lighter than forge-hi, so it reads at 4.5:1 on the editor's lightest
|
|
112124
|
+
* grounds (a hovered row, the selection and the glass over a picture, in a lit room). */
|
|
112125
|
+
"forge-text": "#FF867B",
|
|
112126
|
+
/** What is well and live: the editor's Server with a version live, a teammate in the editor (2026-10-27). */
|
|
112127
|
+
"good": "#6EDB9A",
|
|
112128
|
+
"warn": "#F2B65E",
|
|
112129
|
+
"warn-soft": "#3A2C14",
|
|
112130
|
+
"bad": "#F28A92",
|
|
112131
|
+
"bad-soft": "#3D1D22"
|
|
112132
|
+
};
|
|
112133
|
+
var SPARK = {
|
|
112134
|
+
white: "#FFF3D6",
|
|
112135
|
+
yellow: "#FFC24A",
|
|
112136
|
+
orange: "#FF7A1C",
|
|
112137
|
+
red: "#B8320F"
|
|
112138
|
+
};
|
|
112139
|
+
var CHROME = [
|
|
112140
|
+
[0, "#FFFFFF"],
|
|
112141
|
+
[14, "#FFFFFF"],
|
|
112142
|
+
[30, "#E6E9EE"],
|
|
112143
|
+
[44, "#A9AFB9"],
|
|
112144
|
+
[50, "#3D4148"],
|
|
112145
|
+
[56, "#5D636C"],
|
|
112146
|
+
[66, "#C7CCD4"],
|
|
112147
|
+
[74, "#F0D2B6"],
|
|
112148
|
+
[84, "#FF9A4A"],
|
|
112149
|
+
[100, SPARK.orange]
|
|
112150
|
+
];
|
|
112151
|
+
var GRAIN_ALPHA = 0.06;
|
|
112152
|
+
var GRAIN = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 ${String(GRAIN_ALPHA)} 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)'/%3E%3C/svg%3E")`;
|
|
112153
|
+
var ICON = (paths) => `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E${paths}%3C/svg%3E")`;
|
|
112154
|
+
var ICON_ALERT = ICON("%3Cpath d='m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3'/%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E");
|
|
112155
|
+
var ICON_CHECK = ICON("%3Cpath d='M20 6 9 17l-5-5'/%3E");
|
|
112156
|
+
var CHEVRON = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23${PALETTE2["text-2"].slice(1)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")`;
|
|
112157
|
+
|
|
112158
|
+
// ../studio/dist/loading-mark.js
|
|
112159
|
+
var LOADING_MARK = ["svg", { class: "ed-loading-mark", viewBox: "0 0 160 100", width: "160", height: "100", "aria-hidden": "true", focusable: "false" }, [
|
|
112160
|
+
["defs", {}, [
|
|
112161
|
+
[
|
|
112162
|
+
"linearGradient",
|
|
112163
|
+
{ id: "ed-loading-chrome", gradientUnits: "userSpaceOnUse", x1: "0", y1: "28", x2: "0", y2: "72" },
|
|
112164
|
+
CHROME.map(([at5, hex5]) => ["stop", { offset: `${String(at5)}%`, "stop-color": hex5 }])
|
|
112165
|
+
],
|
|
112166
|
+
["radialGradient", { id: "ed-loading-heat" }, [
|
|
112167
|
+
["stop", { offset: "0%", "stop-color": SPARK.orange, "stop-opacity": "0.75" }],
|
|
112168
|
+
["stop", { offset: "55%", "stop-color": SPARK.red, "stop-opacity": "0.25" }],
|
|
112169
|
+
["stop", { offset: "100%", "stop-color": SPARK.red, "stop-opacity": "0" }]
|
|
112170
|
+
]],
|
|
112171
|
+
["filter", { id: "ed-loading-melt", filterUnits: "userSpaceOnUse", x: "0", y: "0", width: "160", height: "100" }, [
|
|
112172
|
+
["feGaussianBlur", { in: "SourceGraphic", stdDeviation: "4.5" }],
|
|
112173
|
+
["feColorMatrix", { values: "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -8" }]
|
|
112174
|
+
]]
|
|
112175
|
+
]],
|
|
112176
|
+
["ellipse", { class: "ed-loading-heat", cx: "80", cy: "78", rx: "58", ry: "12", fill: "url(#ed-loading-heat)" }],
|
|
112177
|
+
["g", { filter: "url(#ed-loading-melt)", fill: "url(#ed-loading-chrome)" }, [
|
|
112178
|
+
["circle", { class: "ed-loading-drop is-left", cx: "67", cy: "50", r: "20" }],
|
|
112179
|
+
["circle", { class: "ed-loading-drop is-right", cx: "95", cy: "51", r: "16" }]
|
|
112180
|
+
]],
|
|
112181
|
+
["ellipse", { class: "ed-loading-shine is-left", cx: "60", cy: "39", rx: "7", ry: "2.6", fill: "#FFFFFF" }],
|
|
112182
|
+
["ellipse", { class: "ed-loading-shine is-right", cx: "90", cy: "42", rx: "5", ry: "2", fill: "#FFFFFF" }]
|
|
112183
|
+
]];
|
|
112184
|
+
var attribute = (text3) => text3.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<").replaceAll(">", ">");
|
|
112185
|
+
function markupOf([tag, attributes, inside2 = []]) {
|
|
112186
|
+
const named3 = Object.entries(attributes).map(([name2, value]) => ` ${name2}="${attribute(value)}"`).join("");
|
|
112187
|
+
return inside2.length === 0 ? `<${tag}${named3}/>` : `<${tag}${named3}>${inside2.map(markupOf).join("")}</${tag}>`;
|
|
112188
|
+
}
|
|
112189
|
+
var LOADING_PAGE = `<div class="ed-loading"><span class="ed-loading-art">${markupOf(LOADING_MARK)}</span><div class="ed-loading-words"></div><div class="ed-loading-bar"></div><div class="ed-loading-slow"></div></div>`;
|
|
112190
|
+
|
|
112191
|
+
// ../studio/dist/server/http.js
|
|
112037
112192
|
import { createServer as createServer2 } from "node:http";
|
|
112038
112193
|
function paramsOf(pattern, path) {
|
|
112039
112194
|
if (!pattern.includes(":") && !pattern.endsWith("/*"))
|
|
@@ -113266,7 +113421,7 @@ function editRoutes(manager, options = {}) {
|
|
|
113266
113421
|
method: "GET",
|
|
113267
113422
|
path: `${base}/scripts`,
|
|
113268
113423
|
// The game's scripts as files (the free engine spec, §2.5), each with
|
|
113269
|
-
// what the type checker found in it, for the Code
|
|
113424
|
+
// what the type checker found in it, for the Code view and the Console.
|
|
113270
113425
|
handle: (_req, res, _body, params) => {
|
|
113271
113426
|
game(res, params["slug"], (s5) => {
|
|
113272
113427
|
const version2 = s5.version;
|
|
@@ -114891,7 +115046,7 @@ ${said4}` };
|
|
|
114891
115046
|
}
|
|
114892
115047
|
/** What a frame of this game is drawn with: the library and the game's
|
|
114893
115048
|
* own art, each image by its file, and the HUD in the game's style, as
|
|
114894
|
-
* the
|
|
115049
|
+
* the editor's run draws it. */
|
|
114895
115050
|
artOf(slug) {
|
|
114896
115051
|
const { store } = this.options.manager;
|
|
114897
115052
|
this.library ??= libraryCatalogue().map((entry) => ({ ...entry, src: join18(LIBRARY_ROOT2, entry.src) }));
|
|
@@ -116090,73 +116245,6 @@ import { dirname as dirname8, join as join20, resolve as resolve3 } from "node:p
|
|
|
116090
116245
|
import { promisify } from "node:util";
|
|
116091
116246
|
import { gzip } from "node:zlib";
|
|
116092
116247
|
|
|
116093
|
-
// ../design/dist/tokens.js
|
|
116094
|
-
var PALETTE2 = {
|
|
116095
|
-
"bg": "#050607",
|
|
116096
|
-
/** A panel's solid stand-in, and what the scene canvas reads. */
|
|
116097
|
-
"surface": "#101216",
|
|
116098
|
-
/** Raised, solid: banners, and floating glass where blur is missing. */
|
|
116099
|
-
"surface-2": "#181B20",
|
|
116100
|
-
/** The code editor's well. */
|
|
116101
|
-
"field": "#08090B",
|
|
116102
|
-
/** The scene canvas's grid and frame; glass draws its hairlines in white. */
|
|
116103
|
-
"line": "#232833",
|
|
116104
|
-
"line-strong": "#2E3440",
|
|
116105
|
-
"text": "#F3F5F8",
|
|
116106
|
-
"text-2": "#B0B6C0",
|
|
116107
|
-
"text-3": "#999FAA",
|
|
116108
|
-
/** The editor's mint. The renderer's palette and the chrome still agree. */
|
|
116109
|
-
"accent": "#5ED3A0",
|
|
116110
|
-
/** The top of the primary button's gradient, where light catches it. */
|
|
116111
|
-
"accent-hi": "#8EF2C7",
|
|
116112
|
-
/** Words on an accent fill. */
|
|
116113
|
-
"accent-ink": "#06281A",
|
|
116114
|
-
/** Accent chips, solid. */
|
|
116115
|
-
"accent-soft": "#163A2C",
|
|
116116
|
-
/** The AI's colour in the editor: its edits, its outlines, its entries in
|
|
116117
|
-
* the console. Reserved for it, so a person always knows which changes
|
|
116118
|
-
* were not theirs. */
|
|
116119
|
-
"ai": "#8EAEFF",
|
|
116120
|
-
/** The forge's red: the landing page's room glows in it (the owner, 2026-10-12: "put the glow on red"), and it is
|
|
116121
|
-
* the site's accent where the editor has its mint. */
|
|
116122
|
-
"forge": "#EC1B1C",
|
|
116123
|
-
/** The forge's red, light enough to read as words: the site's code and its accent text. */
|
|
116124
|
-
"forge-hi": "#FF6B5E",
|
|
116125
|
-
/** Words on a forge fill. */
|
|
116126
|
-
"forge-ink": "#2B0705",
|
|
116127
|
-
/** The forge's red as the editor's words: lighter than forge-hi, so it reads at 4.5:1 on the editor's lightest
|
|
116128
|
-
* grounds (a hovered row, the selection and the glass over a picture, in a lit room). */
|
|
116129
|
-
"forge-text": "#FF867B",
|
|
116130
|
-
"warn": "#F2B65E",
|
|
116131
|
-
"warn-soft": "#3A2C14",
|
|
116132
|
-
"bad": "#F28A92",
|
|
116133
|
-
"bad-soft": "#3D1D22"
|
|
116134
|
-
};
|
|
116135
|
-
var SPARK = {
|
|
116136
|
-
white: "#FFF3D6",
|
|
116137
|
-
yellow: "#FFC24A",
|
|
116138
|
-
orange: "#FF7A1C",
|
|
116139
|
-
red: "#B8320F"
|
|
116140
|
-
};
|
|
116141
|
-
var CHROME = [
|
|
116142
|
-
[0, "#FFFFFF"],
|
|
116143
|
-
[14, "#FFFFFF"],
|
|
116144
|
-
[30, "#E6E9EE"],
|
|
116145
|
-
[44, "#A9AFB9"],
|
|
116146
|
-
[50, "#3D4148"],
|
|
116147
|
-
[56, "#5D636C"],
|
|
116148
|
-
[66, "#C7CCD4"],
|
|
116149
|
-
[74, "#F0D2B6"],
|
|
116150
|
-
[84, "#FF9A4A"],
|
|
116151
|
-
[100, SPARK.orange]
|
|
116152
|
-
];
|
|
116153
|
-
var GRAIN_ALPHA = 0.06;
|
|
116154
|
-
var GRAIN = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 ${String(GRAIN_ALPHA)} 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)'/%3E%3C/svg%3E")`;
|
|
116155
|
-
var ICON = (paths) => `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E${paths}%3C/svg%3E")`;
|
|
116156
|
-
var ICON_ALERT = ICON("%3Cpath d='m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3'/%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E");
|
|
116157
|
-
var ICON_CHECK = ICON("%3Cpath d='M20 6 9 17l-5-5'/%3E");
|
|
116158
|
-
var CHEVRON = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23${PALETTE2["text-2"].slice(1)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")`;
|
|
116159
|
-
|
|
116160
116248
|
// ../design/dist/fonts.js
|
|
116161
116249
|
var resolve2 = __gamesteelResolve;
|
|
116162
116250
|
var FONT_FILES = {
|
|
@@ -116168,6 +116256,18 @@ var FONT_FILES = {
|
|
|
116168
116256
|
ui: resolve2("@fontsource-variable/geist/files/geist-latin-wght-normal.woff2")
|
|
116169
116257
|
};
|
|
116170
116258
|
|
|
116259
|
+
// ../design/dist/roles.js
|
|
116260
|
+
var PERMISSIONS = ["play", "edit", "publish", "servers", "members", "billing", "workspace"];
|
|
116261
|
+
var GRANTS = {
|
|
116262
|
+
owner: new Set(PERMISSIONS),
|
|
116263
|
+
admin: /* @__PURE__ */ new Set(["play", "edit", "publish", "servers", "members"]),
|
|
116264
|
+
member: /* @__PURE__ */ new Set(["play", "edit"]),
|
|
116265
|
+
viewer: /* @__PURE__ */ new Set(["play"])
|
|
116266
|
+
};
|
|
116267
|
+
function can(role, permission) {
|
|
116268
|
+
return role != null && GRANTS[role].has(permission);
|
|
116269
|
+
}
|
|
116270
|
+
|
|
116171
116271
|
// ../i18n/dist/index.js
|
|
116172
116272
|
function chooseLanguage(header2) {
|
|
116173
116273
|
const best = { en: { q: 0, at: Infinity }, es: { q: 0, at: Infinity } };
|
|
@@ -116214,6 +116314,8 @@ var SITE = resolve3(dirname8(__gamesteelResolve("@gamesteel/site/package.json"))
|
|
|
116214
116314
|
var WORDS = resolve3(SITE, "..", "i18n");
|
|
116215
116315
|
var KEEP_LANGUAGE = 365 * 24 * 60 * 60;
|
|
116216
116316
|
var KEEP_FONT = 7 * 24 * 60 * 60;
|
|
116317
|
+
var PUBLIC_PAGES = ["/", "/engine", "/updates", "/demo", "/connect", "/pricing", "/docs", "/terms", "/privacy"];
|
|
116318
|
+
var PUBLIC = new Set(PUBLIC_PAGES);
|
|
116217
116319
|
var gzipped = promisify(gzip);
|
|
116218
116320
|
|
|
116219
116321
|
// ../studio/dist/server/export.js
|
|
@@ -119584,7 +119686,7 @@ function pushFileProblem(platform, id, text3) {
|
|
|
119584
119686
|
const file = PUSH_FILES[platform];
|
|
119585
119687
|
const app = platform === "android" ? "the Android app" : "the Apple app";
|
|
119586
119688
|
if (text3 === null)
|
|
119587
|
-
return `${file} is missing: download it from the game's Firebase project (Project settings, Your apps, ${app} ${id}) and add it
|
|
119689
|
+
return `${file} is missing: download it from the game's Firebase project (Project settings, Your apps, ${app} ${id}) and add it on the Server page`;
|
|
119588
119690
|
const ids = firebaseIds(platform, text3);
|
|
119589
119691
|
if (ids === null)
|
|
119590
119692
|
return `${file} is not the file Firebase gives: download it again`;
|
|
@@ -121839,7 +121941,7 @@ function settingsWords(state, builder, releases, icon) {
|
|
|
121839
121941
|
`- icon: ${s5.icon === "title" ? "the title icon (the first letter on the HUD plate)" : s5.icon}${icon === null ? "" : `, but ${icon}`}`,
|
|
121840
121942
|
`- splash: ${s5.splash}`,
|
|
121841
121943
|
`- audience: ${s5.children === true ? "includes children under 13, so it shows no ads" : "does not include children under 13"}`,
|
|
121842
|
-
state.keyHidden === true ? "Upload key: read by the owner's or an admin's program for a release build." : state.key === null ? "Upload key: none yet; a release build makes one, and Gamesteel keeps it sealed." : "Upload key: kept by Gamesteel, sealed; the workspace's owner and admins download a copy from
|
|
121944
|
+
state.keyHidden === true ? "Upload key: read by the owner's or an admin's program for a release build." : state.key === null ? "Upload key: none yet; a release build makes one, and Gamesteel keeps it sealed." : "Upload key: kept by Gamesteel, sealed; the workspace's owner and admins download a copy from Upload \u203A App.",
|
|
121843
121945
|
builder,
|
|
121844
121946
|
...release,
|
|
121845
121947
|
`Build targets: ${TARGETS2}.`
|
|
@@ -121879,7 +121981,7 @@ function hostedAppRoutes(options) {
|
|
|
121879
121981
|
const mayExport = options.mayExport ?? ((req2) => requestPlaces.get(req2)?.export !== false);
|
|
121880
121982
|
const mayKeepKeys = options.mayKeepKeys ?? ((req2) => {
|
|
121881
121983
|
const role = requestPlaces.get(req2)?.role;
|
|
121882
|
-
return !agentRequests.has(req2) && (role
|
|
121984
|
+
return !agentRequests.has(req2) && can(role, "publish");
|
|
121883
121985
|
});
|
|
121884
121986
|
const gameOf = (params) => {
|
|
121885
121987
|
const slug = params["slug"] ?? "";
|
|
@@ -122144,7 +122246,7 @@ function hostedAppRoutes(options) {
|
|
|
122144
122246
|
store.saveStoreListing(slug, listing);
|
|
122145
122247
|
const problems = listingProblems(listing);
|
|
122146
122248
|
const kept = `${slug}'s listing in ${language} is kept with the game.`;
|
|
122147
|
-
const pictures = problems.some((one2) => PICTURE_PROBLEMS.has(one2)) ? "; the person makes the pictures in the editor's App and
|
|
122249
|
+
const pictures = problems.some((one2) => PICTURE_PROBLEMS.has(one2)) ? "; the person makes the pictures in the editor's Upload \u203A App, and with Store screenshot while the game plays" : "";
|
|
122148
122250
|
const play2 = problems.length === 0 ? " It is finished." : ` It still needs ${listingNeeds(problems)}${pictures}.`;
|
|
122149
122251
|
const appleSet = APPLE_TEXT2.some((key) => set2[key] !== void 0);
|
|
122150
122252
|
const update = appleSet && options.stores !== void 0 ? await options.stores.appleUpdate(slug).catch(() => false) : false;
|
|
@@ -127834,6 +127936,7 @@ var HOUR = 36e5;
|
|
|
127834
127936
|
var DAY = 24 * HOUR;
|
|
127835
127937
|
var SESSION_LIFE = 30 * DAY;
|
|
127836
127938
|
var FREE_WEEK = 7 * DAY;
|
|
127939
|
+
var PICTURE_MOST = 512 * 1024;
|
|
127837
127940
|
|
|
127838
127941
|
// ../accounts/dist/limits.js
|
|
127839
127942
|
var MINUTE = 6e4;
|
|
@@ -127847,6 +127950,8 @@ var LIMITS2 = {
|
|
|
127847
127950
|
loginAddress: { tries: 30, per: 15 * MINUTE },
|
|
127848
127951
|
/** Sign-ups from one address. */
|
|
127849
127952
|
signup: { tries: 5, per: HOUR2 },
|
|
127953
|
+
/** Google log-ins started from one address, each keeping a state until it comes back (the Google log-in spec). */
|
|
127954
|
+
googleStart: { tries: 30, per: 15 * MINUTE },
|
|
127850
127955
|
/** Requests that can email an address (sign up, send again, forgot), counted whether or not one goes out. */
|
|
127851
127956
|
emails: { tries: 3, per: HOUR2 },
|
|
127852
127957
|
/** Apps registering to sign agents in, from one address (the agents by address spec, part 2). */
|
|
@@ -127858,6 +127963,9 @@ var LIMITS2 = {
|
|
|
127858
127963
|
oauthTokenAll: { tries: 600, per: MINUTE }
|
|
127859
127964
|
};
|
|
127860
127965
|
|
|
127966
|
+
// ../accounts/dist/mail.js
|
|
127967
|
+
var INK = { band: PALETTE2.bg, red: PALETTE2.forge, link: "#C81516", page: "#F2F2F3", card: "#FFFFFF", heading: "#0B0B0C", text: "#3F3F46", small: "#52525B", faint: "#A1A1AA", foot: "#FAFAFA", rule: "#ECECEE" };
|
|
127968
|
+
|
|
127861
127969
|
// ../accounts/dist/workspaces.js
|
|
127862
127970
|
var DAY2 = 864e5;
|
|
127863
127971
|
var INVITE_LIFE = 7 * DAY2;
|
|
@@ -128132,7 +128240,7 @@ Team since your last call: ${news.map((event) => event.words).join("; ")}.`;
|
|
|
128132
128240
|
own3.delete(listener);
|
|
128133
128241
|
};
|
|
128134
128242
|
}
|
|
128135
|
-
/** The editor's Team
|
|
128243
|
+
/** The editor's Team page: the board as the person sees it, and their actions on it. */
|
|
128136
128244
|
routes() {
|
|
128137
128245
|
const personOf = (req2) => {
|
|
128138
128246
|
const id = requestPlaces.get(req2)?.person;
|
|
@@ -128183,7 +128291,7 @@ Team since your last call: ${news.map((event) => event.words).join("; ")}.`;
|
|
|
128183
128291
|
}
|
|
128184
128292
|
];
|
|
128185
128293
|
}
|
|
128186
|
-
/** The board as the Team
|
|
128294
|
+
/** The board as the Team page shows it to `viewer`. */
|
|
128187
128295
|
async view(slug, viewer) {
|
|
128188
128296
|
const board = await this.load(slug);
|
|
128189
128297
|
const doc = this.docOf(slug);
|
|
@@ -134850,6 +134958,11 @@ var SiteRefusal = class extends Error {
|
|
|
134850
134958
|
status;
|
|
134851
134959
|
body;
|
|
134852
134960
|
};
|
|
134961
|
+
var SignInMoved = class extends SiteRefusal {
|
|
134962
|
+
constructor() {
|
|
134963
|
+
super(409, "this program's sign-in moved to another account or workspace: ask again", { error: "workspace" });
|
|
134964
|
+
}
|
|
134965
|
+
};
|
|
134853
134966
|
var pathOf3 = (path) => path.split("/").map(encodeURIComponent).join("/");
|
|
134854
134967
|
var sha2562 = (bytes2) => createHash5("sha256").update(bytes2).digest("hex");
|
|
134855
134968
|
var DeviceSite = class {
|
|
@@ -134862,11 +134975,19 @@ var DeviceSite = class {
|
|
|
134862
134975
|
get site() {
|
|
134863
134976
|
return this.options.site;
|
|
134864
134977
|
}
|
|
134978
|
+
/** The workspace the program started in, named in each request once it is known, so a sign-in that moved under the
|
|
134979
|
+
* program to another workspace is refused there, and nothing of this one is ever sent into it. */
|
|
134980
|
+
pinned = null;
|
|
134981
|
+
pin(workspaceId) {
|
|
134982
|
+
this.pinned = workspaceId;
|
|
134983
|
+
}
|
|
134865
134984
|
async request(method, path, init2 = {}) {
|
|
134985
|
+
const pinned = init2.unpinned === true ? null : this.pinned;
|
|
134866
134986
|
const headers = {
|
|
134867
134987
|
authorization: `Bearer ${await this.options.token()}`,
|
|
134868
134988
|
"x-gamesteel-engine": ENGINE_VERSION,
|
|
134869
134989
|
"x-gamesteel-session": init2.session ?? this.options.session(),
|
|
134990
|
+
...pinned === null ? {} : { "x-gamesteel-workspace": pinned },
|
|
134870
134991
|
...init2.headers
|
|
134871
134992
|
};
|
|
134872
134993
|
if (init2.json !== void 0) headers["content-type"] = "application/json";
|
|
@@ -134883,6 +135004,13 @@ var DeviceSite = class {
|
|
|
134883
135004
|
await response.body?.cancel();
|
|
134884
135005
|
throw new SiteRefusal(401, `this program is signed out of ${this.options.site}: sign in again`, null);
|
|
134885
135006
|
}
|
|
135007
|
+
if (response.status === 409 && pinned !== null) {
|
|
135008
|
+
const said4 = await response.clone().json().catch(() => null);
|
|
135009
|
+
if (said4?.error === "workspace") {
|
|
135010
|
+
await response.body?.cancel();
|
|
135011
|
+
throw new SignInMoved();
|
|
135012
|
+
}
|
|
135013
|
+
}
|
|
134886
135014
|
return response;
|
|
134887
135015
|
}
|
|
134888
135016
|
/** The answer's body when its status is one the route answers with, else thrown in its words. */
|
|
@@ -134930,8 +135058,21 @@ var DeviceSite = class {
|
|
|
134930
135058
|
async githubConfirmed(confirmation) {
|
|
134931
135059
|
return (await this.answer(await this.request("POST", "github/confirmed", { json: confirmation }), [200])).confirmed;
|
|
134932
135060
|
}
|
|
135061
|
+
/** Who the sign-in is now, whatever workspace the program started in. */
|
|
134933
135062
|
async standing() {
|
|
134934
|
-
return this.answer(await this.request("GET", "standing"), [200]);
|
|
135063
|
+
return this.answer(await this.request("GET", "standing", { unpinned: true }), [200]);
|
|
135064
|
+
}
|
|
135065
|
+
/** The person's workspaces, their role in each, and the one this works in (the website tool). */
|
|
135066
|
+
async workspaces() {
|
|
135067
|
+
return (await this.answer(await this.request("GET", "workspaces"), [200])).workspaces;
|
|
135068
|
+
}
|
|
135069
|
+
/** The workspace's people and their roles. */
|
|
135070
|
+
async members() {
|
|
135071
|
+
return (await this.answer(await this.request("GET", "members"), [200])).members;
|
|
135072
|
+
}
|
|
135073
|
+
/** What the workspace keeps, game by game: each item, its size, and whether its game uses it. */
|
|
135074
|
+
async storage() {
|
|
135075
|
+
return this.answer(await this.request("GET", "storage"), [200]);
|
|
134935
135076
|
}
|
|
134936
135077
|
/** A person of the workspace by name, or their agent. */
|
|
134937
135078
|
async name(user, grant) {
|
|
@@ -135173,7 +135314,7 @@ var DeviceSite = class {
|
|
|
135173
135314
|
const game = answer3.endpoint?.game;
|
|
135174
135315
|
return answer3.open && answer3.server != null && typeof host === "string" && typeof game === "string" ? { host, game, push: answer3.push === true, purchases: answer3.purchases === true, rewards: answer3.rewards === true } : null;
|
|
135175
135316
|
}
|
|
135176
|
-
/** The game's online settings whole, as the
|
|
135317
|
+
/** The game's online settings whole, as the Server page reads them (the online services spec), for its agent's words. */
|
|
135177
135318
|
async onlineState(slug) {
|
|
135178
135319
|
return this.answer(await this.request("GET", `games/${slug}/online`), [200]);
|
|
135179
135320
|
}
|
|
@@ -135242,7 +135383,7 @@ var DeviceSite = class {
|
|
|
135242
135383
|
};
|
|
135243
135384
|
|
|
135244
135385
|
// src/program/online-agent.ts
|
|
135245
|
-
var TAB = "the editor's
|
|
135386
|
+
var TAB = "the editor's Server page";
|
|
135246
135387
|
var MOST_CHARS = 7e3;
|
|
135247
135388
|
var REGIONS = {
|
|
135248
135389
|
"aws-us-east-2": "Ohio",
|
|
@@ -135301,20 +135442,20 @@ var AgentOnline = class {
|
|
|
135301
135442
|
const days = online.wallet?.daysLeft ?? null;
|
|
135302
135443
|
return [
|
|
135303
135444
|
`- server: ${server}: ${String(ceilings.eventsPerSecond)} calls a second, ${String(ceilings.connected)} players at once, ${String(ceilings.cpuMs)} ms of work a call`,
|
|
135304
|
-
database === null ? "- database: off: the person turns it on
|
|
135305
|
-
online.rooms === true ? "- live rooms: on" : "- live rooms: off: a game made for children turns them on once the person confirms,
|
|
135445
|
+
database === null ? "- database: off: the person turns it on, on the Server page" : `- database: ${database}: up to ${String(DATABASE_SIZES[database].maxCu)} compute units and ${String(DATABASE_SIZES[database].dataBytes / 1e9)} GB of data${online.region == null ? "" : `, in ${REGIONS[online.region] ?? online.region}`}` + (online.databaseUse == null ? "" : `; data kept: ${String(Math.round(online.databaseUse.bytes / 1e6))} MB, compute this month: ${String(Math.round(online.databaseUse.cuHours * 10) / 10)} compute-unit hours`),
|
|
135446
|
+
online.rooms === true ? "- live rooms: on" : "- live rooms: off: a game made for children turns them on once the person confirms, on the Server page, they are designed to be safe",
|
|
135306
135447
|
`- test: ${versions?.test == null ? "no code deployed yet: each save of server/main.ts goes to test a moment later" : `version ${String(versions.test)}`}`,
|
|
135307
|
-
`- live: ${versions?.live == null ? "nothing published yet: the person sends test's code to players with Publish to players,
|
|
135448
|
+
`- live: ${versions?.live == null ? "nothing published yet: the person sends test's code to players with Publish to players, on the Server page" : `version ${String(versions.live)}; the person publishes test's code to players with Publish to players`}`,
|
|
135308
135449
|
`- players this week: ${players.length === 0 ? "no live calls" : players.map((one2) => `${one2.version} (${count2(one2.calls, "call")})`).join(", ")}`,
|
|
135309
135450
|
`- today: ${count2(online.use?.events ?? 0, "event")}, ${count2(online.use?.calls ?? 0, "call")}; errors: ${count2(online.errors?.calls ?? 0, "call")}, ${count2(online.errors?.runs ?? 0, "run")}`,
|
|
135310
|
-
...(online.errors?.busy ?? 0) > 0 ? [`- ${count2(online.errors?.busy ?? 0, "call")} answered busy today: the game reaches its size's ceilings; a bigger size, which the person chooses
|
|
135451
|
+
...(online.errors?.busy ?? 0) > 0 ? [`- ${count2(online.errors?.busy ?? 0, "call")} answered busy today: the game reaches its size's ceilings; a bigger size, which the person chooses on the Server page, lets it do more`] : [],
|
|
135311
135452
|
...online.errors?.deploy == null ? [] : [`- not deployed: ${online.errors.deploy}`],
|
|
135312
135453
|
`- this month: ${dollars(online.cost ?? 0)}${wallet === null ? "" : `; ${wallet}${days === null ? "" : `, about ${count2(days, "day")} at this rate`}`}`,
|
|
135313
135454
|
...online.notice == null ? [] : [`- paused since ${online.notice.pausedAt.slice(0, 10)}: the wallet ran out, its calls answer resting${online.notice.deleteOn === null ? "" : `, and its data goes on ${online.notice.deleteOn.slice(0, 10)} unless what is owed is paid`}; the person adds credit on the Plan page`],
|
|
135314
135455
|
// Part 2: players' files and push, each on the database.
|
|
135315
|
-
online.files === true ? `- player files: on: a file is at most ${grouped(FILE_CAPS[server])} bytes on this server size; kept: ${String(Math.round((online.filesUse?.bytes ?? 0) / 1e5) / 10)} MB, ` + (online.filesUse?.measured == null ? "measured each day" : `measured ${online.filesUse.measured.slice(0, 10)}`) : "- player files: off: the person turns them on
|
|
135316
|
-
online.push === true ? "- push: on, through the game's own Firebase project: each push costs an event; never needed to play, never a reward for saying yes" : "- push: off: the person turns it on
|
|
135317
|
-
`- live players' data: ${online.liveForAgent === true ? "the person lets the agent read it" : "read only once the person lets the agent,
|
|
135456
|
+
online.files === true ? `- player files: on: a file is at most ${grouped(FILE_CAPS[server])} bytes on this server size; kept: ${String(Math.round((online.filesUse?.bytes ?? 0) / 1e5) / 10)} MB, ` + (online.filesUse?.measured == null ? "measured each day" : `measured ${online.filesUse.measured.slice(0, 10)}`) : "- player files: off: the person turns them on, on the Server page, with the database",
|
|
135457
|
+
online.push === true ? "- push: on, through the game's own Firebase project: each push costs an event; never needed to play, never a reward for saying yes" : "- push: off: the person turns it on, on the Server page, with the game's own Firebase project and the database",
|
|
135458
|
+
`- live players' data: ${online.liveForAgent === true ? "the person lets the agent read it" : "read only once the person lets the agent, on the Server page"}`
|
|
135318
135459
|
];
|
|
135319
135460
|
}
|
|
135320
135461
|
async logs(slug, stage) {
|
|
@@ -136178,7 +136319,7 @@ var KEEP = 5;
|
|
|
136178
136319
|
var LOG_MOST = 200;
|
|
136179
136320
|
var TARGETS3 = ["android-debug", "android-release", "ios-project", "ios-release"];
|
|
136180
136321
|
var CLOSED = "the program closed while this was being built";
|
|
136181
|
-
var KEY_NOTICE = "Gamesteel keeps this app's upload key on the website, sealed. The workspace's owner and admins download a copy from
|
|
136322
|
+
var KEY_NOTICE = "Gamesteel keeps this app's upload key on the website, sealed. The workspace's owner and admins download a copy from Upload \u203A App: keep it safe, as Google Play takes updates to the app only when they are signed with it.";
|
|
136182
136323
|
var MEMBERS_RELEASE = "release builds run from the workspace owner's or an admin's program, which signs them with the key the website keeps: ask one of them to build android-release";
|
|
136183
136324
|
var timeWords = (ms) => ms < 6e4 ? `${String(ms / 1e3)} seconds` : `${String(Math.round(ms / 6e4))} minutes`;
|
|
136184
136325
|
var ranPast = (ms) => `the build ran longer than ${timeWords(ms)} and was stopped: try again`;
|
|
@@ -136298,7 +136439,7 @@ var DeviceApps = class {
|
|
|
136298
136439
|
const path = join34(this.folder, slug, name2);
|
|
136299
136440
|
return existsSync22(path) ? new Uint8Array(readFileSync30(path)) : null;
|
|
136300
136441
|
}
|
|
136301
|
-
/** The key never leaves the website through the program: its owner and admins download it from
|
|
136442
|
+
/** The key never leaves the website through the program: its owner and admins download it from Upload › App. */
|
|
136302
136443
|
async keyZip() {
|
|
136303
136444
|
return null;
|
|
136304
136445
|
}
|
|
@@ -136438,7 +136579,7 @@ var DeviceApps = class {
|
|
|
136438
136579
|
const made3 = await this.options.site.storeState(slug, store).then((state) => state.products?.fingerprint ?? null, () => null);
|
|
136439
136580
|
if (made3 !== purchasesFingerprint(sold)) {
|
|
136440
136581
|
const count3 = sold.products.length;
|
|
136441
|
-
noSale = `this release sells ${String(count3)} product${count3 === 1 ? "" : "s"} not yet made in ${appStore ? "the App Store" : "Google Play"}: make them in the editor's Upload \u203A ${appStore ? "App Store" : "Play
|
|
136582
|
+
noSale = `this release sells ${String(count3)} product${count3 === 1 ? "" : "s"} not yet made in ${appStore ? "the App Store" : "Google Play"}: make them in the editor's Upload \u203A ${appStore ? "App Store" : "Google Play"}, in its Purchases step`;
|
|
136442
136583
|
}
|
|
136443
136584
|
}
|
|
136444
136585
|
const checkedAds = server?.rewards === true && ads.exported !== void 0 ? { ...ads, exported: { ...ads.exported, rewardsChecked: true } } : ads;
|
|
@@ -136966,6 +137107,7 @@ var GitHubClient = class {
|
|
|
136966
137107
|
}
|
|
136967
137108
|
const response = await this.fetch(`${this.api}${path}`, {
|
|
136968
137109
|
method,
|
|
137110
|
+
cache: "no-cache",
|
|
136969
137111
|
headers: {
|
|
136970
137112
|
authorization: `Bearer ${await this.options.token()}`,
|
|
136971
137113
|
accept: accept2,
|
|
@@ -137581,6 +137723,26 @@ var SignIn = class {
|
|
|
137581
137723
|
signedIn() {
|
|
137582
137724
|
return this.options.key !== null || this.read() !== null;
|
|
137583
137725
|
}
|
|
137726
|
+
/** The sign-in the program holds now, by its client (each sign-in registers its own), or the key; null for none.
|
|
137727
|
+
* Another sign-in on this machine changes it under every program running. */
|
|
137728
|
+
current() {
|
|
137729
|
+
return this.options.key !== null ? "key" : this.read()?.clientId ?? null;
|
|
137730
|
+
}
|
|
137731
|
+
/** This machine's sign-in ended for every program on it, and revoked on the website, so it leaves the account's
|
|
137732
|
+
* connected apps; the next call signs in afresh, as another account or into another workspace (a person with two
|
|
137733
|
+
* accounts, 2026-10-26). A website that cannot be reached now keeps the grant, which is forgotten here all the same. */
|
|
137734
|
+
async forget() {
|
|
137735
|
+
this.close();
|
|
137736
|
+
const held = await this.locked(async () => {
|
|
137737
|
+
const was = this.read();
|
|
137738
|
+
if (was !== null) this.write(null);
|
|
137739
|
+
return was;
|
|
137740
|
+
});
|
|
137741
|
+
if (held === null) return;
|
|
137742
|
+
await this.form("/oauth/revoke", { token: held.refresh }).then(async (answer3) => {
|
|
137743
|
+
await answer3.body?.cancel();
|
|
137744
|
+
}, () => void 0);
|
|
137745
|
+
}
|
|
137584
137746
|
/** A live token: the key, or the access token, refreshed when it ends within a minute. Throws SignInNeeded. */
|
|
137585
137747
|
async token() {
|
|
137586
137748
|
if (this.options.key !== null) return this.options.key;
|
|
@@ -137954,7 +138116,7 @@ function appleNextStep(wizard, facts) {
|
|
|
137954
138116
|
const { state } = facts;
|
|
137955
138117
|
if (!facts.connected) return `the person gives their App Store Connect key (an Admin team key) in ${wizard}`;
|
|
137956
138118
|
if (state.app === null) return `the person creates the app in App Store Connect (New App, iOS, the bundle id ${facts.bundleId}); app store_check then finds it`;
|
|
137957
|
-
if (!facts.listed) return "finish the listing: app store_listing sets its text; the person makes its App Store screenshots in the editor
|
|
138119
|
+
if (!facts.listed) return "finish the listing: app store_listing sets its text; the person makes its App Store screenshots with Store screenshot while the game plays in the editor and gives App Review's contact in the wizard";
|
|
137958
138120
|
if (!facts.confirmed) return `the person reads, edits and confirms the privacy policy as theirs in ${wizard}`;
|
|
137959
138121
|
if (!facts.opens) return "add a small Privacy button that calls this.game.openPrivacy() (the playbook's privacy button)";
|
|
137960
138122
|
if (!answersComplete(state.answers)) return `the person answers Apple's age rating questions in ${wizard}`;
|
|
@@ -138061,7 +138223,7 @@ var AppleWork = class {
|
|
|
138061
138223
|
}
|
|
138062
138224
|
async settingsOf(slug) {
|
|
138063
138225
|
const settings = await this.kit.settings(slug);
|
|
138064
|
-
if (settings === null) throw new Refused3(`${slug} has no app settings yet: set the app up in the editor's App
|
|
138226
|
+
if (settings === null) throw new Refused3(`${slug} has no app settings yet: set the app up in the editor's Upload \u203A App first`);
|
|
138065
138227
|
return settings;
|
|
138066
138228
|
}
|
|
138067
138229
|
/** The person's app on App Store Connect, recorded once found; null before they made it. */
|
|
@@ -138307,7 +138469,7 @@ var ADS_TXT_KEPT_MS = 3e4;
|
|
|
138307
138469
|
function nextStep(wizard, facts, now) {
|
|
138308
138470
|
const { state } = facts;
|
|
138309
138471
|
if (!facts.connected) return `the person signs in with Google, or the workspace's owner gives a key file, in ${wizard}`;
|
|
138310
|
-
if (!facts.listed) return "finish the listing: app store_listing sets its text; the person makes its pictures in the editor's App and
|
|
138472
|
+
if (!facts.listed) return "finish the listing: app store_listing sets its text; the person makes its pictures in the editor's Upload \u203A App, and with Store screenshot while the game plays";
|
|
138311
138473
|
if (!facts.confirmed) return `the person reads, edits and confirms the privacy policy as theirs in ${wizard}`;
|
|
138312
138474
|
if (!facts.opens) return "add a small Privacy button that calls this.game.openPrivacy() (the playbook's privacy button)";
|
|
138313
138475
|
if (state.upload === null) return "upload it to testing with app store_upload";
|
|
@@ -138368,7 +138530,7 @@ var DeviceStores = class _DeviceStores {
|
|
|
138368
138530
|
return this.options.clock?.() ?? /* @__PURE__ */ new Date();
|
|
138369
138531
|
}
|
|
138370
138532
|
wizard(slug) {
|
|
138371
|
-
return `the editor's Upload \u203A Play
|
|
138533
|
+
return `the editor's Upload \u203A Google Play at ${this.siteUrl}/editor?game=${slug}`;
|
|
138372
138534
|
}
|
|
138373
138535
|
/** What the store ask does, in words; `from` the agent or the person's click in the editor. */
|
|
138374
138536
|
async act(slug, ask, from, report) {
|
|
@@ -138532,7 +138694,7 @@ ${await this.apple.state(slug)}`;
|
|
|
138532
138694
|
/** What every upload and submission needs first: the listing, the privacy policy, and the game opening it. */
|
|
138533
138695
|
async ready(slug) {
|
|
138534
138696
|
const settings = await this.options.apps.settingsOf(this.options.workspaceId(), slug, this.game(slug));
|
|
138535
|
-
if (settings === null) throw new Refused4(`${slug} has no app settings yet: set the app up in the editor's App
|
|
138697
|
+
if (settings === null) throw new Refused4(`${slug} has no app settings yet: set the app up in the editor's Upload \u203A App first`);
|
|
138536
138698
|
this.appIds.set(slug, settings.id);
|
|
138537
138699
|
const listing = this.listingOf(slug);
|
|
138538
138700
|
const problems = listingProblems(listing);
|
|
@@ -138635,7 +138797,7 @@ Warning: ${checked3.words}`;
|
|
|
138635
138797
|
async products(slug) {
|
|
138636
138798
|
const { site, apps } = this.options;
|
|
138637
138799
|
const settings = await apps.settingsOf(this.options.workspaceId(), slug, this.game(slug));
|
|
138638
|
-
if (settings === null) throw new Refused4(`${slug} has no app settings yet: set the app up in the editor's App
|
|
138800
|
+
if (settings === null) throw new Refused4(`${slug} has no app settings yet: set the app up in the editor's Upload \u203A App first`);
|
|
138639
138801
|
this.appIds.set(slug, settings.id);
|
|
138640
138802
|
const kept = await sellable(slug, this.options.sessions().store.loadPurchases(slug), settings, site);
|
|
138641
138803
|
if (typeof kept === "string") throw new Refused4(kept);
|
|
@@ -138733,7 +138895,7 @@ Warning: ${checked3.words}`;
|
|
|
138733
138895
|
async check(slug) {
|
|
138734
138896
|
const { site } = this.options;
|
|
138735
138897
|
const settings = await this.options.apps.settingsOf(this.options.workspaceId(), slug, this.game(slug));
|
|
138736
|
-
if (settings === null) throw new Refused4(`${slug} has no app settings yet: set the app up in the editor's App
|
|
138898
|
+
if (settings === null) throw new Refused4(`${slug} has no app settings yet: set the app up in the editor's Upload \u203A App first`);
|
|
138737
138899
|
this.appIds.set(slug, settings.id);
|
|
138738
138900
|
const { client } = await this.access(slug);
|
|
138739
138901
|
if (!await client.exists(settings.id)) {
|
|
@@ -139163,6 +139325,90 @@ function processLink() {
|
|
|
139163
139325
|
};
|
|
139164
139326
|
}
|
|
139165
139327
|
|
|
139328
|
+
// src/program/website.ts
|
|
139329
|
+
var ANOTHER = "For another account or workspace: website { action: 'sign_in' }.";
|
|
139330
|
+
var whoOf = (standing2) => `${standing2.email == null ? "" : ` as ${standing2.email}`}${standing2.workspaceName === void 0 ? "" : ` in the workspace ${standing2.workspaceName}`}`;
|
|
139331
|
+
function planSaid(standing2) {
|
|
139332
|
+
const plan = standing2.plan === null ? null : planById(standing2.plan).name;
|
|
139333
|
+
switch (standing2.state) {
|
|
139334
|
+
case "unlimited":
|
|
139335
|
+
return "has every feature on";
|
|
139336
|
+
case "free-week":
|
|
139337
|
+
return "is in its free week";
|
|
139338
|
+
case "active":
|
|
139339
|
+
return `is on the ${plan ?? ""} plan`;
|
|
139340
|
+
case "past-due":
|
|
139341
|
+
return `is on the ${plan ?? ""} plan, its payment past due`;
|
|
139342
|
+
case "ended":
|
|
139343
|
+
return "is read-only: its free week or plan has ended";
|
|
139344
|
+
default:
|
|
139345
|
+
return `is ${standing2.state}`;
|
|
139346
|
+
}
|
|
139347
|
+
}
|
|
139348
|
+
function accountSaid(standing2, site) {
|
|
139349
|
+
const { used, allowed } = standing2.storage ?? { used: 0, allowed: null };
|
|
139350
|
+
const keeps = allowed === null ? `${sizeText(used)}, with no limit` : `${sizeText(used)} of ${sizeText(allowed)}`;
|
|
139351
|
+
const { games, gameBytes } = standing2.limits;
|
|
139352
|
+
const makes = [
|
|
139353
|
+
games === null ? "any number of games" : `${String(games)} game${games === 1 ? "" : "s"}`,
|
|
139354
|
+
gameBytes === void 0 ? null : `each up to ${sizeText(gameBytes)}`
|
|
139355
|
+
].filter((part2) => part2 !== null).join(", ");
|
|
139356
|
+
return [
|
|
139357
|
+
`Signed in${whoOf(standing2)}, as its ${standing2.role}.`,
|
|
139358
|
+
`The workspace ${planSaid(standing2)}, and keeps ${keeps}. Its plan makes ${makes}${standing2.limits.export ? "" : ", and does not export"}.`,
|
|
139359
|
+
`The person's pages: their games ${site}/editor, what the workspace keeps ${site}/editor#storage, the plan ${site}/editor#plan.`,
|
|
139360
|
+
ANOTHER
|
|
139361
|
+
].join(" ");
|
|
139362
|
+
}
|
|
139363
|
+
function workspacesSaid(workspaces) {
|
|
139364
|
+
const lines = workspaces.map((one2) => `- ${one2.name}: ${one2.role}${one2.here ? " (this works in it)" : ""}`);
|
|
139365
|
+
return [`The person's workspaces, ${String(workspaces.length)}:`, ...lines, `To work in another: website { action: 'sign_in' }, and choose it on the page.`].join("\n");
|
|
139366
|
+
}
|
|
139367
|
+
function membersSaid(members, site) {
|
|
139368
|
+
const lines = members.map((one2) => `- ${one2.name === one2.email ? one2.email : `${one2.name} (${one2.email})`}: ${one2.role}${one2.you ? " (the person)" : ""}`);
|
|
139369
|
+
return [`The workspace's people, ${String(members.length)}:`, ...lines, `Its owner and admins invite and remove people at ${site}/editor#organization.`].join("\n");
|
|
139370
|
+
}
|
|
139371
|
+
var KINDS3 = {
|
|
139372
|
+
picture: "picture",
|
|
139373
|
+
sound: "sound",
|
|
139374
|
+
music: "music",
|
|
139375
|
+
font: "font",
|
|
139376
|
+
film: "film",
|
|
139377
|
+
replay: "replay",
|
|
139378
|
+
store: "store picture",
|
|
139379
|
+
game: "game text and settings",
|
|
139380
|
+
other: "file"
|
|
139381
|
+
};
|
|
139382
|
+
var PLURAL = {
|
|
139383
|
+
picture: "pictures",
|
|
139384
|
+
sound: "sounds",
|
|
139385
|
+
music: "music",
|
|
139386
|
+
font: "fonts",
|
|
139387
|
+
film: "films",
|
|
139388
|
+
replay: "replays",
|
|
139389
|
+
store: "store pictures",
|
|
139390
|
+
game: "game text and settings",
|
|
139391
|
+
other: "other files"
|
|
139392
|
+
};
|
|
139393
|
+
var LARGEST = 10;
|
|
139394
|
+
function storageSaid(storage, standing2, site) {
|
|
139395
|
+
const items = storage.games.flatMap((game) => game.items.map((item) => ({ ...item, game: game.title ?? game.slug })));
|
|
139396
|
+
const used = standing2.storage?.used ?? items.reduce((sum, item) => sum + item.bytes, 0) + storage.styles.bytes;
|
|
139397
|
+
const allowed = standing2.storage?.allowed ?? null;
|
|
139398
|
+
const kinds = /* @__PURE__ */ new Map();
|
|
139399
|
+
for (const item of items) kinds.set(item.kind, (kinds.get(item.kind) ?? 0) + item.bytes);
|
|
139400
|
+
const largest = [...items].sort((a4, b5) => b5.bytes - a4.bytes).slice(0, LARGEST);
|
|
139401
|
+
const unused = items.filter((item) => item.used === false && !item.kept);
|
|
139402
|
+
const useOf = (item) => item.kept ? "part of the game" : item.used === null ? "not checked" : item.used ? "in use" : "not in use";
|
|
139403
|
+
return [
|
|
139404
|
+
`The workspace keeps ${sizeText(used)}${allowed === null ? ", with no limit" : ` of ${sizeText(allowed)}`}, in ${String(storage.games.length)} game${storage.games.length === 1 ? "" : "s"}.`,
|
|
139405
|
+
kinds.size === 0 ? null : `By kind: ${[...kinds].sort((a4, b5) => b5[1] - a4[1]).map(([kind, bytes2]) => `${PLURAL[kind] ?? kind} ${sizeText(bytes2)}`).join(", ")}.`,
|
|
139406
|
+
largest.length === 0 ? null : "Largest:",
|
|
139407
|
+
...largest.map((item) => `- ${item.kind === "game" ? "the game itself" : item.name}, ${KINDS3[item.kind] ?? item.kind} of ${item.game}: ${sizeText(item.bytes)}, ${useOf(item)}`),
|
|
139408
|
+
unused.length === 0 ? "No game is known not to use anything it keeps." : `Not in use by their games: ${String(unused.length)}, ${sizeText(unused.reduce((sum, item) => sum + item.bytes, 0))}. The person deletes them at ${site}/editor#storage, where each says its game and whether it is used.`
|
|
139409
|
+
].filter((line2) => line2 !== null).join("\n");
|
|
139410
|
+
}
|
|
139411
|
+
|
|
139166
139412
|
// src/program/program.ts
|
|
139167
139413
|
var STANDING_MS = 3e4;
|
|
139168
139414
|
var OLD_EXPORT_BYTES = 100 * MB;
|
|
@@ -139172,6 +139418,7 @@ var signInFirst = (needed) => ({
|
|
|
139172
139418
|
${needed.message}` }]
|
|
139173
139419
|
});
|
|
139174
139420
|
var clientName = (name2) => (name2 ?? "agent").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "agent";
|
|
139421
|
+
var movedFrom = (was, now) => was.workspaceId !== now.workspaceId || was.userId !== now.userId || was.grantId !== now.grantId;
|
|
139175
139422
|
async function startProgram(options) {
|
|
139176
139423
|
const { settings, signIn } = options;
|
|
139177
139424
|
const log = options.log ?? ((line2) => {
|
|
@@ -139200,9 +139447,11 @@ async function startProgram(options) {
|
|
|
139200
139447
|
let standingAt = 0;
|
|
139201
139448
|
let standing2 = null;
|
|
139202
139449
|
const start2 = async () => {
|
|
139450
|
+
const signedWith = signIn.current();
|
|
139203
139451
|
const known2 = await site.standing();
|
|
139204
139452
|
standing2 = known2;
|
|
139205
139453
|
standingAt = Date.now();
|
|
139454
|
+
site.pin(known2.workspaceId);
|
|
139206
139455
|
const grantId = known2.grantId ?? "";
|
|
139207
139456
|
session = `${settings.programId}:${clientName(server.getClientVersion()?.name)}`;
|
|
139208
139457
|
const author = `ai:${grantId}:${session}`;
|
|
@@ -139259,7 +139508,7 @@ async function startProgram(options) {
|
|
|
139259
139508
|
...known2.team === null ? {} : { team: { board: remoteBoard(site), names: remoteNames(site) } },
|
|
139260
139509
|
orderer: (slug) => siteOrderer({ site, slug, grantId }),
|
|
139261
139510
|
models: machineModels(options.models),
|
|
139262
|
-
// The plan's rule for an app build is the website's, read here; the key is downloaded only from
|
|
139511
|
+
// The plan's rule for an app build is the website's, read here; the key is downloaded only from Upload › App.
|
|
139263
139512
|
apps,
|
|
139264
139513
|
appOptions: {
|
|
139265
139514
|
mayExport: async () => (await plan()).limits.export,
|
|
@@ -139369,6 +139618,7 @@ async function startProgram(options) {
|
|
|
139369
139618
|
madeStores = publishing;
|
|
139370
139619
|
return {
|
|
139371
139620
|
standing: known2,
|
|
139621
|
+
signedWith,
|
|
139372
139622
|
copy,
|
|
139373
139623
|
uploader: sending,
|
|
139374
139624
|
follower,
|
|
@@ -139396,9 +139646,24 @@ async function startProgram(options) {
|
|
|
139396
139646
|
const listen = () => {
|
|
139397
139647
|
if (signIn.signedIn()) desk.start();
|
|
139398
139648
|
};
|
|
139649
|
+
let ending = Promise.resolve();
|
|
139650
|
+
const restart = (from = running) => {
|
|
139651
|
+
if (from === null || running !== from) return ending;
|
|
139652
|
+
const was = running;
|
|
139653
|
+
running = null;
|
|
139654
|
+
standing2 = null;
|
|
139655
|
+
current = null;
|
|
139656
|
+
told = false;
|
|
139657
|
+
storageTold = false;
|
|
139658
|
+
nearTold.clear();
|
|
139659
|
+
if (was !== null) ending = ending.then(async () => {
|
|
139660
|
+
await (await was.catch(() => null))?.close();
|
|
139661
|
+
});
|
|
139662
|
+
return ending;
|
|
139663
|
+
};
|
|
139399
139664
|
const prepared = () => {
|
|
139400
139665
|
if (running === null) {
|
|
139401
|
-
const starting = start2
|
|
139666
|
+
const starting = ending.then(start2);
|
|
139402
139667
|
running = starting;
|
|
139403
139668
|
starting.catch(() => {
|
|
139404
139669
|
if (running === starting) running = null;
|
|
@@ -139426,20 +139691,67 @@ async function startProgram(options) {
|
|
|
139426
139691
|
return `${slug} holds ${sizeText(bytes2)} of the ${sizeText(cap)} a game may hold on this plan. Past that it takes no new files, though it still saves; its owner can choose a bigger plan at ${settings.site}/editor#plan.`;
|
|
139427
139692
|
};
|
|
139428
139693
|
const gamesHere = (root2) => new Set(readdirSync17(root2).filter((slug) => existsSync26(join40(root2, slug, "game.json"))));
|
|
139694
|
+
const signInAgain = async () => {
|
|
139695
|
+
if (settings.key !== null) {
|
|
139696
|
+
return refusal3("this program signs in with a personal key (GAMESTEEL_KEY), which is of one account and workspace: to work as another, give it a key made there, on the editor's Agents page");
|
|
139697
|
+
}
|
|
139698
|
+
const was = (await running?.catch(() => null))?.standing ?? standing2;
|
|
139699
|
+
await restart();
|
|
139700
|
+
await signIn.forget();
|
|
139701
|
+
const needed = new SignInNeeded(await signIn.address());
|
|
139702
|
+
const from = was === null ? "" : whoOf(was);
|
|
139703
|
+
return { content: [{ type: "text", text: `Gamesteel signed out${from}, so nothing else was done. On the page, the person can use another account and chooses the workspace.
|
|
139704
|
+
${needed.message}` }] };
|
|
139705
|
+
};
|
|
139706
|
+
const websiteSaid = async (name2, now) => {
|
|
139707
|
+
switch (name2) {
|
|
139708
|
+
case WEBSITE_READS.account:
|
|
139709
|
+
return accountSaid(now, settings.site);
|
|
139710
|
+
case WEBSITE_READS.workspaces:
|
|
139711
|
+
return workspacesSaid(await site.workspaces());
|
|
139712
|
+
case WEBSITE_READS.members:
|
|
139713
|
+
return membersSaid(await site.members(), settings.site);
|
|
139714
|
+
case WEBSITE_READS.storage:
|
|
139715
|
+
return storageSaid(await site.storage(), now, settings.site);
|
|
139716
|
+
default:
|
|
139717
|
+
return null;
|
|
139718
|
+
}
|
|
139719
|
+
};
|
|
139429
139720
|
const around2 = async (name2, args2, call) => {
|
|
139721
|
+
if (name2 === SIGN_IN_TOOL.name) return signInAgain();
|
|
139430
139722
|
let program;
|
|
139431
139723
|
let now;
|
|
139724
|
+
let switched = null;
|
|
139725
|
+
let on2 = null;
|
|
139432
139726
|
try {
|
|
139433
139727
|
if (!signIn.signedIn()) throw new SignInNeeded(await signIn.address());
|
|
139434
|
-
|
|
139728
|
+
on2 = prepared();
|
|
139729
|
+
program = await on2;
|
|
139730
|
+
if (signIn.current() !== program.signedWith || name2 === WEBSITE_READS.account || name2 === WEBSITE_READS.storage) standing2 = null;
|
|
139435
139731
|
now = await plan();
|
|
139732
|
+
if (movedFrom(program.standing, now)) {
|
|
139733
|
+
await restart(on2);
|
|
139734
|
+
on2 = prepared();
|
|
139735
|
+
program = await on2;
|
|
139736
|
+
now = await plan();
|
|
139737
|
+
switched = `Gamesteel's sign-in on this computer changed: it works${whoOf(now)} now.`;
|
|
139738
|
+
}
|
|
139436
139739
|
listen();
|
|
139437
139740
|
} catch (error2) {
|
|
139438
139741
|
if (error2 instanceof SignInNeeded) return signInFirst(error2);
|
|
139439
139742
|
if (error2 instanceof SiteRefusal && error2.status === 401) return signInFirst(new SignInNeeded(await signIn.address()));
|
|
139743
|
+
if (error2 instanceof SignInMoved) {
|
|
139744
|
+
await restart(on2);
|
|
139745
|
+
return refusal3(error2.message);
|
|
139746
|
+
}
|
|
139440
139747
|
return refusal3(`the website could not be reached: ${error2.message}. Ask again in a moment`);
|
|
139441
139748
|
}
|
|
139442
139749
|
current = program.ctx;
|
|
139750
|
+
const website = await websiteSaid(name2, now).catch((error2) => ({ refused: error2.message }));
|
|
139751
|
+
if (typeof website === "object" && website !== null) return refusal3(website.refused);
|
|
139752
|
+
if (website !== null) return { content: [{ type: "text", text: switched === null ? website : `${switched}
|
|
139753
|
+
|
|
139754
|
+
${website}` }] };
|
|
139443
139755
|
const { copy, follower, team: team2, author, uploader, moves: moves2 } = program;
|
|
139444
139756
|
const reading = READING_TOOLS.has(name2) || name2 === "team" && args2["action"] === "read";
|
|
139445
139757
|
if (!reading && !now.limits.writes) {
|
|
@@ -139486,6 +139798,7 @@ async function startProgram(options) {
|
|
|
139486
139798
|
}
|
|
139487
139799
|
if (unsaved.length > 0) pending.push(...(await uploader.send(unsaved)).pending);
|
|
139488
139800
|
} catch (error2) {
|
|
139801
|
+
if (error2 instanceof SignInMoved) await restart(on2);
|
|
139489
139802
|
return refusal3(error2.message);
|
|
139490
139803
|
}
|
|
139491
139804
|
const cap = now.limits.exportBytes;
|
|
@@ -139532,6 +139845,11 @@ ${line2}` }] };
|
|
|
139532
139845
|
${line2}` }] };
|
|
139533
139846
|
}
|
|
139534
139847
|
}
|
|
139848
|
+
if (name2 === "list_games" && answer3.isError !== true && now.email != null) {
|
|
139849
|
+
answer3 = { ...answer3, content: [...answer3.content, { type: "text", text: `
|
|
139850
|
+
|
|
139851
|
+
Signed in${whoOf(now)}. For another account or workspace: website { action: 'sign_in' }.` }] };
|
|
139852
|
+
}
|
|
139535
139853
|
if (name2 === "list_games" && unread.length > 0) {
|
|
139536
139854
|
answer3 = { ...answer3, content: [...answer3.content, { type: "text", text: `
|
|
139537
139855
|
|
|
@@ -139550,6 +139868,9 @@ Not on the website yet: ${pending.join("; ")}.` }] };
|
|
|
139550
139868
|
const team3 = program.standing.team;
|
|
139551
139869
|
answer3 = withFirstWords(answer3, team3 === null ? null : teamInstructions({ agent: team3.agent, workspace: team3.workspace, people: [...team3.people] }));
|
|
139552
139870
|
}
|
|
139871
|
+
if (switched !== null) answer3 = { ...answer3, content: [{ type: "text", text: `${switched}
|
|
139872
|
+
|
|
139873
|
+
` }, ...answer3.content] };
|
|
139553
139874
|
return answer3;
|
|
139554
139875
|
};
|
|
139555
139876
|
serveTools(server, {
|
|
@@ -139572,6 +139893,7 @@ Not on the website yet: ${pending.join("; ")}.` }] };
|
|
|
139572
139893
|
await desk.close();
|
|
139573
139894
|
signIn.close();
|
|
139574
139895
|
if (running !== null) await (await running.catch(() => null))?.close();
|
|
139896
|
+
await ending;
|
|
139575
139897
|
})();
|
|
139576
139898
|
return closing;
|
|
139577
139899
|
}
|