wicked-crew 0.5.0 → 0.7.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 (167) hide show
  1. package/dist/api/audit.d.ts +70 -0
  2. package/dist/api/audit.d.ts.map +1 -0
  3. package/dist/api/audit.js +131 -0
  4. package/dist/api/audit.js.map +1 -0
  5. package/dist/api/auth.d.ts +192 -0
  6. package/dist/api/auth.d.ts.map +1 -0
  7. package/dist/api/auth.js +515 -0
  8. package/dist/api/auth.js.map +1 -0
  9. package/dist/api/gate-cache.d.ts +8 -0
  10. package/dist/api/gate-cache.d.ts.map +1 -1
  11. package/dist/api/gate-cache.js +10 -0
  12. package/dist/api/gate-cache.js.map +1 -1
  13. package/dist/api/guidance-index.d.ts +39 -0
  14. package/dist/api/guidance-index.d.ts.map +1 -0
  15. package/dist/api/guidance-index.js +67 -0
  16. package/dist/api/guidance-index.js.map +1 -0
  17. package/dist/api/open-path.d.ts +34 -0
  18. package/dist/api/open-path.d.ts.map +1 -0
  19. package/dist/api/open-path.js +101 -0
  20. package/dist/api/open-path.js.map +1 -0
  21. package/dist/api/retry-index.d.ts +30 -0
  22. package/dist/api/retry-index.d.ts.map +1 -0
  23. package/dist/api/retry-index.js +45 -0
  24. package/dist/api/retry-index.js.map +1 -0
  25. package/dist/api/routes.d.ts +164 -1
  26. package/dist/api/routes.d.ts.map +1 -1
  27. package/dist/api/routes.js +769 -21
  28. package/dist/api/routes.js.map +1 -1
  29. package/dist/api/run-files.d.ts +63 -0
  30. package/dist/api/run-files.d.ts.map +1 -0
  31. package/dist/api/run-files.js +271 -0
  32. package/dist/api/run-files.js.map +1 -0
  33. package/dist/api/seat-health.d.ts +55 -0
  34. package/dist/api/seat-health.d.ts.map +1 -0
  35. package/dist/api/seat-health.js +273 -0
  36. package/dist/api/seat-health.js.map +1 -0
  37. package/dist/api/seat-signin.d.ts +27 -0
  38. package/dist/api/seat-signin.d.ts.map +1 -0
  39. package/dist/api/seat-signin.js +143 -0
  40. package/dist/api/seat-signin.js.map +1 -0
  41. package/dist/api/server.d.ts +190 -3
  42. package/dist/api/server.d.ts.map +1 -1
  43. package/dist/api/server.js +365 -11
  44. package/dist/api/server.js.map +1 -1
  45. package/dist/api/stall-watchdog.d.ts +62 -0
  46. package/dist/api/stall-watchdog.d.ts.map +1 -0
  47. package/dist/api/stall-watchdog.js +138 -0
  48. package/dist/api/stall-watchdog.js.map +1 -0
  49. package/dist/cli/index.js +171 -12
  50. package/dist/cli/index.js.map +1 -1
  51. package/dist/cli/mcp.d.ts +14 -0
  52. package/dist/cli/mcp.d.ts.map +1 -0
  53. package/dist/cli/mcp.js +119 -0
  54. package/dist/cli/mcp.js.map +1 -0
  55. package/dist/core/adapter.d.ts +87 -10
  56. package/dist/core/adapter.d.ts.map +1 -1
  57. package/dist/core/adapter.js +347 -28
  58. package/dist/core/adapter.js.map +1 -1
  59. package/dist/core/bridge-reaper.d.ts +134 -0
  60. package/dist/core/bridge-reaper.d.ts.map +1 -0
  61. package/dist/core/bridge-reaper.js +286 -0
  62. package/dist/core/bridge-reaper.js.map +1 -0
  63. package/dist/core/deliver.d.ts +118 -0
  64. package/dist/core/deliver.d.ts.map +1 -0
  65. package/dist/core/deliver.js +241 -0
  66. package/dist/core/deliver.js.map +1 -0
  67. package/dist/core/deliverable-floor.d.ts +103 -0
  68. package/dist/core/deliverable-floor.d.ts.map +1 -0
  69. package/dist/core/deliverable-floor.js +173 -0
  70. package/dist/core/deliverable-floor.js.map +1 -0
  71. package/dist/core/exec.d.ts +2 -0
  72. package/dist/core/exec.d.ts.map +1 -1
  73. package/dist/core/exec.js.map +1 -1
  74. package/dist/core/types.d.ts +92 -352
  75. package/dist/core/types.d.ts.map +1 -1
  76. package/dist/core/types.js +14 -4
  77. package/dist/core/types.js.map +1 -1
  78. package/dist/interactive/bridge-pool.d.ts +99 -0
  79. package/dist/interactive/bridge-pool.d.ts.map +1 -0
  80. package/dist/interactive/bridge-pool.js +244 -0
  81. package/dist/interactive/bridge-pool.js.map +1 -0
  82. package/dist/interactive/bridge-root.d.ts +36 -0
  83. package/dist/interactive/bridge-root.d.ts.map +1 -0
  84. package/dist/interactive/bridge-root.js +48 -0
  85. package/dist/interactive/bridge-root.js.map +1 -0
  86. package/dist/interactive/chat-events.d.ts +207 -0
  87. package/dist/interactive/chat-events.d.ts.map +1 -0
  88. package/dist/interactive/chat-events.js +769 -0
  89. package/dist/interactive/chat-events.js.map +1 -0
  90. package/dist/interactive/demo-events.d.ts +283 -0
  91. package/dist/interactive/demo-events.d.ts.map +1 -0
  92. package/dist/interactive/demo-events.js +889 -0
  93. package/dist/interactive/demo-events.js.map +1 -0
  94. package/dist/interactive/draft-events.d.ts +224 -0
  95. package/dist/interactive/draft-events.d.ts.map +1 -0
  96. package/dist/interactive/draft-events.js +793 -0
  97. package/dist/interactive/draft-events.js.map +1 -0
  98. package/dist/interactive/edit-events.d.ts +194 -0
  99. package/dist/interactive/edit-events.d.ts.map +1 -0
  100. package/dist/interactive/edit-events.js +601 -0
  101. package/dist/interactive/edit-events.js.map +1 -0
  102. package/dist/interactive/ledger.d.ts +39 -0
  103. package/dist/interactive/ledger.d.ts.map +1 -0
  104. package/dist/interactive/ledger.js +93 -0
  105. package/dist/interactive/ledger.js.map +1 -0
  106. package/dist/interactive/proxy-routes.d.ts +39 -0
  107. package/dist/interactive/proxy-routes.d.ts.map +1 -0
  108. package/dist/interactive/proxy-routes.js +189 -0
  109. package/dist/interactive/proxy-routes.js.map +1 -0
  110. package/dist/interactive/repo-snapshot.d.ts +100 -0
  111. package/dist/interactive/repo-snapshot.d.ts.map +1 -0
  112. package/dist/interactive/repo-snapshot.js +289 -0
  113. package/dist/interactive/repo-snapshot.js.map +1 -0
  114. package/dist/interactive/ws-relay.d.ts +85 -0
  115. package/dist/interactive/ws-relay.d.ts.map +1 -0
  116. package/dist/interactive/ws-relay.js +191 -0
  117. package/dist/interactive/ws-relay.js.map +1 -0
  118. package/dist/projects/activity.d.ts +29 -0
  119. package/dist/projects/activity.d.ts.map +1 -0
  120. package/dist/projects/activity.js +172 -0
  121. package/dist/projects/activity.js.map +1 -0
  122. package/dist/projects/charter.d.ts +28 -0
  123. package/dist/projects/charter.d.ts.map +1 -0
  124. package/dist/projects/charter.js +53 -0
  125. package/dist/projects/charter.js.map +1 -0
  126. package/dist/projects/events.d.ts +55 -0
  127. package/dist/projects/events.d.ts.map +1 -0
  128. package/dist/projects/events.js +141 -0
  129. package/dist/projects/events.js.map +1 -0
  130. package/dist/projects/graph-paths.d.ts +92 -0
  131. package/dist/projects/graph-paths.d.ts.map +1 -0
  132. package/dist/projects/graph-paths.js +130 -0
  133. package/dist/projects/graph-paths.js.map +1 -0
  134. package/dist/projects/graph.d.ts +179 -0
  135. package/dist/projects/graph.d.ts.map +1 -0
  136. package/dist/projects/graph.js +775 -0
  137. package/dist/projects/graph.js.map +1 -0
  138. package/dist/projects/membership-index.d.ts +25 -0
  139. package/dist/projects/membership-index.d.ts.map +1 -0
  140. package/dist/projects/membership-index.js +46 -0
  141. package/dist/projects/membership-index.js.map +1 -0
  142. package/dist/projects/routes.d.ts +97 -0
  143. package/dist/projects/routes.d.ts.map +1 -0
  144. package/dist/projects/routes.js +510 -0
  145. package/dist/projects/routes.js.map +1 -0
  146. package/dist/projects/settings.d.ts +32 -0
  147. package/dist/projects/settings.d.ts.map +1 -0
  148. package/dist/projects/settings.js +64 -0
  149. package/dist/projects/settings.js.map +1 -0
  150. package/dist/qe/acceptance.d.ts +137 -0
  151. package/dist/qe/acceptance.d.ts.map +1 -0
  152. package/dist/qe/acceptance.js +249 -0
  153. package/dist/qe/acceptance.js.map +1 -0
  154. package/dist/qe/gate-events.d.ts +111 -0
  155. package/dist/qe/gate-events.d.ts.map +1 -0
  156. package/dist/qe/gate-events.js +168 -0
  157. package/dist/qe/gate-events.js.map +1 -0
  158. package/dist/qe/ledger.d.ts +100 -0
  159. package/dist/qe/ledger.d.ts.map +1 -0
  160. package/dist/qe/ledger.js +154 -0
  161. package/dist/qe/ledger.js.map +1 -0
  162. package/dist/studio/assets/index-8p8uwCxG.js +530 -0
  163. package/dist/studio/assets/index-D6S9zUtO.css +32 -0
  164. package/dist/studio/index.html +5 -3
  165. package/package.json +10 -4
  166. package/dist/studio/assets/index-DaaUU8Ep.css +0 -32
  167. package/dist/studio/assets/index-Fu5DRC00.js +0 -423
@@ -0,0 +1,172 @@
1
+ /**
2
+ * The project activity feed (DES-PROJECT-001 §5.2) — a READ-SIDE MERGE, not a new store.
3
+ *
4
+ * Two sources, one normalized shape:
5
+ * - CREW: the durable core-event log of the project's `crew.run`/`crew.chat` members
6
+ * (`adapter.runEvents` — the same log `/runs/:id/events` serves), entry id `crew:<run>:<seq>`.
7
+ * - INTERACTIVE: `wicked.interactive.*` bus events carrying this `project_id`, read directly
8
+ * from the bus SQLite READ-ONLY (`node:sqlite`, the `requirements.ts` precedent — wicked-bus
9
+ * exposes subscribe-cursors, not ad-hoc history queries, and a durable cursor per feed-read
10
+ * would turn a GET into a write), entry id `bus:<event_id>`.
11
+ *
12
+ * Newest-first, cursor on `(ts, id)` — an opaque `<ts>:<id>` token, base64url. The merge is
13
+ * recomputed per read; members are few and the log excludes high-volume frames, so the simple
14
+ * full-merge is the honest v1 (the ADR explicitly rejects a new store here).
15
+ */
16
+ let sqliteMod;
17
+ async function sqlite() {
18
+ if (sqliteMod === undefined) {
19
+ try {
20
+ const name = 'node:sqlite';
21
+ sqliteMod = (await import(name));
22
+ }
23
+ catch {
24
+ sqliteMod = null;
25
+ }
26
+ }
27
+ return sqliteMod;
28
+ }
29
+ /** One-line human summary of a core frame (best-effort; `raw` carries the whole frame). */
30
+ function summarizeCoreEvent(frame, runId) {
31
+ const type = typeof frame['type'] === 'string' ? frame['type'] : 'event';
32
+ switch (type) {
33
+ case 'sessionStarted':
34
+ return `run ${runId} started`;
35
+ case 'awaitingHuman':
36
+ return `run ${runId} paused at a human gate${typeof frame['prompt'] === 'string' ? `: ${frame['prompt'].slice(0, 120)}` : ''}`;
37
+ case 'resumed':
38
+ return `run ${runId} resumed`;
39
+ case 'sessionCompleted':
40
+ return `run ${runId} completed`;
41
+ case 'sessionFailed':
42
+ return `run ${runId} failed`;
43
+ case 'runCancelled':
44
+ return `run ${runId} cancelled`;
45
+ case 'gateDecided':
46
+ return `run ${runId}: gate decided (${String(frame['allow'] ?? '?')})`;
47
+ default:
48
+ return `run ${runId}: ${type}`;
49
+ }
50
+ }
51
+ function summarizeInteractive(eventType, payload) {
52
+ const doc = payload !== null && typeof payload['document_id'] === 'string' ? payload['document_id'] : '?';
53
+ const short = eventType.replace(/^wicked\.interactive\./, '');
54
+ return `doc ${doc}: ${short}`;
55
+ }
56
+ /** Opaque cursor: base64url of `<ts>:<id>`. */
57
+ export function encodeCursor(ts, id) {
58
+ return Buffer.from(`${ts}:${id}`, 'utf8').toString('base64url');
59
+ }
60
+ export function decodeCursor(cursor) {
61
+ try {
62
+ const raw = Buffer.from(cursor, 'base64url').toString('utf8');
63
+ const sep = raw.indexOf(':');
64
+ if (sep < 1)
65
+ return null;
66
+ const ts = Number(raw.slice(0, sep));
67
+ if (!Number.isFinite(ts))
68
+ return null;
69
+ return { ts, id: raw.slice(sep + 1) };
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }
75
+ /** Newest-first ordering on `(ts, id)` — the cursor's total order. */
76
+ function newerFirst(a, b) {
77
+ return b.ts - a.ts || (a.id < b.id ? 1 : a.id > b.id ? -1 : 0);
78
+ }
79
+ /** Strictly older than the cursor position (the page the caller has not seen). */
80
+ function olderThan(entry, cur) {
81
+ return entry.ts < cur.ts || (entry.ts === cur.ts && entry.id < cur.id);
82
+ }
83
+ /** The crew half: fold each member run/chat's durable log into entries. */
84
+ async function crewEntries(adapter, refs) {
85
+ const entries = [];
86
+ for (const ref of refs) {
87
+ const events = await adapter.runEvents(ref);
88
+ if (events === null)
89
+ break; // no event-log binding on this addon — the crew half is empty
90
+ for (const event of events) {
91
+ const frame = event;
92
+ const ts = typeof frame['ts'] === 'number' ? frame['ts'] : 0;
93
+ const seq = typeof frame['seq'] === 'number' ? frame['seq'] : 0;
94
+ entries.push({
95
+ id: `crew:${ref}:${seq}`,
96
+ ts,
97
+ source: 'crew',
98
+ kind: typeof frame['type'] === 'string' ? frame['type'] : 'event',
99
+ ref,
100
+ summary: summarizeCoreEvent(frame, ref),
101
+ raw: event,
102
+ });
103
+ }
104
+ }
105
+ return entries;
106
+ }
107
+ /** The interactive half: bus events carrying this `project_id`, read-only. */
108
+ async function interactiveEntries(busDbPath, projectId) {
109
+ if (busDbPath === null)
110
+ return [];
111
+ const mod = await sqlite();
112
+ if (mod === null)
113
+ return [];
114
+ let db = null;
115
+ const entries = [];
116
+ try {
117
+ db = new mod.DatabaseSync(busDbPath, { readOnly: true });
118
+ const rows = db
119
+ .prepare(`SELECT event_id, event_type, payload, emitted_at FROM events
120
+ WHERE event_type LIKE 'wicked.interactive.%'
121
+ AND json_extract(payload, '$.project_id') = ?
122
+ ORDER BY emitted_at DESC LIMIT 1000`)
123
+ .all(projectId);
124
+ for (const row of rows) {
125
+ let payload = null;
126
+ try {
127
+ payload = JSON.parse(row.payload);
128
+ }
129
+ catch {
130
+ /* raw stays the string */
131
+ }
132
+ entries.push({
133
+ id: `bus:${row.event_id}`,
134
+ ts: row.emitted_at,
135
+ source: 'interactive',
136
+ kind: row.event_type,
137
+ ref: payload !== null && typeof payload['document_id'] === 'string'
138
+ ? payload['document_id']
139
+ : '',
140
+ summary: summarizeInteractive(row.event_type, payload),
141
+ raw: { event_type: row.event_type, payload: payload ?? row.payload, emitted_at: row.emitted_at },
142
+ });
143
+ }
144
+ }
145
+ catch {
146
+ // No bus db / schema mismatch — the interactive half is empty, never an error:
147
+ // a project with no bound docs must not 500 its activity feed.
148
+ return entries;
149
+ }
150
+ finally {
151
+ db?.close();
152
+ }
153
+ return entries;
154
+ }
155
+ /** Build one page of the merged feed. `refs` = the project's run/chat member refs. */
156
+ export async function buildActivityPage(adapter, projectId, refs, busDbPath, cursor, limit) {
157
+ const merged = [...(await crewEntries(adapter, refs)), ...(await interactiveEntries(busDbPath, projectId))];
158
+ merged.sort(newerFirst);
159
+ let page = merged;
160
+ if (cursor !== undefined) {
161
+ const decoded = decodeCursor(cursor);
162
+ if (decoded !== null)
163
+ page = merged.filter((e) => olderThan(e, decoded));
164
+ }
165
+ const entries = page.slice(0, limit);
166
+ const last = entries[entries.length - 1];
167
+ const nextCursor = entries.length === limit && page.length > limit && last !== undefined
168
+ ? encodeCursor(last.ts, last.id)
169
+ : null;
170
+ return { entries, nextCursor };
171
+ }
172
+ //# sourceMappingURL=activity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity.js","sourceRoot":"","sources":["../../src/projects/activity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The foundation record of a project (DES-PROJECT-001 §3.2) — thin crew glue, deliberately.
3
+ *
4
+ * On create/update/archive the daemon writes the PROJECT CHARTER (name, description, lifecycle
5
+ * note) into the engine's knowledge store under the project's stored scope, and a memory at that
6
+ * scope. On a project-bound run's completion it writes the EVIDENCE POINTER (run id → the
7
+ * conventional in-repo verdict path). Estate needs no schema change: `project:` is pure
8
+ * convention over the kind:id scope grammar.
9
+ *
10
+ * Everything here is BEST-EFFORT with retry-free logging: the record is eventually consistent
11
+ * with the state by design ("no cross-plane transactionality", ADR §10) — a knowledge-store
12
+ * hiccup must never fail the API call that already committed control state. And the plane rule
13
+ * holds in both directions: delete core.db and the charter + memories still say what this
14
+ * project was; delete the record and the project still operates, with amnesia.
15
+ */
16
+ import type { CoreAdapter } from '../core/adapter.js';
17
+ import type { Project } from '../core/types.js';
18
+ type Log = (msg: string) => void;
19
+ /** Write/refresh the charter after a committed create/update/archive. Fire-and-forget. */
20
+ export declare function writeCharter(adapter: CoreAdapter, project: Project, action: 'created' | 'updated' | 'archived' | 'restored', log: Log): Promise<void>;
21
+ /**
22
+ * Write the evidence pointer for a completed project-bound run (§3.2 row 3): the run id plus the
23
+ * conventional in-repo verdict path. The evidence FILES stay in-repo (QE convention) — the
24
+ * foundation holds pointers, never copies.
25
+ */
26
+ export declare function writeRunEvidencePointer(adapter: CoreAdapter, projectScope: string, runId: string, repoRoot: string | null, log: Log): Promise<void>;
27
+ export {};
28
+ //# sourceMappingURL=charter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"charter.d.ts","sourceRoot":"","sources":["../../src/projects/charter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;AAEjC,0FAA0F;AAC1F,wBAAsB,YAAY,CAChC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,EACvD,GAAG,EAAE,GAAG,GACP,OAAO,CAAC,IAAI,CAAC,CAsBf;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,WAAW,EACpB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,GAAG,EAAE,GAAG,GACP,OAAO,CAAC,IAAI,CAAC,CAiBf"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The foundation record of a project (DES-PROJECT-001 §3.2) — thin crew glue, deliberately.
3
+ *
4
+ * On create/update/archive the daemon writes the PROJECT CHARTER (name, description, lifecycle
5
+ * note) into the engine's knowledge store under the project's stored scope, and a memory at that
6
+ * scope. On a project-bound run's completion it writes the EVIDENCE POINTER (run id → the
7
+ * conventional in-repo verdict path). Estate needs no schema change: `project:` is pure
8
+ * convention over the kind:id scope grammar.
9
+ *
10
+ * Everything here is BEST-EFFORT with retry-free logging: the record is eventually consistent
11
+ * with the state by design ("no cross-plane transactionality", ADR §10) — a knowledge-store
12
+ * hiccup must never fail the API call that already committed control state. And the plane rule
13
+ * holds in both directions: delete core.db and the charter + memories still say what this
14
+ * project was; delete the record and the project still operates, with amnesia.
15
+ */
16
+ /** Write/refresh the charter after a committed create/update/archive. Fire-and-forget. */
17
+ export async function writeCharter(adapter, project, action, log) {
18
+ try {
19
+ const when = new Date(project.updated_at).toISOString();
20
+ const chunks = [
21
+ `Project charter — ${project.name} (${project.id}). Status: ${project.status}. ` +
22
+ `Scope: ${project.scope}. ${action} at ${when} via crew.`,
23
+ ...(project.description !== null && project.description !== undefined
24
+ ? [project.description]
25
+ : []),
26
+ ];
27
+ await adapter.ingestKnowledge(project.scope, chunks);
28
+ await adapter.captureMemory(`Project '${project.name}' (${project.id}) ${action}. Charter recorded in the knowledge store under ${project.scope}.`, project.scope);
29
+ }
30
+ catch (err) {
31
+ log(`[projects] charter write for ${project.id} failed (state committed; record lags): ${err instanceof Error ? err.message : String(err)}`);
32
+ }
33
+ }
34
+ /**
35
+ * Write the evidence pointer for a completed project-bound run (§3.2 row 3): the run id plus the
36
+ * conventional in-repo verdict path. The evidence FILES stay in-repo (QE convention) — the
37
+ * foundation holds pointers, never copies.
38
+ */
39
+ export async function writeRunEvidencePointer(adapter, projectScope, runId, repoRoot, log) {
40
+ try {
41
+ const scope = `${projectScope}/run:${runId}`;
42
+ const pointer = repoRoot !== null
43
+ ? `${repoRoot}/.wicked-testing/evidence/${runId}/verdict.json`
44
+ : `(repo-less run — no evidence tree)`;
45
+ await adapter.ingestKnowledge(scope, [
46
+ `Run ${runId} completed under ${projectScope}. Evidence pointer: ${pointer}`,
47
+ ]);
48
+ }
49
+ catch (err) {
50
+ log(`[projects] evidence-pointer write for ${runId} failed: ${err instanceof Error ? err.message : String(err)}`);
51
+ }
52
+ }
53
+ //# sourceMappingURL=charter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"charter.js","sourceRoot":"","sources":["../../src/projects/charter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,0FAA0F;AAC1F,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAoB,EACpB,OAAgB,EAChB,MAAuD,EACvD,GAAQ;IAER,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;QACxD,MAAM,MAAM,GAAG;YACb,qBAAqB,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,EAAE,cAAc,OAAO,CAAC,MAAM,IAAI;gBAC9E,UAAU,OAAO,CAAC,KAAK,KAAK,MAAM,OAAO,IAAI,YAAY;YAC3D,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,IAAI,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS;gBACnE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;gBACvB,CAAC,CAAC,EAAE,CAAC;SACR,CAAC;QACF,MAAM,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACrD,MAAM,OAAO,CAAC,aAAa,CACzB,YAAY,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC,EAAE,KAAK,MAAM,mDAAmD,OAAO,CAAC,KAAK,GAAG,EACtH,OAAO,CAAC,KAAK,CACd,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CACD,gCAAgC,OAAO,CAAC,EAAE,2CACxC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,OAAoB,EACpB,YAAoB,EACpB,KAAa,EACb,QAAuB,EACvB,GAAQ;IAER,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,GAAG,YAAY,QAAQ,KAAK,EAAE,CAAC;QAC7C,MAAM,OAAO,GACX,QAAQ,KAAK,IAAI;YACf,CAAC,CAAC,GAAG,QAAQ,6BAA6B,KAAK,eAAe;YAC9D,CAAC,CAAC,oCAAoC,CAAC;QAC3C,MAAM,OAAO,CAAC,eAAe,CAAC,KAAK,EAAE;YACnC,OAAO,KAAK,oBAAoB,YAAY,uBAAuB,OAAO,EAAE;SAC7E,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CACD,yCAAyC,KAAK,YAC5C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The project bus seam (DES-PROJECT-001 §4) — crew's side of the conversation.
3
+ *
4
+ * Two halves, one bus handle:
5
+ *
6
+ * 1. EMIT `wicked.crew.project.{created,updated,archived}` and
7
+ * `wicked.crew.membership.{attached,detached}` — always POST-COMMIT (the route handlers call
8
+ * these only after the engine's reply, so a phantom event for a write that never landed cannot
9
+ * exist). Idempotency keys are TYPE-INCLUSIVE (DES-EXEC-001 rev0.2 correction #4a — two event
10
+ * types sharing a key silently drop one on the bus's UNIQUE constraint) and occurrence-unique
11
+ * (an attach after a detach is a new fact, so the key carries the attach timestamp).
12
+ *
13
+ * 2. BRIDGE `wicked.interactive.*` events that carry a `project_id` onto the daemon's `/ws`
14
+ * stream as `projectActivity` frames, so an open project view sees creator-skin progress live
15
+ * without a reload (ADR §5.2 / §8 step 7). The durable feed read is `activity.ts`; this is
16
+ * only the liveness tap.
17
+ *
18
+ * Posture mirrors the QE/interactive-draft seams: the bus is OPTIONAL. A machine without
19
+ * wicked-bus (or with a broken db) gets a loudly-logged null — project CRUD keeps working, the
20
+ * events simply don't ride, and the daemon boots regardless.
21
+ */
22
+ /** Crew's bus DOMAIN COLUMN value — the product-scoped plugin name, matching the repo precedent
23
+ * (`INTERACTIVE_DOMAIN = 'wicked-interactive'`). The EVENT TYPES carry the §4 grammar's bare
24
+ * `crew` segment (`wicked.crew.project.created`); the two spellings are different fields. */
25
+ export declare const CREW_BUS_DOMAIN = "wicked-crew";
26
+ export declare const PROJECT_CREATED = "wicked.crew.project.created";
27
+ export declare const PROJECT_UPDATED = "wicked.crew.project.updated";
28
+ export declare const PROJECT_ARCHIVED = "wicked.crew.project.archived";
29
+ export declare const MEMBERSHIP_ATTACHED = "wicked.crew.membership.attached";
30
+ export declare const MEMBERSHIP_DETACHED = "wicked.crew.membership.detached";
31
+ export interface ProjectBus {
32
+ /** Emit one post-commit project event. Never throws; WB-002 (duplicate key) is success. */
33
+ emit(type: string, payload: Record<string, unknown>, idempotencyKey: string): boolean;
34
+ /** The resolved bus db path (the activity feed's read side opens the same file). */
35
+ dbPath: string | null;
36
+ stop(): Promise<void>;
37
+ }
38
+ export interface ProjectBusOptions {
39
+ /** Bus db path; omit for wicked-bus's own resolution (honors WICKED_BUS_DATA_DIR). */
40
+ dbPath?: string;
41
+ /** Poll cadence for the /ws bridge subscriber, ms (tests shorten it). */
42
+ pollIntervalMs?: number;
43
+ log?: (msg: string) => void;
44
+ }
45
+ /**
46
+ * Open the bus and arm both halves. Returns `null` (logged) when wicked-bus is not importable
47
+ * or the db cannot open — the caller degrades to CRUD-without-events.
48
+ */
49
+ export declare function startProjectBus(opts?: ProjectBusOptions): Promise<ProjectBus | null>;
50
+ export declare function projectCreatedKey(projectId: string): string;
51
+ export declare function projectUpdatedKey(projectId: string, updatedAt: number): string;
52
+ export declare function projectArchivedKey(projectId: string, updatedAt: number): string;
53
+ export declare function membershipAttachedKey(projectId: string, kind: string, ref: string, attachedAt: number): string;
54
+ export declare function membershipDetachedKey(projectId: string, memberId: string, at: number): string;
55
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/projects/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAKH;;8FAE8F;AAC9F,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAM7C,eAAO,MAAM,eAAe,gCAAgC,CAAC;AAC7D,eAAO,MAAM,eAAe,gCAAgC,CAAC;AAC7D,eAAO,MAAM,gBAAgB,iCAAiC,CAAC;AAC/D,eAAO,MAAM,mBAAmB,oCAAoC,CAAC;AACrE,eAAO,MAAM,mBAAmB,oCAAoC,CAAC;AAErE,MAAM,WAAW,UAAU;IACzB,2FAA2F;IAC3F,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;IACtF,oFAAoF;IACpF,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAgG9F;AAID,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3D;AACD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE9E;AACD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE/E;AACD,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GACjB,MAAM,CAER;AACD,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE7F"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * The project bus seam (DES-PROJECT-001 §4) — crew's side of the conversation.
3
+ *
4
+ * Two halves, one bus handle:
5
+ *
6
+ * 1. EMIT `wicked.crew.project.{created,updated,archived}` and
7
+ * `wicked.crew.membership.{attached,detached}` — always POST-COMMIT (the route handlers call
8
+ * these only after the engine's reply, so a phantom event for a write that never landed cannot
9
+ * exist). Idempotency keys are TYPE-INCLUSIVE (DES-EXEC-001 rev0.2 correction #4a — two event
10
+ * types sharing a key silently drop one on the bus's UNIQUE constraint) and occurrence-unique
11
+ * (an attach after a detach is a new fact, so the key carries the attach timestamp).
12
+ *
13
+ * 2. BRIDGE `wicked.interactive.*` events that carry a `project_id` onto the daemon's `/ws`
14
+ * stream as `projectActivity` frames, so an open project view sees creator-skin progress live
15
+ * without a reload (ADR §5.2 / §8 step 7). The durable feed read is `activity.ts`; this is
16
+ * only the liveness tap.
17
+ *
18
+ * Posture mirrors the QE/interactive-draft seams: the bus is OPTIONAL. A machine without
19
+ * wicked-bus (or with a broken db) gets a loudly-logged null — project CRUD keeps working, the
20
+ * events simply don't ride, and the daemon boots regardless.
21
+ */
22
+ import { broadcast } from '../events/bus.js';
23
+ /** Crew's bus DOMAIN COLUMN value — the product-scoped plugin name, matching the repo precedent
24
+ * (`INTERACTIVE_DOMAIN = 'wicked-interactive'`). The EVENT TYPES carry the §4 grammar's bare
25
+ * `crew` segment (`wicked.crew.project.created`); the two spellings are different fields. */
26
+ export const CREW_BUS_DOMAIN = 'wicked-crew';
27
+ const CREW_PRODUCER = 'wicked-crew';
28
+ /** The subscriber identity of the /ws liveness bridge. */
29
+ const BRIDGE_PLUGIN = 'wicked-crew-projects';
30
+ const INTERACTIVE_FILTER = 'wicked.interactive.**';
31
+ export const PROJECT_CREATED = 'wicked.crew.project.created';
32
+ export const PROJECT_UPDATED = 'wicked.crew.project.updated';
33
+ export const PROJECT_ARCHIVED = 'wicked.crew.project.archived';
34
+ export const MEMBERSHIP_ATTACHED = 'wicked.crew.membership.attached';
35
+ export const MEMBERSHIP_DETACHED = 'wicked.crew.membership.detached';
36
+ /**
37
+ * Open the bus and arm both halves. Returns `null` (logged) when wicked-bus is not importable
38
+ * or the db cannot open — the caller degrades to CRUD-without-events.
39
+ */
40
+ export async function startProjectBus(opts = {}) {
41
+ const log = opts.log ?? (() => undefined);
42
+ let bus;
43
+ try {
44
+ bus = await import('wicked-bus');
45
+ }
46
+ catch (err) {
47
+ log(`[projects] wicked-bus is not importable — project events disabled: ${err instanceof Error ? err.message : String(err)}`);
48
+ return null;
49
+ }
50
+ let db;
51
+ let config;
52
+ try {
53
+ config = bus.loadConfig(opts.dbPath !== undefined ? { db_path: opts.dbPath } : {});
54
+ db = bus.openDb(opts.dbPath !== undefined ? { db_path: opts.dbPath } : {});
55
+ }
56
+ catch (err) {
57
+ log(`[projects] could not open the bus db${opts.dbPath !== undefined ? ` at ${opts.dbPath}` : ''} — project events disabled: ${err instanceof Error ? err.message : String(err)}`);
58
+ return null;
59
+ }
60
+ function emit(type, payload, idempotencyKey) {
61
+ try {
62
+ bus.emit(db, config, {
63
+ event_type: type,
64
+ domain: CREW_BUS_DOMAIN,
65
+ subdomain: type.startsWith('wicked.crew.membership.') ? 'membership' : 'project',
66
+ payload: { ts: new Date().toISOString(), ...payload },
67
+ producer_id: CREW_PRODUCER,
68
+ idempotency_key: idempotencyKey,
69
+ });
70
+ return true;
71
+ }
72
+ catch (err) {
73
+ const code = err.error;
74
+ if (code === 'WB-002')
75
+ return true; // duplicate key — the emit already happened
76
+ log(`[projects] emit ${type} failed: ${err instanceof Error ? err.message : String(err)}`);
77
+ return false;
78
+ }
79
+ }
80
+ // The /ws liveness bridge: interactive events that name a project become `projectActivity`
81
+ // frames. Consumers that don't know the frame ignore it (additive CoreEvent contract).
82
+ let bridge = null;
83
+ try {
84
+ bridge = bus.subscribe({
85
+ db,
86
+ plugin: BRIDGE_PLUGIN,
87
+ filter: INTERACTIVE_FILTER,
88
+ cursor_init: 'latest',
89
+ pollIntervalMs: opts.pollIntervalMs ?? 2000,
90
+ // Fold-into-a-socket only; a retry loop / DLQ entry for a lost liveness frame is noise —
91
+ // the durable read (`/projects/:id/activity`) is the record, this is the tap.
92
+ maxRetries: 0,
93
+ handler: (event) => {
94
+ const payload = event.payload;
95
+ const projectId = payload !== null && typeof payload === 'object' ? payload['project_id'] : undefined;
96
+ if (typeof projectId !== 'string' || projectId === '')
97
+ return;
98
+ broadcast({
99
+ type: 'projectActivity',
100
+ project_id: projectId,
101
+ source: 'interactive',
102
+ event_type: event.event_type,
103
+ payload,
104
+ ts: event.emitted_at,
105
+ });
106
+ },
107
+ onError: (err, event) => {
108
+ log(`[projects] /ws bridge error on event ${String(event?.event_id ?? '?')}: ${err.message}`);
109
+ },
110
+ });
111
+ }
112
+ catch (err) {
113
+ log(`[projects] could not arm the /ws activity bridge (events still emit): ${err instanceof Error ? err.message : String(err)}`);
114
+ }
115
+ const dbPath = typeof config['db_path'] === 'string' ? config['db_path'] : null;
116
+ return {
117
+ emit,
118
+ dbPath,
119
+ stop: async () => {
120
+ if (bridge !== null)
121
+ await bridge.stop();
122
+ },
123
+ };
124
+ }
125
+ // ── Idempotency keys (type-inclusive + occurrence-unique) ───────────────────────
126
+ export function projectCreatedKey(projectId) {
127
+ return `crew:project.created:${projectId}:v1`;
128
+ }
129
+ export function projectUpdatedKey(projectId, updatedAt) {
130
+ return `crew:project.updated:${projectId}:${updatedAt}`;
131
+ }
132
+ export function projectArchivedKey(projectId, updatedAt) {
133
+ return `crew:project.archived:${projectId}:${updatedAt}`;
134
+ }
135
+ export function membershipAttachedKey(projectId, kind, ref, attachedAt) {
136
+ return `crew:membership.attached:${projectId}:${kind}:${ref}:${attachedAt}`;
137
+ }
138
+ export function membershipDetachedKey(projectId, memberId, at) {
139
+ return `crew:membership.detached:${projectId}:${memberId}:${at}`;
140
+ }
141
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/projects/events.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Where a PROJECT's co-located code graph lives, and what each member repo is called inside it.
3
+ *
4
+ * # Why a module instead of a `join()` at each call site
5
+ *
6
+ * This is `repoPaths.ts`'s posture applied one level up (see that module's header for the bill:
7
+ * five spellings of the per-repo graph path against a sixth inside wicked-core, and every graph
8
+ * query answering "nothing found" about a repo full of code). One producer, one consumer. Nothing
9
+ * outside this module spells a project-graph path or mints a repo label.
10
+ *
11
+ * # Why NOT inside a repo checkout
12
+ *
13
+ * The per-repo graph lives at `<repo>/.codegraph/estate.db` — inside the working tree — and we have
14
+ * just spent the effort removing that directory from six checkouts it had polluted. A PROJECT graph
15
+ * is worse on that axis, not better: it holds N repos, so there is no one checkout it belongs to,
16
+ * and writing it into the first member's tree would make repo A's working directory grow with repo
17
+ * B's symbols. It goes in the daemon's own state directory.
18
+ *
19
+ * # Why `~/.wicked-crew/project-graphs/<projectId>/`
20
+ *
21
+ * `~/.wicked-crew/` is already where this package keeps crew-side per-project state that the engine
22
+ * does not own — `project-settings.json` (DES-MERGE-001 §7.1) sits there today. The project graph is
23
+ * exactly that kind of thing: crew-owned, per-project, derived, and rebuildable. A directory per
24
+ * project (rather than `<projectId>.db` files in one flat folder) keeps the db and the refresh
25
+ * manifest that describes it together, so removing a project's graph is one `rm -rf` that cannot
26
+ * leave a manifest describing a database that is gone.
27
+ *
28
+ * Overridable with `WICKED_CREW_PROJECT_GRAPH_ROOT` — the same escape hatch
29
+ * `WICKED_CREW_PROJECT_SETTINGS` gives the settings store, and what lets the tests and the proof
30
+ * scripts run without touching a developer's real home.
31
+ */
32
+ /**
33
+ * The estate label charset, copied from the rule that enforces it
34
+ * (`wicked-estate/src/repo_scope.rs::validate_label`): 1–64 chars of `[A-Za-z0-9._-]`, and never
35
+ * `.` or `..`.
36
+ *
37
+ * The constraint is not cosmetic and not ours to relax. estate splices the label into `files.path`
38
+ * AND into every SymbolId derived from it, so a `/` or a `..` in a label would forge paths inside
39
+ * another repo's namespace — the exact collision the whole labelling mechanism exists to prevent.
40
+ * A label that fails this test is refused by estate before a single row is written; we mint labels
41
+ * that pass it instead of discovering that at index time.
42
+ */
43
+ export declare function isValidRepoLabel(label: string): boolean;
44
+ /**
45
+ * The label one registered repo carries inside a project graph — its REGISTRY ID when that id is
46
+ * already a legal estate label, and a deterministic sanitized form when it is not.
47
+ *
48
+ * WHY the registry id. The label becomes a path prefix on every row the repo writes and shows up in
49
+ * every result this surface returns, so it has to be a name an operator can map back to a repo
50
+ * without a lookup table. It also has to be STABLE: estate binds a label to a repo permanently
51
+ * (re-indexing under a second label is refused as a duplicate; re-using a label for a different repo
52
+ * is refused as a collision), so a label derived from anything mutable — the repo's display name,
53
+ * its root path, its position in the member list — would break the next refresh after a rename or a
54
+ * move. The registry id is the one identifier that is both stable and already unique per repo.
55
+ *
56
+ * Every repo id in the live registry today (`wicked-ledger`, `wicked-vault`, `pageindex-proof`, …)
57
+ * satisfies estate's rule as-is, so the common path is the identity function and the label IS the
58
+ * id. That is not guaranteed for ids minted later, so the fallback is real rather than theoretical:
59
+ * illegal characters collapse to `-`, the stem is capped, and a 12-hex digest of the FULL id is
60
+ * appended — the digest is what keeps `a/b` and `a:b` (which sanitize identically) apart, and what
61
+ * keeps a truncated 200-character id distinct from another that shares its first 51 characters.
62
+ *
63
+ * The mapping is a pure function of the id, which is what makes it safe against estate's permanent
64
+ * binding: the same repo resolves to the same label on every refresh, for the life of the graph.
65
+ */
66
+ export declare function repoLabel(repoId: string): string;
67
+ /**
68
+ * Project ids are REJECTED, never sanitized.
69
+ *
70
+ * A sanitizing map is what would let two distinct project ids land in one directory and share one
71
+ * database — the same class of silent collision that made a multi-repo graph impossible in the first
72
+ * place, reintroduced at the project level. It is also the traversal guard: the id is a path
73
+ * segment, so `../../x` must never be spelled into a path. Engine-minted ids (`proj_1787…`) and the
74
+ * synthesized `default` both pass; anything that does not is a bug worth hearing about.
75
+ */
76
+ export declare function assertProjectIdIsPathSafe(projectId: string): void;
77
+ /** The root every project graph directory hangs off. */
78
+ export declare function projectGraphRoot(env?: NodeJS.ProcessEnv): string;
79
+ /** One project's graph directory — holds the database and the manifest that describes it. */
80
+ export declare function projectGraphDir(projectId: string, env?: NodeJS.ProcessEnv): string;
81
+ /** The ONE database holding every member repo of this project, co-located under their labels. */
82
+ export declare function projectGraphDb(projectId: string, env?: NodeJS.ProcessEnv): string;
83
+ /**
84
+ * What the last refresh put in that database, per repo — the record incremental refresh reads.
85
+ *
86
+ * Deliberately a SIDECAR rather than a read of estate's own `repo:<label>:commit` meta: estate
87
+ * exposes that only through `stats`, whose output is human text with no `--json`, and deciding
88
+ * whether to re-index a repo by regex-matching a report is the kind of coupling that breaks on a
89
+ * formatting change with no test to catch it.
90
+ */
91
+ export declare function projectGraphManifest(projectId: string, env?: NodeJS.ProcessEnv): string;
92
+ //# sourceMappingURL=graph-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-paths.d.ts","sourceRoot":"","sources":["../../src/projects/graph-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAMH;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAQvD;AAMD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAcjE;AAED,wDAAwD;AACxD,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE7E;AAED,6FAA6F;AAC7F,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAG/F;AAED,iGAAiG;AACjG,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE9F;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAEpG"}