@crowdedkingdoms/crowdyjs 15.12.1 → 16.0.0-dev.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.
Files changed (119) hide show
  1. package/MIGRATION.md +55 -0
  2. package/README.md +67 -41
  3. package/dist/client.d.ts +1 -0
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js +3 -0
  6. package/dist/crowdy-client.d.ts +2 -3
  7. package/dist/crowdy-client.d.ts.map +1 -1
  8. package/dist/crowdy-client.js +9 -11
  9. package/dist/crowdy-dsh/bridge.d.ts +156 -0
  10. package/dist/crowdy-dsh/bridge.d.ts.map +1 -0
  11. package/dist/crowdy-dsh/bridge.js +523 -0
  12. package/dist/crowdy-dsh/index.d.ts +11 -0
  13. package/dist/crowdy-dsh/index.d.ts.map +1 -0
  14. package/dist/crowdy-dsh/index.js +10 -0
  15. package/dist/crowdy-dsh/pane.d.ts +64 -0
  16. package/dist/crowdy-dsh/pane.d.ts.map +1 -0
  17. package/dist/crowdy-dsh/pane.js +247 -0
  18. package/dist/crowdy-dsh/protocol.d.ts +249 -0
  19. package/dist/crowdy-dsh/protocol.d.ts.map +1 -0
  20. package/dist/crowdy-dsh/protocol.js +39 -0
  21. package/dist/crowdy-dsh/transport.d.ts +66 -0
  22. package/dist/crowdy-dsh/transport.d.ts.map +1 -0
  23. package/dist/crowdy-dsh/transport.js +88 -0
  24. package/dist/crowdy-studio/controller.d.ts +8 -0
  25. package/dist/crowdy-studio/controller.d.ts.map +1 -1
  26. package/dist/crowdy-studio/controller.js +33 -1
  27. package/dist/crowdy-studio/dom-shell.d.ts +21 -4
  28. package/dist/crowdy-studio/dom-shell.d.ts.map +1 -1
  29. package/dist/crowdy-studio/dom-shell.js +32 -17
  30. package/dist/crowdy-studio/embed/index.d.ts +1 -1
  31. package/dist/crowdy-studio/embed/index.d.ts.map +1 -1
  32. package/dist/crowdy-studio/embed/panel.d.ts +18 -18
  33. package/dist/crowdy-studio/embed/panel.d.ts.map +1 -1
  34. package/dist/crowdy-studio/embed/panel.js +7 -21
  35. package/dist/crowdy-studio/index.d.ts +2 -3
  36. package/dist/crowdy-studio/index.d.ts.map +1 -1
  37. package/dist/crowdy-studio/index.js +1 -2
  38. package/dist/crowdy-studio/mount.d.ts +29 -14
  39. package/dist/crowdy-studio/mount.d.ts.map +1 -1
  40. package/dist/crowdy-studio/mount.js +39 -126
  41. package/dist/crowdy-studio/styles.d.ts +1 -1
  42. package/dist/crowdy-studio/styles.d.ts.map +1 -1
  43. package/dist/crowdy-studio/styles.js +5 -0
  44. package/dist/default-origin.d.ts +6 -6
  45. package/dist/default-origin.d.ts.map +1 -1
  46. package/dist/default-origin.js +6 -6
  47. package/dist/generated/graphql.d.ts +76 -2438
  48. package/dist/generated/graphql.d.ts.map +1 -1
  49. package/dist/generated/graphql.js +0 -50
  50. package/dist/index.d.ts +1 -1
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +1 -1
  53. package/dist/kit/npcs.d.ts.map +1 -1
  54. package/dist/kit/social.d.ts.map +1 -1
  55. package/dist/{crowdy-agent/errors.d.ts → player-host/agent-errors.d.ts} +1 -1
  56. package/dist/player-host/agent-errors.d.ts.map +1 -0
  57. package/dist/player-host/agent-types.d.ts +18 -0
  58. package/dist/player-host/agent-types.d.ts.map +1 -0
  59. package/dist/player-host/agent-types.js +7 -0
  60. package/dist/player-host/index.d.ts +12 -4
  61. package/dist/player-host/index.d.ts.map +1 -1
  62. package/dist/player-host/index.js +11 -4
  63. package/dist/{crowdy-agent/schema.d.ts → player-host/json-schema.d.ts} +1 -1
  64. package/dist/player-host/json-schema.d.ts.map +1 -0
  65. package/dist/{crowdy-agent/schema.js → player-host/json-schema.js} +1 -1
  66. package/dist/player-host/schemas.d.ts +1 -1
  67. package/dist/player-host/schemas.d.ts.map +1 -1
  68. package/dist/player-host/schemas.js +2 -2
  69. package/dist/player-host/types.d.ts +2 -2
  70. package/dist/player-host/types.d.ts.map +1 -1
  71. package/package.json +5 -7
  72. package/dist/crowdy-agent/browser-dispatcher.d.ts +0 -41
  73. package/dist/crowdy-agent/browser-dispatcher.d.ts.map +0 -1
  74. package/dist/crowdy-agent/browser-dispatcher.js +0 -264
  75. package/dist/crowdy-agent/controller.d.ts +0 -152
  76. package/dist/crowdy-agent/controller.d.ts.map +0 -1
  77. package/dist/crowdy-agent/controller.js +0 -1289
  78. package/dist/crowdy-agent/errors.d.ts.map +0 -1
  79. package/dist/crowdy-agent/graphql-transport.d.ts +0 -154
  80. package/dist/crowdy-agent/graphql-transport.d.ts.map +0 -1
  81. package/dist/crowdy-agent/graphql-transport.js +0 -854
  82. package/dist/crowdy-agent/index.d.ts +0 -17
  83. package/dist/crowdy-agent/index.d.ts.map +0 -1
  84. package/dist/crowdy-agent/index.js +0 -16
  85. package/dist/crowdy-agent/registry.d.ts +0 -29
  86. package/dist/crowdy-agent/registry.d.ts.map +0 -1
  87. package/dist/crowdy-agent/registry.js +0 -281
  88. package/dist/crowdy-agent/schema.d.ts.map +0 -1
  89. package/dist/crowdy-agent/session-resume.d.ts +0 -24
  90. package/dist/crowdy-agent/session-resume.d.ts.map +0 -1
  91. package/dist/crowdy-agent/session-resume.js +0 -76
  92. package/dist/crowdy-agent/studio-tools.d.ts +0 -15
  93. package/dist/crowdy-agent/studio-tools.d.ts.map +0 -1
  94. package/dist/crowdy-agent/studio-tools.js +0 -280
  95. package/dist/crowdy-agent/tool-descriptors.d.ts +0 -4
  96. package/dist/crowdy-agent/tool-descriptors.d.ts.map +0 -1
  97. package/dist/crowdy-agent/tool-descriptors.js +0 -1049
  98. package/dist/crowdy-agent/transport.d.ts +0 -152
  99. package/dist/crowdy-agent/transport.d.ts.map +0 -1
  100. package/dist/crowdy-agent/transport.js +0 -27
  101. package/dist/crowdy-agent/types.d.ts +0 -328
  102. package/dist/crowdy-agent/types.d.ts.map +0 -1
  103. package/dist/crowdy-agent/types.js +0 -37
  104. package/dist/crowdy-studio/agent-dom-shell.d.ts +0 -68
  105. package/dist/crowdy-studio/agent-dom-shell.d.ts.map +0 -1
  106. package/dist/crowdy-studio/agent-dom-shell.js +0 -418
  107. package/dist/player-host/control-banner.d.ts +0 -40
  108. package/dist/player-host/control-banner.d.ts.map +0 -1
  109. package/dist/player-host/control-banner.js +0 -252
  110. package/dist/player-host/control-gate.d.ts +0 -77
  111. package/dist/player-host/control-gate.d.ts.map +0 -1
  112. package/dist/player-host/control-gate.js +0 -197
  113. package/dist/player-host/lease-manager.d.ts +0 -66
  114. package/dist/player-host/lease-manager.d.ts.map +0 -1
  115. package/dist/player-host/lease-manager.js +0 -428
  116. package/dist/player-host/tools.d.ts +0 -13
  117. package/dist/player-host/tools.d.ts.map +0 -1
  118. package/dist/player-host/tools.js +0 -79
  119. /package/dist/{crowdy-agent/errors.js → player-host/agent-errors.js} +0 -0
@@ -0,0 +1,247 @@
1
+ /**
2
+ * The Studio agent pane: a same-origin iframe hosting the DeepSeek Harness
3
+ * web UI (running in a Web Worker) plus the Crowdy chrome around it — the
4
+ * provider-data notice the player accepts once, a Screenshot button, the
5
+ * payer / spend line, and the bridge status.
6
+ *
7
+ * The harness owns the conversation UI (chat, diffs, plan mode, approvals);
8
+ * this pane owns what the game knows: who pays, whether the player consented,
9
+ * and the capture button that pushes a frame into the model's `captures/`.
10
+ */
11
+ import { StudioDshBridge } from './bridge.js';
12
+ const USAGE_REFRESH_MS = 30000;
13
+ export class CrowdyStudioDshPane {
14
+ constructor(parent, options) {
15
+ this.options = options;
16
+ this.usageTimer = null;
17
+ this.started = false;
18
+ this.disposed = false;
19
+ this.root = el('section', 'ck-crowdy-studio-dsh');
20
+ this.root.setAttribute('aria-label', 'Crowdy Studio agent');
21
+ const header = el('header');
22
+ const title = el('h2');
23
+ title.textContent = 'Agent';
24
+ this.status = el('span', 'ck-crowdy-studio-dsh-status');
25
+ this.status.textContent = 'Starting…';
26
+ this.captureButton = el('button', 'ck-crowdy-studio-dsh-capture');
27
+ this.captureButton.type = 'button';
28
+ this.captureButton.textContent = 'Screenshot';
29
+ this.captureButton.title = 'Share what you see with the agent (saved under captures/)';
30
+ this.captureButton.disabled = true;
31
+ this.captureButton.addEventListener('click', () => {
32
+ void this.run(async () => {
33
+ const shot = await this.bridge.shareCapture('player capture');
34
+ this.say(`Shared ${shot.name} (${shot.width}x${shot.height}) with the agent.`);
35
+ });
36
+ });
37
+ header.append(title, this.status, this.captureButton);
38
+ this.notice = el('div', 'ck-crowdy-studio-dsh-notice');
39
+ this.notice.hidden = true;
40
+ this.frame = document.createElement('iframe');
41
+ this.frame.className = 'ck-crowdy-studio-dsh-frame';
42
+ this.frame.title = 'Crowdy Studio agent';
43
+ this.frame.setAttribute('allow', 'clipboard-write');
44
+ // The harness is served from the game's own origin: `BroadcastChannel`
45
+ // and OPFS are origin-scoped, so it cannot live anywhere else today.
46
+ // `allow-same-origin` is therefore required and `allow-scripts` runs the
47
+ // worker. With both set the sandbox does NOT isolate the frame from this
48
+ // page (the browser says as much in its console); what it still removes is
49
+ // top-level navigation, popups, forms, downloads and modals. The real
50
+ // control is the CSP the host serves on `/dsh/*`.
51
+ this.frame.setAttribute('sandbox', 'allow-scripts allow-same-origin');
52
+ this.spend = el('div', 'ck-crowdy-studio-dsh-spend');
53
+ this.message = el('div', 'ck-crowdy-studio-dsh-message');
54
+ this.message.hidden = true;
55
+ this.root.append(header, this.notice, this.frame, this.spend, this.message);
56
+ parent.appendChild(this.root);
57
+ this.bridge = new StudioDshBridge({
58
+ controller: options.controller,
59
+ transport: options.transport,
60
+ appId: options.appId,
61
+ persistScope: options.persistScope,
62
+ getToken: options.getToken,
63
+ graphqlUrl: options.graphqlUrl,
64
+ host: options.host,
65
+ confirmLiveDeploy: (summary) => this.confirmLiveDeploy(summary.projectName),
66
+ onWarning: (text) => {
67
+ this.say(text);
68
+ options.onWarning?.(text);
69
+ },
70
+ onStatus: (status) => this.renderStatus(status),
71
+ onFileChanged: (change) => this.say(`Agent updated ${change.target.toLowerCase()}/${change.path}.`),
72
+ });
73
+ }
74
+ /**
75
+ * The page-side gate on a live deploy. The harness UI has its own approval
76
+ * inside the iframe, but the page cannot see it and the channel is open to
77
+ * any same-origin script, so the player confirms here too. Auto-declines
78
+ * after a minute so a stale prompt cannot be accepted by accident later.
79
+ */
80
+ confirmLiveDeploy(projectName) {
81
+ return new Promise((resolve) => {
82
+ this.message.hidden = false;
83
+ this.message.replaceChildren();
84
+ const text = el('span');
85
+ text.textContent = `The agent wants to deploy ${projectName} live to everyone in this grid. `;
86
+ const approve = el('button', 'ck-crowdy-studio-dsh-confirm');
87
+ approve.type = 'button';
88
+ approve.textContent = 'Deploy live';
89
+ const decline = el('button', 'ck-crowdy-studio-dsh-decline');
90
+ decline.type = 'button';
91
+ decline.textContent = 'Not now';
92
+ let settled = false;
93
+ const finish = (answer) => {
94
+ if (settled)
95
+ return;
96
+ settled = true;
97
+ clearTimeout(timer);
98
+ this.message.replaceChildren();
99
+ this.message.hidden = true;
100
+ resolve(answer);
101
+ };
102
+ const timer = setTimeout(() => finish(false), 60000);
103
+ approve.addEventListener('click', () => finish(true));
104
+ decline.addEventListener('click', () => finish(false));
105
+ this.message.append(text, approve, ' ', decline);
106
+ });
107
+ }
108
+ /** Boot once the pane is visible; the notice gates the first boot on consent. */
109
+ async start() {
110
+ if (this.started || this.disposed)
111
+ return;
112
+ this.started = true;
113
+ try {
114
+ const consent = await this.options.transport.consent(this.options.appId);
115
+ if (!consent.consented) {
116
+ this.started = false;
117
+ this.renderNotice();
118
+ this.status.textContent = 'Waiting for your OK';
119
+ return;
120
+ }
121
+ await this.boot();
122
+ }
123
+ catch (error) {
124
+ this.started = false;
125
+ this.renderStatus({ phase: 'failed', message: error instanceof Error ? error.message : String(error) });
126
+ }
127
+ }
128
+ dispose() {
129
+ this.disposed = true;
130
+ if (this.usageTimer)
131
+ clearInterval(this.usageTimer);
132
+ this.bridge.detach();
133
+ this.root.remove();
134
+ }
135
+ async boot() {
136
+ const base = this.options.webBase.endsWith('/') ? this.options.webBase : `${this.options.webBase}/`;
137
+ this.frame.src = `${base}index.html`;
138
+ await this.bridge.attach(this.frame);
139
+ this.captureButton.disabled = !this.options.host?.captureFrame;
140
+ await this.refreshUsage();
141
+ this.usageTimer = setInterval(() => void this.refreshUsage(), USAGE_REFRESH_MS);
142
+ }
143
+ renderNotice() {
144
+ this.notice.hidden = false;
145
+ this.notice.replaceChildren();
146
+ const text = el('p');
147
+ text.textContent =
148
+ 'The agent sends your project source, your messages and any screenshots you share to a model provider through Crowded Kingdoms (zero data retention, no training). ' +
149
+ 'Model usage is charged to your wallet at the app\'s rates unless the app pays for it.';
150
+ const actions = el('div', 'ck-crowdy-studio-dsh-notice-actions');
151
+ const accept = el('button');
152
+ accept.type = 'button';
153
+ accept.textContent = 'I understand, start the agent';
154
+ accept.addEventListener('click', () => {
155
+ void this.run(async () => {
156
+ await this.options.transport.setConsent(this.options.appId, true);
157
+ this.notice.hidden = true;
158
+ this.started = true;
159
+ await this.boot();
160
+ });
161
+ });
162
+ actions.append(accept);
163
+ this.notice.append(text, actions);
164
+ }
165
+ renderStatus(status) {
166
+ this.root.dataset.phase = status.phase;
167
+ switch (status.phase) {
168
+ case 'idle':
169
+ this.status.textContent = '';
170
+ break;
171
+ case 'waiting-for-frame':
172
+ case 'booting':
173
+ this.status.textContent = 'Starting…';
174
+ break;
175
+ case 'ready':
176
+ this.status.textContent = status.store ? 'Ready · GitHub' : 'Ready';
177
+ this.status.title = status.store ?? '';
178
+ break;
179
+ case 'failed':
180
+ this.status.textContent = 'Unavailable';
181
+ this.say(status.message);
182
+ break;
183
+ }
184
+ }
185
+ async refreshUsage() {
186
+ if (this.disposed)
187
+ return;
188
+ try {
189
+ const usage = await this.options.transport.usage(this.options.appId, 1);
190
+ this.renderSpend(usage);
191
+ }
192
+ catch {
193
+ // Usage is informational; the endpoint enforces the budget.
194
+ }
195
+ }
196
+ renderSpend(usage) {
197
+ const spent = Number(usage.todayChargeMicrousd) / 1000000;
198
+ const limit = Number(usage.dayLimitMicrousd) / 1000000;
199
+ const payer = usage.payerKind === 'PLAYER' ? 'your wallet' : usage.payerKind === 'ORG' ? "the app's wallet" : 'the platform';
200
+ this.spend.replaceChildren();
201
+ const text = el('span');
202
+ text.textContent = `Today: $${spent.toFixed(2)} of $${limit.toFixed(2)} · ${usage.todayRequests} requests · paid by ${payer}`;
203
+ this.spend.append(text);
204
+ if (usage.payerKind === 'PLAYER' && this.options.studioOrigin) {
205
+ const link = el('a');
206
+ link.href = `${this.options.studioOrigin.replace(/\/+$/, '')}/account/wallet`;
207
+ link.target = '_blank';
208
+ link.rel = 'noopener';
209
+ link.textContent = 'Add funds';
210
+ this.spend.append(' · ', link);
211
+ }
212
+ }
213
+ say(text) {
214
+ this.message.hidden = false;
215
+ this.message.textContent = text;
216
+ }
217
+ async run(work) {
218
+ try {
219
+ await work();
220
+ }
221
+ catch (error) {
222
+ this.say(error instanceof Error ? error.message : String(error));
223
+ }
224
+ }
225
+ }
226
+ function el(tag, className) {
227
+ const node = document.createElement(tag);
228
+ if (className)
229
+ node.className = className;
230
+ return node;
231
+ }
232
+ export const CROWDY_STUDIO_DSH_STYLES = `
233
+ .ck-crowdy-studio-dsh{flex:none;display:flex;flex-direction:column;min-width:0;min-height:0;border-left:1px solid var(--ck-line);background:#0b1220}
234
+ .ck-crowdy-studio-dsh>header{display:flex;align-items:center;gap:8px;padding:6px 10px;border-bottom:1px solid var(--ck-line)}
235
+ .ck-crowdy-studio-dsh>header h2{margin:0;font-size:13px;flex:none}
236
+ .ck-crowdy-studio-dsh-status{flex:1;min-width:0;color:var(--ck-muted);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
237
+ .ck-crowdy-studio-dsh[data-phase=failed] .ck-crowdy-studio-dsh-status{color:#fca5a5}
238
+ .ck-crowdy-studio-dsh-frame{flex:1;min-height:0;width:100%;border:0;background:#fff}
239
+ .ck-crowdy-studio-dsh-notice{padding:10px;font-size:12px;color:var(--ck-text);border-bottom:1px solid var(--ck-line)}
240
+ .ck-crowdy-studio-dsh-notice p{margin:0 0 8px}
241
+ .ck-crowdy-studio-dsh-notice-actions{display:flex;gap:6px}
242
+ .ck-crowdy-studio-dsh-spend{padding:4px 10px;font-size:11px;color:var(--ck-muted);border-top:1px solid var(--ck-line)}
243
+ .ck-crowdy-studio-dsh-spend a{color:#bae6fd}
244
+ .ck-crowdy-studio-dsh-message{padding:4px 10px;font-size:11px;color:#fde68a;border-top:1px solid var(--ck-line)}
245
+ .ck-crowdy-studio-dsh-message button{font-size:11px;padding:2px 8px;margin-left:4px}
246
+ .ck-crowdy-studio-dsh-confirm{background:#b91c1c;color:#fff;border:0;border-radius:3px}
247
+ `;
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Wire protocol between the Crowdy Studio page and the DeepSeek Harness that
3
+ * runs in a Web Worker inside the Studio agent pane.
4
+ *
5
+ * The harness side lives in the published `@crowdedkingdoms/crowdy-dsh`
6
+ * package (`bridge-protocol` export); this file mirrors it so the SDK stays
7
+ * dependency-free. Keep the two in step.
8
+ *
9
+ * Two channels exist:
10
+ *
11
+ * - `postMessage` between the page and the harness iframe: the iframe says
12
+ * `crowdy-dsh:ready`, the page answers `crowdy-dsh:boot` with the home
13
+ * files to seed and a one-time `nonce`, and the iframe reports
14
+ * `crowdy-dsh:booted` or `:failed`.
15
+ * - one `BroadcastChannel` (name chosen by the page) between the page and
16
+ * the worker: the worker asks the page to do what only the page can
17
+ * (draft tests, screenshots, project switching, game observation) and the
18
+ * page pushes token, project and context updates.
19
+ *
20
+ * A `BroadcastChannel` is open to every same-origin script, so the channel
21
+ * name is unguessable and every frame carries the boot nonce; a frame without
22
+ * the right nonce is dropped. The page still confirms a live deploy with the
23
+ * player before running it, because the harness UI's approval is inside the
24
+ * iframe and the page cannot see it.
25
+ */
26
+ export declare const CROWDY_DSH_PROTOCOL_VERSION: 2;
27
+ export type DshBridgeSide = 'page' | 'worker';
28
+ export type DshBridgeFrame = {
29
+ v: 2;
30
+ n: string;
31
+ from: DshBridgeSide;
32
+ t: 'req';
33
+ id: string;
34
+ method: string;
35
+ params: unknown;
36
+ } | {
37
+ v: 2;
38
+ n: string;
39
+ from: DshBridgeSide;
40
+ t: 'res';
41
+ id: string;
42
+ result: unknown;
43
+ } | {
44
+ v: 2;
45
+ n: string;
46
+ from: DshBridgeSide;
47
+ t: 'err';
48
+ id: string;
49
+ code: string;
50
+ message: string;
51
+ } | {
52
+ v: 2;
53
+ n: string;
54
+ from: DshBridgeSide;
55
+ t: 'event';
56
+ event: string;
57
+ payload: unknown;
58
+ };
59
+ export interface DshScreenshotResult {
60
+ name: string;
61
+ mediaType: 'image/png' | 'image/jpeg' | 'image/webp';
62
+ bytes: ArrayBuffer;
63
+ width: number;
64
+ height: number;
65
+ caption?: string;
66
+ }
67
+ export interface DshDiagnostic {
68
+ target: 'SERVER' | 'CLIENT';
69
+ path: string;
70
+ line?: number;
71
+ column?: number;
72
+ severity: 'error' | 'warning' | 'info';
73
+ message: string;
74
+ }
75
+ export interface DshRuntimeStatus {
76
+ target: 'SERVER' | 'CLIENT';
77
+ phase: string;
78
+ moduleName?: string;
79
+ runningRevision?: string;
80
+ savedRevision?: string;
81
+ message?: string;
82
+ }
83
+ export interface DshBuildResult {
84
+ ok: boolean;
85
+ mode: 'draft' | 'live';
86
+ summary: string;
87
+ diagnostics: DshDiagnostic[];
88
+ buildLog: string;
89
+ runtime: DshRuntimeStatus[];
90
+ screenshot?: DshScreenshotResult;
91
+ }
92
+ export interface DshProjectSummary {
93
+ projectId: string;
94
+ name: string;
95
+ kind: string;
96
+ updatedAt: string;
97
+ github?: string;
98
+ }
99
+ export interface DshBridgeRequestMap {
100
+ 'studio.screenshot': {
101
+ params: {
102
+ label?: string;
103
+ };
104
+ result: DshScreenshotResult;
105
+ };
106
+ 'studio.draftTest': {
107
+ params: Record<string, never>;
108
+ result: DshBuildResult;
109
+ };
110
+ 'studio.deployLive': {
111
+ params: Record<string, never>;
112
+ result: DshBuildResult;
113
+ };
114
+ 'studio.runtimeStatus': {
115
+ params: Record<string, never>;
116
+ result: {
117
+ runtime: DshRuntimeStatus[];
118
+ };
119
+ };
120
+ 'studio.runtimeLogs': {
121
+ params: {
122
+ limit?: number;
123
+ };
124
+ result: {
125
+ lines: string[];
126
+ truncated: boolean;
127
+ };
128
+ };
129
+ 'studio.clientLogs': {
130
+ params: {
131
+ limit?: number;
132
+ };
133
+ result: {
134
+ lines: string[];
135
+ truncated: boolean;
136
+ };
137
+ };
138
+ 'studio.projectList': {
139
+ params: Record<string, never>;
140
+ result: {
141
+ projects: DshProjectSummary[];
142
+ currentProjectId: string | null;
143
+ };
144
+ };
145
+ 'studio.projectOpen': {
146
+ params: {
147
+ projectId: string;
148
+ };
149
+ result: {
150
+ project: DshProjectSummary;
151
+ };
152
+ };
153
+ 'studio.projectCreate': {
154
+ params: {
155
+ name: string;
156
+ template?: string;
157
+ };
158
+ result: {
159
+ project: DshProjectSummary;
160
+ };
161
+ };
162
+ 'game.observe': {
163
+ params: Record<string, never>;
164
+ result: {
165
+ observation: unknown;
166
+ capturedAt: string;
167
+ };
168
+ };
169
+ }
170
+ export type DshBridgeMethod = keyof DshBridgeRequestMap;
171
+ export interface DshPageEventMap {
172
+ 'page.hello': {
173
+ appId: string;
174
+ projectId: string | null;
175
+ appToken?: string;
176
+ };
177
+ 'page.token': {
178
+ appToken: string;
179
+ };
180
+ 'page.project': {
181
+ projectId: string | null;
182
+ };
183
+ 'page.saved': {
184
+ revision?: string;
185
+ };
186
+ 'page.context': {
187
+ observation?: unknown;
188
+ clientLogs?: string[];
189
+ diagnostics?: DshDiagnostic[];
190
+ runtime?: DshRuntimeStatus[];
191
+ note?: string;
192
+ };
193
+ 'page.capture': DshScreenshotResult;
194
+ /** Text for the agent, queued into the latest live session (or a new one). */
195
+ 'page.prompt': {
196
+ text: string;
197
+ mode?: 'queue' | 'steer';
198
+ };
199
+ 'page.bye': Record<string, never>;
200
+ }
201
+ export interface DshWorkerEventMap {
202
+ 'worker.ready': {
203
+ root: string;
204
+ store?: string;
205
+ };
206
+ 'worker.fileChanged': {
207
+ target: 'SERVER' | 'CLIENT';
208
+ path: string;
209
+ };
210
+ 'worker.warning': {
211
+ message: string;
212
+ };
213
+ 'worker.openFile': {
214
+ target: 'SERVER' | 'CLIENT';
215
+ path: string;
216
+ line?: number;
217
+ };
218
+ }
219
+ /** Messages the harness iframe posts to its parent (`source: 'crowdy-dsh'`). */
220
+ export type DshFrameMessage = {
221
+ source: 'crowdy-dsh';
222
+ type: 'crowdy-dsh:ready';
223
+ } | {
224
+ source: 'crowdy-dsh';
225
+ type: 'crowdy-dsh:booted';
226
+ mount: string;
227
+ } | {
228
+ source: 'crowdy-dsh';
229
+ type: 'crowdy-dsh:failed';
230
+ message: string;
231
+ };
232
+ /** The page's answer to `crowdy-dsh:ready`. */
233
+ export interface DshBootMessage {
234
+ type: 'crowdy-dsh:boot';
235
+ /**
236
+ * Home-relative files to seed (`crowdy.json`, `settings.yaml`). Never the
237
+ * app token: that travels over the channel (`page.hello` / `page.token`) and
238
+ * lives only in the worker's memory.
239
+ */
240
+ files: Record<string, string>;
241
+ /** One-time secret every bridge frame must carry; also in `crowdy.json`. */
242
+ nonce: string;
243
+ /** OPFS scope for restored sessions; omit to start fresh. */
244
+ persistScope?: string;
245
+ mount?: string;
246
+ }
247
+ export declare function isDshBridgeFrame(value: unknown, nonce?: string): value is DshBridgeFrame;
248
+ export declare function isDshFrameMessage(value: unknown): value is DshFrameMessage;
249
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/crowdy-dsh/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,eAAO,MAAM,2BAA2B,EAAG,CAAU,CAAC;AAEtD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE9C,MAAM,MAAM,cAAc,GACtB;IAAE,CAAC,EAAE,CAAC,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,aAAa,CAAC;IAAC,CAAC,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAC/F;IAAE,CAAC,EAAE,CAAC,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,aAAa,CAAC;IAAC,CAAC,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAC/E;IAAE,CAAC,EAAE,CAAC,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,aAAa,CAAC;IAAC,CAAC,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC7F;IAAE,CAAC,EAAE,CAAC,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,aAAa,CAAC;IAAC,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAE1F,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,WAAW,GAAG,YAAY,GAAG,YAAY,CAAC;IACrD,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,aAAa,EAAE,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,mBAAmB,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,mBAAmB,EAAE;QAAE,MAAM,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,MAAM,EAAE,mBAAmB,CAAA;KAAE,CAAC;IACjF,kBAAkB,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAAC,MAAM,EAAE,cAAc,CAAA;KAAE,CAAC;IAC9E,mBAAmB,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAAC,MAAM,EAAE,cAAc,CAAA;KAAE,CAAC;IAC/E,sBAAsB,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAAC,MAAM,EAAE;YAAE,OAAO,EAAE,gBAAgB,EAAE,CAAA;SAAE,CAAA;KAAE,CAAC;IACnG,oBAAoB,EAAE;QAAE,MAAM,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,MAAM,EAAE;YAAE,KAAK,EAAE,MAAM,EAAE,CAAC;YAAC,SAAS,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE,CAAC;IACtG,mBAAmB,EAAE;QAAE,MAAM,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,MAAM,EAAE;YAAE,KAAK,EAAE,MAAM,EAAE,CAAC;YAAC,SAAS,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE,CAAC;IACrG,oBAAoB,EAAE;QACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC9B,MAAM,EAAE;YAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;YAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;SAAE,CAAC;KAC5E,CAAC;IACF,oBAAoB,EAAE;QAAE,MAAM,EAAE;YAAE,SAAS,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,MAAM,EAAE;YAAE,OAAO,EAAE,iBAAiB,CAAA;SAAE,CAAA;KAAE,CAAC;IAChG,sBAAsB,EAAE;QAAE,MAAM,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,MAAM,EAAE;YAAE,OAAO,EAAE,iBAAiB,CAAA;SAAE,CAAA;KAAE,CAAC;IAChH,cAAc,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAAC,MAAM,EAAE;YAAE,WAAW,EAAE,OAAO,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CACzG;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,mBAAmB,CAAC;AAExD,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7E,YAAY,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,cAAc,EAAE;QAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC7C,YAAY,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,cAAc,EAAE;QACd,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,WAAW,CAAC,EAAE,aAAa,EAAE,CAAC;QAC9B,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;QAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,cAAc,EAAE,mBAAmB,CAAC;IACpC,8EAA8E;IAC9E,aAAa,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;KAAE,CAAC;IAC1D,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,oBAAoB,EAAE;QAAE,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,gBAAgB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,iBAAiB,EAAE;QAAE,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACjF;AAED,gFAAgF;AAChF,MAAM,MAAM,eAAe,GACvB;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,kBAAkB,CAAA;CAAE,GAClD;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,mBAAmB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,mBAAmB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzE,+CAA+C;AAC/C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,iBAAiB,CAAC;IACxB;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,IAAI,cAAc,CAMxF;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAE1E"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Wire protocol between the Crowdy Studio page and the DeepSeek Harness that
3
+ * runs in a Web Worker inside the Studio agent pane.
4
+ *
5
+ * The harness side lives in the published `@crowdedkingdoms/crowdy-dsh`
6
+ * package (`bridge-protocol` export); this file mirrors it so the SDK stays
7
+ * dependency-free. Keep the two in step.
8
+ *
9
+ * Two channels exist:
10
+ *
11
+ * - `postMessage` between the page and the harness iframe: the iframe says
12
+ * `crowdy-dsh:ready`, the page answers `crowdy-dsh:boot` with the home
13
+ * files to seed and a one-time `nonce`, and the iframe reports
14
+ * `crowdy-dsh:booted` or `:failed`.
15
+ * - one `BroadcastChannel` (name chosen by the page) between the page and
16
+ * the worker: the worker asks the page to do what only the page can
17
+ * (draft tests, screenshots, project switching, game observation) and the
18
+ * page pushes token, project and context updates.
19
+ *
20
+ * A `BroadcastChannel` is open to every same-origin script, so the channel
21
+ * name is unguessable and every frame carries the boot nonce; a frame without
22
+ * the right nonce is dropped. The page still confirms a live deploy with the
23
+ * player before running it, because the harness UI's approval is inside the
24
+ * iframe and the page cannot see it.
25
+ */
26
+ export const CROWDY_DSH_PROTOCOL_VERSION = 2;
27
+ export function isDshBridgeFrame(value, nonce) {
28
+ if (!value || typeof value !== 'object')
29
+ return false;
30
+ const frame = value;
31
+ if (frame.v !== CROWDY_DSH_PROTOCOL_VERSION || typeof frame.n !== 'string' || typeof frame.t !== 'string')
32
+ return false;
33
+ if (frame.from !== 'page' && frame.from !== 'worker')
34
+ return false;
35
+ return nonce === undefined || frame.n === nonce;
36
+ }
37
+ export function isDshFrameMessage(value) {
38
+ return !!value && typeof value === 'object' && value.source === 'crowdy-dsh';
39
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The game-API surface the Studio agent pane needs beyond project files:
3
+ * the provider-data consent, the player's own model usage, and the list of
4
+ * models the metered endpoint will serve for this app.
5
+ *
6
+ * Every call carries the app-scoped token the game already holds; there is no
7
+ * second credential. The model list is REST (`GET /v1/model/models`, the
8
+ * OpenAI shape the harness reads) and the rest is GraphQL.
9
+ */
10
+ import type { GraphQLClient } from '../client.js';
11
+ export interface CrowdyStudioProviderConsent {
12
+ appId: string;
13
+ consented: boolean;
14
+ consentedAt: string | null;
15
+ }
16
+ export interface CrowdyStudioModelUsageEntry {
17
+ usageId: string;
18
+ payerKind: string;
19
+ status: string;
20
+ requestedModel: string;
21
+ resolvedModel: string | null;
22
+ promptTokens: string;
23
+ completionTokens: string;
24
+ reasoningTokens: string;
25
+ chargeMicrousd: string;
26
+ occurredAt: string;
27
+ client: string;
28
+ }
29
+ export interface CrowdyStudioModelUsage {
30
+ appId: string;
31
+ /** `PLAYER` (the caller's wallet), `ORG` (the app's org wallet) or `PLATFORM`. */
32
+ payerKind: string;
33
+ todayRequests: string;
34
+ todayChargeMicrousd: string;
35
+ dayLimitMicrousd: string;
36
+ recent: CrowdyStudioModelUsageEntry[];
37
+ }
38
+ export interface CrowdyStudioModelCatalogEntry {
39
+ id: string;
40
+ name: string;
41
+ contextWindow: number | null;
42
+ inputModalities: string[];
43
+ pricingMicrousdPerMillion: {
44
+ input: number;
45
+ output: number;
46
+ reasoning: number;
47
+ cachedInput: number;
48
+ };
49
+ }
50
+ export declare class CrowdyStudioDshTransport {
51
+ private readonly graphql;
52
+ private readonly options;
53
+ constructor(graphql: GraphQLClient, options: {
54
+ /** Origin of the game API, e.g. `https://ck-or.dev.crowdedkingdoms.com` or the page origin behind a proxy. */
55
+ apiOrigin: string;
56
+ getToken(): string | null;
57
+ });
58
+ /** Base URL the harness posts completions to (`<origin>/v1/model`). */
59
+ get modelBaseUrl(): string;
60
+ consent(appId: string): Promise<CrowdyStudioProviderConsent>;
61
+ setConsent(appId: string, consented: boolean): Promise<CrowdyStudioProviderConsent>;
62
+ usage(appId: string, limit?: number): Promise<CrowdyStudioModelUsage>;
63
+ /** Models the metered endpoint offers this caller; empty when the agent is disabled for the app. */
64
+ models(appId: string): Promise<CrowdyStudioModelCatalogEntry[]>;
65
+ }
66
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/crowdy-dsh/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,2BAA2B,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,6BAA6B;IAC5C,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,yBAAyB,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CACtG;AAuBD,qBAAa,wBAAwB;IAEjC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE;QACxB,8GAA8G;QAC9G,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,IAAI,MAAM,GAAG,IAAI,CAAC;KAC3B;IAGH,uEAAuE;IACvE,IAAI,YAAY,IAAI,MAAM,CAEzB;IAEK,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,2BAA2B,CAAC;IAO5D,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,2BAA2B,CAAC;IAQnF,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAQvE,oGAAoG;IAC9F,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,6BAA6B,EAAE,CAAC;CAsCtE"}