@seriousgaming/sdk-tools 0.0.0-stage → 1.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +93 -0
- package/README.md +36 -2
- package/dist/bundling/logic-exports.d.ts +34 -0
- package/dist/bundling/node-builtins.d.ts +21 -0
- package/dist/bundling/transpiler.d.ts +7 -0
- package/dist/cli.js +27368 -0
- package/dist/client/dev-server.d.ts +59 -0
- package/dist/client/index.d.ts +28 -0
- package/dist/client/stub-services.d.ts +32 -0
- package/dist/client/test-session.d.ts +195 -0
- package/dist/client.js +16884 -0
- package/dist/core/asset-reference-translator.d.ts +46 -0
- package/dist/core/clock.d.ts +57 -0
- package/dist/core/faults.d.ts +65 -0
- package/dist/core/game-settings.d.ts +65 -0
- package/dist/core/history.d.ts +89 -0
- package/dist/core/listen-with-fallback.d.ts +30 -0
- package/dist/core/mutation-queue.d.ts +61 -0
- package/dist/core/package-scenario.d.ts +89 -0
- package/dist/core/participant-registry.d.ts +109 -0
- package/dist/core/pending-changes.d.ts +14 -0
- package/dist/core/protocol.d.ts +83 -0
- package/dist/core/scenario-drafts.d.ts +32 -0
- package/dist/core/session-endpoints.d.ts +108 -0
- package/dist/core/session-gates.d.ts +75 -0
- package/dist/core/session-host.d.ts +94 -0
- package/dist/core/session-refusals.d.ts +56 -0
- package/dist/core/session-rewind.d.ts +32 -0
- package/dist/core/session-roster.d.ts +128 -0
- package/dist/core/session-surfaces.d.ts +32 -0
- package/dist/core/session-ticks.d.ts +18 -0
- package/dist/core/session-timers.d.ts +84 -0
- package/dist/core/session-writes.d.ts +40 -0
- package/dist/core/session.d.ts +385 -0
- package/dist/core/settings-locks.d.ts +22 -0
- package/dist/core/team-names.d.ts +52 -0
- package/dist/core/team-state-store.d.ts +209 -0
- package/dist/core/tick-timer.d.ts +75 -0
- package/dist/dev/blob-links.d.ts +10 -0
- package/dist/dev/console/assets/index-DgFglsnA.js +204 -0
- package/dist/dev/console/index.html +12 -0
- package/dist/dev/console-path.d.ts +19 -0
- package/dist/dev/lockfile.d.ts +124 -0
- package/dist/dev/package-watcher.d.ts +21 -0
- package/dist/dev/routes.d.ts +67 -0
- package/dist/dev/shell.d.ts +260 -0
- package/dist/dev/surfaces.d.ts +41 -0
- package/dist/dev/tools-identity.d.ts +23 -0
- package/dist/dev/view/batch.d.ts +172 -0
- package/dist/dev/view/commands.d.ts +75 -0
- package/dist/dev/view/route.d.ts +58 -0
- package/dist/dev/view/view.d.ts +78 -0
- package/dist/dev/view/viewer.d.ts +32 -0
- package/dist/discovery/config.d.ts +9 -0
- package/dist/discovery/manifest-paths.d.ts +21 -0
- package/dist/discovery/removed-fields.d.ts +90 -0
- package/dist/docs/api/deck.md +386 -0
- package/dist/docs/api/index.md +67 -0
- package/dist/docs/api/sdk-commands.md +286 -0
- package/dist/docs/api/sdk-e2e.md +433 -0
- package/dist/docs/api/sdk-projection.md +207 -0
- package/dist/docs/api/sdk-testing.md +214 -0
- package/dist/docs/api/sdk-tools-client.md +560 -0
- package/dist/docs/api/sdk-ui-testing.md +231 -0
- package/dist/docs/api/sdk-ui.md +1269 -0
- package/dist/docs/api/sdk.md +2140 -0
- package/dist/docs/changelog-0.x.md +1156 -0
- package/dist/docs/changelog.md +236 -0
- package/dist/docs/cli-and-dev-server.md +217 -0
- package/dist/docs/cli-error-codes.md +154 -0
- package/dist/docs/cli-reference.md +2488 -0
- package/dist/docs/cli-simulation-testing.md +222 -0
- package/dist/docs/concepts/index.md +46 -0
- package/dist/docs/concepts/terminology.md +322 -0
- package/dist/docs/environment-variables.md +102 -0
- package/dist/docs/getting-started.md +145 -0
- package/dist/docs/guides/ai-facilitation.md +220 -0
- package/dist/docs/guides/capabilities.md +460 -0
- package/dist/docs/guides/endpoints.md +335 -0
- package/dist/docs/guides/engine-testing.md +477 -0
- package/dist/docs/guides/engine.md +527 -0
- package/dist/docs/guides/file-handling.md +405 -0
- package/dist/docs/guides/games.md +176 -0
- package/dist/docs/guides/global-settings.md +94 -0
- package/dist/docs/guides/guide.md +123 -0
- package/dist/docs/guides/hooks.md +183 -0
- package/dist/docs/guides/index.md +51 -0
- package/dist/docs/guides/internationalization.md +288 -0
- package/dist/docs/guides/llm-integration.md +635 -0
- package/dist/docs/guides/multi-team-games.md +586 -0
- package/dist/docs/guides/patterns.md +391 -0
- package/dist/docs/guides/presentation.md +196 -0
- package/dist/docs/guides/production-constraints.md +130 -0
- package/dist/docs/guides/scenario-editor.md +143 -0
- package/dist/docs/guides/scenarios.md +515 -0
- package/dist/docs/guides/session.md +972 -0
- package/dist/docs/guides/settings.md +458 -0
- package/dist/docs/guides/setup.md +193 -0
- package/dist/docs/guides/speech-generation.md +233 -0
- package/dist/docs/guides/starter-game.md +164 -0
- package/dist/docs/guides/testing.md +575 -0
- package/dist/docs/guides/tick-controller.md +569 -0
- package/dist/docs/guides/type-safety.md +516 -0
- package/dist/docs/guides/ui.md +1389 -0
- package/dist/docs/guides/upgrading.md +636 -0
- package/dist/docs/guides/validators.md +217 -0
- package/dist/docs/guides/waiting-room.md +244 -0
- package/dist/docs/index.md +41 -0
- package/dist/docs/recipes/act-on-the-session-from-an-endpoint.md +95 -0
- package/dist/docs/recipes/adapt-a-game-for-walkthroughs.md +133 -0
- package/dist/docs/recipes/adapt-ui-for-observers.md +74 -0
- package/dist/docs/recipes/call-endpoint-from-ui.md +109 -0
- package/dist/docs/recipes/check-a-package-from-a-script.md +117 -0
- package/dist/docs/recipes/create-capability.md +66 -0
- package/dist/docs/recipes/create-endpoint.md +127 -0
- package/dist/docs/recipes/create-engine.md +111 -0
- package/dist/docs/recipes/create-game.md +70 -0
- package/dist/docs/recipes/create-guide.md +75 -0
- package/dist/docs/recipes/create-hook.md +80 -0
- package/dist/docs/recipes/create-presentation.md +142 -0
- package/dist/docs/recipes/create-scenario-editor.md +102 -0
- package/dist/docs/recipes/create-scenario.md +98 -0
- package/dist/docs/recipes/create-session-ui.md +116 -0
- package/dist/docs/recipes/create-settings.md +88 -0
- package/dist/docs/recipes/create-setup.md +115 -0
- package/dist/docs/recipes/create-tick-controller.md +65 -0
- package/dist/docs/recipes/create-user-interface.md +96 -0
- package/dist/docs/recipes/create-validator.md +67 -0
- package/dist/docs/recipes/create-waiting-room-ui.md +109 -0
- package/dist/docs/recipes/declare-api-uses.md +67 -0
- package/dist/docs/recipes/design-state-for-concurrent-writes.md +96 -0
- package/dist/docs/recipes/distribute-package.md +80 -0
- package/dist/docs/recipes/e2e-testing.md +116 -0
- package/dist/docs/recipes/end-game-on-condition.md +154 -0
- package/dist/docs/recipes/fix-duplicate-packages.md +101 -0
- package/dist/docs/recipes/index.md +86 -0
- package/dist/docs/recipes/make-an-endpoint-external.md +101 -0
- package/dist/docs/recipes/negotiate-across-teams.md +164 -0
- package/dist/docs/recipes/observe-other-teams-state.md +119 -0
- package/dist/docs/recipes/publish-a-package.md +139 -0
- package/dist/docs/recipes/remember-a-viewers-choice.md +55 -0
- package/dist/docs/recipes/review-a-past-day.md +73 -0
- package/dist/docs/recipes/share-assets-across-scenarios.md +120 -0
- package/dist/docs/recipes/share-logic-between-engine-and-ui.md +130 -0
- package/dist/docs/recipes/show-all-teams-participants.md +109 -0
- package/dist/docs/recipes/start-from-an-empty-package.md +169 -0
- package/dist/docs/recipes/take-a-file-in-an-endpoint.md +82 -0
- package/dist/docs/recipes/test-a-multi-role-game.md +51 -0
- package/dist/docs/recipes/test-user-interface.md +95 -0
- package/dist/docs/recipes/test-with-engine.md +115 -0
- package/dist/docs/recipes/upload-and-display-files.md +148 -0
- package/dist/docs/recipes/use-request-play.md +65 -0
- package/dist/docs/recipes/use-request-tick.md +75 -0
- package/dist/docs/troubleshooting.md +816 -0
- package/dist/execution/blob-inputs.d.ts +57 -0
- package/dist/execution/bundle-reload.d.ts +42 -0
- package/dist/execution/config-resolver.d.ts +127 -0
- package/dist/execution/configuration-builder.d.ts +66 -0
- package/dist/execution/context-composition.d.ts +161 -0
- package/dist/execution/endpoint-actions.d.ts +9 -0
- package/dist/execution/endpoint-payloads.d.ts +32 -0
- package/dist/execution/endpoint-runner.d.ts +129 -0
- package/dist/execution/engine-state.d.ts +40 -0
- package/dist/execution/executor-callbacks.d.ts +56 -0
- package/dist/execution/executor.d.ts +436 -0
- package/dist/execution/gates.d.ts +45 -0
- package/dist/execution/hook-chain.d.ts +25 -0
- package/dist/execution/logic-calls.d.ts +190 -0
- package/dist/execution/logic-failure.d.ts +84 -0
- package/dist/execution/logic-loader.d.ts +54 -0
- package/dist/execution/logic-result.d.ts +76 -0
- package/dist/execution/tick-controller-call.d.ts +32 -0
- package/dist/execution/types.d.ts +48 -0
- package/dist/generation/ts-type.d.ts +39 -0
- package/dist/guide/locale.d.ts +12 -0
- package/dist/guide/video-registry.d.ts +42 -0
- package/dist/help-examples.d.ts +125 -0
- package/dist/host/adapter.d.ts +67 -0
- package/dist/host/host-channel.d.ts +222 -0
- package/dist/host/index.d.ts +20 -0
- package/dist/host/protocol.d.ts +783 -0
- package/dist/host/surfaces.d.ts +42 -0
- package/dist/host/theme.d.ts +55 -0
- package/dist/host.js +583 -0
- package/dist/prompts/index.d.ts +7 -0
- package/dist/prompts/schema-builder.d.ts +29 -0
- package/dist/prompts/types.d.ts +108 -0
- package/dist/prompts/validators.d.ts +32 -0
- package/dist/runtime/capability-scope.d.ts +6 -0
- package/dist/runtime/context-merge.d.ts +29 -0
- package/dist/runtime/context-projection.d.ts +41 -0
- package/dist/runtime/guarded-callbacks.d.ts +15 -0
- package/dist/runtime/index.d.ts +27 -0
- package/dist/runtime/manifest-version.d.ts +55 -0
- package/dist/runtime/tick-validation.d.ts +25 -0
- package/dist/runtime.js +346 -0
- package/dist/schema.json +1546 -0
- package/dist/server/vite-plugin.d.ts +46 -0
- package/dist/services/tts/client.d.ts +51 -0
- package/dist/services/tts/elevenlabs.d.ts +24 -0
- package/dist/services/tts/types.d.ts +44 -0
- package/dist/storage/asset-resolver.d.ts +49 -0
- package/dist/storage/blob-store.d.ts +41 -0
- package/dist/templates/endpoint/logic.test.ts.liquid +61 -0
- package/dist/templates/endpoint/logic.ts.liquid +25 -0
- package/dist/templates/engine/init.json.liquid +7 -0
- package/dist/templates/engine/logic-aggregate.test.ts.liquid +90 -0
- package/dist/templates/engine/logic-aggregate.ts.liquid +46 -0
- package/dist/templates/engine/logic.test.ts.liquid +61 -0
- package/dist/templates/engine/logic.ts.liquid +32 -0
- package/dist/templates/engine/run.json.liquid +7 -0
- package/dist/templates/guide/01-at-a-glance.md.liquid +19 -0
- package/dist/templates/guide/02-preparation.md.liquid +18 -0
- package/dist/templates/guide/03-running-the-session.md.liquid +37 -0
- package/dist/templates/hook/logic.test.ts.liquid +46 -0
- package/dist/templates/hook/logic.ts.liquid +12 -0
- package/dist/templates/package/CLAUDE.md.template +338 -0
- package/dist/templates/package/DESIGN.md.liquid +144 -0
- package/dist/templates/package/README.md.liquid +143 -0
- package/dist/templates/package/claude/rules/workflow-create-capability.md +26 -0
- package/dist/templates/package/claude/rules/workflow-create-endpoint.md +35 -0
- package/dist/templates/package/claude/rules/workflow-create-engine.md +35 -0
- package/dist/templates/package/claude/rules/workflow-create-guide.md +40 -0
- package/dist/templates/package/claude/rules/workflow-create-presentation.md +37 -0
- package/dist/templates/package/claude/rules/workflow-create-user-interface.md +31 -0
- package/dist/templates/package/claude/rules/workflow-publish-package.md +54 -0
- package/dist/templates/package/claude/rules/workflow-test-engine-or-ui.md +30 -0
- package/dist/templates/package/claude/rules/workflow-update-sdk-version.md +43 -0
- package/dist/templates/package/claude/settings.json.template +16 -0
- package/dist/templates/package/gitignore.template +15 -0
- package/dist/templates/package/global-setup.ts.template +21 -0
- package/dist/templates/package/globals.d.ts.template +2 -0
- package/dist/templates/package/oxfmtrc.json.template +14 -0
- package/dist/templates/package/oxlintrc.json.template +238 -0
- package/dist/templates/package/package.json.liquid +65 -0
- package/dist/templates/package/playwright.config.ts.liquid +19 -0
- package/dist/templates/package/pnpm-workspace.yaml.template +27 -0
- package/dist/templates/package/test-setup.ts.liquid +7 -0
- package/dist/templates/package/tsconfig.json.liquid +15 -0
- package/dist/templates/package/vite.config.ts.liquid +19 -0
- package/dist/templates/package/vitest.config.ts.liquid +25 -0
- package/dist/templates/package/vitest.engine.config.ts.liquid +12 -0
- package/dist/templates/presentation/index.html.liquid +20 -0
- package/dist/templates/presentation/src/App.tsx.liquid +49 -0
- package/dist/templates/presentation/src/Slides.tsx.liquid +81 -0
- package/dist/templates/presentation/src/i18n.ts.liquid +54 -0
- package/dist/templates/presentation/src/main.tsx.liquid +14 -0
- package/dist/templates/scenario_editor/src/App.test.tsx.liquid +30 -0
- package/dist/templates/scenario_editor/src/App.tsx.liquid +95 -0
- package/dist/templates/scenario_editor/src/i18n.ts.liquid +44 -0
- package/dist/templates/session/src/App.test.tsx.liquid +60 -0
- package/dist/templates/session/src/App.tsx.liquid +128 -0
- package/dist/templates/session/src/i18n.ts.liquid +53 -0
- package/dist/templates/session/src/main.tsx.liquid +48 -0
- package/dist/templates/session/src/theme.ts.liquid +46 -0
- package/dist/templates/settings-editor/index.html.liquid +12 -0
- package/dist/templates/settings-editor/src/main.tsx.liquid +48 -0
- package/dist/templates/settings-editor/src/theme.ts.liquid +24 -0
- package/dist/templates/setup/src/App.test.tsx.liquid +39 -0
- package/dist/templates/setup/src/App.tsx.liquid +102 -0
- package/dist/templates/setup/src/i18n.ts.liquid +47 -0
- package/dist/templates/starter-game/capabilities/briefing/capability.sdk.json +18 -0
- package/dist/templates/starter-game/capabilities/plan/capability.sdk.json +17 -0
- package/dist/templates/starter-game/capabilities/results/capability.sdk.json +61 -0
- package/dist/templates/starter-game/capabilities/roles/capability.sdk.json +17 -0
- package/dist/templates/starter-game/capabilities/weather/capability.sdk.json +23 -0
- package/dist/templates/starter-game/engine/defaults.ts +81 -0
- package/dist/templates/starter-game/engine/engine.sdk.json +12 -0
- package/dist/templates/starter-game/engine/logic.test.ts +221 -0
- package/dist/templates/starter-game/engine/logic.ts +149 -0
- package/dist/templates/starter-game/engine/test-helpers.ts +74 -0
- package/dist/templates/starter-game/engine/types.ts +139 -0
- package/dist/templates/starter-game/guide/content/en/01-at-a-glance.md +51 -0
- package/dist/templates/starter-game/guide/content/en/02-preparation.md +35 -0
- package/dist/templates/starter-game/guide/content/en/03-running-the-day.md +58 -0
- package/dist/templates/starter-game/guide/guide.sdk.json +10 -0
- package/dist/templates/starter-game/guide/guide.test.ts +108 -0
- package/dist/templates/starter-game/hooks/assignproducts/defaults.ts +37 -0
- package/dist/templates/starter-game/hooks/assignproducts/hook.sdk.json +13 -0
- package/dist/templates/starter-game/hooks/assignproducts/logic/logic.test.ts +77 -0
- package/dist/templates/starter-game/hooks/assignproducts/logic/logic.ts +47 -0
- package/dist/templates/starter-game/hooks/assignproducts/types.ts +55 -0
- package/dist/templates/starter-game/package.json +68 -0
- package/dist/templates/starter-game/playwright.config.ts +19 -0
- package/dist/templates/starter-game/scenarios/heatwave/scenario.sdk.json +20 -0
- package/dist/templates/starter-game/scenarios/ordinary_week/scenario.sdk.json +12 -0
- package/dist/templates/starter-game/seriousgaming.sdk.json +23 -0
- package/dist/templates/starter-game/session/defaults.ts +80 -0
- package/dist/templates/starter-game/session/index.html +12 -0
- package/dist/templates/starter-game/session/session.sdk.json +11 -0
- package/dist/templates/starter-game/session/src/App.test.tsx +307 -0
- package/dist/templates/starter-game/session/src/App.tsx +309 -0
- package/dist/templates/starter-game/session/src/Section.tsx +37 -0
- package/dist/templates/starter-game/session/src/TeamDoor.tsx +96 -0
- package/dist/templates/starter-game/session/src/format.ts +8 -0
- package/dist/templates/starter-game/session/src/i18n.ts +51 -0
- package/dist/templates/starter-game/session/src/main.tsx +48 -0
- package/dist/templates/starter-game/session/src/theme.ts +45 -0
- package/dist/templates/starter-game/session/types.ts +135 -0
- package/dist/templates/starter-game/settings/engine/settings.sdk.json +50 -0
- package/dist/templates/starter-game/settings/menu/settings.sdk.json +78 -0
- package/dist/templates/starter-game/settings/session/settings.sdk.json +23 -0
- package/dist/templates/starter-game/settings/ui/settings.sdk.json +25 -0
- package/dist/templates/starter-game/test-setup.ts +7 -0
- package/dist/templates/starter-game/testing/e2e/game.spec.ts +139 -0
- package/dist/templates/starter-game/testing/e2e/global-setup.ts +21 -0
- package/dist/templates/starter-game/testing/engine/game.engine.test.ts +171 -0
- package/dist/templates/starter-game/tsconfig.json +19 -0
- package/dist/templates/starter-game/ui/defaults.ts +60 -0
- package/dist/templates/starter-game/ui/index.html +12 -0
- package/dist/templates/starter-game/ui/src/App.test.tsx +138 -0
- package/dist/templates/starter-game/ui/src/App.tsx +175 -0
- package/dist/templates/starter-game/ui/src/DayBar.test.tsx +78 -0
- package/dist/templates/starter-game/ui/src/DayBar.tsx +72 -0
- package/dist/templates/starter-game/ui/src/HistoryPanel.test.tsx +74 -0
- package/dist/templates/starter-game/ui/src/HistoryPanel.tsx +120 -0
- package/dist/templates/starter-game/ui/src/MakeRow.tsx +129 -0
- package/dist/templates/starter-game/ui/src/Section.tsx +37 -0
- package/dist/templates/starter-game/ui/src/ThemeToggle.tsx +49 -0
- package/dist/templates/starter-game/ui/src/TodayPanel.test.tsx +179 -0
- package/dist/templates/starter-game/ui/src/TodayPanel.tsx +92 -0
- package/dist/templates/starter-game/ui/src/YesterdayPanel.tsx +142 -0
- package/dist/templates/starter-game/ui/src/format.ts +12 -0
- package/dist/templates/starter-game/ui/src/i18n.ts +95 -0
- package/dist/templates/starter-game/ui/src/main.tsx +48 -0
- package/dist/templates/starter-game/ui/src/menu.ts +18 -0
- package/dist/templates/starter-game/ui/src/test-helpers.ts +58 -0
- package/dist/templates/starter-game/ui/src/theme.ts +45 -0
- package/dist/templates/starter-game/ui/src/writes.ts +43 -0
- package/dist/templates/starter-game/ui/types.ts +137 -0
- package/dist/templates/starter-game/ui/ui.sdk.json +11 -0
- package/dist/templates/starter-game/validators/capacity/defaults.ts +37 -0
- package/dist/templates/starter-game/validators/capacity/logic/logic.test.ts +96 -0
- package/dist/templates/starter-game/validators/capacity/logic/logic.ts +45 -0
- package/dist/templates/starter-game/validators/capacity/types.ts +55 -0
- package/dist/templates/starter-game/validators/capacity/validator.sdk.json +11 -0
- package/dist/templates/starter-game/vite.config.ts +19 -0
- package/dist/templates/starter-game/vitest.config.ts +15 -0
- package/dist/templates/starter-game/vitest.engine.config.ts +19 -0
- package/dist/templates/starter-game/waiting_room/defaults.ts +40 -0
- package/dist/templates/starter-game/waiting_room/index.html +12 -0
- package/dist/templates/starter-game/waiting_room/src/App.test.tsx +102 -0
- package/dist/templates/starter-game/waiting_room/src/App.tsx +194 -0
- package/dist/templates/starter-game/waiting_room/src/i18n.ts +33 -0
- package/dist/templates/starter-game/waiting_room/src/main.tsx +48 -0
- package/dist/templates/starter-game/waiting_room/src/theme.ts +45 -0
- package/dist/templates/starter-game/waiting_room/types.ts +59 -0
- package/dist/templates/starter-game/waiting_room/waiting_room.sdk.json +10 -0
- package/dist/templates/tick-controller/logic.ts.liquid +8 -0
- package/dist/templates/ui/index.html.liquid +12 -0
- package/dist/templates/ui/src/App.test.tsx.liquid +41 -0
- package/dist/templates/ui/src/App.tsx.liquid +103 -0
- package/dist/templates/ui/src/i18n.ts.liquid +47 -0
- package/dist/templates/ui/src/main.tsx.liquid +48 -0
- package/dist/templates/ui/src/theme.ts.liquid +46 -0
- package/dist/templates/validator/logic.test.ts.liquid +42 -0
- package/dist/templates/validator/logic.ts.liquid +21 -0
- package/dist/templates/waiting-room/index.html.liquid +12 -0
- package/dist/templates/waiting-room/src/App.test.tsx.liquid +57 -0
- package/dist/templates/waiting-room/src/App.tsx.liquid +118 -0
- package/dist/templates/waiting-room/src/i18n.ts.liquid +44 -0
- package/dist/templates/waiting-room/src/main.tsx.liquid +48 -0
- package/dist/templates/waiting-room/src/theme.ts.liquid +46 -0
- package/dist/templating/resource-metadata.d.ts +131 -0
- package/dist/types.d.ts +14 -0
- package/dist/utils/atomic-write.d.ts +40 -0
- package/dist/utils/cli-error.d.ts +64 -0
- package/dist/utils/document-parser-client.d.ts +20 -0
- package/dist/utils/env-loader.d.ts +40 -0
- package/dist/utils/error-codes.d.ts +335 -0
- package/dist/utils/errors.d.ts +19 -0
- package/dist/utils/global-settings.d.ts +65 -0
- package/dist/utils/llm-client.d.ts +34 -0
- package/dist/utils/logger.d.ts +36 -0
- package/dist/utils/output.d.ts +109 -0
- package/dist/utils/platform.d.ts +14 -0
- package/dist/utils/prompt.d.ts +36 -0
- package/dist/utils/real-path.d.ts +6 -0
- package/dist/utils/schema-properties.d.ts +35 -0
- package/dist/validation/ajv.d.ts +17 -0
- package/dist/validation/facilitator-guide-validator.d.ts +27 -0
- package/dist/validation/findings.d.ts +40 -0
- package/dist/validation/game-constraints.d.ts +10 -0
- package/dist/validation/game-validator.d.ts +34 -0
- package/dist/validation/package-validation.d.ts +41 -0
- package/dist/validation/package-validator.d.ts +19 -0
- package/dist/validation/resource-validator.d.ts +79 -0
- package/dist/validation/schema-defaults-validator.d.ts +29 -0
- package/dist/validation/schema-validation.d.ts +115 -0
- package/dist/validation/semver.d.ts +9 -0
- package/package.json +122 -3
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
# File handling (assets and blobs)
|
|
2
|
+
|
|
3
|
+
The SDK reads every file, whether it ships in the package or arrives at runtime, through one reference scheme. This guide explains the scheme, then covers uploading files at runtime, displaying them in UIs, and reading their bytes in logic.
|
|
4
|
+
|
|
5
|
+
Common uses include uploading a market research PDF in a decisions UI, displaying it in a results UI, and summarising it with an LLM inside an engine.
|
|
6
|
+
|
|
7
|
+
## Asset references
|
|
8
|
+
|
|
9
|
+
Every file a game can read is named by a self-describing string:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
asset:<path> // a file inside the active scenario's assets/ directory
|
|
13
|
+
shared:<path> // a file inside the package-level shared-assets/ directory
|
|
14
|
+
blob:<id> // a file uploaded at runtime, in the blob store
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Any string that begins with `asset:`, `shared:`, or `blob:` is an asset reference, and no other string is. So settings and context can mix scenario files, package files, and uploads freely: the prefix tells the resolver which store to read, and the code that uses a reference never needs to know which kind it holds.
|
|
18
|
+
|
|
19
|
+
- **`asset:`** is scenario-scoped. It resolves against the active scenario's `assets/` directory, and resolves to nothing when no scenario is active
|
|
20
|
+
- **`shared:`** is package-level. It resolves against `shared-assets/` at the package root, whatever scenario is active, or none (see [Sharing assets across scenarios](./scenarios.md#sharing-assets-across-scenarios))
|
|
21
|
+
- **`blob:`** names an upload. `uploadBlob` in a UI and `writeBlob` in logic both return one, so store it directly in context: there is no separate id to track
|
|
22
|
+
|
|
23
|
+
`asset:` and `shared:` are disjoint: there is no shadowing or precedence between them.
|
|
24
|
+
|
|
25
|
+
### The callbacks
|
|
26
|
+
|
|
27
|
+
Every callback that works on a file takes a reference:
|
|
28
|
+
|
|
29
|
+
- **`getAssetUrl(reference)`** returns a URL the browser can fetch, for `<img>`, `<embed>`, `<video>` and the like. Synchronous, and in a UI only. In a hosted runtime the URL is on the runtime's asset host, cross-origin to the surface's iframe. `useAssetUrl` is the hook form
|
|
30
|
+
- **`readAsset(reference)`** returns the bytes, MIME type, and display name. Asynchronous. In logic the runtime resolves the reference server-side; in a UI the SDK fetches the URL with `credentials: 'include'`. `useAsset` is the hook form that reads text, JSON, or a `Blob`
|
|
31
|
+
- **`extractText(reference)`** returns plain text from a PDF. It is offered to the engine, hooks, tick controllers, and endpoints that declare it in `uses`; a UI reaches it through an endpoint
|
|
32
|
+
|
|
33
|
+
In a UI, `getAssetUrl` strips the prefix and appends the rest to an absolute prefix the host supplies for each scheme. Under `sdk dev` those prefixes are on the asset origin (`--asset-port`), so `asset:shared/hero.jpg` becomes `http://localhost:<asset-port>/assets/scenario/shared/hero.jpg`; a hosted runtime points them at its own asset host. With no scenario active an `asset:` reference still produces a URL, which answers 404. A string with none of the three prefixes throws.
|
|
34
|
+
|
|
35
|
+
### Writing references in a scenario
|
|
36
|
+
|
|
37
|
+
A scenario writes references with the prefix in place, usually as values of settings the engine and UIs read:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"definition": {
|
|
42
|
+
"settings": {
|
|
43
|
+
"briefing": {
|
|
44
|
+
"Logo": "asset:shared/logo.svg",
|
|
45
|
+
"ScenarioBrief": "asset:en/intro.pdf"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Here `briefing` is a settings resource (`settings/briefing/`) declaring `Logo` and `ScenarioBrief` as strings with an empty default, which the engine and UI examples below list in their `settings`. The runtime takes these strings as they are: nothing checks at scenario load that the file exists, and a reference that does not resolve fails when something calls `getAssetUrl`, `readAsset`, or `extractText` with it. See [Scenarios](./scenarios.md#2-add-assets-optional) for where the files go.
|
|
53
|
+
|
|
54
|
+
### How a file moves through a game
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
User uploads file in decisions UI
|
|
58
|
+
↓
|
|
59
|
+
uploadBlob(file) → returns 'blob:<id>'
|
|
60
|
+
↓
|
|
61
|
+
setData({ 'upload': { DocumentRef: reference } })
|
|
62
|
+
↓
|
|
63
|
+
Next tick: engine reads bytes via readAsset(reference)
|
|
64
|
+
Results UI displays via getAssetUrl(reference)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Uploading a file in a UI
|
|
68
|
+
|
|
69
|
+
Use the `uploadBlob` callback from `useUserInterfaceCallbacks`. It returns a `blob:`-prefixed reference; store it directly:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { useUserInterfaceCallbacks, useUserInterfaceData } from '@seriousgaming/sdk/ui';
|
|
73
|
+
import type { Context, Settings } from '../types';
|
|
74
|
+
|
|
75
|
+
function DocumentUpload() {
|
|
76
|
+
const callbacks = useUserInterfaceCallbacks();
|
|
77
|
+
const { setData } = useUserInterfaceData<Context, Settings>();
|
|
78
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
79
|
+
|
|
80
|
+
const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
81
|
+
const file = e.target.files?.[0];
|
|
82
|
+
if (!file) return;
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
setIsUploading(true);
|
|
86
|
+
const reference = await callbacks.uploadBlob(file);
|
|
87
|
+
|
|
88
|
+
setData({
|
|
89
|
+
upload: { DocumentRef: reference },
|
|
90
|
+
});
|
|
91
|
+
} finally {
|
|
92
|
+
setIsUploading(false);
|
|
93
|
+
e.target.value = '';
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<div>
|
|
99
|
+
<input
|
|
100
|
+
type="file"
|
|
101
|
+
accept=".pdf,.csv,.txt"
|
|
102
|
+
onChange={handleFileUpload}
|
|
103
|
+
disabled={isUploading}
|
|
104
|
+
/>
|
|
105
|
+
{isUploading && <p>Uploading...</p>}
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Notes:**
|
|
112
|
+
|
|
113
|
+
- You do not choose the blob's lifetime: the host assigns it from the UI type (see [Blob scopes](#blob-scopes) below)
|
|
114
|
+
- The maximum file size is **20 MB**
|
|
115
|
+
- The returned string starts with `blob:`. Persist it in context via `setData` so the engine and other UIs can resolve it
|
|
116
|
+
|
|
117
|
+
## Displaying a file in a UI
|
|
118
|
+
|
|
119
|
+
Use `useAssetUrl` to resolve any reference to a URL suitable for `<img>`, `<embed>`, `<video>`, or any HTML element that takes a `src`. It returns `undefined` for an absent reference. Whether the reference came from an uploader (`blob:abc123`) or a scenario (`asset:shared/intro.pdf`), the component is the same:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { useAssetUrl, useUserInterfaceData } from '@seriousgaming/sdk/ui';
|
|
123
|
+
import type { Context, Settings } from '../types';
|
|
124
|
+
|
|
125
|
+
function DocumentViewer() {
|
|
126
|
+
const { data, settings } = useUserInterfaceData<Context, Settings>();
|
|
127
|
+
const src = useAssetUrl(data.upload?.DocumentRef || settings.briefing.ScenarioBrief);
|
|
128
|
+
|
|
129
|
+
if (!src) {
|
|
130
|
+
return <p>No document uploaded.</p>;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<embed
|
|
135
|
+
src={src}
|
|
136
|
+
type="application/pdf"
|
|
137
|
+
style={{ width: '100%', height: '500px', border: 'none' }}
|
|
138
|
+
/>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
For images, including a logo bundled in a scenario:
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
const logo = useAssetUrl('asset:shared/logo.svg');
|
|
147
|
+
return <img src={logo} alt="Logo" />;
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Outside a component, the surface's `getAssetUrl` callback does the same. To render a file's contents rather than link to it, such as a localised briefing, use `useAsset(reference, { as: 'text' })`, which fetches and reads it (see [Reading an asset's contents](./ui.md#reading-an-assets-contents)).
|
|
151
|
+
|
|
152
|
+
## Reading an asset in an engine
|
|
153
|
+
|
|
154
|
+
The engine receives `readAsset` in its callbacks. It accepts an `asset:`, `shared:`, or `blob:` reference and returns the bytes plus metadata:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import { decodeUtf8, getText, type RunFunction } from '@seriousgaming/sdk';
|
|
158
|
+
import type { Context, Settings } from './types';
|
|
159
|
+
|
|
160
|
+
export const Run: RunFunction<Context, Settings> = async ({ context, callbacks }) => {
|
|
161
|
+
const reference = context.upload?.DocumentRef;
|
|
162
|
+
|
|
163
|
+
if (reference) {
|
|
164
|
+
const { data, mimeType, name } = await callbacks.readAsset(reference);
|
|
165
|
+
|
|
166
|
+
const text = decodeUtf8(data);
|
|
167
|
+
|
|
168
|
+
const summary = await callbacks.generateText({
|
|
169
|
+
prompt: `Summarise this document:\n\n${text}`,
|
|
170
|
+
tier: 'standard',
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
capabilities: {
|
|
175
|
+
...context,
|
|
176
|
+
summary: {
|
|
177
|
+
...context.summary,
|
|
178
|
+
Summary: getText(summary),
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return { capabilities: context };
|
|
185
|
+
};
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Notes:**
|
|
189
|
+
|
|
190
|
+
- `readAsset` needs no `uses` entry: every engine, hook, tick controller, and endpoint has it
|
|
191
|
+
- `data` is a `Uint8Array`. Use `decodeUtf8` for text formats; pass binary directly to downstream APIs
|
|
192
|
+
- The same callback resolves `asset:shared/brief.pdf` and `blob:abc123`: there is no separate API for bundled and uploaded files
|
|
193
|
+
|
|
194
|
+
## Extracting text from a document
|
|
195
|
+
|
|
196
|
+
Use the `extractText` callback to convert a document into plain text suitable for passing to an LLM. Available in the engine, hooks, tick controllers, and endpoints that declare `"extractText"` in `uses`. UIs reach it through an endpoint. Currently supports **PDF** files.
|
|
197
|
+
|
|
198
|
+
### In an engine
|
|
199
|
+
|
|
200
|
+
Declare `"uses": ["extractText", "generateText"]` on the engine, and pass the generated `Callbacks` as `RunFunction`'s third type argument so both are typed as present:
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { getText, type RunFunction } from '@seriousgaming/sdk';
|
|
204
|
+
import type { Callbacks, Context, Settings } from './types';
|
|
205
|
+
|
|
206
|
+
export const Run: RunFunction<Context, Settings, Callbacks> = async ({
|
|
207
|
+
context,
|
|
208
|
+
settings,
|
|
209
|
+
callbacks,
|
|
210
|
+
}) => {
|
|
211
|
+
// An upload if there is one, else the brief the scenario supplies
|
|
212
|
+
const reference = context.upload?.DocumentRef || settings.briefing.ScenarioBrief;
|
|
213
|
+
|
|
214
|
+
if (reference) {
|
|
215
|
+
const { content } = await callbacks.extractText(reference);
|
|
216
|
+
|
|
217
|
+
const summary = await callbacks.generateText({
|
|
218
|
+
prompt: `Summarise this document:\n\n${content}`,
|
|
219
|
+
tier: 'standard',
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
capabilities: {
|
|
224
|
+
...context,
|
|
225
|
+
summary: {
|
|
226
|
+
...context.summary,
|
|
227
|
+
Summary: getText(summary),
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return { capabilities: context };
|
|
234
|
+
};
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### From a UI, through an endpoint
|
|
238
|
+
|
|
239
|
+
UIs cannot call `extractText` directly. Upload the document with `uploadBlob`, then pass the returned reference to an endpoint that wraps `extractText`:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
// endpoints/extract/logic/logic.ts
|
|
243
|
+
import type { Handler } from '../types';
|
|
244
|
+
|
|
245
|
+
export const Handle: Handler = async ({ request, callbacks }) => {
|
|
246
|
+
// Declared in `uses`, so always present: no check needed.
|
|
247
|
+
const { content } = await callbacks.extractText(request.reference);
|
|
248
|
+
return { text: content };
|
|
249
|
+
};
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The endpoint bundle declares `"uses": ["extractText"]`; the UI declares the endpoint in its `endpoints` array and calls it:
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
import { useState } from 'react';
|
|
256
|
+
import { useUserInterfaceCallbacks } from '@seriousgaming/sdk/ui';
|
|
257
|
+
import type { Callbacks } from '../types';
|
|
258
|
+
|
|
259
|
+
function DocumentProcessor() {
|
|
260
|
+
const callbacks = useUserInterfaceCallbacks() as Callbacks;
|
|
261
|
+
const [extractedText, setExtractedText] = useState<string | null>(null);
|
|
262
|
+
|
|
263
|
+
const handleExtract = async (reference: string) => {
|
|
264
|
+
const { text } = await callbacks.endpoints['extract']({ reference });
|
|
265
|
+
setExtractedText(text);
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
return extractedText ? <pre>{extractedText}</pre> : <p>No text extracted.</p>;
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
See [Call an endpoint from a UI](../recipes/call-endpoint-from-ui.md).
|
|
273
|
+
|
|
274
|
+
**Notes:**
|
|
275
|
+
|
|
276
|
+
- Without the generated `Callbacks` type argument, `extractText` is typed as optional, and a resource that calls it without declaring it in `uses` throws at run time
|
|
277
|
+
- The extracted text is plain text. For PDFs, the quality depends on the source document (text-based PDFs extract well; scanned documents may not)
|
|
278
|
+
|
|
279
|
+
## Sending a file to an endpoint
|
|
280
|
+
|
|
281
|
+
An endpoint that takes a file declares it in its request schema with `blob`, so the runtime checks the file before the handler runs:
|
|
282
|
+
|
|
283
|
+
```json
|
|
284
|
+
"reference": {
|
|
285
|
+
"type": "string",
|
|
286
|
+
"blob": { "accept": ["application/pdf"], "maxBytes": 5242880 }
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
The field accepts two kinds of string, so the same endpoint serves every caller:
|
|
291
|
+
|
|
292
|
+
- **A `blob:` reference** to a file already stored. A UI passes the reference `uploadBlob` returned, as in the example above; a script or an agent uploads first (locally, `POST /api/blobs` or `pnpm run sdk blob upload`)
|
|
293
|
+
- **A `data:` URI** carrying the file inline, for a caller with no upload of its own: `data:application/pdf;name=brief.pdf;base64,...`. Up to 5 MB
|
|
294
|
+
|
|
295
|
+
Either way the handler receives a `blob:` reference, typed `` `blob:${string}` ``, that names a file of an accepted type, judged by its bytes, within the size. A URL, a wrong type or an oversized file is refused before the handler runs. Without `blob`, a field is a plain string the handler must check itself.
|
|
296
|
+
|
|
297
|
+
See [Take a file in an endpoint](../recipes/take-a-file-in-an-endpoint.md).
|
|
298
|
+
|
|
299
|
+
## Blob scopes
|
|
300
|
+
|
|
301
|
+
Every blob is stored with a retention that controls its lifetime:
|
|
302
|
+
|
|
303
|
+
| Retention | Lifetime | When to use |
|
|
304
|
+
| ------------ | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
305
|
+
| `"game"` | Cleared on a session reset or a re-initialise | Participant-uploaded content for a specific game session |
|
|
306
|
+
| `"settings"` | Survives a session reset, cleared on a re-initialise | Documents set up before the game starts (e.g. a scenario brief uploaded from a setup screen or scenario editor) |
|
|
307
|
+
|
|
308
|
+
Retention is a deletion-lifecycle bucket only: it is not an access or visibility control. It is set at upload time, and who sets it depends on the surface:
|
|
309
|
+
|
|
310
|
+
- **UIs** never choose the retention. The host assigns it from the UI type: uploads from setup or the scenario editor are `"settings"`, every other UI's uploads are `"game"`. The `uploadBlob` callback takes no retention option
|
|
311
|
+
- **Engine / endpoint `writeBlob`** has no UI to derive from, so it takes an explicit `retention` option (default `"game"`)
|
|
312
|
+
- **CLI uploads** default to `"game"` retention but accept `--settings` to use `"settings"` retention
|
|
313
|
+
|
|
314
|
+
A deleted blob's reference answers 404, as an unknown one does. In the dev server, `sdk init`, the console's Re-initialise and Reconfigure, and selecting a scenario clear both retentions. The automatic re-initialise after a package edit clears `"game"` blobs only, so the scenario editor's uploads survive an edit. A per-team reset clears nothing, since blobs are not held per team, and neither does resetting the walkthrough.
|
|
315
|
+
|
|
316
|
+
## Uploading a blob via CLI
|
|
317
|
+
|
|
318
|
+
You can pre-load a file into a game session from the command line:
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
pnpm run sdk blob upload <file-path>
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**Options:**
|
|
325
|
+
|
|
326
|
+
- `--settings`: store with `'settings'` retention (survives a session reset). Default is `'game'` retention
|
|
327
|
+
- `--json`: print one JSON document for scripts, with the result as its `data`
|
|
328
|
+
|
|
329
|
+
**Output (default):**
|
|
330
|
+
|
|
331
|
+
```
|
|
332
|
+
Uploaded blob: blob:a3f7c2d1-84b0-4e12-9f1a-0c7e8a3b1d5e
|
|
333
|
+
File: market-research.pdf
|
|
334
|
+
Type: application/pdf
|
|
335
|
+
Retention: game
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Output (`--json`):** one JSON document (see [JSON output](../cli-reference.md#json-output)), whose `data` is:
|
|
339
|
+
|
|
340
|
+
```json
|
|
341
|
+
{
|
|
342
|
+
"reference": "blob:a3f7c2d1-84b0-4e12-9f1a-0c7e8a3b1d5e",
|
|
343
|
+
"name": "market-research.pdf",
|
|
344
|
+
"mimeType": "application/pdf",
|
|
345
|
+
"retention": "game"
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
After uploading, patch the reference into the game context so the engine can find it:
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
REFERENCE=$(pnpm run --silent sdk blob upload market-research.pdf --json | jq -r '.data.reference')
|
|
353
|
+
pnpm run sdk patch \
|
|
354
|
+
"[{\"op\":\"replace\",\"path\":\"/upload/DocumentRef\",\"value\":\"$REFERENCE\"}]"
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Supported file types
|
|
358
|
+
|
|
359
|
+
MIME types are detected from the file extension:
|
|
360
|
+
|
|
361
|
+
| Extension | MIME type |
|
|
362
|
+
| --------------- | -------------------------- |
|
|
363
|
+
| `.pdf` | `application/pdf` |
|
|
364
|
+
| `.png` | `image/png` |
|
|
365
|
+
| `.jpg`, `.jpeg` | `image/jpeg` |
|
|
366
|
+
| `.gif` | `image/gif` |
|
|
367
|
+
| `.webp` | `image/webp` |
|
|
368
|
+
| `.svg` | `image/svg+xml` |
|
|
369
|
+
| `.mp4` | `video/mp4` |
|
|
370
|
+
| `.webm` | `video/webm` |
|
|
371
|
+
| `.mp3` | `audio/mpeg` |
|
|
372
|
+
| `.wav` | `audio/wav` |
|
|
373
|
+
| `.json` | `application/json` |
|
|
374
|
+
| `.csv` | `text/csv` |
|
|
375
|
+
| `.txt` | `text/plain` |
|
|
376
|
+
| Other | `application/octet-stream` |
|
|
377
|
+
|
|
378
|
+
## Capability design
|
|
379
|
+
|
|
380
|
+
Define a capability to hold asset references:
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
pnpm run sdk new capability '{
|
|
384
|
+
"definition": {
|
|
385
|
+
"identifier": "upload",
|
|
386
|
+
"variablesSchema": {
|
|
387
|
+
"type": "object",
|
|
388
|
+
"properties": {
|
|
389
|
+
"DocumentRef": { "type": "string", "default": "" }
|
|
390
|
+
},
|
|
391
|
+
"required": ["DocumentRef"],
|
|
392
|
+
"additionalProperties": false
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}'
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
The directory is the declaration: there is no list in `seriousgaming.sdk.json` to register it in. Then reference it from the resources that need it:
|
|
399
|
+
|
|
400
|
+
- The **game UI** (`ui/`) lists `upload` in both its `writes` and its `reads`: the decisions and results panels are both components inside it
|
|
401
|
+
- Any **engine** that processes the document lists `upload` in its `reads`
|
|
402
|
+
|
|
403
|
+
## Example
|
|
404
|
+
|
|
405
|
+
Put together, the pieces above make an upload round trip. One component inside the game UI uploads a PDF with the code in [Uploading a file in a UI](#uploading-a-file-in-a-ui) and writes the returned reference to `upload.DocumentRef`; another reads `upload.DocumentRef` and embeds the file with the code in [Displaying a file in a UI](#displaying-a-file-in-a-ui). The engine reads the same reference when it needs the document's contents, as [Reading an asset in an engine](#reading-an-asset-in-an-engine) shows.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Shaping games
|
|
2
|
+
|
|
3
|
+
A package is a game. This guide shows you how to shape the one you have.
|
|
4
|
+
|
|
5
|
+
## What you'll learn
|
|
6
|
+
|
|
7
|
+
- Where a game is authored
|
|
8
|
+
- How to configure the engine and game UI it runs
|
|
9
|
+
- How to add a setup screen and a scenario editor
|
|
10
|
+
- How to set up multi-language support
|
|
11
|
+
- Where to read about goal-based games with dynamic end conditions
|
|
12
|
+
|
|
13
|
+
## Where the game lives
|
|
14
|
+
|
|
15
|
+
The game is `seriousgaming.sdk.json`: the manifest is the package, and the package is the game.
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"identifier": "com.example.mygame",
|
|
20
|
+
"supportedLocales": ["en"],
|
|
21
|
+
"locales": { "en": { "name": "My Game" } },
|
|
22
|
+
"maxTicks": 10
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The game's own identifier is `com.example.mygame.game`, the package identifier with `.game` appended, so it is never authored. The version is `package.json`'s. What the game wires is whatever the package declares, and what the package declares is whatever sits in the conventional directories beside this file:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
com.example.mygame/
|
|
30
|
+
├── seriousgaming.sdk.json
|
|
31
|
+
├── capabilities/stock/capability.sdk.json
|
|
32
|
+
├── engine/engine.sdk.json
|
|
33
|
+
└── ui/ui.sdk.json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Edit the file by hand, or through the CLI:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pnpm run sdk update package --patch '[{ "op": "replace", "path": "/maxTicks", "value": 20 }]'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Engines and UIs
|
|
43
|
+
|
|
44
|
+
A game holds one engine and one game UI: whichever ones the package declares. There is nothing to list. Order the simulation's steps by the order you call them inside the engine's `Run`.
|
|
45
|
+
|
|
46
|
+
## Configuring them
|
|
47
|
+
|
|
48
|
+
A game's parameters live in settings resources, `settings/<name>/settings.sdk.json`, not in the manifest. Each property declares a default, and the default is the game's own value: to change it, edit the settings resource. A resource reads the settings it lists in its `settings`:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"definition": {
|
|
53
|
+
"reads": ["stock"],
|
|
54
|
+
"writes": ["stock"],
|
|
55
|
+
"settings": ["engine"]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
A scenario or the facilitator's setup screen overrides settings by identifier, and nothing else does. The manifest has no `configuration` block, and the build refuses one. See [Settings](./settings.md).
|
|
61
|
+
|
|
62
|
+
The manifest's `maxTicks`, `teams` and `waitingRoom` are the exception, because every game has them: they are the game's own values for the built-in `game` settings, which a scenario or setup overrides under `game` like any other. Only `teams.mode` is fixed, since nothing can change a game's interaction mode. `gameSettingsConstraints` narrows or locks the rest, for example to four to ten rounds (see [Narrow or lock the game settings](./settings.md#narrow-or-lock-the-game-settings)). See [The game settings](./settings.md#the-game-settings).
|
|
63
|
+
|
|
64
|
+
## Adding setup and a scenario editor
|
|
65
|
+
|
|
66
|
+
Two optional slots edit a game's settings. Each is a slot, so nothing in the manifest names it:
|
|
67
|
+
|
|
68
|
+
- `setup/`: a short form at session creation, writing that session's overrides only. Every session of a game with a setup slot starts at its configuration step, with the setup screen open and participants waiting, until the facilitator presses **Play**; switching scenario returns it there. A session of a game without one starts at tick 0
|
|
69
|
+
- `scenario_editor/`: a screen for building lasting scenarios, at any time and with no session. The host offers save-as-scenario from it, never from setup
|
|
70
|
+
|
|
71
|
+
Each lists the settings resources it edits in `configures`. A game may have either, both, or neither. Custom scenarios in a hosted runtime are switched on by `customScenarios: { "supported": true }`, not by the editor: an agent can author a scenario from well-described settings schemas alone.
|
|
72
|
+
|
|
73
|
+
See [Building a setup screen](./setup.md) and [Building a scenario editor](./scenario-editor.md).
|
|
74
|
+
|
|
75
|
+
## Multi-language support
|
|
76
|
+
|
|
77
|
+
Add localisation to your game:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"identifier": "com.example.production",
|
|
82
|
+
"supportedLocales": ["en", "es", "fr"],
|
|
83
|
+
"locales": {
|
|
84
|
+
"en": { "name": "Production Game" },
|
|
85
|
+
"es": { "name": "Simulación de Producción" },
|
|
86
|
+
"fr": { "name": "Game de Production" }
|
|
87
|
+
},
|
|
88
|
+
"maxTicks": 10
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The facilitator switches the session language with the locale select in the console's dev drawer; the choice applies to every participant.
|
|
93
|
+
|
|
94
|
+
## Goal-based games
|
|
95
|
+
|
|
96
|
+
A game can end when a condition is met rather than after a fixed count: leave out `maxTicks` and return `complete: true` from the engine's `Run` (or from a tick controller). See [Game completion](./tick-controller.md#game-completion) for the reference, and [End a game when a condition is met](../recipes/end-game-on-condition.md) for the steps.
|
|
97
|
+
|
|
98
|
+
## Host controls
|
|
99
|
+
|
|
100
|
+
**The Play control is the host's.** It is always rendered on the configuration
|
|
101
|
+
step and a game cannot hide it. A setup screen may add a finish control of
|
|
102
|
+
its own with the [`requestPlay` callback](./setup.md#starting-play-from-your-own-control),
|
|
103
|
+
which does the same thing, but it is an addition, never a replacement, so a bug
|
|
104
|
+
in a game's own control cannot leave a facilitator with no way out of
|
|
105
|
+
configuration.
|
|
106
|
+
|
|
107
|
+
**Tick controls are the game's own.** There is no built-in next tick button: use the [`requestTick` callback](./ui.md#requesting-a-tick) in a game or session UI to advance the game. The console's dev drawer keeps a developer-only **Tick** button for testing.
|
|
108
|
+
|
|
109
|
+
**Tip**: To finish configuration from inside your own form, call `requestPlay` from your [setup screen](./setup.md). The host's Play control stays alongside it.
|
|
110
|
+
|
|
111
|
+
## Full example game
|
|
112
|
+
|
|
113
|
+
Here's a complete game example:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"identifier": "com.example.retail",
|
|
118
|
+
"supportedLocales": ["en", "es"],
|
|
119
|
+
"locales": {
|
|
120
|
+
"en": { "name": "Retail Business Game" },
|
|
121
|
+
"es": { "name": "Simulación de Negocio Minorista" }
|
|
122
|
+
},
|
|
123
|
+
"maxTicks": 52,
|
|
124
|
+
"teams": { "mode": "independent", "min": 2, "max": 6 }
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Testing games
|
|
129
|
+
|
|
130
|
+
Start the dev server to test your game:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
pnpm run sdk dev
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Open http://localhost:3000 to view your game. You can:
|
|
137
|
+
|
|
138
|
+
- Step through ticks
|
|
139
|
+
- Inspect context state
|
|
140
|
+
- Undo/redo changes
|
|
141
|
+
- Switch languages
|
|
142
|
+
|
|
143
|
+
## Updating games
|
|
144
|
+
|
|
145
|
+
`update package` takes no identifier of its own, and a patch's paths start at the manifest:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
pnpm run sdk update package --patch '[{ "op": "replace", "path": "/maxTicks", "value": 20 }]'
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Best practices
|
|
152
|
+
|
|
153
|
+
1. **Name clearly**: use descriptive package identifiers, since the game takes its name from the package
|
|
154
|
+
- ✅ `com.example.retailbusinessgame`
|
|
155
|
+
- ❌ `com.example.sim1`
|
|
156
|
+
|
|
157
|
+
2. **Reasonable tick count**: balance educational value and execution time
|
|
158
|
+
- ✅ 10-52 ticks for most games
|
|
159
|
+
- ❌ 1000+ ticks
|
|
160
|
+
|
|
161
|
+
3. **Organise resources**: group related variables into one capability
|
|
162
|
+
- ✅ Call the engine's steps in the order their data flows
|
|
163
|
+
- ❌ Reading a value a later step writes
|
|
164
|
+
|
|
165
|
+
4. **Test before deploying**: use the dev server to validate everything works
|
|
166
|
+
- ✅ Run through all ticks, check engine behaviour
|
|
167
|
+
- ❌ Deploy without testing
|
|
168
|
+
|
|
169
|
+
5. **Support languages**: add localisation from the start
|
|
170
|
+
- ✅ Include English, Spanish, French
|
|
171
|
+
- ❌ Only English
|
|
172
|
+
|
|
173
|
+
## Next steps
|
|
174
|
+
|
|
175
|
+
- [Getting started](../getting-started.md): test your game
|
|
176
|
+
- [Building user interfaces](./ui.md): build custom UIs
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Global settings
|
|
2
|
+
|
|
3
|
+
The global settings file lets you store API keys and defaults once, in a single user-owned file, so every project picks them up. It is the convenient default for the credentials the SDK needs: ElevenLabs and the Serious Gaming platform.
|
|
4
|
+
|
|
5
|
+
Without it, each new shell needs the keys re-exported, and project-level `.env` files are easy to leak into git. The global file lives outside any project, so it is shared across every game you work on.
|
|
6
|
+
|
|
7
|
+
## Location
|
|
8
|
+
|
|
9
|
+
The file lives at:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
~/.seriousgaming-sdk/config.json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This resolves correctly on every platform via the home directory, with no platform branching:
|
|
16
|
+
|
|
17
|
+
- macOS: `~/.seriousgaming-sdk/config.json`
|
|
18
|
+
- Linux: `~/.seriousgaming-sdk/config.json`
|
|
19
|
+
- Windows: `C:\Users\<user>\.seriousgaming-sdk\config.json`
|
|
20
|
+
|
|
21
|
+
To use a different location, set `SERIOUSGAMING_SDK_CONFIG` to an absolute path:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
export SERIOUSGAMING_SDK_CONFIG=/path/to/my-settings.json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Run `pnpm run sdk config path` to print the resolved location.
|
|
28
|
+
|
|
29
|
+
## File shape
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"elevenLabs": { "apiKey": "..." },
|
|
34
|
+
"platform": {
|
|
35
|
+
"apiHost": "https://app.businesssimulations.com",
|
|
36
|
+
"apiKey": "sg_...",
|
|
37
|
+
"defaultPublisher": "..."
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Every field is optional. Set only what you need.
|
|
43
|
+
|
|
44
|
+
## Managing settings from the CLI
|
|
45
|
+
|
|
46
|
+
You do not have to edit the file by hand. The `config` commands manage it for you:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Set a value (creates the file if needed)
|
|
50
|
+
pnpm run sdk config set platform.apiKey sg_...
|
|
51
|
+
|
|
52
|
+
# Read a value
|
|
53
|
+
pnpm run sdk config get platform.apiKey
|
|
54
|
+
|
|
55
|
+
# Print the resolved file path
|
|
56
|
+
pnpm run sdk config path
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The available keys are:
|
|
60
|
+
|
|
61
|
+
- `elevenLabs.apiKey`
|
|
62
|
+
- `platform.apiHost`, `platform.apiKey`, `platform.defaultPublisher`
|
|
63
|
+
|
|
64
|
+
`config set` replaces whatever the key held before, and says so when it does. Check with `config get` before you set a key you are only trying to confirm, and be wary of pasting an example value from these docs over a real one.
|
|
65
|
+
|
|
66
|
+
Only `config set` writes to the file. `config get` and `config path` never change it.
|
|
67
|
+
|
|
68
|
+
If the file exists but is not valid JSON, `config set` refuses to write rather than replacing it with just the key you asked for. Fix or remove the file, then run the command again.
|
|
69
|
+
|
|
70
|
+
## Precedence
|
|
71
|
+
|
|
72
|
+
The global settings file is the lowest-priority source. From lowest to highest, the SDK merges:
|
|
73
|
+
|
|
74
|
+
1. Global settings file (`~/.seriousgaming-sdk/config.json`)
|
|
75
|
+
2. Project `.env` file
|
|
76
|
+
3. Environment variables
|
|
77
|
+
4. CLI flags
|
|
78
|
+
|
|
79
|
+
So a value in your shell environment, a project `.env`, or a `--flag` always wins over the global file. This keeps CI and one-off shells in control: the file is a convenience default, never an override.
|
|
80
|
+
|
|
81
|
+
For example, with `platform.apiKey` set in the global file, running a command in a shell that also exports `SERIOUSGAMING_API_KEY` uses the environment value, not the file.
|
|
82
|
+
|
|
83
|
+
## Security
|
|
84
|
+
|
|
85
|
+
The file is stored in plaintext and is not encrypted. Treat it like any other secrets file:
|
|
86
|
+
|
|
87
|
+
- On macOS and Linux, the SDK creates it with owner-only permissions. You can confirm or re-apply this with `chmod 600 ~/.seriousgaming-sdk/config.json`
|
|
88
|
+
- On Windows, restrict access with a restrictive ACL so only your user account can read it
|
|
89
|
+
- Never commit the file to git. It lives outside your project directory by default, so it is not picked up by a project's version control unless you move it
|
|
90
|
+
|
|
91
|
+
## Related
|
|
92
|
+
|
|
93
|
+
- [LLM integration](llm-integration.md)
|
|
94
|
+
- [Speech generation](speech-generation.md)
|