wicked-crew 0.4.0 → 0.6.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.
- package/dist/api/api-prefix.d.ts +13 -0
- package/dist/api/api-prefix.d.ts.map +1 -0
- package/dist/api/api-prefix.js +13 -0
- package/dist/api/api-prefix.js.map +1 -0
- package/dist/api/audit.d.ts +57 -0
- package/dist/api/audit.d.ts.map +1 -0
- package/dist/api/audit.js +115 -0
- package/dist/api/audit.js.map +1 -0
- package/dist/api/auth.d.ts +192 -0
- package/dist/api/auth.d.ts.map +1 -0
- package/dist/api/auth.js +515 -0
- package/dist/api/auth.js.map +1 -0
- package/dist/api/evidence.d.ts +11 -0
- package/dist/api/evidence.d.ts.map +1 -1
- package/dist/api/evidence.js +8 -3
- package/dist/api/evidence.js.map +1 -1
- package/dist/api/gate-cache.d.ts +8 -0
- package/dist/api/gate-cache.d.ts.map +1 -1
- package/dist/api/gate-cache.js +10 -0
- package/dist/api/gate-cache.js.map +1 -1
- package/dist/api/open-path.d.ts +18 -0
- package/dist/api/open-path.d.ts.map +1 -0
- package/dist/api/open-path.js +79 -0
- package/dist/api/open-path.js.map +1 -0
- package/dist/api/routes.d.ts +113 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +516 -27
- package/dist/api/routes.js.map +1 -1
- package/dist/api/seat-health.d.ts +55 -0
- package/dist/api/seat-health.d.ts.map +1 -0
- package/dist/api/seat-health.js +273 -0
- package/dist/api/seat-health.js.map +1 -0
- package/dist/api/seat-signin.d.ts +27 -0
- package/dist/api/seat-signin.d.ts.map +1 -0
- package/dist/api/seat-signin.js +143 -0
- package/dist/api/seat-signin.js.map +1 -0
- package/dist/api/server.d.ts +111 -3
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +236 -11
- package/dist/api/server.js.map +1 -1
- package/dist/api/unit-output.d.ts +62 -0
- package/dist/api/unit-output.d.ts.map +1 -0
- package/dist/api/unit-output.js +108 -0
- package/dist/api/unit-output.js.map +1 -0
- package/dist/cli/index.js +97 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp.d.ts +14 -0
- package/dist/cli/mcp.d.ts.map +1 -0
- package/dist/cli/mcp.js +119 -0
- package/dist/cli/mcp.js.map +1 -0
- package/dist/core/adapter.d.ts +106 -1
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +293 -12
- package/dist/core/adapter.js.map +1 -1
- package/dist/core/bridge-path.d.ts +17 -6
- package/dist/core/bridge-path.d.ts.map +1 -1
- package/dist/core/bridge-path.js +17 -6
- package/dist/core/bridge-path.js.map +1 -1
- package/dist/core/types.d.ts +23 -352
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +11 -4
- package/dist/core/types.js.map +1 -1
- package/dist/interactive/bridge-pool.d.ts +71 -0
- package/dist/interactive/bridge-pool.d.ts.map +1 -0
- package/dist/interactive/bridge-pool.js +187 -0
- package/dist/interactive/bridge-pool.js.map +1 -0
- package/dist/interactive/bridge-root.d.ts +36 -0
- package/dist/interactive/bridge-root.d.ts.map +1 -0
- package/dist/interactive/bridge-root.js +48 -0
- package/dist/interactive/bridge-root.js.map +1 -0
- package/dist/interactive/draft-events.d.ts +144 -0
- package/dist/interactive/draft-events.d.ts.map +1 -0
- package/dist/interactive/draft-events.js +490 -0
- package/dist/interactive/draft-events.js.map +1 -0
- package/dist/interactive/edit-events.d.ts +172 -0
- package/dist/interactive/edit-events.d.ts.map +1 -0
- package/dist/interactive/edit-events.js +530 -0
- package/dist/interactive/edit-events.js.map +1 -0
- package/dist/interactive/ledger.d.ts +39 -0
- package/dist/interactive/ledger.d.ts.map +1 -0
- package/dist/interactive/ledger.js +93 -0
- package/dist/interactive/ledger.js.map +1 -0
- package/dist/interactive/proxy-routes.d.ts +39 -0
- package/dist/interactive/proxy-routes.d.ts.map +1 -0
- package/dist/interactive/proxy-routes.js +189 -0
- package/dist/interactive/proxy-routes.js.map +1 -0
- package/dist/interactive/ws-relay.d.ts +85 -0
- package/dist/interactive/ws-relay.d.ts.map +1 -0
- package/dist/interactive/ws-relay.js +191 -0
- package/dist/interactive/ws-relay.js.map +1 -0
- package/dist/projects/activity.d.ts +29 -0
- package/dist/projects/activity.d.ts.map +1 -0
- package/dist/projects/activity.js +172 -0
- package/dist/projects/activity.js.map +1 -0
- package/dist/projects/charter.d.ts +28 -0
- package/dist/projects/charter.d.ts.map +1 -0
- package/dist/projects/charter.js +53 -0
- package/dist/projects/charter.js.map +1 -0
- package/dist/projects/events.d.ts +55 -0
- package/dist/projects/events.d.ts.map +1 -0
- package/dist/projects/events.js +141 -0
- package/dist/projects/events.js.map +1 -0
- package/dist/projects/membership-index.d.ts +25 -0
- package/dist/projects/membership-index.d.ts.map +1 -0
- package/dist/projects/membership-index.js +46 -0
- package/dist/projects/membership-index.js.map +1 -0
- package/dist/projects/routes.d.ts +90 -0
- package/dist/projects/routes.d.ts.map +1 -0
- package/dist/projects/routes.js +388 -0
- package/dist/projects/routes.js.map +1 -0
- package/dist/projects/settings.d.ts +32 -0
- package/dist/projects/settings.d.ts.map +1 -0
- package/dist/projects/settings.js +64 -0
- package/dist/projects/settings.js.map +1 -0
- package/dist/qe/acceptance.d.ts +137 -0
- package/dist/qe/acceptance.d.ts.map +1 -0
- package/dist/qe/acceptance.js +249 -0
- package/dist/qe/acceptance.js.map +1 -0
- package/dist/qe/gate-events.d.ts +111 -0
- package/dist/qe/gate-events.d.ts.map +1 -0
- package/dist/qe/gate-events.js +168 -0
- package/dist/qe/gate-events.js.map +1 -0
- package/dist/qe/ledger.d.ts +100 -0
- package/dist/qe/ledger.d.ts.map +1 -0
- package/dist/qe/ledger.js +154 -0
- package/dist/qe/ledger.js.map +1 -0
- package/dist/studio/assets/index-CCwXa1cn.js +428 -0
- package/dist/studio/assets/index-HWxo0h41.css +32 -0
- package/dist/studio/index.html +2 -2
- package/package.json +10 -4
- package/dist/studio/assets/index-Ci_R6ARr.js +0 -423
- package/dist/studio/assets/index-Dio_c1Q3.css +0 -32
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
import type { CoreAdapter } from '../core/adapter.js';
|
|
17
|
+
import type { ActivityEntry } from '../core/types.js';
|
|
18
|
+
/** Opaque cursor: base64url of `<ts>:<id>`. */
|
|
19
|
+
export declare function encodeCursor(ts: number, id: string): string;
|
|
20
|
+
export declare function decodeCursor(cursor: string): {
|
|
21
|
+
ts: number;
|
|
22
|
+
id: string;
|
|
23
|
+
} | null;
|
|
24
|
+
/** Build one page of the merged feed. `refs` = the project's run/chat member refs. */
|
|
25
|
+
export declare function buildActivityPage(adapter: CoreAdapter, projectId: string, refs: string[], busDbPath: string | null, cursor: string | undefined, limit: number): Promise<{
|
|
26
|
+
entries: ActivityEntry[];
|
|
27
|
+
nextCursor: string | null;
|
|
28
|
+
}>;
|
|
29
|
+
//# sourceMappingURL=activity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../../src/projects/activity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAuDtD,+CAA+C;AAC/C,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAW9E;AAsFD,sFAAsF;AACtF,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;IAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAgBlE"}
|
|
@@ -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,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The run→project index the daemon tags `/ws` frames with (DES-PROJECT-001 §5.2: "the daemon
|
|
3
|
+
* tags frames with `project_id` from the membership table" — no new socket, no engine change).
|
|
4
|
+
*
|
|
5
|
+
* A latency layer, NOT truth: the membership table in core.db is authoritative; this map exists
|
|
6
|
+
* because the tag is applied on the synchronous event fan-out path where an engine read per frame
|
|
7
|
+
* is unaffordable. It hydrates once from the engine at server start and is updated by exactly the
|
|
8
|
+
* route handlers that change memberships (launch-with-projectId, attach, detach) — the same
|
|
9
|
+
* post-commit points that emit the bus events, so the map can only lag by a failed hydrate, never
|
|
10
|
+
* diverge silently.
|
|
11
|
+
*/
|
|
12
|
+
import type { CoreAdapter } from '../core/adapter.js';
|
|
13
|
+
export declare class MembershipIndex {
|
|
14
|
+
private readonly runToProject;
|
|
15
|
+
/**
|
|
16
|
+
* Load every live `crew.run`/`crew.chat` membership from the engine. Best-effort: a pre-0.6.0
|
|
17
|
+
* addon (no project surface) leaves the index empty — frames simply go untagged, which is the
|
|
18
|
+
* pre-projects behavior, not an error.
|
|
19
|
+
*/
|
|
20
|
+
hydrate(adapter: CoreAdapter, log?: (msg: string) => void): Promise<void>;
|
|
21
|
+
set(runOrChatId: string, projectId: string): void;
|
|
22
|
+
delete(runOrChatId: string): void;
|
|
23
|
+
projectOf(runOrChatId: string): string | undefined;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=membership-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"membership-index.d.ts","sourceRoot":"","sources":["../../src/projects/membership-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAE1D;;;;OAIG;IACG,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB/E,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAIjD,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAIjC,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;CAGnD"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The run→project index the daemon tags `/ws` frames with (DES-PROJECT-001 §5.2: "the daemon
|
|
3
|
+
* tags frames with `project_id` from the membership table" — no new socket, no engine change).
|
|
4
|
+
*
|
|
5
|
+
* A latency layer, NOT truth: the membership table in core.db is authoritative; this map exists
|
|
6
|
+
* because the tag is applied on the synchronous event fan-out path where an engine read per frame
|
|
7
|
+
* is unaffordable. It hydrates once from the engine at server start and is updated by exactly the
|
|
8
|
+
* route handlers that change memberships (launch-with-projectId, attach, detach) — the same
|
|
9
|
+
* post-commit points that emit the bus events, so the map can only lag by a failed hydrate, never
|
|
10
|
+
* diverge silently.
|
|
11
|
+
*/
|
|
12
|
+
export class MembershipIndex {
|
|
13
|
+
runToProject = new Map();
|
|
14
|
+
/**
|
|
15
|
+
* Load every live `crew.run`/`crew.chat` membership from the engine. Best-effort: a pre-0.6.0
|
|
16
|
+
* addon (no project surface) leaves the index empty — frames simply go untagged, which is the
|
|
17
|
+
* pre-projects behavior, not an error.
|
|
18
|
+
*/
|
|
19
|
+
async hydrate(adapter, log) {
|
|
20
|
+
if (!adapter.projectsSupported())
|
|
21
|
+
return;
|
|
22
|
+
try {
|
|
23
|
+
const projects = await adapter.projectList();
|
|
24
|
+
for (const project of projects) {
|
|
25
|
+
for (const member of await adapter.projectMembers(project.id)) {
|
|
26
|
+
if (member.member_kind === 'crew.run' || member.member_kind === 'crew.chat') {
|
|
27
|
+
this.runToProject.set(member.member_ref, project.id);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
log?.(`[projects] membership-index hydrate failed (frames go untagged until the next write): ${err instanceof Error ? err.message : String(err)}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
set(runOrChatId, projectId) {
|
|
37
|
+
this.runToProject.set(runOrChatId, projectId);
|
|
38
|
+
}
|
|
39
|
+
delete(runOrChatId) {
|
|
40
|
+
this.runToProject.delete(runOrChatId);
|
|
41
|
+
}
|
|
42
|
+
projectOf(runOrChatId) {
|
|
43
|
+
return this.runToProject.get(runOrChatId);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=membership-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"membership-index.js","sourceRoot":"","sources":["../../src/projects/membership-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,OAAO,eAAe;IACT,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE1D;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,OAAoB,EAAE,GAA2B;QAC7D,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE;YAAE,OAAO;QACzC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;YAC7C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,KAAK,MAAM,MAAM,IAAI,MAAM,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;oBAC9D,IAAI,MAAM,CAAC,WAAW,KAAK,UAAU,IAAI,MAAM,CAAC,WAAW,KAAK,WAAW,EAAE,CAAC;wBAC5E,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;oBACvD,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,EAAE,CACH,yFACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;QACJ,CAAC;IACH,CAAC;IAED,GAAG,CAAC,WAAmB,EAAE,SAAiB;QACxC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,CAAC,WAAmB;QACxB,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACxC,CAAC;IAED,SAAS,CAAC,WAAmB;QAC3B,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC5C,CAAC;CACF"}
|