@agimon-ai/doompi-loop 0.0.1-alpha.74 → 0.0.1-alpha.76
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/package.json +6 -6
- package/dist/api-contracts.cjs +0 -7
- package/dist/api-contracts.d.cts +0 -2
- package/dist/api-contracts.d.mts +0 -2
- package/dist/api-contracts.mjs +0 -2
- package/dist/extensions/pi.cjs +0 -36
- package/dist/extensions/pi.cjs.map +0 -1
- package/dist/extensions/pi.d.cts +0 -5
- package/dist/extensions/pi.d.cts.map +0 -1
- package/dist/extensions/pi.d.mts +0 -5
- package/dist/extensions/pi.d.mts.map +0 -1
- package/dist/extensions/pi.mjs +0 -31
- package/dist/extensions/pi.mjs.map +0 -1
- package/dist/extensions/server.cjs +0 -51
- package/dist/extensions/server.cjs.map +0 -1
- package/dist/extensions/server.d.cts +0 -5
- package/dist/extensions/server.d.cts.map +0 -1
- package/dist/extensions/server.d.mts +0 -5
- package/dist/extensions/server.d.mts.map +0 -1
- package/dist/extensions/server.mjs +0 -46
- package/dist/extensions/server.mjs.map +0 -1
- package/dist/extensions/web.mjs +0 -228
- package/dist/extensions/web.mjs.map +0 -1
- package/dist/index.cjs +0 -0
- package/dist/index.d.cts +0 -2
- package/dist/index.d.mts +0 -2
- package/dist/index.mjs +0 -1
- package/dist/loopLaunchers.cjs +0 -5
- package/dist/loopLaunchers.d.cts +0 -2
- package/dist/loopLaunchers.d.mts +0 -2
- package/dist/loopLaunchers.mjs +0 -2
- package/dist/src/constants/loop.cjs +0 -12
- package/dist/src/constants/loop.cjs.map +0 -1
- package/dist/src/constants/loop.mjs +0 -9
- package/dist/src/constants/loop.mjs.map +0 -1
- package/dist/src/constants/piLoop.cjs +0 -16
- package/dist/src/constants/piLoop.cjs.map +0 -1
- package/dist/src/constants/piLoop.mjs +0 -11
- package/dist/src/constants/piLoop.mjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/runtime.cli.cjs +0 -26
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/runtime.cli.cjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/runtime.cli.mjs +0 -26
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/runtime.cli.mjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/runtime.server.cjs +0 -7
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/runtime.server.cjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/runtime.server.mjs +0 -7
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/runtime.server.mjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loop.cli.cjs +0 -6
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loop.cli.cjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loop.cli.mjs +0 -7
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loop.cli.mjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loop.server.cjs +0 -6
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loop.server.cjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loop.server.mjs +0 -7
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loop.server.mjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loops.cli.cjs +0 -6
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loops.cli.cjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loops.cli.mjs +0 -7
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loops.cli.mjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loops.server.cjs +0 -6
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loops.server.cjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loops.server.mjs +0 -7
- package/dist/src/extensions/workspaces/sessions/(backend)/command/loops.server.mjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/agent-settled.cli.cjs +0 -6
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/agent-settled.cli.cjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/agent-settled.cli.mjs +0 -7
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/agent-settled.cli.mjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.cjs +0 -6
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.cjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.mjs +0 -7
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.mjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.server.cjs +0 -6
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.server.cjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.server.mjs +0 -7
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.server.mjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.cli.cjs +0 -6
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.cli.cjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.cli.mjs +0 -7
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.cli.mjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.server.cjs +0 -6
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.server.cjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.server.mjs +0 -7
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-loop.server.mjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.cjs +0 -14
- package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.cjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.mjs +0 -15
- package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.mjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/root.server.cjs +0 -17
- package/dist/src/extensions/workspaces/sessions/(backend)/root.server.cjs.map +0 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/root.server.mjs +0 -18
- package/dist/src/extensions/workspaces/sessions/(backend)/root.server.mjs.map +0 -1
- package/dist/src/schemas/apiContracts.cjs +0 -13
- package/dist/src/schemas/apiContracts.cjs.map +0 -1
- package/dist/src/schemas/apiContracts.d.cts +0 -53
- package/dist/src/schemas/apiContracts.d.cts.map +0 -1
- package/dist/src/schemas/apiContracts.d.mts +0 -53
- package/dist/src/schemas/apiContracts.d.mts.map +0 -1
- package/dist/src/schemas/apiContracts.mjs +0 -13
- package/dist/src/schemas/apiContracts.mjs.map +0 -1
- package/dist/src/schemas/loopLaunchers.cjs +0 -17
- package/dist/src/schemas/loopLaunchers.cjs.map +0 -1
- package/dist/src/schemas/loopLaunchers.d.cts +0 -60
- package/dist/src/schemas/loopLaunchers.d.cts.map +0 -1
- package/dist/src/schemas/loopLaunchers.d.mts +0 -60
- package/dist/src/schemas/loopLaunchers.d.mts.map +0 -1
- package/dist/src/schemas/loopLaunchers.mjs +0 -15
- package/dist/src/schemas/loopLaunchers.mjs.map +0 -1
- package/dist/src/services/defaultLoopLauncher/index.cjs +0 -92
- package/dist/src/services/defaultLoopLauncher/index.cjs.map +0 -1
- package/dist/src/services/defaultLoopLauncher/index.mjs +0 -92
- package/dist/src/services/defaultLoopLauncher/index.mjs.map +0 -1
- package/dist/src/services/loopCommand/index.cjs +0 -15
- package/dist/src/services/loopCommand/index.cjs.map +0 -1
- package/dist/src/services/loopCommand/index.mjs +0 -15
- package/dist/src/services/loopCommand/index.mjs.map +0 -1
- package/dist/src/services/loopLaunchers/index.cjs +0 -180
- package/dist/src/services/loopLaunchers/index.cjs.map +0 -1
- package/dist/src/services/loopLaunchers/index.mjs +0 -180
- package/dist/src/services/loopLaunchers/index.mjs.map +0 -1
- package/dist/src/services/serverRuntime/index.cjs +0 -293
- package/dist/src/services/serverRuntime/index.cjs.map +0 -1
- package/dist/src/services/serverRuntime/index.mjs +0 -293
- package/dist/src/services/serverRuntime/index.mjs.map +0 -1
- package/dist/src/tui/leader.cjs +0 -36
- package/dist/src/tui/leader.cjs.map +0 -1
- package/dist/src/tui/leader.mjs +0 -36
- package/dist/src/tui/leader.mjs.map +0 -1
- package/dist/src/tui/loopListOverlay.cjs +0 -101
- package/dist/src/tui/loopListOverlay.cjs.map +0 -1
- package/dist/src/tui/loopListOverlay.mjs +0 -100
- package/dist/src/tui/loopListOverlay.mjs.map +0 -1
- package/dist/src/tui/loopRuntime.cjs +0 -256
- package/dist/src/tui/loopRuntime.cjs.map +0 -1
- package/dist/src/tui/loopRuntime.mjs +0 -256
- package/dist/src/tui/loopRuntime.mjs.map +0 -1
- package/dist/src/tui/startLoopOverlay.cjs +0 -71
- package/dist/src/tui/startLoopOverlay.cjs.map +0 -1
- package/dist/src/tui/startLoopOverlay.mjs +0 -70
- package/dist/src/tui/startLoopOverlay.mjs.map +0 -1
- package/dist/src/types/loopView.cjs +0 -19
- package/dist/src/types/loopView.cjs.map +0 -1
- package/dist/src/types/loopView.mjs +0 -18
- package/dist/src/types/loopView.mjs.map +0 -1
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
//#region src/services/loopLaunchers/index.ts
|
|
2
|
-
const MAX_ID_LENGTH = 256;
|
|
3
|
-
const MAX_GENERATION_LENGTH = 256;
|
|
4
|
-
const LAUNCHER_ID_LABEL = "loop launcher id";
|
|
5
|
-
const INSTANCE_ID_LABEL = "loop instance id";
|
|
6
|
-
function validId(value, label) {
|
|
7
|
-
const normalized = value.trim();
|
|
8
|
-
let hasControl = false;
|
|
9
|
-
for (const character of normalized) {
|
|
10
|
-
const code = character.codePointAt(0);
|
|
11
|
-
if (code !== void 0 && (code <= 31 || code === 127)) {
|
|
12
|
-
hasControl = true;
|
|
13
|
-
break;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
if (!normalized || normalized.length > MAX_ID_LENGTH || hasControl) throw new Error(`Invalid ${label}.`);
|
|
17
|
-
return normalized;
|
|
18
|
-
}
|
|
19
|
-
function snapshot(entry) {
|
|
20
|
-
return {
|
|
21
|
-
instanceId: entry.instanceId,
|
|
22
|
-
launcherId: entry.launcher.definition.id,
|
|
23
|
-
launcherLabel: entry.launcher.definition.label,
|
|
24
|
-
...entry.handle?.label ? { label: entry.handle.label } : {},
|
|
25
|
-
...entry.handle?.detail ? { detail: entry.handle.detail } : {},
|
|
26
|
-
state: entry.state,
|
|
27
|
-
...entry.startedAt ? { startedAt: entry.startedAt } : {}
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
/** Creates the provider-owned launcher registry for one active Doom session. */
|
|
31
|
-
function createDoomLoopLaunchersService({ generation, createInstanceId, timestamp }) {
|
|
32
|
-
if (!generation || generation.length > MAX_GENERATION_LENGTH) throw new TypeError("Doom loop launchers require a valid generation.");
|
|
33
|
-
const launchers = /* @__PURE__ */ new Map();
|
|
34
|
-
const instances = /* @__PURE__ */ new Map();
|
|
35
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
36
|
-
let registrationSequence = 0;
|
|
37
|
-
let disposed = false;
|
|
38
|
-
const ensureActive = () => {
|
|
39
|
-
if (disposed) throw new Error("The Doom loop-launchers service is disposed.");
|
|
40
|
-
};
|
|
41
|
-
const notify = () => {
|
|
42
|
-
for (const listener of listeners) listener();
|
|
43
|
-
};
|
|
44
|
-
const stopEntry = async (entry, reason) => {
|
|
45
|
-
if (instances.get(entry.instanceId) !== entry) return;
|
|
46
|
-
entry.state = "stopping";
|
|
47
|
-
entry.abortController.abort(reason);
|
|
48
|
-
notify();
|
|
49
|
-
try {
|
|
50
|
-
await entry.handle?.stop(reason);
|
|
51
|
-
} finally {
|
|
52
|
-
if (instances.get(entry.instanceId) === entry) instances.delete(entry.instanceId);
|
|
53
|
-
notify();
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
return Object.freeze({
|
|
57
|
-
generation,
|
|
58
|
-
register(definition) {
|
|
59
|
-
ensureActive();
|
|
60
|
-
const id = validId(definition.id, LAUNCHER_ID_LABEL);
|
|
61
|
-
if (launchers.has(id)) throw new Error(`Loop launcher '${id}' is already registered.`);
|
|
62
|
-
const token = Symbol(id);
|
|
63
|
-
const entry = {
|
|
64
|
-
token,
|
|
65
|
-
definition: {
|
|
66
|
-
...definition,
|
|
67
|
-
id
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
launchers.set(id, entry);
|
|
71
|
-
registrationSequence += 1;
|
|
72
|
-
notify();
|
|
73
|
-
let registrationDisposed = false;
|
|
74
|
-
return Object.freeze({
|
|
75
|
-
id,
|
|
76
|
-
generation: `${generation}:launcher:${registrationSequence}`,
|
|
77
|
-
async dispose(reason = "Loop launcher disposed.") {
|
|
78
|
-
if (registrationDisposed) return;
|
|
79
|
-
registrationDisposed = true;
|
|
80
|
-
if (launchers.get(id)?.token !== token) return;
|
|
81
|
-
launchers.delete(id);
|
|
82
|
-
const owned = [...instances.values()].filter((instance) => instance.launcher.token === token);
|
|
83
|
-
await Promise.allSettled(owned.map((instance) => stopEntry(instance, reason)));
|
|
84
|
-
notify();
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
},
|
|
88
|
-
listLaunchers() {
|
|
89
|
-
ensureActive();
|
|
90
|
-
return [...launchers.values()].map(({ definition }) => ({
|
|
91
|
-
id: definition.id,
|
|
92
|
-
source: definition.source,
|
|
93
|
-
label: definition.label,
|
|
94
|
-
...definition.description ? { description: definition.description } : {}
|
|
95
|
-
})).sort((left, right) => left.label.localeCompare(right.label) || left.id.localeCompare(right.id));
|
|
96
|
-
},
|
|
97
|
-
listInstances() {
|
|
98
|
-
ensureActive();
|
|
99
|
-
return [...instances.values()].map(snapshot).sort((left, right) => left.instanceId.localeCompare(right.instanceId));
|
|
100
|
-
},
|
|
101
|
-
subscribe(listener) {
|
|
102
|
-
ensureActive();
|
|
103
|
-
listeners.add(listener);
|
|
104
|
-
let subscribed = true;
|
|
105
|
-
return () => {
|
|
106
|
-
if (!subscribed) return;
|
|
107
|
-
subscribed = false;
|
|
108
|
-
listeners.delete(listener);
|
|
109
|
-
};
|
|
110
|
-
},
|
|
111
|
-
async launch(launcherId, options) {
|
|
112
|
-
ensureActive();
|
|
113
|
-
const normalizedLauncherId = validId(launcherId, LAUNCHER_ID_LABEL);
|
|
114
|
-
const launcher = launchers.get(normalizedLauncherId);
|
|
115
|
-
if (!launcher) throw new Error(`Loop launcher '${normalizedLauncherId}' is unavailable.`);
|
|
116
|
-
const instanceId = validId(options?.instanceId ?? createInstanceId(), INSTANCE_ID_LABEL);
|
|
117
|
-
if (instances.has(instanceId)) throw new Error(`Loop instance '${instanceId}' already exists.`);
|
|
118
|
-
const entry = {
|
|
119
|
-
instanceId,
|
|
120
|
-
launcher,
|
|
121
|
-
abortController: new AbortController(),
|
|
122
|
-
state: "starting"
|
|
123
|
-
};
|
|
124
|
-
instances.set(instanceId, entry);
|
|
125
|
-
notify();
|
|
126
|
-
try {
|
|
127
|
-
const handle = await launcher.definition.launch({
|
|
128
|
-
instanceId,
|
|
129
|
-
signal: entry.abortController.signal
|
|
130
|
-
});
|
|
131
|
-
const active = !disposed && instances.get(instanceId) === entry && !entry.abortController.signal.aborted;
|
|
132
|
-
if (!handle) {
|
|
133
|
-
if (instances.get(instanceId) === entry) instances.delete(instanceId);
|
|
134
|
-
notify();
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
if (!active || handle.instanceId !== instanceId) {
|
|
138
|
-
await handle.stop(active ? "Loop launcher returned a mismatched instance id." : "Loop launch was cancelled.");
|
|
139
|
-
if (instances.get(instanceId) === entry) instances.delete(instanceId);
|
|
140
|
-
notify();
|
|
141
|
-
if (active) throw new Error(`Loop launcher '${normalizedLauncherId}' returned a mismatched instance id.`);
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
entry.handle = handle;
|
|
145
|
-
entry.state = "running";
|
|
146
|
-
entry.startedAt = timestamp();
|
|
147
|
-
notify();
|
|
148
|
-
return snapshot(entry);
|
|
149
|
-
} catch (error) {
|
|
150
|
-
const cancelled = entry.abortController.signal.aborted || disposed;
|
|
151
|
-
if (instances.get(instanceId) === entry) instances.delete(instanceId);
|
|
152
|
-
notify();
|
|
153
|
-
if (cancelled) return void 0;
|
|
154
|
-
throw error;
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
async stop(instanceId, reason) {
|
|
158
|
-
ensureActive();
|
|
159
|
-
const entry = instances.get(validId(instanceId, INSTANCE_ID_LABEL));
|
|
160
|
-
if (!entry) return false;
|
|
161
|
-
await stopEntry(entry, reason);
|
|
162
|
-
return true;
|
|
163
|
-
},
|
|
164
|
-
async stopAll(reason) {
|
|
165
|
-
if (disposed && instances.size === 0) return;
|
|
166
|
-
await Promise.allSettled([...instances.values()].map((entry) => stopEntry(entry, reason)));
|
|
167
|
-
},
|
|
168
|
-
async dispose(reason = "Doom loop-launchers service disposed.") {
|
|
169
|
-
if (disposed) return;
|
|
170
|
-
disposed = true;
|
|
171
|
-
launchers.clear();
|
|
172
|
-
await Promise.allSettled([...instances.values()].map((entry) => stopEntry(entry, reason)));
|
|
173
|
-
listeners.clear();
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
//#endregion
|
|
178
|
-
exports.createDoomLoopLaunchersService = createDoomLoopLaunchersService;
|
|
179
|
-
|
|
180
|
-
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/services/loopLaunchers/index.ts"],"sourcesContent":["import type {\n DoomLoopLaunchersService,\n LoopInstanceSnapshot,\n LoopLauncherDefinition,\n LoopLauncherRegistration,\n StoppableLoop,\n} from '../../schemas/loopLaunchers';\n\nconst MAX_ID_LENGTH = 256;\nconst MAX_GENERATION_LENGTH = 256;\nconst LAUNCHER_ID_LABEL = 'loop launcher id';\nconst INSTANCE_ID_LABEL = 'loop instance id';\n\ninterface LauncherEntry {\n readonly token: symbol;\n readonly definition: LoopLauncherDefinition;\n}\n\ninterface InstanceEntry {\n readonly instanceId: string;\n readonly launcher: LauncherEntry;\n readonly abortController: AbortController;\n state: LoopInstanceSnapshot['state'];\n handle?: StoppableLoop;\n startedAt?: string;\n}\n\nexport interface LoopLaunchersDependencies {\n readonly generation: string;\n readonly createInstanceId: () => string;\n readonly timestamp: () => string;\n}\n\nfunction validId(value: string, label: string): string {\n const normalized = value.trim();\n let hasControl = false;\n for (const character of normalized) {\n const code = character.codePointAt(0);\n if (code !== undefined && (code <= 0x1f || code === 0x7f)) {\n hasControl = true;\n break;\n }\n }\n if (!normalized || normalized.length > MAX_ID_LENGTH || hasControl) throw new Error(`Invalid ${label}.`);\n return normalized;\n}\n\nfunction snapshot(entry: InstanceEntry): LoopInstanceSnapshot {\n return {\n instanceId: entry.instanceId,\n launcherId: entry.launcher.definition.id,\n launcherLabel: entry.launcher.definition.label,\n ...(entry.handle?.label ? { label: entry.handle.label } : {}),\n ...(entry.handle?.detail ? { detail: entry.handle.detail } : {}),\n state: entry.state,\n ...(entry.startedAt ? { startedAt: entry.startedAt } : {}),\n };\n}\n\n/** Creates the provider-owned launcher registry for one active Doom session. */\nexport function createDoomLoopLaunchersService({\n generation,\n createInstanceId,\n timestamp,\n}: LoopLaunchersDependencies): DoomLoopLaunchersService {\n if (!generation || generation.length > MAX_GENERATION_LENGTH) {\n throw new TypeError('Doom loop launchers require a valid generation.');\n }\n\n const launchers = new Map<string, LauncherEntry>();\n const instances = new Map<string, InstanceEntry>();\n const listeners = new Set<() => void>();\n let registrationSequence = 0;\n let disposed = false;\n\n const ensureActive = (): void => {\n if (disposed) throw new Error('The Doom loop-launchers service is disposed.');\n };\n const notify = (): void => {\n for (const listener of listeners) listener();\n };\n const stopEntry = async (entry: InstanceEntry, reason?: string): Promise<void> => {\n if (instances.get(entry.instanceId) !== entry) return;\n entry.state = 'stopping';\n entry.abortController.abort(reason);\n notify();\n try {\n await entry.handle?.stop(reason);\n } finally {\n if (instances.get(entry.instanceId) === entry) instances.delete(entry.instanceId);\n notify();\n }\n };\n\n const service: DoomLoopLaunchersService = {\n generation,\n register(definition): LoopLauncherRegistration {\n ensureActive();\n const id = validId(definition.id, LAUNCHER_ID_LABEL);\n if (launchers.has(id)) throw new Error(`Loop launcher '${id}' is already registered.`);\n const token = Symbol(id);\n const entry: LauncherEntry = { token, definition: { ...definition, id } };\n launchers.set(id, entry);\n registrationSequence += 1;\n notify();\n let registrationDisposed = false;\n return Object.freeze({\n id,\n generation: `${generation}:launcher:${registrationSequence}`,\n async dispose(reason = 'Loop launcher disposed.'): Promise<void> {\n if (registrationDisposed) return;\n registrationDisposed = true;\n if (launchers.get(id)?.token !== token) return;\n launchers.delete(id);\n const owned = [...instances.values()].filter((instance) => instance.launcher.token === token);\n await Promise.allSettled(owned.map((instance) => stopEntry(instance, reason)));\n notify();\n },\n });\n },\n listLaunchers() {\n ensureActive();\n return [...launchers.values()]\n .map(({ definition }) => ({\n id: definition.id,\n source: definition.source,\n label: definition.label,\n ...(definition.description ? { description: definition.description } : {}),\n }))\n .sort((left, right) => left.label.localeCompare(right.label) || left.id.localeCompare(right.id));\n },\n listInstances() {\n ensureActive();\n return [...instances.values()]\n .map(snapshot)\n .sort((left, right) => left.instanceId.localeCompare(right.instanceId));\n },\n subscribe(listener) {\n ensureActive();\n listeners.add(listener);\n let subscribed = true;\n return () => {\n if (!subscribed) return;\n subscribed = false;\n listeners.delete(listener);\n };\n },\n async launch(launcherId, options) {\n ensureActive();\n const normalizedLauncherId = validId(launcherId, LAUNCHER_ID_LABEL);\n const launcher = launchers.get(normalizedLauncherId);\n if (!launcher) throw new Error(`Loop launcher '${normalizedLauncherId}' is unavailable.`);\n const instanceId = validId(options?.instanceId ?? createInstanceId(), INSTANCE_ID_LABEL);\n if (instances.has(instanceId)) throw new Error(`Loop instance '${instanceId}' already exists.`);\n const entry: InstanceEntry = { instanceId, launcher, abortController: new AbortController(), state: 'starting' };\n instances.set(instanceId, entry);\n notify();\n try {\n const handle = await launcher.definition.launch({ instanceId, signal: entry.abortController.signal });\n const active = !disposed && instances.get(instanceId) === entry && !entry.abortController.signal.aborted;\n if (!handle) {\n if (instances.get(instanceId) === entry) instances.delete(instanceId);\n notify();\n return undefined;\n }\n if (!active || handle.instanceId !== instanceId) {\n await handle.stop(active ? 'Loop launcher returned a mismatched instance id.' : 'Loop launch was cancelled.');\n if (instances.get(instanceId) === entry) instances.delete(instanceId);\n notify();\n if (active) throw new Error(`Loop launcher '${normalizedLauncherId}' returned a mismatched instance id.`);\n return undefined;\n }\n entry.handle = handle;\n entry.state = 'running';\n entry.startedAt = timestamp();\n notify();\n return snapshot(entry);\n } catch (error) {\n const cancelled = entry.abortController.signal.aborted || disposed;\n if (instances.get(instanceId) === entry) instances.delete(instanceId);\n notify();\n if (cancelled) return undefined;\n throw error;\n }\n },\n async stop(instanceId, reason) {\n ensureActive();\n const entry = instances.get(validId(instanceId, INSTANCE_ID_LABEL));\n if (!entry) return false;\n await stopEntry(entry, reason);\n return true;\n },\n async stopAll(reason) {\n if (disposed && instances.size === 0) return;\n await Promise.allSettled([...instances.values()].map((entry) => stopEntry(entry, reason)));\n },\n async dispose(reason = 'Doom loop-launchers service disposed.') {\n if (disposed) return;\n disposed = true;\n launchers.clear();\n await Promise.allSettled([...instances.values()].map((entry) => stopEntry(entry, reason)));\n listeners.clear();\n },\n };\n\n return Object.freeze(service);\n}\n"],"mappings":"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"}
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
//#region src/services/loopLaunchers/index.ts
|
|
2
|
-
const MAX_ID_LENGTH = 256;
|
|
3
|
-
const MAX_GENERATION_LENGTH = 256;
|
|
4
|
-
const LAUNCHER_ID_LABEL = "loop launcher id";
|
|
5
|
-
const INSTANCE_ID_LABEL = "loop instance id";
|
|
6
|
-
function validId(value, label) {
|
|
7
|
-
const normalized = value.trim();
|
|
8
|
-
let hasControl = false;
|
|
9
|
-
for (const character of normalized) {
|
|
10
|
-
const code = character.codePointAt(0);
|
|
11
|
-
if (code !== void 0 && (code <= 31 || code === 127)) {
|
|
12
|
-
hasControl = true;
|
|
13
|
-
break;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
if (!normalized || normalized.length > MAX_ID_LENGTH || hasControl) throw new Error(`Invalid ${label}.`);
|
|
17
|
-
return normalized;
|
|
18
|
-
}
|
|
19
|
-
function snapshot(entry) {
|
|
20
|
-
return {
|
|
21
|
-
instanceId: entry.instanceId,
|
|
22
|
-
launcherId: entry.launcher.definition.id,
|
|
23
|
-
launcherLabel: entry.launcher.definition.label,
|
|
24
|
-
...entry.handle?.label ? { label: entry.handle.label } : {},
|
|
25
|
-
...entry.handle?.detail ? { detail: entry.handle.detail } : {},
|
|
26
|
-
state: entry.state,
|
|
27
|
-
...entry.startedAt ? { startedAt: entry.startedAt } : {}
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
/** Creates the provider-owned launcher registry for one active Doom session. */
|
|
31
|
-
function createDoomLoopLaunchersService({ generation, createInstanceId, timestamp }) {
|
|
32
|
-
if (!generation || generation.length > MAX_GENERATION_LENGTH) throw new TypeError("Doom loop launchers require a valid generation.");
|
|
33
|
-
const launchers = /* @__PURE__ */ new Map();
|
|
34
|
-
const instances = /* @__PURE__ */ new Map();
|
|
35
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
36
|
-
let registrationSequence = 0;
|
|
37
|
-
let disposed = false;
|
|
38
|
-
const ensureActive = () => {
|
|
39
|
-
if (disposed) throw new Error("The Doom loop-launchers service is disposed.");
|
|
40
|
-
};
|
|
41
|
-
const notify = () => {
|
|
42
|
-
for (const listener of listeners) listener();
|
|
43
|
-
};
|
|
44
|
-
const stopEntry = async (entry, reason) => {
|
|
45
|
-
if (instances.get(entry.instanceId) !== entry) return;
|
|
46
|
-
entry.state = "stopping";
|
|
47
|
-
entry.abortController.abort(reason);
|
|
48
|
-
notify();
|
|
49
|
-
try {
|
|
50
|
-
await entry.handle?.stop(reason);
|
|
51
|
-
} finally {
|
|
52
|
-
if (instances.get(entry.instanceId) === entry) instances.delete(entry.instanceId);
|
|
53
|
-
notify();
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
return Object.freeze({
|
|
57
|
-
generation,
|
|
58
|
-
register(definition) {
|
|
59
|
-
ensureActive();
|
|
60
|
-
const id = validId(definition.id, LAUNCHER_ID_LABEL);
|
|
61
|
-
if (launchers.has(id)) throw new Error(`Loop launcher '${id}' is already registered.`);
|
|
62
|
-
const token = Symbol(id);
|
|
63
|
-
const entry = {
|
|
64
|
-
token,
|
|
65
|
-
definition: {
|
|
66
|
-
...definition,
|
|
67
|
-
id
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
launchers.set(id, entry);
|
|
71
|
-
registrationSequence += 1;
|
|
72
|
-
notify();
|
|
73
|
-
let registrationDisposed = false;
|
|
74
|
-
return Object.freeze({
|
|
75
|
-
id,
|
|
76
|
-
generation: `${generation}:launcher:${registrationSequence}`,
|
|
77
|
-
async dispose(reason = "Loop launcher disposed.") {
|
|
78
|
-
if (registrationDisposed) return;
|
|
79
|
-
registrationDisposed = true;
|
|
80
|
-
if (launchers.get(id)?.token !== token) return;
|
|
81
|
-
launchers.delete(id);
|
|
82
|
-
const owned = [...instances.values()].filter((instance) => instance.launcher.token === token);
|
|
83
|
-
await Promise.allSettled(owned.map((instance) => stopEntry(instance, reason)));
|
|
84
|
-
notify();
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
},
|
|
88
|
-
listLaunchers() {
|
|
89
|
-
ensureActive();
|
|
90
|
-
return [...launchers.values()].map(({ definition }) => ({
|
|
91
|
-
id: definition.id,
|
|
92
|
-
source: definition.source,
|
|
93
|
-
label: definition.label,
|
|
94
|
-
...definition.description ? { description: definition.description } : {}
|
|
95
|
-
})).sort((left, right) => left.label.localeCompare(right.label) || left.id.localeCompare(right.id));
|
|
96
|
-
},
|
|
97
|
-
listInstances() {
|
|
98
|
-
ensureActive();
|
|
99
|
-
return [...instances.values()].map(snapshot).sort((left, right) => left.instanceId.localeCompare(right.instanceId));
|
|
100
|
-
},
|
|
101
|
-
subscribe(listener) {
|
|
102
|
-
ensureActive();
|
|
103
|
-
listeners.add(listener);
|
|
104
|
-
let subscribed = true;
|
|
105
|
-
return () => {
|
|
106
|
-
if (!subscribed) return;
|
|
107
|
-
subscribed = false;
|
|
108
|
-
listeners.delete(listener);
|
|
109
|
-
};
|
|
110
|
-
},
|
|
111
|
-
async launch(launcherId, options) {
|
|
112
|
-
ensureActive();
|
|
113
|
-
const normalizedLauncherId = validId(launcherId, LAUNCHER_ID_LABEL);
|
|
114
|
-
const launcher = launchers.get(normalizedLauncherId);
|
|
115
|
-
if (!launcher) throw new Error(`Loop launcher '${normalizedLauncherId}' is unavailable.`);
|
|
116
|
-
const instanceId = validId(options?.instanceId ?? createInstanceId(), INSTANCE_ID_LABEL);
|
|
117
|
-
if (instances.has(instanceId)) throw new Error(`Loop instance '${instanceId}' already exists.`);
|
|
118
|
-
const entry = {
|
|
119
|
-
instanceId,
|
|
120
|
-
launcher,
|
|
121
|
-
abortController: new AbortController(),
|
|
122
|
-
state: "starting"
|
|
123
|
-
};
|
|
124
|
-
instances.set(instanceId, entry);
|
|
125
|
-
notify();
|
|
126
|
-
try {
|
|
127
|
-
const handle = await launcher.definition.launch({
|
|
128
|
-
instanceId,
|
|
129
|
-
signal: entry.abortController.signal
|
|
130
|
-
});
|
|
131
|
-
const active = !disposed && instances.get(instanceId) === entry && !entry.abortController.signal.aborted;
|
|
132
|
-
if (!handle) {
|
|
133
|
-
if (instances.get(instanceId) === entry) instances.delete(instanceId);
|
|
134
|
-
notify();
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
if (!active || handle.instanceId !== instanceId) {
|
|
138
|
-
await handle.stop(active ? "Loop launcher returned a mismatched instance id." : "Loop launch was cancelled.");
|
|
139
|
-
if (instances.get(instanceId) === entry) instances.delete(instanceId);
|
|
140
|
-
notify();
|
|
141
|
-
if (active) throw new Error(`Loop launcher '${normalizedLauncherId}' returned a mismatched instance id.`);
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
entry.handle = handle;
|
|
145
|
-
entry.state = "running";
|
|
146
|
-
entry.startedAt = timestamp();
|
|
147
|
-
notify();
|
|
148
|
-
return snapshot(entry);
|
|
149
|
-
} catch (error) {
|
|
150
|
-
const cancelled = entry.abortController.signal.aborted || disposed;
|
|
151
|
-
if (instances.get(instanceId) === entry) instances.delete(instanceId);
|
|
152
|
-
notify();
|
|
153
|
-
if (cancelled) return void 0;
|
|
154
|
-
throw error;
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
async stop(instanceId, reason) {
|
|
158
|
-
ensureActive();
|
|
159
|
-
const entry = instances.get(validId(instanceId, INSTANCE_ID_LABEL));
|
|
160
|
-
if (!entry) return false;
|
|
161
|
-
await stopEntry(entry, reason);
|
|
162
|
-
return true;
|
|
163
|
-
},
|
|
164
|
-
async stopAll(reason) {
|
|
165
|
-
if (disposed && instances.size === 0) return;
|
|
166
|
-
await Promise.allSettled([...instances.values()].map((entry) => stopEntry(entry, reason)));
|
|
167
|
-
},
|
|
168
|
-
async dispose(reason = "Doom loop-launchers service disposed.") {
|
|
169
|
-
if (disposed) return;
|
|
170
|
-
disposed = true;
|
|
171
|
-
launchers.clear();
|
|
172
|
-
await Promise.allSettled([...instances.values()].map((entry) => stopEntry(entry, reason)));
|
|
173
|
-
listeners.clear();
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
//#endregion
|
|
178
|
-
export { createDoomLoopLaunchersService };
|
|
179
|
-
|
|
180
|
-
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/loopLaunchers/index.ts"],"sourcesContent":["import type {\n DoomLoopLaunchersService,\n LoopInstanceSnapshot,\n LoopLauncherDefinition,\n LoopLauncherRegistration,\n StoppableLoop,\n} from '../../schemas/loopLaunchers';\n\nconst MAX_ID_LENGTH = 256;\nconst MAX_GENERATION_LENGTH = 256;\nconst LAUNCHER_ID_LABEL = 'loop launcher id';\nconst INSTANCE_ID_LABEL = 'loop instance id';\n\ninterface LauncherEntry {\n readonly token: symbol;\n readonly definition: LoopLauncherDefinition;\n}\n\ninterface InstanceEntry {\n readonly instanceId: string;\n readonly launcher: LauncherEntry;\n readonly abortController: AbortController;\n state: LoopInstanceSnapshot['state'];\n handle?: StoppableLoop;\n startedAt?: string;\n}\n\nexport interface LoopLaunchersDependencies {\n readonly generation: string;\n readonly createInstanceId: () => string;\n readonly timestamp: () => string;\n}\n\nfunction validId(value: string, label: string): string {\n const normalized = value.trim();\n let hasControl = false;\n for (const character of normalized) {\n const code = character.codePointAt(0);\n if (code !== undefined && (code <= 0x1f || code === 0x7f)) {\n hasControl = true;\n break;\n }\n }\n if (!normalized || normalized.length > MAX_ID_LENGTH || hasControl) throw new Error(`Invalid ${label}.`);\n return normalized;\n}\n\nfunction snapshot(entry: InstanceEntry): LoopInstanceSnapshot {\n return {\n instanceId: entry.instanceId,\n launcherId: entry.launcher.definition.id,\n launcherLabel: entry.launcher.definition.label,\n ...(entry.handle?.label ? { label: entry.handle.label } : {}),\n ...(entry.handle?.detail ? { detail: entry.handle.detail } : {}),\n state: entry.state,\n ...(entry.startedAt ? { startedAt: entry.startedAt } : {}),\n };\n}\n\n/** Creates the provider-owned launcher registry for one active Doom session. */\nexport function createDoomLoopLaunchersService({\n generation,\n createInstanceId,\n timestamp,\n}: LoopLaunchersDependencies): DoomLoopLaunchersService {\n if (!generation || generation.length > MAX_GENERATION_LENGTH) {\n throw new TypeError('Doom loop launchers require a valid generation.');\n }\n\n const launchers = new Map<string, LauncherEntry>();\n const instances = new Map<string, InstanceEntry>();\n const listeners = new Set<() => void>();\n let registrationSequence = 0;\n let disposed = false;\n\n const ensureActive = (): void => {\n if (disposed) throw new Error('The Doom loop-launchers service is disposed.');\n };\n const notify = (): void => {\n for (const listener of listeners) listener();\n };\n const stopEntry = async (entry: InstanceEntry, reason?: string): Promise<void> => {\n if (instances.get(entry.instanceId) !== entry) return;\n entry.state = 'stopping';\n entry.abortController.abort(reason);\n notify();\n try {\n await entry.handle?.stop(reason);\n } finally {\n if (instances.get(entry.instanceId) === entry) instances.delete(entry.instanceId);\n notify();\n }\n };\n\n const service: DoomLoopLaunchersService = {\n generation,\n register(definition): LoopLauncherRegistration {\n ensureActive();\n const id = validId(definition.id, LAUNCHER_ID_LABEL);\n if (launchers.has(id)) throw new Error(`Loop launcher '${id}' is already registered.`);\n const token = Symbol(id);\n const entry: LauncherEntry = { token, definition: { ...definition, id } };\n launchers.set(id, entry);\n registrationSequence += 1;\n notify();\n let registrationDisposed = false;\n return Object.freeze({\n id,\n generation: `${generation}:launcher:${registrationSequence}`,\n async dispose(reason = 'Loop launcher disposed.'): Promise<void> {\n if (registrationDisposed) return;\n registrationDisposed = true;\n if (launchers.get(id)?.token !== token) return;\n launchers.delete(id);\n const owned = [...instances.values()].filter((instance) => instance.launcher.token === token);\n await Promise.allSettled(owned.map((instance) => stopEntry(instance, reason)));\n notify();\n },\n });\n },\n listLaunchers() {\n ensureActive();\n return [...launchers.values()]\n .map(({ definition }) => ({\n id: definition.id,\n source: definition.source,\n label: definition.label,\n ...(definition.description ? { description: definition.description } : {}),\n }))\n .sort((left, right) => left.label.localeCompare(right.label) || left.id.localeCompare(right.id));\n },\n listInstances() {\n ensureActive();\n return [...instances.values()]\n .map(snapshot)\n .sort((left, right) => left.instanceId.localeCompare(right.instanceId));\n },\n subscribe(listener) {\n ensureActive();\n listeners.add(listener);\n let subscribed = true;\n return () => {\n if (!subscribed) return;\n subscribed = false;\n listeners.delete(listener);\n };\n },\n async launch(launcherId, options) {\n ensureActive();\n const normalizedLauncherId = validId(launcherId, LAUNCHER_ID_LABEL);\n const launcher = launchers.get(normalizedLauncherId);\n if (!launcher) throw new Error(`Loop launcher '${normalizedLauncherId}' is unavailable.`);\n const instanceId = validId(options?.instanceId ?? createInstanceId(), INSTANCE_ID_LABEL);\n if (instances.has(instanceId)) throw new Error(`Loop instance '${instanceId}' already exists.`);\n const entry: InstanceEntry = { instanceId, launcher, abortController: new AbortController(), state: 'starting' };\n instances.set(instanceId, entry);\n notify();\n try {\n const handle = await launcher.definition.launch({ instanceId, signal: entry.abortController.signal });\n const active = !disposed && instances.get(instanceId) === entry && !entry.abortController.signal.aborted;\n if (!handle) {\n if (instances.get(instanceId) === entry) instances.delete(instanceId);\n notify();\n return undefined;\n }\n if (!active || handle.instanceId !== instanceId) {\n await handle.stop(active ? 'Loop launcher returned a mismatched instance id.' : 'Loop launch was cancelled.');\n if (instances.get(instanceId) === entry) instances.delete(instanceId);\n notify();\n if (active) throw new Error(`Loop launcher '${normalizedLauncherId}' returned a mismatched instance id.`);\n return undefined;\n }\n entry.handle = handle;\n entry.state = 'running';\n entry.startedAt = timestamp();\n notify();\n return snapshot(entry);\n } catch (error) {\n const cancelled = entry.abortController.signal.aborted || disposed;\n if (instances.get(instanceId) === entry) instances.delete(instanceId);\n notify();\n if (cancelled) return undefined;\n throw error;\n }\n },\n async stop(instanceId, reason) {\n ensureActive();\n const entry = instances.get(validId(instanceId, INSTANCE_ID_LABEL));\n if (!entry) return false;\n await stopEntry(entry, reason);\n return true;\n },\n async stopAll(reason) {\n if (disposed && instances.size === 0) return;\n await Promise.allSettled([...instances.values()].map((entry) => stopEntry(entry, reason)));\n },\n async dispose(reason = 'Doom loop-launchers service disposed.') {\n if (disposed) return;\n disposed = true;\n launchers.clear();\n await Promise.allSettled([...instances.values()].map((entry) => stopEntry(entry, reason)));\n listeners.clear();\n },\n };\n\n return Object.freeze(service);\n}\n"],"mappings":";AAQA,MAAM,gBAAgB;AACtB,MAAM,wBAAwB;AAC9B,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAsB1B,SAAS,QAAQ,OAAe,OAAuB;CACrD,MAAM,aAAa,MAAM,KAAK;CAC9B,IAAI,aAAa;CACjB,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,OAAO,UAAU,YAAY,CAAC;EACpC,IAAI,SAAS,KAAA,MAAc,QAAQ,MAAQ,SAAS,MAAO;GACzD,aAAa;GACb;EACF;CACF;CACA,IAAI,CAAC,cAAc,WAAW,SAAS,iBAAiB,YAAY,MAAM,IAAI,MAAM,WAAW,MAAM,EAAE;CACvG,OAAO;AACT;AAEA,SAAS,SAAS,OAA4C;CAC5D,OAAO;EACL,YAAY,MAAM;EAClB,YAAY,MAAM,SAAS,WAAW;EACtC,eAAe,MAAM,SAAS,WAAW;EACzC,GAAI,MAAM,QAAQ,QAAQ,EAAE,OAAO,MAAM,OAAO,MAAM,IAAI,CAAC;EAC3D,GAAI,MAAM,QAAQ,SAAS,EAAE,QAAQ,MAAM,OAAO,OAAO,IAAI,CAAC;EAC9D,OAAO,MAAM;EACb,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;CAC1D;AACF;;AAGA,SAAgB,+BAA+B,EAC7C,YACA,kBACA,aACsD;CACtD,IAAI,CAAC,cAAc,WAAW,SAAS,uBACrC,MAAM,IAAI,UAAU,iDAAiD;CAGvE,MAAM,4BAAY,IAAI,IAA2B;CACjD,MAAM,4BAAY,IAAI,IAA2B;CACjD,MAAM,4BAAY,IAAI,IAAgB;CACtC,IAAI,uBAAuB;CAC3B,IAAI,WAAW;CAEf,MAAM,qBAA2B;EAC/B,IAAI,UAAU,MAAM,IAAI,MAAM,8CAA8C;CAC9E;CACA,MAAM,eAAqB;EACzB,KAAK,MAAM,YAAY,WAAW,SAAS;CAC7C;CACA,MAAM,YAAY,OAAO,OAAsB,WAAmC;EAChF,IAAI,UAAU,IAAI,MAAM,UAAU,MAAM,OAAO;EAC/C,MAAM,QAAQ;EACd,MAAM,gBAAgB,MAAM,MAAM;EAClC,OAAO;EACP,IAAI;GACF,MAAM,MAAM,QAAQ,KAAK,MAAM;EACjC,UAAU;GACR,IAAI,UAAU,IAAI,MAAM,UAAU,MAAM,OAAO,UAAU,OAAO,MAAM,UAAU;GAChF,OAAO;EACT;CACF;CAiHA,OAAO,OAAO,OAAO;EA9GnB;EACA,SAAS,YAAsC;GAC7C,aAAa;GACb,MAAM,KAAK,QAAQ,WAAW,IAAI,iBAAiB;GACnD,IAAI,UAAU,IAAI,EAAE,GAAG,MAAM,IAAI,MAAM,kBAAkB,GAAG,yBAAyB;GACrF,MAAM,QAAQ,OAAO,EAAE;GACvB,MAAM,QAAuB;IAAE;IAAO,YAAY;KAAE,GAAG;KAAY;IAAG;GAAE;GACxE,UAAU,IAAI,IAAI,KAAK;GACvB,wBAAwB;GACxB,OAAO;GACP,IAAI,uBAAuB;GAC3B,OAAO,OAAO,OAAO;IACnB;IACA,YAAY,GAAG,WAAW,YAAY;IACtC,MAAM,QAAQ,SAAS,2BAA0C;KAC/D,IAAI,sBAAsB;KAC1B,uBAAuB;KACvB,IAAI,UAAU,IAAI,EAAE,CAAC,EAAE,UAAU,OAAO;KACxC,UAAU,OAAO,EAAE;KACnB,MAAM,QAAQ,CAAC,GAAG,UAAU,OAAO,CAAC,CAAC,CAAC,QAAQ,aAAa,SAAS,SAAS,UAAU,KAAK;KAC5F,MAAM,QAAQ,WAAW,MAAM,KAAK,aAAa,UAAU,UAAU,MAAM,CAAC,CAAC;KAC7E,OAAO;IACT;GACF,CAAC;EACH;EACA,gBAAgB;GACd,aAAa;GACb,OAAO,CAAC,GAAG,UAAU,OAAO,CAAC,CAAC,CAC3B,KAAK,EAAE,kBAAkB;IACxB,IAAI,WAAW;IACf,QAAQ,WAAW;IACnB,OAAO,WAAW;IAClB,GAAI,WAAW,cAAc,EAAE,aAAa,WAAW,YAAY,IAAI,CAAC;GAC1E,EAAE,CAAC,CACF,MAAM,MAAM,UAAU,KAAK,MAAM,cAAc,MAAM,KAAK,KAAK,KAAK,GAAG,cAAc,MAAM,EAAE,CAAC;EACnG;EACA,gBAAgB;GACd,aAAa;GACb,OAAO,CAAC,GAAG,UAAU,OAAO,CAAC,CAAC,CAC3B,IAAI,QAAQ,CAAC,CACb,MAAM,MAAM,UAAU,KAAK,WAAW,cAAc,MAAM,UAAU,CAAC;EAC1E;EACA,UAAU,UAAU;GAClB,aAAa;GACb,UAAU,IAAI,QAAQ;GACtB,IAAI,aAAa;GACjB,aAAa;IACX,IAAI,CAAC,YAAY;IACjB,aAAa;IACb,UAAU,OAAO,QAAQ;GAC3B;EACF;EACA,MAAM,OAAO,YAAY,SAAS;GAChC,aAAa;GACb,MAAM,uBAAuB,QAAQ,YAAY,iBAAiB;GAClE,MAAM,WAAW,UAAU,IAAI,oBAAoB;GACnD,IAAI,CAAC,UAAU,MAAM,IAAI,MAAM,kBAAkB,qBAAqB,kBAAkB;GACxF,MAAM,aAAa,QAAQ,SAAS,cAAc,iBAAiB,GAAG,iBAAiB;GACvF,IAAI,UAAU,IAAI,UAAU,GAAG,MAAM,IAAI,MAAM,kBAAkB,WAAW,kBAAkB;GAC9F,MAAM,QAAuB;IAAE;IAAY;IAAU,iBAAiB,IAAI,gBAAgB;IAAG,OAAO;GAAW;GAC/G,UAAU,IAAI,YAAY,KAAK;GAC/B,OAAO;GACP,IAAI;IACF,MAAM,SAAS,MAAM,SAAS,WAAW,OAAO;KAAE;KAAY,QAAQ,MAAM,gBAAgB;IAAO,CAAC;IACpG,MAAM,SAAS,CAAC,YAAY,UAAU,IAAI,UAAU,MAAM,SAAS,CAAC,MAAM,gBAAgB,OAAO;IACjG,IAAI,CAAC,QAAQ;KACX,IAAI,UAAU,IAAI,UAAU,MAAM,OAAO,UAAU,OAAO,UAAU;KACpE,OAAO;KACP;IACF;IACA,IAAI,CAAC,UAAU,OAAO,eAAe,YAAY;KAC/C,MAAM,OAAO,KAAK,SAAS,qDAAqD,4BAA4B;KAC5G,IAAI,UAAU,IAAI,UAAU,MAAM,OAAO,UAAU,OAAO,UAAU;KACpE,OAAO;KACP,IAAI,QAAQ,MAAM,IAAI,MAAM,kBAAkB,qBAAqB,qCAAqC;KACxG;IACF;IACA,MAAM,SAAS;IACf,MAAM,QAAQ;IACd,MAAM,YAAY,UAAU;IAC5B,OAAO;IACP,OAAO,SAAS,KAAK;GACvB,SAAS,OAAO;IACd,MAAM,YAAY,MAAM,gBAAgB,OAAO,WAAW;IAC1D,IAAI,UAAU,IAAI,UAAU,MAAM,OAAO,UAAU,OAAO,UAAU;IACpE,OAAO;IACP,IAAI,WAAW,OAAO,KAAA;IACtB,MAAM;GACR;EACF;EACA,MAAM,KAAK,YAAY,QAAQ;GAC7B,aAAa;GACb,MAAM,QAAQ,UAAU,IAAI,QAAQ,YAAY,iBAAiB,CAAC;GAClE,IAAI,CAAC,OAAO,OAAO;GACnB,MAAM,UAAU,OAAO,MAAM;GAC7B,OAAO;EACT;EACA,MAAM,QAAQ,QAAQ;GACpB,IAAI,YAAY,UAAU,SAAS,GAAG;GACtC,MAAM,QAAQ,WAAW,CAAC,GAAG,UAAU,OAAO,CAAC,CAAC,CAAC,KAAK,UAAU,UAAU,OAAO,MAAM,CAAC,CAAC;EAC3F;EACA,MAAM,QAAQ,SAAS,yCAAyC;GAC9D,IAAI,UAAU;GACd,WAAW;GACX,UAAU,MAAM;GAChB,MAAM,QAAQ,WAAW,CAAC,GAAG,UAAU,OAAO,CAAC,CAAC,CAAC,KAAK,UAAU,UAAU,OAAO,MAAM,CAAC,CAAC;GACzF,UAAU,MAAM;EAClB;CAGyB,CAAC;AAC9B"}
|