@db-lyon/flowkit 0.11.2 → 0.13.0

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 (38) hide show
  1. package/README.md +386 -375
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/flow/runner.d.ts +23 -2
  4. package/dist/flow/runner.d.ts.map +1 -1
  5. package/dist/flow/runner.js +89 -29
  6. package/dist/flow/runner.js.map +1 -1
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +2 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/references.d.ts +41 -0
  12. package/dist/references.d.ts.map +1 -0
  13. package/dist/references.js +110 -0
  14. package/dist/references.js.map +1 -0
  15. package/dist/task/agent-task.d.ts.map +1 -1
  16. package/dist/task/agent-task.js +1 -7
  17. package/dist/task/agent-task.js.map +1 -1
  18. package/dist/task/base-task.d.ts +28 -3
  19. package/dist/task/base-task.d.ts.map +1 -1
  20. package/dist/task/base-task.js +13 -4
  21. package/dist/task/base-task.js.map +1 -1
  22. package/dist/task/shell-task.d.ts +2 -0
  23. package/dist/task/shell-task.d.ts.map +1 -1
  24. package/dist/task/shell-task.js +186 -40
  25. package/dist/task/shell-task.js.map +1 -1
  26. package/dist/task/shell-termination.d.ts +34 -0
  27. package/dist/task/shell-termination.d.ts.map +1 -0
  28. package/dist/task/shell-termination.js +179 -0
  29. package/dist/task/shell-termination.js.map +1 -0
  30. package/dist/task/task-resolution.d.ts +33 -0
  31. package/dist/task/task-resolution.d.ts.map +1 -0
  32. package/dist/task/task-resolution.js +34 -0
  33. package/dist/task/task-resolution.js.map +1 -0
  34. package/docs/ai-agents.md +368 -368
  35. package/docs/api-reference.md +458 -430
  36. package/docs/configuration.md +347 -343
  37. package/docs/custom-tasks.md +258 -203
  38. package/package.json +53 -52
@@ -0,0 +1,179 @@
1
+ import { spawn } from 'node:child_process';
2
+ export const POSIX_SIGTERM_GRACE_MS = 250;
3
+ export const POSIX_TERMINATION_GRACE_MS = 1_000;
4
+ export const WINDOWS_TERMINATION_GRACE_MS = 3_000;
5
+ export function terminationGraceMs(platform) {
6
+ return platform === 'win32' ? WINDOWS_TERMINATION_GRACE_MS : POSIX_TERMINATION_GRACE_MS;
7
+ }
8
+ /**
9
+ * Start termination for one shell invocation.
10
+ *
11
+ * This module is deliberately internal. Passing the platform and process
12
+ * functions as dependencies makes the policy testable on every CI platform
13
+ * without changing ShellTask's public API or relying on global mutable state.
14
+ */
15
+ export function beginShellTermination(child, dependencies = {}) {
16
+ const platform = dependencies.platform ?? process.platform;
17
+ const spawnProcess = dependencies.spawnProcess ?? spawn;
18
+ const killProcessGroup = dependencies.killProcessGroup ??
19
+ ((pid, signal) => {
20
+ process.kill(pid, signal);
21
+ });
22
+ let active = true;
23
+ let escalated = false;
24
+ let rootForceRequested = false;
25
+ let treeKiller;
26
+ let treeKillerFinished = false;
27
+ let treeKillComplete = platform !== 'win32' || child.pid === undefined;
28
+ const treeKillListeners = new Set();
29
+ const markTreeKillComplete = () => {
30
+ if (treeKillComplete)
31
+ return;
32
+ treeKillComplete = true;
33
+ for (const listener of treeKillListeners)
34
+ listener();
35
+ treeKillListeners.clear();
36
+ };
37
+ const killRoot = (signal) => {
38
+ if (!active)
39
+ return;
40
+ if (signal === 'SIGKILL' && rootForceRequested)
41
+ return;
42
+ try {
43
+ const sent = child.kill(signal);
44
+ if (signal === 'SIGKILL' && sent)
45
+ rootForceRequested = true;
46
+ }
47
+ catch {
48
+ // The shell may have closed between the terminal event and this call.
49
+ }
50
+ };
51
+ const killGroup = (signal) => {
52
+ if (!active || child.pid === undefined) {
53
+ killRoot(signal);
54
+ return;
55
+ }
56
+ try {
57
+ killProcessGroup(-child.pid, signal);
58
+ }
59
+ catch {
60
+ // A missing process group or unsupported group signal falls back to the
61
+ // direct shell. This cannot promise termination of escaped descendants.
62
+ killRoot(signal);
63
+ }
64
+ };
65
+ const stopTreeKiller = () => {
66
+ if (!treeKiller || treeKillerFinished)
67
+ return;
68
+ // Retain an error handler because a forced helper close may race an error.
69
+ treeKiller.once('error', () => undefined);
70
+ try {
71
+ treeKiller.kill('SIGKILL');
72
+ }
73
+ catch {
74
+ // The helper may already be exiting.
75
+ }
76
+ try {
77
+ treeKiller.unref();
78
+ }
79
+ catch {
80
+ // Releasing the task result must not be defeated by an unref race.
81
+ }
82
+ treeKillerFinished = true;
83
+ };
84
+ if (platform === 'win32' && child.pid !== undefined) {
85
+ try {
86
+ treeKiller = spawnProcess('taskkill', ['/pid', String(child.pid), '/T', '/F'], { stdio: 'ignore', windowsHide: true });
87
+ const killOnError = () => {
88
+ if (!active)
89
+ return;
90
+ killRoot('SIGKILL');
91
+ markTreeKillComplete();
92
+ };
93
+ treeKiller.once('error', killOnError);
94
+ treeKiller.once('close', (code) => {
95
+ if (!active)
96
+ return;
97
+ treeKillerFinished = true;
98
+ if (code !== 0)
99
+ killRoot('SIGKILL');
100
+ markTreeKillComplete();
101
+ });
102
+ }
103
+ catch {
104
+ killRoot('SIGKILL');
105
+ markTreeKillComplete();
106
+ }
107
+ }
108
+ else {
109
+ // Cooperative first phase. ShellTask schedules the bounded SIGKILL
110
+ // escalation so normal commands can flush and clean up.
111
+ killGroup('SIGTERM');
112
+ }
113
+ const escalate = () => {
114
+ if (!active || escalated)
115
+ return;
116
+ escalated = true;
117
+ if (platform === 'win32') {
118
+ killRoot('SIGKILL');
119
+ stopTreeKiller();
120
+ return;
121
+ }
122
+ killGroup('SIGKILL');
123
+ };
124
+ const dispose = () => {
125
+ if (!active)
126
+ return;
127
+ treeKillListeners.clear();
128
+ try {
129
+ stopTreeKiller();
130
+ }
131
+ finally {
132
+ active = false;
133
+ }
134
+ };
135
+ const release = () => {
136
+ if (!active)
137
+ return;
138
+ treeKillListeners.clear();
139
+ escalate();
140
+ try {
141
+ child.stdout?.destroy();
142
+ }
143
+ catch {
144
+ // The stream may have closed between escalation and release.
145
+ }
146
+ try {
147
+ child.stderr?.destroy();
148
+ }
149
+ catch {
150
+ // The stream may have closed between escalation and release.
151
+ }
152
+ try {
153
+ child.unref();
154
+ }
155
+ catch {
156
+ // A bounded result still settles if Node rejects an unref race.
157
+ }
158
+ try {
159
+ stopTreeKiller();
160
+ }
161
+ finally {
162
+ active = false;
163
+ }
164
+ };
165
+ return {
166
+ platform,
167
+ isTreeKillComplete: () => treeKillComplete,
168
+ onTreeKillComplete: (listener) => {
169
+ if (treeKillComplete)
170
+ listener();
171
+ else
172
+ treeKillListeners.add(listener);
173
+ },
174
+ escalate,
175
+ release,
176
+ dispose,
177
+ };
178
+ }
179
+ //# sourceMappingURL=shell-termination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-termination.js","sourceRoot":"","sources":["../../src/task/shell-termination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAwC,MAAM,oBAAoB,CAAC;AAEjF,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAC1C,MAAM,CAAC,MAAM,0BAA0B,GAAG,KAAK,CAAC;AAChD,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAC;AA4BlD,MAAM,UAAU,kBAAkB,CAAC,QAAyB;IAC1D,OAAO,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,0BAA0B,CAAC;AAC1F,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAmB,EACnB,eAA6C,EAAE;IAE/C,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IAC3D,MAAM,YAAY,GAAG,YAAY,CAAC,YAAY,IAAK,KAAsB,CAAC;IAC1E,MAAM,gBAAgB,GACpB,YAAY,CAAC,gBAAgB;QAC7B,CAAC,CAAC,GAAW,EAAE,MAAsB,EAAE,EAAE;YACvC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IAEL,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,kBAAkB,GAAG,KAAK,CAAC;IAC/B,IAAI,UAAoC,CAAC;IACzC,IAAI,kBAAkB,GAAG,KAAK,CAAC;IAC/B,IAAI,gBAAgB,GAAG,QAAQ,KAAK,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC;IACvE,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAc,CAAC;IAEhD,MAAM,oBAAoB,GAAG,GAAG,EAAE;QAChC,IAAI,gBAAgB;YAAE,OAAO;QAC7B,gBAAgB,GAAG,IAAI,CAAC;QACxB,KAAK,MAAM,QAAQ,IAAI,iBAAiB;YAAE,QAAQ,EAAE,CAAC;QACrD,iBAAiB,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,CAAC,MAAsB,EAAE,EAAE;QAC1C,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,MAAM,KAAK,SAAS,IAAI,kBAAkB;YAAE,OAAO;QACvD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,MAAM,KAAK,SAAS,IAAI,IAAI;gBAAE,kBAAkB,GAAG,IAAI,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,sEAAsE;QACxE,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,CAAC,MAAsB,EAAE,EAAE;QAC3C,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YACvC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACjB,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,gBAAgB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,wEAAwE;YACxE,wEAAwE;YACxE,QAAQ,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,cAAc,GAAG,GAAG,EAAE;QAC1B,IAAI,CAAC,UAAU,IAAI,kBAAkB;YAAE,OAAO;QAC9C,2EAA2E;QAC3E,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC1C,IAAI,CAAC;YACH,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,qCAAqC;QACvC,CAAC;QACD,IAAI,CAAC;YACH,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,mEAAmE;QACrE,CAAC;QACD,kBAAkB,GAAG,IAAI,CAAC;IAC5B,CAAC,CAAC;IAEF,IAAI,QAAQ,KAAK,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QACpD,IAAI,CAAC;YACH,UAAU,GAAG,YAAY,CACvB,UAAU,EACV,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,EACvC,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CACvC,CAAC;YACF,MAAM,WAAW,GAAG,GAAG,EAAE;gBACvB,IAAI,CAAC,MAAM;oBAAE,OAAO;gBACpB,QAAQ,CAAC,SAAS,CAAC,CAAC;gBACpB,oBAAoB,EAAE,CAAC;YACzB,CAAC,CAAC;YACF,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;YACtC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;gBAChC,IAAI,CAAC,MAAM;oBAAE,OAAO;gBACpB,kBAAkB,GAAG,IAAI,CAAC;gBAC1B,IAAI,IAAI,KAAK,CAAC;oBAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;gBACpC,oBAAoB,EAAE,CAAC;YACzB,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,QAAQ,CAAC,SAAS,CAAC,CAAC;YACpB,oBAAoB,EAAE,CAAC;QACzB,CAAC;IACH,CAAC;SAAM,CAAC;QACN,mEAAmE;QACnE,wDAAwD;QACxD,SAAS,CAAC,SAAS,CAAC,CAAC;IACvB,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,EAAE;QACpB,IAAI,CAAC,MAAM,IAAI,SAAS;YAAE,OAAO;QACjC,SAAS,GAAG,IAAI,CAAC;QACjB,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YACzB,QAAQ,CAAC,SAAS,CAAC,CAAC;YACpB,cAAc,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,SAAS,CAAC,SAAS,CAAC,CAAC;IACvB,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,cAAc,EAAE,CAAC;QACnB,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,KAAK,CAAC;QACjB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAC1B,QAAQ,EAAE,CAAC;QACX,IAAI,CAAC;YACH,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,6DAA6D;QAC/D,CAAC;QACD,IAAI,CAAC;YACH,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,6DAA6D;QAC/D,CAAC;QACD,IAAI,CAAC;YACH,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACP,gEAAgE;QAClE,CAAC;QACD,IAAI,CAAC;YACH,cAAc,EAAE,CAAC;QACnB,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,KAAK,CAAC;QACjB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACL,QAAQ;QACR,kBAAkB,EAAE,GAAG,EAAE,CAAC,gBAAgB;QAC1C,kBAAkB,EAAE,CAAC,QAAQ,EAAE,EAAE;YAC/B,IAAI,gBAAgB;gBAAE,QAAQ,EAAE,CAAC;;gBAC5B,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACvC,CAAC;QACD,QAAQ;QACR,OAAO;QACP,OAAO;KACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { TaskDefinition } from '../config/schema.js';
2
+ import { type ReferenceContext } from '../references.js';
3
+ /**
4
+ * A configured task name resolved to the class the registry loads and the
5
+ * options it is instantiated with.
6
+ */
7
+ export interface ResolvedTask {
8
+ classPath: string;
9
+ options: Record<string, unknown>;
10
+ }
11
+ /**
12
+ * Resolve a configured task name to its class path and default options.
13
+ *
14
+ * An option-only entry (no `class_path`) layers options onto a base of the same
15
+ * name; absent a base class_path, it resolves by the task name itself. A name
16
+ * with no definition at all resolves to itself, so a bare class path still
17
+ * works outside — or alongside — a configuration.
18
+ */
19
+ export declare function resolveTaskDefinition(taskName: string, taskDefinitions?: Record<string, TaskDefinition>): ResolvedTask;
20
+ /**
21
+ * Resolve a call to a configured task: its class path, plus call-time `options`
22
+ * layered over the task's configured defaults.
23
+ *
24
+ * `references` interpolates `${ns.path}` in the **configured defaults only**.
25
+ * `options` are runtime data — a rollback payload, an agent's tool arguments,
26
+ * values one task computed for another — and are merged in verbatim, so a
27
+ * literal `${steps.x}` in runtime data reaches the task unchanged rather than
28
+ * being interpreted as configuration (or throwing, out of a step's scope).
29
+ * Callers whose call-time options *are* configuration resolve them themselves
30
+ * before calling; see `FlowRunner.runTask`.
31
+ */
32
+ export declare function resolveTaskCall(taskName: string, taskDefinitions: Record<string, TaskDefinition> | undefined, options?: Record<string, unknown>, references?: ReferenceContext): ResolvedTask;
33
+ //# sourceMappingURL=task-resolution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-resolution.d.ts","sourceRoot":"","sources":["../../src/task/task-resolution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAE5E;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAC/C,YAAY,CAMd;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,SAAS,EAC3D,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,UAAU,CAAC,EAAE,gBAAgB,GAC5B,YAAY,CAId"}
@@ -0,0 +1,34 @@
1
+ import { resolveReferences } from '../references.js';
2
+ /**
3
+ * Resolve a configured task name to its class path and default options.
4
+ *
5
+ * An option-only entry (no `class_path`) layers options onto a base of the same
6
+ * name; absent a base class_path, it resolves by the task name itself. A name
7
+ * with no definition at all resolves to itself, so a bare class path still
8
+ * works outside — or alongside — a configuration.
9
+ */
10
+ export function resolveTaskDefinition(taskName, taskDefinitions) {
11
+ const def = taskDefinitions?.[taskName];
12
+ return {
13
+ classPath: def?.class_path ?? taskName,
14
+ options: def?.options ?? {},
15
+ };
16
+ }
17
+ /**
18
+ * Resolve a call to a configured task: its class path, plus call-time `options`
19
+ * layered over the task's configured defaults.
20
+ *
21
+ * `references` interpolates `${ns.path}` in the **configured defaults only**.
22
+ * `options` are runtime data — a rollback payload, an agent's tool arguments,
23
+ * values one task computed for another — and are merged in verbatim, so a
24
+ * literal `${steps.x}` in runtime data reaches the task unchanged rather than
25
+ * being interpreted as configuration (or throwing, out of a step's scope).
26
+ * Callers whose call-time options *are* configuration resolve them themselves
27
+ * before calling; see `FlowRunner.runTask`.
28
+ */
29
+ export function resolveTaskCall(taskName, taskDefinitions, options = {}, references) {
30
+ const def = resolveTaskDefinition(taskName, taskDefinitions);
31
+ const defaults = references ? resolveReferences(def.options, references) : def.options;
32
+ return { classPath: def.classPath, options: { ...defaults, ...options } };
33
+ }
34
+ //# sourceMappingURL=task-resolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-resolution.js","sourceRoot":"","sources":["../../src/task/task-resolution.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAyB,MAAM,kBAAkB,CAAC;AAW5E;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACnC,QAAgB,EAChB,eAAgD;IAEhD,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC,QAAQ,CAAC,CAAC;IACxC,OAAO;QACL,SAAS,EAAE,GAAG,EAAE,UAAU,IAAI,QAAQ;QACtC,OAAO,EAAE,GAAG,EAAE,OAAO,IAAI,EAAE;KAC5B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAgB,EAChB,eAA2D,EAC3D,UAAmC,EAAE,EACrC,UAA6B;IAE7B,MAAM,GAAG,GAAG,qBAAqB,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;IAC7D,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC;IACvF,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC;AAC5E,CAAC"}