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.
- package/dist/api/audit.d.ts +70 -0
- package/dist/api/audit.d.ts.map +1 -0
- package/dist/api/audit.js +131 -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/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/guidance-index.d.ts +39 -0
- package/dist/api/guidance-index.d.ts.map +1 -0
- package/dist/api/guidance-index.js +67 -0
- package/dist/api/guidance-index.js.map +1 -0
- package/dist/api/open-path.d.ts +34 -0
- package/dist/api/open-path.d.ts.map +1 -0
- package/dist/api/open-path.js +101 -0
- package/dist/api/open-path.js.map +1 -0
- package/dist/api/retry-index.d.ts +30 -0
- package/dist/api/retry-index.d.ts.map +1 -0
- package/dist/api/retry-index.js +45 -0
- package/dist/api/retry-index.js.map +1 -0
- package/dist/api/routes.d.ts +164 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +769 -21
- package/dist/api/routes.js.map +1 -1
- package/dist/api/run-files.d.ts +63 -0
- package/dist/api/run-files.d.ts.map +1 -0
- package/dist/api/run-files.js +271 -0
- package/dist/api/run-files.js.map +1 -0
- 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 +190 -3
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +365 -11
- package/dist/api/server.js.map +1 -1
- package/dist/api/stall-watchdog.d.ts +62 -0
- package/dist/api/stall-watchdog.d.ts.map +1 -0
- package/dist/api/stall-watchdog.js +138 -0
- package/dist/api/stall-watchdog.js.map +1 -0
- package/dist/cli/index.js +171 -12
- 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 +87 -10
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +347 -28
- package/dist/core/adapter.js.map +1 -1
- package/dist/core/bridge-reaper.d.ts +134 -0
- package/dist/core/bridge-reaper.d.ts.map +1 -0
- package/dist/core/bridge-reaper.js +286 -0
- package/dist/core/bridge-reaper.js.map +1 -0
- package/dist/core/deliver.d.ts +118 -0
- package/dist/core/deliver.d.ts.map +1 -0
- package/dist/core/deliver.js +241 -0
- package/dist/core/deliver.js.map +1 -0
- package/dist/core/deliverable-floor.d.ts +103 -0
- package/dist/core/deliverable-floor.d.ts.map +1 -0
- package/dist/core/deliverable-floor.js +173 -0
- package/dist/core/deliverable-floor.js.map +1 -0
- package/dist/core/exec.d.ts +2 -0
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js.map +1 -1
- package/dist/core/types.d.ts +92 -352
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +14 -4
- package/dist/core/types.js.map +1 -1
- package/dist/interactive/bridge-pool.d.ts +99 -0
- package/dist/interactive/bridge-pool.d.ts.map +1 -0
- package/dist/interactive/bridge-pool.js +244 -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/chat-events.d.ts +207 -0
- package/dist/interactive/chat-events.d.ts.map +1 -0
- package/dist/interactive/chat-events.js +769 -0
- package/dist/interactive/chat-events.js.map +1 -0
- package/dist/interactive/demo-events.d.ts +283 -0
- package/dist/interactive/demo-events.d.ts.map +1 -0
- package/dist/interactive/demo-events.js +889 -0
- package/dist/interactive/demo-events.js.map +1 -0
- package/dist/interactive/draft-events.d.ts +224 -0
- package/dist/interactive/draft-events.d.ts.map +1 -0
- package/dist/interactive/draft-events.js +793 -0
- package/dist/interactive/draft-events.js.map +1 -0
- package/dist/interactive/edit-events.d.ts +194 -0
- package/dist/interactive/edit-events.d.ts.map +1 -0
- package/dist/interactive/edit-events.js +601 -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/repo-snapshot.d.ts +100 -0
- package/dist/interactive/repo-snapshot.d.ts.map +1 -0
- package/dist/interactive/repo-snapshot.js +289 -0
- package/dist/interactive/repo-snapshot.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/graph-paths.d.ts +92 -0
- package/dist/projects/graph-paths.d.ts.map +1 -0
- package/dist/projects/graph-paths.js +130 -0
- package/dist/projects/graph-paths.js.map +1 -0
- package/dist/projects/graph.d.ts +179 -0
- package/dist/projects/graph.d.ts.map +1 -0
- package/dist/projects/graph.js +775 -0
- package/dist/projects/graph.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 +97 -0
- package/dist/projects/routes.d.ts.map +1 -0
- package/dist/projects/routes.js +510 -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-8p8uwCxG.js +530 -0
- package/dist/studio/assets/index-D6S9zUtO.css +32 -0
- package/dist/studio/index.html +5 -3
- package/package.json +10 -4
- package/dist/studio/assets/index-DaaUU8Ep.css +0 -32
- package/dist/studio/assets/index-Fu5DRC00.js +0 -423
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/api/v1/projects` — the complete v1 surface of DES-PROJECT-001 §5.2 (9 routes):
|
|
3
|
+
*
|
|
4
|
+
* POST /projects create {name, description?}
|
|
5
|
+
* GET /projects list (includes the synthesized `default`; ?status=)
|
|
6
|
+
* GET /projects/:id detail + members
|
|
7
|
+
* PATCH /projects/:id rename / describe / archive / restore
|
|
8
|
+
* GET /projects/:id/members list members
|
|
9
|
+
* POST /projects/:id/members attach {kind, ref, meta?}
|
|
10
|
+
* DELETE /projects/:id/members/:mid detach
|
|
11
|
+
* GET /projects/:id/activity merged feed, cursor-paginated
|
|
12
|
+
* GET /projects/:id/prompts open interaction requests across member runs
|
|
13
|
+
*
|
|
14
|
+
* …plus the project code graph (projects/graph.ts) — the repo-scoped graph trio, one scope up:
|
|
15
|
+
*
|
|
16
|
+
* GET /projects/:id/graph what the co-located graph holds, and what it cannot answer
|
|
17
|
+
* POST /projects/:id/graph/refresh incremental (re)build over every `crew.repo` member
|
|
18
|
+
* GET /projects/:id/graph/blast-radius ?name= — dependents across member repos, each attributed
|
|
19
|
+
* GET /projects/:id/graph/search ?name= — symbol resolution across member repos
|
|
20
|
+
*
|
|
21
|
+
* Route-layer rules the engine cannot own:
|
|
22
|
+
* - The `default` project ("Unfiled") is SYNTHESIZED here (ADR §7): its members are the runs
|
|
23
|
+
* with no explicit membership row; it lists, feeds, and prompts, and rejects PATCH/attach.
|
|
24
|
+
* - `crew.*` member refs are existence-checked here at attach time (404 on unknown run/repo) —
|
|
25
|
+
* never by the engine, and never with foreign keys (members may outlive what they point to).
|
|
26
|
+
* - Bus events emit POST-COMMIT from these handlers with type-inclusive idempotency keys (§4).
|
|
27
|
+
* - Engine error → status mapping: "not registered" 404 · "already in use"/"archived"/"default"
|
|
28
|
+
* 409 · anything else 400. A pre-0.6.0 addon answers 501 (ProjectsUnsupportedError), never 400.
|
|
29
|
+
*/
|
|
30
|
+
import { z } from 'zod';
|
|
31
|
+
import { ProjectsUnsupportedError } from '../core/adapter.js';
|
|
32
|
+
import { API_PREFIX } from '../api/api-prefix.js';
|
|
33
|
+
import { MEMBERSHIP_ATTACHED, MEMBERSHIP_DETACHED, PROJECT_ARCHIVED, PROJECT_CREATED, PROJECT_UPDATED, membershipAttachedKey, membershipDetachedKey, projectArchivedKey, projectCreatedKey, projectUpdatedKey, } from './events.js';
|
|
34
|
+
import { ProjectSettingsStore } from './settings.js';
|
|
35
|
+
import { buildActivityPage } from './activity.js';
|
|
36
|
+
import { CO_LOCATION_NOTE, ProjectGraphEngineTooOldError, projectBlastRadius, projectGraphStatus, projectSymbolSearch, queryable, refreshProjectGraph, } from './graph.js';
|
|
37
|
+
import { writeCharter } from './charter.js';
|
|
38
|
+
import { AuditLog } from '../api/audit.js';
|
|
39
|
+
import { LOCAL_ACTOR } from '../api/auth.js';
|
|
40
|
+
const V = API_PREFIX;
|
|
41
|
+
/** The reserved, synthesized "Unfiled" project id (ADR §1.1/§7). */
|
|
42
|
+
export const DEFAULT_PROJECT_ID = 'default';
|
|
43
|
+
// Exported for tests/wire-contract.test.ts (the request-direction drift guard).
|
|
44
|
+
export const CreateProjectSchema = z
|
|
45
|
+
.object({
|
|
46
|
+
name: z.string().min(1).max(120),
|
|
47
|
+
description: z.string().optional(),
|
|
48
|
+
})
|
|
49
|
+
.strict();
|
|
50
|
+
export const UpdateProjectSchema = z
|
|
51
|
+
.object({
|
|
52
|
+
name: z.string().min(1).max(120).optional(),
|
|
53
|
+
description: z.string().optional(),
|
|
54
|
+
status: z.enum(['active', 'archived']).optional(),
|
|
55
|
+
/** DES-MERGE-001 §7.1 — the project's wicked-interactive docs root. Explicitly NULLABLE:
|
|
56
|
+
* null clears the binding back to the shared default root, which is a different act from
|
|
57
|
+
* omitting the key (leave it alone). Stored crew-side (see settings.ts), not in the engine. */
|
|
58
|
+
interactiveRoot: z.string().min(1).nullable().optional(),
|
|
59
|
+
})
|
|
60
|
+
.strict();
|
|
61
|
+
/** `<product>.<noun>` — the engine re-validates; this is the 400-with-names layer. */
|
|
62
|
+
export const AttachMemberSchema = z
|
|
63
|
+
.object({
|
|
64
|
+
kind: z.string().min(3).max(64).regex(/^[a-z0-9_-]+\.[a-z0-9_-]+$/, 'kind must be <product>.<noun>, lowercase'),
|
|
65
|
+
ref: z.string().min(1).max(512),
|
|
66
|
+
meta: z.record(z.string(), z.unknown()).optional(),
|
|
67
|
+
attachedBy: z.enum(['studio', 'interactive', 'cli', 'api']).optional(),
|
|
68
|
+
})
|
|
69
|
+
.strict();
|
|
70
|
+
function message(err) {
|
|
71
|
+
return err instanceof Error ? err.message : String(err);
|
|
72
|
+
}
|
|
73
|
+
/** Engine error → HTTP status (the mapping in the module doc). */
|
|
74
|
+
function engineErrorStatus(err) {
|
|
75
|
+
if (err instanceof ProjectsUnsupportedError)
|
|
76
|
+
return 501;
|
|
77
|
+
const msg = message(err);
|
|
78
|
+
if (/not registered/i.test(msg))
|
|
79
|
+
return 404;
|
|
80
|
+
if (/already in use|archived|'default'|synthesized/i.test(msg))
|
|
81
|
+
return 409;
|
|
82
|
+
return 400;
|
|
83
|
+
}
|
|
84
|
+
/** The synthesized `default` row (never stored; ADR §7). */
|
|
85
|
+
function defaultProject() {
|
|
86
|
+
return {
|
|
87
|
+
id: DEFAULT_PROJECT_ID,
|
|
88
|
+
name: 'Unfiled',
|
|
89
|
+
description: 'Runs and chats not filed into any project (synthesized; read-only).',
|
|
90
|
+
status: 'active',
|
|
91
|
+
scope: '',
|
|
92
|
+
created_at: 0,
|
|
93
|
+
updated_at: 0,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export function registerProjectRoutes(app, adapter, deps,
|
|
97
|
+
// Defaulted like the deps above — to a NOOP trail, so a directly-driven
|
|
98
|
+
// route set (unit tests) never writes the real ~/.wicked-crew/audit.log
|
|
99
|
+
// (task #88). The real trail always arrives from `createServer`.
|
|
100
|
+
security = { audit: AuditLog.noop() }) {
|
|
101
|
+
const { bus, index, log } = deps;
|
|
102
|
+
const { audit } = security;
|
|
103
|
+
const settings = deps.settings ?? new ProjectSettingsStore();
|
|
104
|
+
/** The engine's row plus its crew-side settings — the ONE `Project` shape the wire carries
|
|
105
|
+
* (§7.1). `interactiveRoot` is always present and explicitly null when unbound, so a client
|
|
106
|
+
* can tell "shared default" from "this field does not exist on this daemon". */
|
|
107
|
+
const withSettings = (project) => ({
|
|
108
|
+
...project,
|
|
109
|
+
interactiveRoot: settings.get(project.id).interactiveRoot ?? null,
|
|
110
|
+
});
|
|
111
|
+
// The AUTHENTICATED actor for event/audit stamping — locked decision #6
|
|
112
|
+
// replaces the hardcoded 'api' strings these emits used to carry. Falls back
|
|
113
|
+
// to the local actor when no auth hooks are installed (direct-driven tests).
|
|
114
|
+
const actorOf = (req) => req.actor ?? LOCAL_ACTOR;
|
|
115
|
+
/** All explicit `crew.run`/`crew.chat` member refs across every stored project. */
|
|
116
|
+
async function filedRunRefs() {
|
|
117
|
+
const filed = new Set();
|
|
118
|
+
for (const project of await adapter.projectList()) {
|
|
119
|
+
for (const member of await adapter.projectMembers(project.id)) {
|
|
120
|
+
if (member.member_kind === 'crew.run' || member.member_kind === 'crew.chat') {
|
|
121
|
+
filed.add(member.member_ref);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return filed;
|
|
126
|
+
}
|
|
127
|
+
/** The synthesized default project's members (ADR §7: "all runs/chats known to the engine
|
|
128
|
+
* that have no explicit membership row" — computed, never stored). Runs come from the store;
|
|
129
|
+
* chats from the LIVE pool (chats are not persisted), guarded because the chat surface is an
|
|
130
|
+
* optional engine capability. */
|
|
131
|
+
async function defaultMembers() {
|
|
132
|
+
const filed = await filedRunRefs();
|
|
133
|
+
const synthesize = (kind, ref) => ({
|
|
134
|
+
id: `default:${kind}:${ref}`,
|
|
135
|
+
project_id: DEFAULT_PROJECT_ID,
|
|
136
|
+
member_kind: kind,
|
|
137
|
+
member_ref: ref,
|
|
138
|
+
meta: null,
|
|
139
|
+
attached_at: 0,
|
|
140
|
+
attached_by: 'api',
|
|
141
|
+
});
|
|
142
|
+
const members = (await adapter.sessions())
|
|
143
|
+
.filter((id) => !filed.has(id))
|
|
144
|
+
.map((id) => synthesize('crew.run', id));
|
|
145
|
+
try {
|
|
146
|
+
for (const chat of await adapter.chatList()) {
|
|
147
|
+
const chatId = chat.chatId;
|
|
148
|
+
if (typeof chatId === 'string' && !filed.has(chatId)) {
|
|
149
|
+
members.push(synthesize('crew.chat', chatId));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// No chat surface on this build (ChatUnsupportedError) — runs alone are the honest set.
|
|
155
|
+
}
|
|
156
|
+
return members;
|
|
157
|
+
}
|
|
158
|
+
/** Resolve a project's run/chat member refs (real or synthesized default). */
|
|
159
|
+
async function memberRunRefs(projectId) {
|
|
160
|
+
if (projectId === DEFAULT_PROJECT_ID) {
|
|
161
|
+
return (await defaultMembers()).map((m) => m.member_ref);
|
|
162
|
+
}
|
|
163
|
+
const project = await adapter.projectGet(projectId);
|
|
164
|
+
if (project === null)
|
|
165
|
+
return null;
|
|
166
|
+
return (await adapter.projectMembers(projectId))
|
|
167
|
+
.filter((m) => m.member_kind === 'crew.run' || m.member_kind === 'crew.chat')
|
|
168
|
+
.map((m) => m.member_ref);
|
|
169
|
+
}
|
|
170
|
+
// ── CRUD ─────────────────────────────────────────────────────────────────────
|
|
171
|
+
app.post(`${V}/projects`, async (req, reply) => {
|
|
172
|
+
const parsed = CreateProjectSchema.safeParse(req.body);
|
|
173
|
+
if (!parsed.success) {
|
|
174
|
+
return reply.code(400).send({ error: 'Invalid request body', details: parsed.error.issues });
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const project = await adapter.projectCreate(parsed.data.name, parsed.data.description);
|
|
178
|
+
// Post-commit, in order: bus event, then the (best-effort) foundation charter.
|
|
179
|
+
bus?.emit(PROJECT_CREATED, { project_id: project.id, name: project.name, scope: project.scope, actor: actorOf(req).id }, projectCreatedKey(project.id));
|
|
180
|
+
audit.record('project.created', actorOf(req), { detail: { projectId: project.id, name: project.name } });
|
|
181
|
+
void writeCharter(adapter, project, 'created', log);
|
|
182
|
+
return reply.code(201).send({ project });
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
return reply.code(engineErrorStatus(err)).send({ error: message(err) });
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
app.get(`${V}/projects`, async (req, reply) => {
|
|
189
|
+
const { status } = req.query;
|
|
190
|
+
if (status !== undefined && status !== 'active' && status !== 'archived') {
|
|
191
|
+
return reply.code(400).send({ error: `unknown status filter '${status}' (active|archived)` });
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
const stored = await adapter.projectList();
|
|
195
|
+
const all = [defaultProject(), ...stored];
|
|
196
|
+
const projects = (status === undefined ? all : all.filter((p) => p.status === status)).map(withSettings);
|
|
197
|
+
return { projects };
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
return reply.code(engineErrorStatus(err)).send({ error: message(err) });
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
app.get(`${V}/projects/:id`, async (req, reply) => {
|
|
204
|
+
const { id } = req.params;
|
|
205
|
+
try {
|
|
206
|
+
if (id === DEFAULT_PROJECT_ID) {
|
|
207
|
+
return { project: withSettings(defaultProject()), members: await defaultMembers() };
|
|
208
|
+
}
|
|
209
|
+
const project = await adapter.projectGet(id);
|
|
210
|
+
if (project === null)
|
|
211
|
+
return reply.code(404).send({ error: `Project ${id} not found` });
|
|
212
|
+
return { project: withSettings(project), members: await adapter.projectMembers(id) };
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
return reply.code(engineErrorStatus(err)).send({ error: message(err) });
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
app.patch(`${V}/projects/:id`, async (req, reply) => {
|
|
219
|
+
const { id } = req.params;
|
|
220
|
+
const parsed = UpdateProjectSchema.safeParse(req.body);
|
|
221
|
+
if (!parsed.success) {
|
|
222
|
+
return reply.code(400).send({ error: 'Invalid request body', details: parsed.error.issues });
|
|
223
|
+
}
|
|
224
|
+
if (id === DEFAULT_PROJECT_ID) {
|
|
225
|
+
return reply.code(409).send({ error: "the synthesized 'default' project cannot be modified" });
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
const before = await adapter.projectGet(id);
|
|
229
|
+
if (before === null)
|
|
230
|
+
return reply.code(404).send({ error: `Project ${id} not found` });
|
|
231
|
+
// The crew-side half of the patch, applied before the engine half so a settings-only
|
|
232
|
+
// PATCH never needs an engine round-trip at all (§7.1).
|
|
233
|
+
if (parsed.data.interactiveRoot !== undefined) {
|
|
234
|
+
settings.set(id, { interactiveRoot: parsed.data.interactiveRoot });
|
|
235
|
+
}
|
|
236
|
+
const touchesEngine = parsed.data.name !== undefined ||
|
|
237
|
+
parsed.data.description !== undefined ||
|
|
238
|
+
parsed.data.status !== undefined;
|
|
239
|
+
if (!touchesEngine) {
|
|
240
|
+
// Settings-only: no engine write (an all-null projectUpdate would bump updated_at for
|
|
241
|
+
// nothing) and no `project.updated` emit — `interactiveRoot` is a crew-side binding,
|
|
242
|
+
// not part of ADR §4's project event vocabulary.
|
|
243
|
+
return { project: withSettings(before) };
|
|
244
|
+
}
|
|
245
|
+
const project = await adapter.projectUpdate(id, parsed.data);
|
|
246
|
+
// Archive gets its own event type; everything else (rename/describe/restore) is `updated`
|
|
247
|
+
// with the changed fields named (ADR §4).
|
|
248
|
+
if (parsed.data.status === 'archived' && before?.status !== 'archived') {
|
|
249
|
+
bus?.emit(PROJECT_ARCHIVED, { project_id: project.id, actor: actorOf(req).id }, projectArchivedKey(project.id, project.updated_at));
|
|
250
|
+
// Archive is the admin-gated write (task #88) — WHO archived it is audit-worthy.
|
|
251
|
+
audit.record('project.archived', actorOf(req), { detail: { projectId: project.id } });
|
|
252
|
+
void writeCharter(adapter, project, 'archived', log);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
const changed = {};
|
|
256
|
+
if (parsed.data.name !== undefined)
|
|
257
|
+
changed['name'] = parsed.data.name;
|
|
258
|
+
if (parsed.data.description !== undefined)
|
|
259
|
+
changed['description'] = parsed.data.description;
|
|
260
|
+
if (parsed.data.status !== undefined)
|
|
261
|
+
changed['status'] = parsed.data.status;
|
|
262
|
+
bus?.emit(PROJECT_UPDATED, { project_id: project.id, changed, actor: actorOf(req).id }, projectUpdatedKey(project.id, project.updated_at));
|
|
263
|
+
void writeCharter(adapter, project, parsed.data.status === 'active' && before?.status === 'archived' ? 'restored' : 'updated', log);
|
|
264
|
+
}
|
|
265
|
+
return { project: withSettings(project) };
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
return reply.code(engineErrorStatus(err)).send({ error: message(err) });
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
// ── Members ──────────────────────────────────────────────────────────────────
|
|
272
|
+
app.get(`${V}/projects/:id/members`, async (req, reply) => {
|
|
273
|
+
const { id } = req.params;
|
|
274
|
+
try {
|
|
275
|
+
if (id === DEFAULT_PROJECT_ID)
|
|
276
|
+
return { projectId: id, members: await defaultMembers() };
|
|
277
|
+
const project = await adapter.projectGet(id);
|
|
278
|
+
if (project === null)
|
|
279
|
+
return reply.code(404).send({ error: `Project ${id} not found` });
|
|
280
|
+
return { projectId: id, members: await adapter.projectMembers(id) };
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
return reply.code(engineErrorStatus(err)).send({ error: message(err) });
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
app.post(`${V}/projects/:id/members`, async (req, reply) => {
|
|
287
|
+
const { id } = req.params;
|
|
288
|
+
const parsed = AttachMemberSchema.safeParse(req.body);
|
|
289
|
+
if (!parsed.success) {
|
|
290
|
+
return reply.code(400).send({ error: 'Invalid request body', details: parsed.error.issues });
|
|
291
|
+
}
|
|
292
|
+
if (id === DEFAULT_PROJECT_ID) {
|
|
293
|
+
return reply
|
|
294
|
+
.code(409)
|
|
295
|
+
.send({ error: "cannot attach members to the synthesized 'default' project" });
|
|
296
|
+
}
|
|
297
|
+
const { kind, ref, meta, attachedBy } = parsed.data;
|
|
298
|
+
try {
|
|
299
|
+
// Referential integrity for crew.* kinds lives HERE, at attach time (ADR §1.2): 404 on an
|
|
300
|
+
// unknown run/repo — but a member may legitimately outlive what it points to later.
|
|
301
|
+
if (kind === 'crew.run' && !(await adapter.sessions()).includes(ref)) {
|
|
302
|
+
return reply.code(404).send({ error: `Run ${ref} not found` });
|
|
303
|
+
}
|
|
304
|
+
if (kind === 'crew.repo' && !(await adapter.listRepos()).some((r) => r.id === ref)) {
|
|
305
|
+
return reply.code(404).send({ error: `Repo ${ref} not found` });
|
|
306
|
+
}
|
|
307
|
+
const { member, created } = await adapter.projectMemberAttach(id, kind, ref, meta, attachedBy);
|
|
308
|
+
if (created) {
|
|
309
|
+
if (kind === 'crew.run' || kind === 'crew.chat')
|
|
310
|
+
index.set(ref, id);
|
|
311
|
+
bus?.emit(MEMBERSHIP_ATTACHED,
|
|
312
|
+
// `attachedBy` stays what it is — the SURFACE that attached (studio/
|
|
313
|
+
// interactive/cli/api), persisted on the row. The event's `actor` is
|
|
314
|
+
// now the authenticated principal, not that caller-supplied label
|
|
315
|
+
// (locked decision #6: no more spoofable actor strings).
|
|
316
|
+
{ project_id: id, member: { kind, ref }, actor: actorOf(req).id, surface: attachedBy ?? 'api' }, membershipAttachedKey(id, kind, ref, member.attached_at));
|
|
317
|
+
}
|
|
318
|
+
return reply.code(created ? 201 : 200).send({ member, created });
|
|
319
|
+
}
|
|
320
|
+
catch (err) {
|
|
321
|
+
return reply.code(engineErrorStatus(err)).send({ error: message(err) });
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
app.delete(`${V}/projects/:id/members/:mid`, async (req, reply) => {
|
|
325
|
+
const { id, mid } = req.params;
|
|
326
|
+
if (id === DEFAULT_PROJECT_ID) {
|
|
327
|
+
return reply
|
|
328
|
+
.code(409)
|
|
329
|
+
.send({ error: "the synthesized 'default' project has no stored members to detach" });
|
|
330
|
+
}
|
|
331
|
+
try {
|
|
332
|
+
// Read the member BEFORE detaching so the event can name (kind, ref) — the row's id alone
|
|
333
|
+
// is opaque to consumers.
|
|
334
|
+
const member = (await adapter.projectMembers(id)).find((m) => m.id === mid);
|
|
335
|
+
const removed = await adapter.projectMemberDetach(id, mid);
|
|
336
|
+
if (!removed) {
|
|
337
|
+
return reply.code(404).send({ error: `No member ${mid} on project ${id}` });
|
|
338
|
+
}
|
|
339
|
+
if (member !== undefined && (member.member_kind === 'crew.run' || member.member_kind === 'crew.chat')) {
|
|
340
|
+
index.delete(member.member_ref);
|
|
341
|
+
}
|
|
342
|
+
bus?.emit(MEMBERSHIP_DETACHED, {
|
|
343
|
+
project_id: id,
|
|
344
|
+
member: { kind: member?.member_kind ?? 'unknown', ref: member?.member_ref ?? mid },
|
|
345
|
+
actor: actorOf(req).id,
|
|
346
|
+
}, membershipDetachedKey(id, mid, Date.now()));
|
|
347
|
+
return { removed: true };
|
|
348
|
+
}
|
|
349
|
+
catch (err) {
|
|
350
|
+
return reply.code(engineErrorStatus(err)).send({ error: message(err) });
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
// ── The two project reads that make multi-skin real (§5.2/§5.3) ─────────────
|
|
354
|
+
app.get(`${V}/projects/:id/activity`, async (req, reply) => {
|
|
355
|
+
const { id } = req.params;
|
|
356
|
+
const { cursor, limit } = req.query;
|
|
357
|
+
const pageSize = Math.min(Math.max(Number(limit ?? 50) || 50, 1), 200);
|
|
358
|
+
try {
|
|
359
|
+
const refs = await memberRunRefs(id);
|
|
360
|
+
if (refs === null)
|
|
361
|
+
return reply.code(404).send({ error: `Project ${id} not found` });
|
|
362
|
+
const { entries, nextCursor } = await buildActivityPage(adapter, id, refs, bus?.dbPath ?? null, cursor, pageSize);
|
|
363
|
+
return { projectId: id, entries, nextCursor };
|
|
364
|
+
}
|
|
365
|
+
catch (err) {
|
|
366
|
+
return reply.code(engineErrorStatus(err)).send({ error: message(err) });
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
app.get(`${V}/projects/:id/prompts`, async (req, reply) => {
|
|
370
|
+
const { id } = req.params;
|
|
371
|
+
try {
|
|
372
|
+
const refs = await memberRunRefs(id);
|
|
373
|
+
if (refs === null)
|
|
374
|
+
return reply.code(404).send({ error: `Project ${id} not found` });
|
|
375
|
+
// Per-member queries, not a daemon-wide scan filtered in-process: the cost scales with
|
|
376
|
+
// THIS project's members, never with global open-prompt volume (Copilot, #243).
|
|
377
|
+
const prompts = [];
|
|
378
|
+
for (const ref of refs) {
|
|
379
|
+
const rows = await adapter.interactionRequests(ref, 'open');
|
|
380
|
+
if (rows === null) {
|
|
381
|
+
// Distinguish "cannot read the durable table" from "no prompts" — the FINDING-050 rule.
|
|
382
|
+
return reply.code(501).send({
|
|
383
|
+
error: 'The durable prompt inbox needs a wicked-core build with the interaction_requests binding (wicked-core-ts >= 0.6.0)',
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
prompts.push(...rows);
|
|
387
|
+
}
|
|
388
|
+
prompts.sort((a, b) => b.created_at - a.created_at);
|
|
389
|
+
return { projectId: id, prompts };
|
|
390
|
+
}
|
|
391
|
+
catch (err) {
|
|
392
|
+
return reply.code(engineErrorStatus(err)).send({ error: message(err) });
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
// ── The project code graph — every member repo, co-located, NOT linked ───────
|
|
396
|
+
//
|
|
397
|
+
// Mirrors the repo-scoped trio (`/repos/:id/graph`, `/repos/:id/graph/blast-radius`,
|
|
398
|
+
// `/repos/:id/domain-graph`) in shape, caps and error handling, one scope up. The one thing it
|
|
399
|
+
// adds to every payload is PROVENANCE: a hit that does not say which repo it came from is not an
|
|
400
|
+
// answer to a cross-repo question. See projects/graph.ts for the co-location/linkage limit.
|
|
401
|
+
/** The synthesized `default` project can never hold a repo — its membership is computed from
|
|
402
|
+
* unfiled runs and chats (ADR §7), and `crew.repo` is not among the kinds it synthesizes. Said
|
|
403
|
+
* here, with its own sentence, rather than by handing the engine an id it does not store. */
|
|
404
|
+
function defaultProjectGraph() {
|
|
405
|
+
return {
|
|
406
|
+
projectId: DEFAULT_PROJECT_ID,
|
|
407
|
+
state: 'no-repo-members',
|
|
408
|
+
detail: "the synthesized 'default' project holds unfiled runs and chats only — it has no repo " +
|
|
409
|
+
'members and cannot have a code graph. Create a project and attach repos to it.',
|
|
410
|
+
dbPath: null,
|
|
411
|
+
repos: [],
|
|
412
|
+
missingRepos: [],
|
|
413
|
+
staleRepos: [],
|
|
414
|
+
linkage: 'co-located',
|
|
415
|
+
note: CO_LOCATION_NOTE,
|
|
416
|
+
updatedAt: null,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
/** Engine capability gaps map to 501, everything else to the projects mapping. */
|
|
420
|
+
function graphErrorStatus(err) {
|
|
421
|
+
return err instanceof ProjectGraphEngineTooOldError ? 501 : engineErrorStatus(err);
|
|
422
|
+
}
|
|
423
|
+
app.get(`${V}/projects/:id/graph`, async (req, reply) => {
|
|
424
|
+
const { id } = req.params;
|
|
425
|
+
if (id === DEFAULT_PROJECT_ID)
|
|
426
|
+
return { status: defaultProjectGraph() };
|
|
427
|
+
try {
|
|
428
|
+
const project = await adapter.projectGet(id);
|
|
429
|
+
if (project === null)
|
|
430
|
+
return reply.code(404).send({ error: `Project ${id} not found` });
|
|
431
|
+
// Always 200: "this project has no repos" and "its graph was never built" are ANSWERS about
|
|
432
|
+
// the graph's standing, which is what this route reports. The query routes below are where
|
|
433
|
+
// they become a refusal, because there they are the reason a question cannot be answered.
|
|
434
|
+
return { status: await projectGraphStatus(adapter, id) };
|
|
435
|
+
}
|
|
436
|
+
catch (err) {
|
|
437
|
+
return reply.code(graphErrorStatus(err)).send({ error: message(err) });
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
app.post(`${V}/projects/:id/graph/refresh`, async (req, reply) => {
|
|
441
|
+
const { id } = req.params;
|
|
442
|
+
if (id === DEFAULT_PROJECT_ID) {
|
|
443
|
+
return reply.code(409).send({ error: defaultProjectGraph().detail });
|
|
444
|
+
}
|
|
445
|
+
try {
|
|
446
|
+
const project = await adapter.projectGet(id);
|
|
447
|
+
if (project === null)
|
|
448
|
+
return reply.code(404).send({ error: `Project ${id} not found` });
|
|
449
|
+
// Synchronous: indexing runs to completion before this answers, so the response describes a
|
|
450
|
+
// graph that IS built rather than one that was asked for. Bounded per repo by graph.ts's
|
|
451
|
+
// index timeout; concurrent callers coalesce onto the one in-flight refresh.
|
|
452
|
+
return await refreshProjectGraph(adapter, id);
|
|
453
|
+
}
|
|
454
|
+
catch (err) {
|
|
455
|
+
return reply.code(graphErrorStatus(err)).send({ error: message(err) });
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
app.get(`${V}/projects/:id/graph/blast-radius`, async (req, reply) => {
|
|
459
|
+
const { id } = req.params;
|
|
460
|
+
const q = req.query;
|
|
461
|
+
if (q.name === undefined || q.name.trim() === '') {
|
|
462
|
+
return reply.code(400).send({ error: 'name query parameter required' });
|
|
463
|
+
}
|
|
464
|
+
if (id === DEFAULT_PROJECT_ID) {
|
|
465
|
+
return reply.code(404).send({ error: defaultProjectGraph().detail, status: defaultProjectGraph() });
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
const project = await adapter.projectGet(id);
|
|
469
|
+
if (project === null)
|
|
470
|
+
return reply.code(404).send({ error: `Project ${id} not found` });
|
|
471
|
+
const ready = await queryable(adapter, id);
|
|
472
|
+
// The FINDING-050 / R3 rule: a project that cannot answer says WHY and HOW TO FIX IT. `[]`
|
|
473
|
+
// here would be a well-formed reply meaning "nothing depends on this symbol", which is a
|
|
474
|
+
// different — and dangerous — statement from "there is no graph to ask".
|
|
475
|
+
if (!ready.ok) {
|
|
476
|
+
const code = ready.status.state === 'engine-too-old' ? 501 : 404;
|
|
477
|
+
return reply.code(code).send({ error: ready.status.detail, status: ready.status });
|
|
478
|
+
}
|
|
479
|
+
return reply.send(await projectBlastRadius(ready, q.name.trim()));
|
|
480
|
+
}
|
|
481
|
+
catch (err) {
|
|
482
|
+
return reply.code(graphErrorStatus(err)).send({ error: message(err) });
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
app.get(`${V}/projects/:id/graph/search`, async (req, reply) => {
|
|
486
|
+
const { id } = req.params;
|
|
487
|
+
const q = req.query;
|
|
488
|
+
if (q.name === undefined || q.name.trim() === '') {
|
|
489
|
+
return reply.code(400).send({ error: 'name query parameter required' });
|
|
490
|
+
}
|
|
491
|
+
if (id === DEFAULT_PROJECT_ID) {
|
|
492
|
+
return reply.code(404).send({ error: defaultProjectGraph().detail, status: defaultProjectGraph() });
|
|
493
|
+
}
|
|
494
|
+
try {
|
|
495
|
+
const project = await adapter.projectGet(id);
|
|
496
|
+
if (project === null)
|
|
497
|
+
return reply.code(404).send({ error: `Project ${id} not found` });
|
|
498
|
+
const ready = await queryable(adapter, id);
|
|
499
|
+
if (!ready.ok) {
|
|
500
|
+
const code = ready.status.state === 'engine-too-old' ? 501 : 404;
|
|
501
|
+
return reply.code(code).send({ error: ready.status.detail, status: ready.status });
|
|
502
|
+
}
|
|
503
|
+
return reply.send(await projectSymbolSearch(ready, q.name.trim()));
|
|
504
|
+
}
|
|
505
|
+
catch (err) {
|
|
506
|
+
return reply.code(graphErrorStatus(err)).send({ error: message(err) });
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/projects/routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crew-side per-project settings (DES-MERGE-001 §7.1's `interactiveRoot`).
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS IS NOT AN ENGINE COLUMN. The engine's project row is fixed (name/description/status
|
|
5
|
+
* — `adapter.projectUpdate` maps exactly those three onto the native addon), and DES-MERGE-001
|
|
6
|
+
* slice 1 is a crew-side transport slice: adding a column would mean a wicked-core release on
|
|
7
|
+
* the critical path of a proxy. So the setting lives here, in a small durable JSON map keyed by
|
|
8
|
+
* project id, and is MERGED onto the engine's record at the route boundary — the wire shape the
|
|
9
|
+
* studio sees is one `Project` with an `interactiveRoot` field, which is what §7.1 asks for and
|
|
10
|
+
* what Phase C depends on. If the setting ever earns an engine column, this store becomes the
|
|
11
|
+
* migration source and the wire shape does not move.
|
|
12
|
+
*
|
|
13
|
+
* Written atomically (tmp + rename), read tolerantly: a corrupt file costs the settings, never
|
|
14
|
+
* the daemon's boot — an unreadable row simply resolves to the shared default root.
|
|
15
|
+
*/
|
|
16
|
+
/** One project's crew-side settings. Nullable-by-design: null ⇒ "use the shared default". */
|
|
17
|
+
export interface ProjectSettings {
|
|
18
|
+
/** DES-MERGE-001 §7.1 — the wicked-interactive docs root this project speaks to. */
|
|
19
|
+
interactiveRoot?: string | null;
|
|
20
|
+
}
|
|
21
|
+
export declare function defaultSettingsPath(env?: NodeJS.ProcessEnv): string;
|
|
22
|
+
export declare class ProjectSettingsStore {
|
|
23
|
+
private readonly path;
|
|
24
|
+
private readonly rows;
|
|
25
|
+
constructor(path?: string);
|
|
26
|
+
/** This project's settings; `{}` when it has never been configured. */
|
|
27
|
+
get(projectId: string): ProjectSettings;
|
|
28
|
+
/** Apply a patch and persist. `interactiveRoot: null` CLEARS the binding (back to shared). */
|
|
29
|
+
set(projectId: string, patch: ProjectSettings): ProjectSettings;
|
|
30
|
+
private persist;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=settings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/projects/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,6FAA6F;AAC7F,MAAM,WAAW,eAAe;IAC9B,oFAAoF;IACpF,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAEhF;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAuC;gBAEhD,IAAI,GAAE,MAA8B;IAkBhD,uEAAuE;IACvE,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe;IAIvC,8FAA8F;IAC9F,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,eAAe;IAQ/D,OAAO,CAAC,OAAO;CAMhB"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crew-side per-project settings (DES-MERGE-001 §7.1's `interactiveRoot`).
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS IS NOT AN ENGINE COLUMN. The engine's project row is fixed (name/description/status
|
|
5
|
+
* — `adapter.projectUpdate` maps exactly those three onto the native addon), and DES-MERGE-001
|
|
6
|
+
* slice 1 is a crew-side transport slice: adding a column would mean a wicked-core release on
|
|
7
|
+
* the critical path of a proxy. So the setting lives here, in a small durable JSON map keyed by
|
|
8
|
+
* project id, and is MERGED onto the engine's record at the route boundary — the wire shape the
|
|
9
|
+
* studio sees is one `Project` with an `interactiveRoot` field, which is what §7.1 asks for and
|
|
10
|
+
* what Phase C depends on. If the setting ever earns an engine column, this store becomes the
|
|
11
|
+
* migration source and the wire shape does not move.
|
|
12
|
+
*
|
|
13
|
+
* Written atomically (tmp + rename), read tolerantly: a corrupt file costs the settings, never
|
|
14
|
+
* the daemon's boot — an unreadable row simply resolves to the shared default root.
|
|
15
|
+
*/
|
|
16
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { homedir } from 'node:os';
|
|
18
|
+
import { dirname, join } from 'node:path';
|
|
19
|
+
export function defaultSettingsPath(env = process.env) {
|
|
20
|
+
return env['WICKED_CREW_PROJECT_SETTINGS'] ?? join(homedir(), '.wicked-crew', 'project-settings.json');
|
|
21
|
+
}
|
|
22
|
+
export class ProjectSettingsStore {
|
|
23
|
+
path;
|
|
24
|
+
rows = {};
|
|
25
|
+
constructor(path = defaultSettingsPath()) {
|
|
26
|
+
this.path = path;
|
|
27
|
+
try {
|
|
28
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
29
|
+
for (const [id, row] of Object.entries(parsed?.projects ?? {})) {
|
|
30
|
+
// Per-row validation, like the handoff ledger: one malformed row must not blank the map.
|
|
31
|
+
if (typeof row !== 'object' || row === null)
|
|
32
|
+
continue;
|
|
33
|
+
const root = row.interactiveRoot;
|
|
34
|
+
if (root === undefined || root === null || typeof root === 'string') {
|
|
35
|
+
this.rows[id] = root === undefined ? {} : { interactiveRoot: root };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Missing or malformed — start empty. Every project then resolves to the shared default,
|
|
41
|
+
// which is the correct degraded answer, not an outage.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** This project's settings; `{}` when it has never been configured. */
|
|
45
|
+
get(projectId) {
|
|
46
|
+
return this.rows[projectId] ?? {};
|
|
47
|
+
}
|
|
48
|
+
/** Apply a patch and persist. `interactiveRoot: null` CLEARS the binding (back to shared). */
|
|
49
|
+
set(projectId, patch) {
|
|
50
|
+
const next = { ...this.get(projectId), ...patch };
|
|
51
|
+
if (next.interactiveRoot === null || next.interactiveRoot === undefined)
|
|
52
|
+
delete next.interactiveRoot;
|
|
53
|
+
this.rows[projectId] = next;
|
|
54
|
+
this.persist();
|
|
55
|
+
return next;
|
|
56
|
+
}
|
|
57
|
+
persist() {
|
|
58
|
+
mkdirSync(dirname(this.path), { recursive: true });
|
|
59
|
+
const tmp = `${this.path}.tmp-${process.pid}`;
|
|
60
|
+
writeFileSync(tmp, JSON.stringify({ projects: this.rows }, null, 2), 'utf8');
|
|
61
|
+
renameSync(tmp, this.path);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=settings.js.map
|