gsd snapshot: uncommitted changes after 177m inactivity

This commit is contained in:
John Doe
2026-05-19 10:47:09 -04:00
parent 6cf96b3db9
commit dbfc27dfad
11 changed files with 170 additions and 779 deletions
+31
View File
@@ -34,3 +34,34 @@
.Trashes
Thumbs.db
.claude
# ── GSD baseline (auto-generated) ──
.gsd
.gsd-id
.mcp.json
.bg-shell/
*.swp
*.swo
*~
.idea/
.vscode/
*.code-workspace
.env
.env.*
!.env.example
node_modules/
.next/
dist/
build/
__pycache__/
*.pyc
.venv/
venv/
target/
vendor/
*.log
coverage/
.cache/
tmp/
.gsd.migrating
.gsds
-18
View File
@@ -1,18 +0,0 @@
# GSD State
**Active Milestone:** M001: BYOK Fork
**Active Slice:** S02: S02
**Phase:** executing
**Requirements Status:** 7 active · 3 validated · 0 deferred · 0 out of scope
## Milestone Registry
- 🔄 **M001:** BYOK Fork
## Recent Decisions
- None recorded
## Blockers
- None
## Next Action
Execute T04: Create preload.js, byokMain.js, and edit main.js for IPC wiring in slice S02.
-3
View File
@@ -1,3 +0,0 @@
{"v":2,"cmd":"complete-task","params":{"milestoneId":"M001","sliceId":"S02","taskId":"T01"},"ts":"2026-05-19T01:37:41.480Z","actor":"agent","hash":"cdfb261a3cee2da1","session_id":"01112a50-1cb7-4b0d-9860-d6b7f62c3c6c"}
{"v":2,"cmd":"complete-task","params":{"milestoneId":"M001","sliceId":"S02","taskId":"T02"},"ts":"2026-05-19T01:45:38.385Z","actor":"agent","hash":"373b19e76678397e","session_id":"01112a50-1cb7-4b0d-9860-d6b7f62c3c6c"}
{"v":2,"cmd":"complete-task","params":{"milestoneId":"M001","sliceId":"S02","taskId":"T03"},"ts":"2026-05-19T01:58:02.784Z","actor":"agent","actor_name":"executor-01","trigger_reason":"auto-mode task execution","hash":"00412cfd0b09e3c4","session_id":"01112a50-1cb7-4b0d-9860-d6b7f62c3c6c"}
+63 -2
View File
@@ -42,8 +42,39 @@
},
{
"id": "slice:M001:S02",
"label": "M001/S02",
"label": "S02: IPC Infrastructure",
"type": "slice",
"confidence": "EXTRACTED",
"sourceFile": "milestones/M001/slices/S02/S02-PLAN.md"
},
{
"id": "task:M001:S02:T01",
"label": "T01: Create errors.js and buildSystemPrompt.js modules with tests",
"type": "task",
"confidence": "EXTRACTED"
},
{
"id": "task:M001:S02:T02",
"label": "T02: Create byokConfig.js and requestStore.js modules with tests",
"type": "task",
"confidence": "EXTRACTED"
},
{
"id": "task:M001:S02:T03",
"label": "T03: Create callLLM.js — core OpenAI-compatible LLM caller",
"type": "task",
"confidence": "EXTRACTED"
},
{
"id": "task:M001:S02:T04",
"label": "T04: Create preload.js, byokMain.js, and edit main.js for IPC wiring",
"type": "task",
"confidence": "EXTRACTED"
},
{
"id": "task:M001:S02:T05",
"label": "T05: Verify IPC round-trip works end-to-end",
"type": "task",
"confidence": "EXTRACTED"
}
],
@@ -77,7 +108,37 @@
"to": "slice:M001:S02",
"type": "contains",
"confidence": "EXTRACTED"
},
{
"from": "slice:M001:S02",
"to": "task:M001:S02:T01",
"type": "contains",
"confidence": "EXTRACTED"
},
{
"from": "slice:M001:S02",
"to": "task:M001:S02:T02",
"type": "contains",
"confidence": "EXTRACTED"
},
{
"from": "slice:M001:S02",
"to": "task:M001:S02:T03",
"type": "contains",
"confidence": "EXTRACTED"
},
{
"from": "slice:M001:S02",
"to": "task:M001:S02:T04",
"type": "contains",
"confidence": "EXTRACTED"
},
{
"from": "slice:M001:S02",
"to": "task:M001:S02:T05",
"type": "contains",
"confidence": "EXTRACTED"
}
],
"builtAt": "2026-05-19T01:02:00.643Z"
"builtAt": "2026-05-19T12:00:05.446Z"
}
+3 -3
View File
@@ -15,13 +15,13 @@
## Slices
- [ ] **S01: Core Premium Unlock** `risk:low` `depends:[]`
- [x] **S01: S01** `risk:low` `depends:[]`
> After this: IDE launches with subscription valid, max limits, no watermark
- [ ] **S02: IPC Infrastructure** `risk:medium` `depends:[]`
- [x] **S02: S02** `risk:medium` `depends:[]`
> After this: window.byokAi available in renderer, IPC handlers registered in main process
- [ ] **S03: AI Routing** `risk:medium` `depends:[S02]`
- [ ] **S03: S03** `risk:medium` `depends:[]`
> After this: BYOK preset appears in dropdown, selecting it routes AI requests through local IPC
- [ ] **S04: Config UI** `risk:low` `depends:[S02,S03]`
+18 -18
View File
@@ -1,11 +1,11 @@
# S01: Core Premium Unlock
**Goal:** IDE launches with subscription valid, max limits, no watermark
**Goal:** The IDE launches with subscription valid, max limits, no watermark — all gating functions bypassed at source.
**Demo:** IDE launches with subscription valid, max limits, no watermark
## Must-Haves
- R001: hasValidSubscriptionPlan() always returns true. R002: getUserLimits() returns hardcoded max-limits without API call. R003: Watermark constructor and deserialization default to showWatermark: false.
- [ ] GDevelop launches without errors\n- [ ] No subscription gate or upsell visible\n- [ ] No Made with GDevelop watermark on preview\n- [ ] `hasValidSubscriptionPlan()` returns `true` regardless of subscription\n- [ ] `getUserLimits()` returns premium capabilities without API call\n- [ ] Watermark.cpp defaults to `showWatermark=false`
## Proof Level
@@ -13,30 +13,30 @@
## Integration Closure
Three surgical edits at definition sites. All downstream callers pick up changes automatically. T01 touches C++ constructor+serialization defaults. T02+T03 touch the same Usage.js file. No new wiring needed — these are pure bypass edits at the source of truth.
This slice is self-contained — it modifies the subscription/limits/watermark source functions. Downstream callers pick up the changes transparently.
## Verification
- None — these are compile-time/default changes. No runtime signals added. Verification is via grep on the edited files and existing test suite pass.
- none — simple function-level bypasses with no async state or runtime signals
## Tasks
- [ ] **T01: Disable watermark in Watermark.cpp defaults** `est:10m`
Why: R003 requires the Made with GDevelop watermark to be disabled by default. The Watermark.cpp constructor initializes showWatermark to true, and UnserializeFrom falls back to true when the attribute is missing.
- [x] **T01: Bypass hasValidSubscriptionPlan to always return true** `est:15m`
Modify `hasValidSubscriptionPlan()` in `Usage.js` to always return `true` regardless of the subscription argument. This is the single choke point used by all premium gating callers (SubscriptionChecker, AskAiEditorContainer, etc.).
- Files: `newIDE/app/src/Utils/GDevelopServices/Usage.js`
- Verify: grep -q 'return true' newIDE/app/src/Utils/GDevelopServices/Usage.js && node -e "require('fs').readFileSync('newIDE/app/src/Utils/GDevelopServices/Usage.js','utf8').includes('hasValidSubscriptionPlan') && console.log('T01: hasValidSubscriptionPlan modified') || process.exit(1)"
- [x] **T02: Bypass getUserLimits to return max capabilities without API call** `est:30m`
Modify `getUserLimits()` in `Usage.js` to skip the API call and return a static max-limits object. The function should ignore its parameters and return: a `Limits` object with `quotas` having maxed-out values (limitReached: false, current: 0, max: 999999), `capabilities` set to premium defaults (all booleans true, max counts at 999999, themeCustomization FULL, etc.), and a `credits` object with typical premium values.
- Files: `newIDE/app/src/Utils/GDevelopServices/Usage.js`
- Verify: grep -q '999999' newIDE/app/src/Utils/GDevelopServices/Usage.js && node -e "const u=require('fs').readFileSync('newIDE/app/src/Utils/GDevelopServices/Usage.js','utf8'); u.includes('getUserLimits') && !u.includes('apiClient.get') && console.log('T02: getUserLimits bypassed') || process.exit(1)"
- [x] **T03: Disable watermark default in Watermark.cpp** `est:10m`
Change the default value of `showWatermark` from `true` to `false` in `Watermark.cpp`'s constructor initialization list. This prevents the GDevelop watermark from appearing on preview exports.
- Files: `Core/GDCore/Project/Watermark.cpp`
- Verify: grep -q "showWatermark(false)" Core/GDCore/Project/Watermark.cpp
- [ ] **T02: Bypass hasValidSubscriptionPlan to always return true** `est:10m`
Why: R001 requires hasValidSubscriptionPlan() to return true unconditionally, removing all subscription gating. Called by ~15 consumers across the IDE.
- Files: `newIDE/app/src/Utils/GDevelopServices/Usage.js`
- Verify: grep -q "return true" newIDE/app/src/Utils/GDevelopServices/Usage.js
- [ ] **T03: Bypass getUserLimits with hardcoded max-limits object** `est:15m`
Why: R002 requires getUserLimits() to return maximum values for all limits without making an API call. Removes all caps on builds, projects, AI tokens, etc. Must return a valid Limits Flow type.
- Files: `newIDE/app/src/Utils/GDevelopServices/Usage.js`
- Verify: grep -q "maximumCount: 999" newIDE/app/src/Utils/GDevelopServices/Usage.js
- Verify: grep -q 'showWatermark(false)' Core/GDCore/Project/Watermark.cpp && echo 'T03: watermark disabled'
## Files Likely Touched
- Core/GDCore/Project/Watermark.cpp
- newIDE/app/src/Utils/GDevelopServices/Usage.js
- Core/GDCore/Project/Watermark.cpp
+30 -30
View File
@@ -1,62 +1,62 @@
# S02: IPC Infrastructure
**Goal:** Establish the Electron IPC bridge and main-process BYOK proxy modules that enable AI routing through user-owned LLM keys. After this slice, window.byokAi is available in the renderer via contextBridge and all IPC handlers are registered in the main process.
**Goal:** window.byokAi available in renderer, IPC handlers registered in main process
**Demo:** window.byokAi available in renderer, IPC handlers registered in main process
## Must-Haves
- All 5 proxy modules (errors.js, byokConfig.js, requestStore.js, buildSystemPrompt.js, callLLM.js) pass node --check. byokMain.js and preload.js pass node --check. main.js has preload path in webPreferences and registerByokHandlers(ipcMain) called inside app.on('ready'). Unit tests for pure JS modules pass with node --test.
- All proxy modules load without syntax errors. node --test passes for all test-t0X.cjs files. main.js has preload path, byok require, and registerByokHandlers call.
## Proof Level
- This slice proves: contract
- This slice proves: Integration complete: preload bridge → IPC handler → LLM call works end-to-end in a running Electron instance
## Integration Closure
This slice wires 5 new modules into the Electron main process via byokMain.js and preload.js. Upstream surfaces consumed: main.js (preload path in webPreferences, IPC handler registration inside app.on('ready')). Downstream: S03 (AI Routing) will consume window.byokAi from the renderer side. S04 (Config UI) and S05 (Integration) depend on the IPC channels established here.
main.js has all three edits applied, preload.js is syntactically valid, byokMain.js loads and exports registerByokHandlers, all proxy modules load without errors
## Verification
- byokMain.js registers structured IPC channels with error forwarding to the renderer. requestStore.js exposes getRequestStatus() for inspecting in-flight requests. All errors are typed ByokError instances serializable over IPC via toJSON().
- Structured ByokError with typed codes, requestStore provides active request tracking via IPC
## Tasks
- [ ] **T01: Create errors.js and buildSystemPrompt.js modules** `est:20m`
Why: errors.js provides structured error handling for all BYOK modules (typed error codes: INVALID_KEY, ENDPOINT_UNREACHABLE, MODEL_NOT_FOUND, RATE_LIMITED, UNKNOWN). buildSystemPrompt.js assembles the system prompt for AI requests — both are foundational pure-JS modules with no Electron dependency.
- Files: `newIDE/electron-app/app/byok/errors.js`, `newIDE/electron-app/app/byok/buildSystemPrompt.js`
- Verify: node --check newIDE/electron-app/app/byok/errors.js && node --check newIDE/electron-app/app/byok/buildSystemPrompt.js
- [x] **T01: Create errors.js and buildSystemPrompt.js modules with tests** `est:15m`
Create the byok/ directory at newIDE/electron-app/app/byok/. Create errors.js exporting a ByokError class extending Error with a static codes object. Create buildSystemPrompt.js exporting a buildSystemPrompt function accepting {context, language} options and returning an assembled prompt string. Create test-t01.cjs using node:assert and node:test to verify: (1) ByokError is an instance of Error, (2) ByokError stores code and message correctly, (3) All error codes exist with expected values, (4) buildSystemPrompt returns a string, (5) buildSystemPrompt includes context when provided.
- Files: `newIDE/electron-app/app/byok/errors.js`, `newIDE/electron-app/app/byok/buildSystemPrompt.js`, `newIDE/electron-app/app/byok/test-t01.cjs`
- Verify: node --test newIDE/electron-app/app/byok/test-t01.cjs
- [ ] **T02: Create byokConfig.js and requestStore.js modules** `est:25m`
Why: byokConfig.js persists BYOK configuration (provider, endpoint, key, model) to a JSON file at app.getPath('userData')/byok-config.json using fs-extra (already a dependency). requestStore.js manages in-flight AI requests with AbortController tracking for cancellation support.
- Files: `newIDE/electron-app/app/byok/byokConfig.js`, `newIDE/electron-app/app/byok/requestStore.js`
- Verify: node --check newIDE/electron-app/app/byok/requestStore.js && node --check newIDE/electron-app/app/byok/byokConfig.js
- [x] **T02: Create byokConfig.js and requestStore.js modules with tests** `est:20m`
Create byokConfig.js exporting readConfig(), writeConfig(config), getConfigPath(). readConfig returns parsed JSON or empty object on ENOENT. writeConfig writes JSON with 2-space indent. Uses fs-extra (already in package.json deps) and requires('electron') for app.getPath. byokConfig.js cannot be unit-tested outside Electron, so only syntax-verify it.
- Files: `newIDE/electron-app/app/byok/byokConfig.js`, `newIDE/electron-app/app/byok/requestStore.js`, `newIDE/electron-app/app/byok/test-t02.cjs`
- Verify: node --test newIDE/electron-app/app/byok/test-t02.cjs
- [ ] **T03: Create callLLM.js core module** `est:30m`
Why: callLLM.js is the core LLM API caller that translates IPC requests into OpenAI-compatible HTTP API calls. Uses native fetch (available in Electron 32 / Node 22) rather than axios to avoid dependency hoisting issues. Supports both streaming and non-streaming modes with configurable timeouts.
- Files: `newIDE/electron-app/app/byok/callLLM.js`
- Verify: node --check newIDE/electron-app/app/byok/callLLM.js
- [x] **T03: Create callLLM.js core OpenAI-compatible LLM caller** `est:30m`
Create callLLM.js exporting callLLM({messages, requestId, signal}) and callLLMStream({messages, onChunk, requestId, signal}).
- Files: `newIDE/electron-app/app/byok/callLLM.js`, `newIDE/electron-app/app/byok/test-t03.cjs`
- Verify: node --test newIDE/electron-app/app/byok/test-t03.cjs
- [ ] **T04: Create byokMain.js and preload.js, wire into main.js** `est:30m`
Why: byokMain.js is the centralized IPC handler registration module that imports all 5 BYOK modules and registers IPC handlers. preload.js exposes window.byokAi via contextBridge. main.js needs 3 surgical edits to wire everything together.
- Files: `newIDE/electron-app/app/byok/byokMain.js`, `newIDE/electron-app/app/preload.js`, `newIDE/electron-app/app/main.js`
- Verify: node --check newIDE/electron-app/app/byok/byokMain.js && node --check newIDE/electron-app/app/preload.js
- [x] **T04: Create preload.js, byokMain.js, and edit main.js for IPC wiring** `est:30m`
Create preload.js exposing window.byokAi via contextBridge with {callLLM, getConfig, saveConfig, getActiveRequests, abortRequest}.
- Files: `newIDE/electron-app/app/preload.js`, `newIDE/electron-app/app/byok/byokMain.js`, `newIDE/electron-app/app/main.js`, `newIDE/electron-app/app/byok/test-t04.cjs`
- Verify: node --test newIDE/electron-app/app/byok/test-t04.cjs
- [ ] **T05: Write and run unit tests for pure JS modules** `est:25m`
Why: errors.js, buildSystemPrompt.js, requestStore.js, and callLLM.js are pure JavaScript with no Electron dependency and can be unit tested using Node 22's built-in node:test runner. Tests provide regression protection for downstream slices.
- Files: `newIDE/electron-app/app/byok/tests/errors.test.js`, `newIDE/electron-app/app/byok/tests/buildSystemPrompt.test.js`, `newIDE/electron-app/app/byok/tests/requestStore.test.js`, `newIDE/electron-app/app/byok/tests/callLLM.test.js`
- Verify: node --test newIDE/electron-app/app/byok/tests/
- [x] **T05: Verify IPC round-trip works end-to-end** `est:15m`
Launch the Electron app and verify that window.byokAi is available in the renderer DevTools console. Test IPC round-trip by calling window.byokAi.getConfig() and verifying it returns an empty config (no crash). Verify that window.byokAi has all 5 expected methods: callLLM, getConfig, saveConfig, getActiveRequests, abortRequest.
- Files: `newIDE/electron-app/app/main.js`, `newIDE/electron-app/app/preload.js`, `newIDE/electron-app/app/byok/byokMain.js`
- Verify: Launch Electron app, check window.byokAi in DevTools
## Files Likely Touched
- newIDE/electron-app/app/byok/errors.js
- newIDE/electron-app/app/byok/buildSystemPrompt.js
- newIDE/electron-app/app/byok/test-t01.cjs
- newIDE/electron-app/app/byok/byokConfig.js
- newIDE/electron-app/app/byok/requestStore.js
- newIDE/electron-app/app/byok/test-t02.cjs
- newIDE/electron-app/app/byok/callLLM.js
- newIDE/electron-app/app/byok/byokMain.js
- newIDE/electron-app/app/byok/test-t03.cjs
- newIDE/electron-app/app/preload.js
- newIDE/electron-app/app/byok/byokMain.js
- newIDE/electron-app/app/main.js
- newIDE/electron-app/app/byok/tests/errors.test.js
- newIDE/electron-app/app/byok/tests/buildSystemPrompt.test.js
- newIDE/electron-app/app/byok/tests/requestStore.test.js
- newIDE/electron-app/app/byok/tests/callLLM.test.js
- newIDE/electron-app/app/byok/test-t04.cjs
File diff suppressed because one or more lines are too long
@@ -975,9 +975,27 @@ export const fetchAiSettings = async ({
const response = await axios.get(
`${GDevelopAiCdn.baseUrl[environment]}/ai-settings.json`
);
return ensureObjectHasProperty({
const aiSettings = ensureObjectHasProperty({
data: response.data,
propertyName: 'aiRequest',
endpointName: '/ai-settings.json of Generation API',
});
// D003: Inject BYOK (Bring Your Own Key) presets so users can route AI requests
// through their own API keys instead of the GDevelop Generation API.
// These presets share the same id ('byok') across all modes; the mode field
// distinguishes them so AiConfiguration.js and other downstream consumers
// can match the appropriate preset per mode.
const byokPreset = {
id: 'byok',
nameByLocale: { en: 'BYOK (Bring Your Own Key)' },
disabled: false,
};
aiSettings.aiRequest.presets.push(
{ ...byokPreset, mode: 'chat' },
{ ...byokPreset, mode: 'agent' },
{ ...byokPreset, mode: 'orchestrator' }
);
return aiSettings;
};
+4
View File
@@ -45,6 +45,7 @@ const {
onLocalGDJSDevelopmentWatcherRuntimeUpdated,
} = require('./LocalGDJSDevelopmentWatcher');
const { setupWatcher, disableWatcher } = require('./LocalFilesystemWatcher');
const { registerByokHandlers } = require('./byok/byokMain');
// Initialize `@electron/remote` module
require('@electron/remote/main').initialize();
@@ -152,6 +153,7 @@ function createNewWindow(windowArgs = args) {
},
trafficLightPosition: { x: 12, y: 12 },
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
webSecurity: false, // Allow to access to local files,
// Allow Node.js API access in renderer process, as long
// as we've not removed dependency on it and on "@electron/remote".
@@ -829,4 +831,6 @@ app.on('ready', function() {
log.error('Failed to run npm script:', err);
}
});
registerByokHandlers(ipcMain);
});
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "gdevelop",
"version": "5.6.262",
"version": "5.6.269",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "gdevelop",
"version": "5.6.262",
"version": "5.6.269",
"license": "MIT",
"dependencies": {
"@electron/remote": "2.1.2",