wicked-crew 0.5.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.
Files changed (116) hide show
  1. package/dist/api/audit.d.ts +57 -0
  2. package/dist/api/audit.d.ts.map +1 -0
  3. package/dist/api/audit.js +115 -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/open-path.d.ts +18 -0
  14. package/dist/api/open-path.d.ts.map +1 -0
  15. package/dist/api/open-path.js +79 -0
  16. package/dist/api/open-path.js.map +1 -0
  17. package/dist/api/routes.d.ts +112 -1
  18. package/dist/api/routes.d.ts.map +1 -1
  19. package/dist/api/routes.js +435 -16
  20. package/dist/api/routes.js.map +1 -1
  21. package/dist/api/seat-health.d.ts +55 -0
  22. package/dist/api/seat-health.d.ts.map +1 -0
  23. package/dist/api/seat-health.js +273 -0
  24. package/dist/api/seat-health.js.map +1 -0
  25. package/dist/api/seat-signin.d.ts +27 -0
  26. package/dist/api/seat-signin.d.ts.map +1 -0
  27. package/dist/api/seat-signin.js +143 -0
  28. package/dist/api/seat-signin.js.map +1 -0
  29. package/dist/api/server.d.ts +111 -3
  30. package/dist/api/server.d.ts.map +1 -1
  31. package/dist/api/server.js +236 -11
  32. package/dist/api/server.js.map +1 -1
  33. package/dist/cli/index.js +97 -3
  34. package/dist/cli/index.js.map +1 -1
  35. package/dist/cli/mcp.d.ts +14 -0
  36. package/dist/cli/mcp.d.ts.map +1 -0
  37. package/dist/cli/mcp.js +119 -0
  38. package/dist/cli/mcp.js.map +1 -0
  39. package/dist/core/adapter.d.ts +64 -1
  40. package/dist/core/adapter.d.ts.map +1 -1
  41. package/dist/core/adapter.js +162 -4
  42. package/dist/core/adapter.js.map +1 -1
  43. package/dist/core/types.d.ts +23 -361
  44. package/dist/core/types.d.ts.map +1 -1
  45. package/dist/core/types.js +11 -4
  46. package/dist/core/types.js.map +1 -1
  47. package/dist/interactive/bridge-pool.d.ts +71 -0
  48. package/dist/interactive/bridge-pool.d.ts.map +1 -0
  49. package/dist/interactive/bridge-pool.js +187 -0
  50. package/dist/interactive/bridge-pool.js.map +1 -0
  51. package/dist/interactive/bridge-root.d.ts +36 -0
  52. package/dist/interactive/bridge-root.d.ts.map +1 -0
  53. package/dist/interactive/bridge-root.js +48 -0
  54. package/dist/interactive/bridge-root.js.map +1 -0
  55. package/dist/interactive/draft-events.d.ts +144 -0
  56. package/dist/interactive/draft-events.d.ts.map +1 -0
  57. package/dist/interactive/draft-events.js +490 -0
  58. package/dist/interactive/draft-events.js.map +1 -0
  59. package/dist/interactive/edit-events.d.ts +172 -0
  60. package/dist/interactive/edit-events.d.ts.map +1 -0
  61. package/dist/interactive/edit-events.js +530 -0
  62. package/dist/interactive/edit-events.js.map +1 -0
  63. package/dist/interactive/ledger.d.ts +39 -0
  64. package/dist/interactive/ledger.d.ts.map +1 -0
  65. package/dist/interactive/ledger.js +93 -0
  66. package/dist/interactive/ledger.js.map +1 -0
  67. package/dist/interactive/proxy-routes.d.ts +39 -0
  68. package/dist/interactive/proxy-routes.d.ts.map +1 -0
  69. package/dist/interactive/proxy-routes.js +189 -0
  70. package/dist/interactive/proxy-routes.js.map +1 -0
  71. package/dist/interactive/ws-relay.d.ts +85 -0
  72. package/dist/interactive/ws-relay.d.ts.map +1 -0
  73. package/dist/interactive/ws-relay.js +191 -0
  74. package/dist/interactive/ws-relay.js.map +1 -0
  75. package/dist/projects/activity.d.ts +29 -0
  76. package/dist/projects/activity.d.ts.map +1 -0
  77. package/dist/projects/activity.js +172 -0
  78. package/dist/projects/activity.js.map +1 -0
  79. package/dist/projects/charter.d.ts +28 -0
  80. package/dist/projects/charter.d.ts.map +1 -0
  81. package/dist/projects/charter.js +53 -0
  82. package/dist/projects/charter.js.map +1 -0
  83. package/dist/projects/events.d.ts +55 -0
  84. package/dist/projects/events.d.ts.map +1 -0
  85. package/dist/projects/events.js +141 -0
  86. package/dist/projects/events.js.map +1 -0
  87. package/dist/projects/membership-index.d.ts +25 -0
  88. package/dist/projects/membership-index.d.ts.map +1 -0
  89. package/dist/projects/membership-index.js +46 -0
  90. package/dist/projects/membership-index.js.map +1 -0
  91. package/dist/projects/routes.d.ts +90 -0
  92. package/dist/projects/routes.d.ts.map +1 -0
  93. package/dist/projects/routes.js +388 -0
  94. package/dist/projects/routes.js.map +1 -0
  95. package/dist/projects/settings.d.ts +32 -0
  96. package/dist/projects/settings.d.ts.map +1 -0
  97. package/dist/projects/settings.js +64 -0
  98. package/dist/projects/settings.js.map +1 -0
  99. package/dist/qe/acceptance.d.ts +137 -0
  100. package/dist/qe/acceptance.d.ts.map +1 -0
  101. package/dist/qe/acceptance.js +249 -0
  102. package/dist/qe/acceptance.js.map +1 -0
  103. package/dist/qe/gate-events.d.ts +111 -0
  104. package/dist/qe/gate-events.d.ts.map +1 -0
  105. package/dist/qe/gate-events.js +168 -0
  106. package/dist/qe/gate-events.js.map +1 -0
  107. package/dist/qe/ledger.d.ts +100 -0
  108. package/dist/qe/ledger.d.ts.map +1 -0
  109. package/dist/qe/ledger.js +154 -0
  110. package/dist/qe/ledger.js.map +1 -0
  111. package/dist/studio/assets/index-CCwXa1cn.js +428 -0
  112. package/dist/studio/assets/index-HWxo0h41.css +32 -0
  113. package/dist/studio/index.html +2 -2
  114. package/package.json +10 -4
  115. package/dist/studio/assets/index-DaaUU8Ep.css +0 -32
  116. package/dist/studio/assets/index-Fu5DRC00.js +0 -423
@@ -0,0 +1,90 @@
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
+ * Route-layer rules the engine cannot own:
15
+ * - The `default` project ("Unfiled") is SYNTHESIZED here (ADR §7): its members are the runs
16
+ * with no explicit membership row; it lists, feeds, and prompts, and rejects PATCH/attach.
17
+ * - `crew.*` member refs are existence-checked here at attach time (404 on unknown run/repo) —
18
+ * never by the engine, and never with foreign keys (members may outlive what they point to).
19
+ * - Bus events emit POST-COMMIT from these handlers with type-inclusive idempotency keys (§4).
20
+ * - Engine error → status mapping: "not registered" 404 · "already in use"/"archived"/"default"
21
+ * 409 · anything else 400. A pre-0.6.0 addon answers 501 (ProjectsUnsupportedError), never 400.
22
+ */
23
+ import type { FastifyInstance } from 'fastify';
24
+ import { z } from 'zod';
25
+ import type { CoreAdapter } from '../core/adapter.js';
26
+ import type { ProjectBus } from './events.js';
27
+ import type { MembershipIndex } from './membership-index.js';
28
+ import { ProjectSettingsStore } from './settings.js';
29
+ import { AuditLog } from '../api/audit.js';
30
+ /** The reserved, synthesized "Unfiled" project id (ADR §1.1/§7). */
31
+ export declare const DEFAULT_PROJECT_ID = "default";
32
+ export declare const CreateProjectSchema: z.ZodObject<{
33
+ name: z.ZodString;
34
+ description: z.ZodOptional<z.ZodString>;
35
+ }, "strict", z.ZodTypeAny, {
36
+ name: string;
37
+ description?: string | undefined;
38
+ }, {
39
+ name: string;
40
+ description?: string | undefined;
41
+ }>;
42
+ export declare const UpdateProjectSchema: z.ZodObject<{
43
+ name: z.ZodOptional<z.ZodString>;
44
+ description: z.ZodOptional<z.ZodString>;
45
+ status: z.ZodOptional<z.ZodEnum<["active", "archived"]>>;
46
+ /** DES-MERGE-001 §7.1 — the project's wicked-interactive docs root. Explicitly NULLABLE:
47
+ * null clears the binding back to the shared default root, which is a different act from
48
+ * omitting the key (leave it alone). Stored crew-side (see settings.ts), not in the engine. */
49
+ interactiveRoot: z.ZodOptional<z.ZodNullable<z.ZodString>>;
50
+ }, "strict", z.ZodTypeAny, {
51
+ status?: "active" | "archived" | undefined;
52
+ description?: string | undefined;
53
+ name?: string | undefined;
54
+ interactiveRoot?: string | null | undefined;
55
+ }, {
56
+ status?: "active" | "archived" | undefined;
57
+ description?: string | undefined;
58
+ name?: string | undefined;
59
+ interactiveRoot?: string | null | undefined;
60
+ }>;
61
+ /** `<product>.<noun>` — the engine re-validates; this is the 400-with-names layer. */
62
+ export declare const AttachMemberSchema: z.ZodObject<{
63
+ kind: z.ZodString;
64
+ ref: z.ZodString;
65
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
66
+ attachedBy: z.ZodOptional<z.ZodEnum<["studio", "interactive", "cli", "api"]>>;
67
+ }, "strict", z.ZodTypeAny, {
68
+ kind: string;
69
+ ref: string;
70
+ meta?: Record<string, unknown> | undefined;
71
+ attachedBy?: "interactive" | "studio" | "cli" | "api" | undefined;
72
+ }, {
73
+ kind: string;
74
+ ref: string;
75
+ meta?: Record<string, unknown> | undefined;
76
+ attachedBy?: "interactive" | "studio" | "cli" | "api" | undefined;
77
+ }>;
78
+ export interface ProjectRoutesDeps {
79
+ bus: ProjectBus | null;
80
+ index: MembershipIndex;
81
+ log: (msg: string) => void;
82
+ /** Crew-side per-project settings (DES-MERGE-001 §7.1's `interactiveRoot`). Optional so the
83
+ * route-level unit tests keep their existing two-line deps; `registerRoutes` always supplies
84
+ * the SAME instance the interactive proxy reads. */
85
+ settings?: ProjectSettingsStore;
86
+ }
87
+ export declare function registerProjectRoutes(app: FastifyInstance, adapter: CoreAdapter, deps: ProjectRoutesDeps, security?: {
88
+ audit: AuditLog;
89
+ }): void;
90
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/projects/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAa9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAGrD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAM3C,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAG5C,eAAO,MAAM,mBAAmB;;;;;;;;;EAKrB,CAAC;AAEZ,eAAO,MAAM,mBAAmB;;;;IAK5B;;oGAEgG;;;;;;;;;;;;EAGzF,CAAC;AAEZ,sFAAsF;AACtF,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;EAOpB,CAAC;AA4BZ,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,eAAe,CAAC;IACvB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B;;yDAEqD;IACrD,QAAQ,CAAC,EAAE,oBAAoB,CAAC;CACjC;AAED,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,iBAAiB,EAIvB,QAAQ,GAAE;IAAE,KAAK,EAAE,QAAQ,CAAA;CAA+B,GACzD,IAAI,CAkUN"}
@@ -0,0 +1,388 @@
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
+ * Route-layer rules the engine cannot own:
15
+ * - The `default` project ("Unfiled") is SYNTHESIZED here (ADR §7): its members are the runs
16
+ * with no explicit membership row; it lists, feeds, and prompts, and rejects PATCH/attach.
17
+ * - `crew.*` member refs are existence-checked here at attach time (404 on unknown run/repo) —
18
+ * never by the engine, and never with foreign keys (members may outlive what they point to).
19
+ * - Bus events emit POST-COMMIT from these handlers with type-inclusive idempotency keys (§4).
20
+ * - Engine error → status mapping: "not registered" 404 · "already in use"/"archived"/"default"
21
+ * 409 · anything else 400. A pre-0.6.0 addon answers 501 (ProjectsUnsupportedError), never 400.
22
+ */
23
+ import { z } from 'zod';
24
+ import { ProjectsUnsupportedError } from '../core/adapter.js';
25
+ import { API_PREFIX } from '../api/api-prefix.js';
26
+ import { MEMBERSHIP_ATTACHED, MEMBERSHIP_DETACHED, PROJECT_ARCHIVED, PROJECT_CREATED, PROJECT_UPDATED, membershipAttachedKey, membershipDetachedKey, projectArchivedKey, projectCreatedKey, projectUpdatedKey, } from './events.js';
27
+ import { ProjectSettingsStore } from './settings.js';
28
+ import { buildActivityPage } from './activity.js';
29
+ import { writeCharter } from './charter.js';
30
+ import { AuditLog } from '../api/audit.js';
31
+ import { LOCAL_ACTOR } from '../api/auth.js';
32
+ const V = API_PREFIX;
33
+ /** The reserved, synthesized "Unfiled" project id (ADR §1.1/§7). */
34
+ export const DEFAULT_PROJECT_ID = 'default';
35
+ // Exported for tests/wire-contract.test.ts (the request-direction drift guard).
36
+ export const CreateProjectSchema = z
37
+ .object({
38
+ name: z.string().min(1).max(120),
39
+ description: z.string().optional(),
40
+ })
41
+ .strict();
42
+ export const UpdateProjectSchema = z
43
+ .object({
44
+ name: z.string().min(1).max(120).optional(),
45
+ description: z.string().optional(),
46
+ status: z.enum(['active', 'archived']).optional(),
47
+ /** DES-MERGE-001 §7.1 — the project's wicked-interactive docs root. Explicitly NULLABLE:
48
+ * null clears the binding back to the shared default root, which is a different act from
49
+ * omitting the key (leave it alone). Stored crew-side (see settings.ts), not in the engine. */
50
+ interactiveRoot: z.string().min(1).nullable().optional(),
51
+ })
52
+ .strict();
53
+ /** `<product>.<noun>` — the engine re-validates; this is the 400-with-names layer. */
54
+ export const AttachMemberSchema = z
55
+ .object({
56
+ kind: z.string().min(3).max(64).regex(/^[a-z0-9_-]+\.[a-z0-9_-]+$/, 'kind must be <product>.<noun>, lowercase'),
57
+ ref: z.string().min(1).max(512),
58
+ meta: z.record(z.string(), z.unknown()).optional(),
59
+ attachedBy: z.enum(['studio', 'interactive', 'cli', 'api']).optional(),
60
+ })
61
+ .strict();
62
+ function message(err) {
63
+ return err instanceof Error ? err.message : String(err);
64
+ }
65
+ /** Engine error → HTTP status (the mapping in the module doc). */
66
+ function engineErrorStatus(err) {
67
+ if (err instanceof ProjectsUnsupportedError)
68
+ return 501;
69
+ const msg = message(err);
70
+ if (/not registered/i.test(msg))
71
+ return 404;
72
+ if (/already in use|archived|'default'|synthesized/i.test(msg))
73
+ return 409;
74
+ return 400;
75
+ }
76
+ /** The synthesized `default` row (never stored; ADR §7). */
77
+ function defaultProject() {
78
+ return {
79
+ id: DEFAULT_PROJECT_ID,
80
+ name: 'Unfiled',
81
+ description: 'Runs and chats not filed into any project (synthesized; read-only).',
82
+ status: 'active',
83
+ scope: '',
84
+ created_at: 0,
85
+ updated_at: 0,
86
+ };
87
+ }
88
+ export function registerProjectRoutes(app, adapter, deps,
89
+ // Defaulted like the deps above — to a NOOP trail, so a directly-driven
90
+ // route set (unit tests) never writes the real ~/.wicked-crew/audit.log
91
+ // (task #88). The real trail always arrives from `createServer`.
92
+ security = { audit: AuditLog.noop() }) {
93
+ const { bus, index, log } = deps;
94
+ const { audit } = security;
95
+ const settings = deps.settings ?? new ProjectSettingsStore();
96
+ /** The engine's row plus its crew-side settings — the ONE `Project` shape the wire carries
97
+ * (§7.1). `interactiveRoot` is always present and explicitly null when unbound, so a client
98
+ * can tell "shared default" from "this field does not exist on this daemon". */
99
+ const withSettings = (project) => ({
100
+ ...project,
101
+ interactiveRoot: settings.get(project.id).interactiveRoot ?? null,
102
+ });
103
+ // The AUTHENTICATED actor for event/audit stamping — locked decision #6
104
+ // replaces the hardcoded 'api' strings these emits used to carry. Falls back
105
+ // to the local actor when no auth hooks are installed (direct-driven tests).
106
+ const actorOf = (req) => req.actor ?? LOCAL_ACTOR;
107
+ /** All explicit `crew.run`/`crew.chat` member refs across every stored project. */
108
+ async function filedRunRefs() {
109
+ const filed = new Set();
110
+ for (const project of await adapter.projectList()) {
111
+ for (const member of await adapter.projectMembers(project.id)) {
112
+ if (member.member_kind === 'crew.run' || member.member_kind === 'crew.chat') {
113
+ filed.add(member.member_ref);
114
+ }
115
+ }
116
+ }
117
+ return filed;
118
+ }
119
+ /** The synthesized default project's members (ADR §7: "all runs/chats known to the engine
120
+ * that have no explicit membership row" — computed, never stored). Runs come from the store;
121
+ * chats from the LIVE pool (chats are not persisted), guarded because the chat surface is an
122
+ * optional engine capability. */
123
+ async function defaultMembers() {
124
+ const filed = await filedRunRefs();
125
+ const synthesize = (kind, ref) => ({
126
+ id: `default:${kind}:${ref}`,
127
+ project_id: DEFAULT_PROJECT_ID,
128
+ member_kind: kind,
129
+ member_ref: ref,
130
+ meta: null,
131
+ attached_at: 0,
132
+ attached_by: 'api',
133
+ });
134
+ const members = (await adapter.sessions())
135
+ .filter((id) => !filed.has(id))
136
+ .map((id) => synthesize('crew.run', id));
137
+ try {
138
+ for (const chat of await adapter.chatList()) {
139
+ const chatId = chat.chatId;
140
+ if (typeof chatId === 'string' && !filed.has(chatId)) {
141
+ members.push(synthesize('crew.chat', chatId));
142
+ }
143
+ }
144
+ }
145
+ catch {
146
+ // No chat surface on this build (ChatUnsupportedError) — runs alone are the honest set.
147
+ }
148
+ return members;
149
+ }
150
+ /** Resolve a project's run/chat member refs (real or synthesized default). */
151
+ async function memberRunRefs(projectId) {
152
+ if (projectId === DEFAULT_PROJECT_ID) {
153
+ return (await defaultMembers()).map((m) => m.member_ref);
154
+ }
155
+ const project = await adapter.projectGet(projectId);
156
+ if (project === null)
157
+ return null;
158
+ return (await adapter.projectMembers(projectId))
159
+ .filter((m) => m.member_kind === 'crew.run' || m.member_kind === 'crew.chat')
160
+ .map((m) => m.member_ref);
161
+ }
162
+ // ── CRUD ─────────────────────────────────────────────────────────────────────
163
+ app.post(`${V}/projects`, async (req, reply) => {
164
+ const parsed = CreateProjectSchema.safeParse(req.body);
165
+ if (!parsed.success) {
166
+ return reply.code(400).send({ error: 'Invalid request body', details: parsed.error.issues });
167
+ }
168
+ try {
169
+ const project = await adapter.projectCreate(parsed.data.name, parsed.data.description);
170
+ // Post-commit, in order: bus event, then the (best-effort) foundation charter.
171
+ bus?.emit(PROJECT_CREATED, { project_id: project.id, name: project.name, scope: project.scope, actor: actorOf(req).id }, projectCreatedKey(project.id));
172
+ audit.record('project.created', actorOf(req), { detail: { projectId: project.id, name: project.name } });
173
+ void writeCharter(adapter, project, 'created', log);
174
+ return reply.code(201).send({ project });
175
+ }
176
+ catch (err) {
177
+ return reply.code(engineErrorStatus(err)).send({ error: message(err) });
178
+ }
179
+ });
180
+ app.get(`${V}/projects`, async (req, reply) => {
181
+ const { status } = req.query;
182
+ if (status !== undefined && status !== 'active' && status !== 'archived') {
183
+ return reply.code(400).send({ error: `unknown status filter '${status}' (active|archived)` });
184
+ }
185
+ try {
186
+ const stored = await adapter.projectList();
187
+ const all = [defaultProject(), ...stored];
188
+ const projects = (status === undefined ? all : all.filter((p) => p.status === status)).map(withSettings);
189
+ return { projects };
190
+ }
191
+ catch (err) {
192
+ return reply.code(engineErrorStatus(err)).send({ error: message(err) });
193
+ }
194
+ });
195
+ app.get(`${V}/projects/:id`, async (req, reply) => {
196
+ const { id } = req.params;
197
+ try {
198
+ if (id === DEFAULT_PROJECT_ID) {
199
+ return { project: withSettings(defaultProject()), members: await defaultMembers() };
200
+ }
201
+ const project = await adapter.projectGet(id);
202
+ if (project === null)
203
+ return reply.code(404).send({ error: `Project ${id} not found` });
204
+ return { project: withSettings(project), members: await adapter.projectMembers(id) };
205
+ }
206
+ catch (err) {
207
+ return reply.code(engineErrorStatus(err)).send({ error: message(err) });
208
+ }
209
+ });
210
+ app.patch(`${V}/projects/:id`, async (req, reply) => {
211
+ const { id } = req.params;
212
+ const parsed = UpdateProjectSchema.safeParse(req.body);
213
+ if (!parsed.success) {
214
+ return reply.code(400).send({ error: 'Invalid request body', details: parsed.error.issues });
215
+ }
216
+ if (id === DEFAULT_PROJECT_ID) {
217
+ return reply.code(409).send({ error: "the synthesized 'default' project cannot be modified" });
218
+ }
219
+ try {
220
+ const before = await adapter.projectGet(id);
221
+ if (before === null)
222
+ return reply.code(404).send({ error: `Project ${id} not found` });
223
+ // The crew-side half of the patch, applied before the engine half so a settings-only
224
+ // PATCH never needs an engine round-trip at all (§7.1).
225
+ if (parsed.data.interactiveRoot !== undefined) {
226
+ settings.set(id, { interactiveRoot: parsed.data.interactiveRoot });
227
+ }
228
+ const touchesEngine = parsed.data.name !== undefined ||
229
+ parsed.data.description !== undefined ||
230
+ parsed.data.status !== undefined;
231
+ if (!touchesEngine) {
232
+ // Settings-only: no engine write (an all-null projectUpdate would bump updated_at for
233
+ // nothing) and no `project.updated` emit — `interactiveRoot` is a crew-side binding,
234
+ // not part of ADR §4's project event vocabulary.
235
+ return { project: withSettings(before) };
236
+ }
237
+ const project = await adapter.projectUpdate(id, parsed.data);
238
+ // Archive gets its own event type; everything else (rename/describe/restore) is `updated`
239
+ // with the changed fields named (ADR §4).
240
+ if (parsed.data.status === 'archived' && before?.status !== 'archived') {
241
+ bus?.emit(PROJECT_ARCHIVED, { project_id: project.id, actor: actorOf(req).id }, projectArchivedKey(project.id, project.updated_at));
242
+ // Archive is the admin-gated write (task #88) — WHO archived it is audit-worthy.
243
+ audit.record('project.archived', actorOf(req), { detail: { projectId: project.id } });
244
+ void writeCharter(adapter, project, 'archived', log);
245
+ }
246
+ else {
247
+ const changed = {};
248
+ if (parsed.data.name !== undefined)
249
+ changed['name'] = parsed.data.name;
250
+ if (parsed.data.description !== undefined)
251
+ changed['description'] = parsed.data.description;
252
+ if (parsed.data.status !== undefined)
253
+ changed['status'] = parsed.data.status;
254
+ bus?.emit(PROJECT_UPDATED, { project_id: project.id, changed, actor: actorOf(req).id }, projectUpdatedKey(project.id, project.updated_at));
255
+ void writeCharter(adapter, project, parsed.data.status === 'active' && before?.status === 'archived' ? 'restored' : 'updated', log);
256
+ }
257
+ return { project: withSettings(project) };
258
+ }
259
+ catch (err) {
260
+ return reply.code(engineErrorStatus(err)).send({ error: message(err) });
261
+ }
262
+ });
263
+ // ── Members ──────────────────────────────────────────────────────────────────
264
+ app.get(`${V}/projects/:id/members`, async (req, reply) => {
265
+ const { id } = req.params;
266
+ try {
267
+ if (id === DEFAULT_PROJECT_ID)
268
+ return { projectId: id, members: await defaultMembers() };
269
+ const project = await adapter.projectGet(id);
270
+ if (project === null)
271
+ return reply.code(404).send({ error: `Project ${id} not found` });
272
+ return { projectId: id, members: await adapter.projectMembers(id) };
273
+ }
274
+ catch (err) {
275
+ return reply.code(engineErrorStatus(err)).send({ error: message(err) });
276
+ }
277
+ });
278
+ app.post(`${V}/projects/:id/members`, async (req, reply) => {
279
+ const { id } = req.params;
280
+ const parsed = AttachMemberSchema.safeParse(req.body);
281
+ if (!parsed.success) {
282
+ return reply.code(400).send({ error: 'Invalid request body', details: parsed.error.issues });
283
+ }
284
+ if (id === DEFAULT_PROJECT_ID) {
285
+ return reply
286
+ .code(409)
287
+ .send({ error: "cannot attach members to the synthesized 'default' project" });
288
+ }
289
+ const { kind, ref, meta, attachedBy } = parsed.data;
290
+ try {
291
+ // Referential integrity for crew.* kinds lives HERE, at attach time (ADR §1.2): 404 on an
292
+ // unknown run/repo — but a member may legitimately outlive what it points to later.
293
+ if (kind === 'crew.run' && !(await adapter.sessions()).includes(ref)) {
294
+ return reply.code(404).send({ error: `Run ${ref} not found` });
295
+ }
296
+ if (kind === 'crew.repo' && !(await adapter.listRepos()).some((r) => r.id === ref)) {
297
+ return reply.code(404).send({ error: `Repo ${ref} not found` });
298
+ }
299
+ const { member, created } = await adapter.projectMemberAttach(id, kind, ref, meta, attachedBy);
300
+ if (created) {
301
+ if (kind === 'crew.run' || kind === 'crew.chat')
302
+ index.set(ref, id);
303
+ bus?.emit(MEMBERSHIP_ATTACHED,
304
+ // `attachedBy` stays what it is — the SURFACE that attached (studio/
305
+ // interactive/cli/api), persisted on the row. The event's `actor` is
306
+ // now the authenticated principal, not that caller-supplied label
307
+ // (locked decision #6: no more spoofable actor strings).
308
+ { project_id: id, member: { kind, ref }, actor: actorOf(req).id, surface: attachedBy ?? 'api' }, membershipAttachedKey(id, kind, ref, member.attached_at));
309
+ }
310
+ return reply.code(created ? 201 : 200).send({ member, created });
311
+ }
312
+ catch (err) {
313
+ return reply.code(engineErrorStatus(err)).send({ error: message(err) });
314
+ }
315
+ });
316
+ app.delete(`${V}/projects/:id/members/:mid`, async (req, reply) => {
317
+ const { id, mid } = req.params;
318
+ if (id === DEFAULT_PROJECT_ID) {
319
+ return reply
320
+ .code(409)
321
+ .send({ error: "the synthesized 'default' project has no stored members to detach" });
322
+ }
323
+ try {
324
+ // Read the member BEFORE detaching so the event can name (kind, ref) — the row's id alone
325
+ // is opaque to consumers.
326
+ const member = (await adapter.projectMembers(id)).find((m) => m.id === mid);
327
+ const removed = await adapter.projectMemberDetach(id, mid);
328
+ if (!removed) {
329
+ return reply.code(404).send({ error: `No member ${mid} on project ${id}` });
330
+ }
331
+ if (member !== undefined && (member.member_kind === 'crew.run' || member.member_kind === 'crew.chat')) {
332
+ index.delete(member.member_ref);
333
+ }
334
+ bus?.emit(MEMBERSHIP_DETACHED, {
335
+ project_id: id,
336
+ member: { kind: member?.member_kind ?? 'unknown', ref: member?.member_ref ?? mid },
337
+ actor: actorOf(req).id,
338
+ }, membershipDetachedKey(id, mid, Date.now()));
339
+ return { removed: true };
340
+ }
341
+ catch (err) {
342
+ return reply.code(engineErrorStatus(err)).send({ error: message(err) });
343
+ }
344
+ });
345
+ // ── The two project reads that make multi-skin real (§5.2/§5.3) ─────────────
346
+ app.get(`${V}/projects/:id/activity`, async (req, reply) => {
347
+ const { id } = req.params;
348
+ const { cursor, limit } = req.query;
349
+ const pageSize = Math.min(Math.max(Number(limit ?? 50) || 50, 1), 200);
350
+ try {
351
+ const refs = await memberRunRefs(id);
352
+ if (refs === null)
353
+ return reply.code(404).send({ error: `Project ${id} not found` });
354
+ const { entries, nextCursor } = await buildActivityPage(adapter, id, refs, bus?.dbPath ?? null, cursor, pageSize);
355
+ return { projectId: id, entries, nextCursor };
356
+ }
357
+ catch (err) {
358
+ return reply.code(engineErrorStatus(err)).send({ error: message(err) });
359
+ }
360
+ });
361
+ app.get(`${V}/projects/:id/prompts`, async (req, reply) => {
362
+ const { id } = req.params;
363
+ try {
364
+ const refs = await memberRunRefs(id);
365
+ if (refs === null)
366
+ return reply.code(404).send({ error: `Project ${id} not found` });
367
+ // Per-member queries, not a daemon-wide scan filtered in-process: the cost scales with
368
+ // THIS project's members, never with global open-prompt volume (Copilot, #243).
369
+ const prompts = [];
370
+ for (const ref of refs) {
371
+ const rows = await adapter.interactionRequests(ref, 'open');
372
+ if (rows === null) {
373
+ // Distinguish "cannot read the durable table" from "no prompts" — the FINDING-050 rule.
374
+ return reply.code(501).send({
375
+ error: 'The durable prompt inbox needs a wicked-core build with the interaction_requests binding (wicked-core-ts >= 0.6.0)',
376
+ });
377
+ }
378
+ prompts.push(...rows);
379
+ }
380
+ prompts.sort((a, b) => b.created_at - a.created_at);
381
+ return { projectId: id, prompts };
382
+ }
383
+ catch (err) {
384
+ return reply.code(engineErrorStatus(err)).send({ error: message(err) });
385
+ }
386
+ });
387
+ }
388
+ //# 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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/projects/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAQ1C,MAAM,UAAU,mBAAmB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACtE,OAAO,GAAG,CAAC,8BAA8B,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,EAAE,uBAAuB,CAAC,CAAC;AACzG,CAAC;AAED,MAAM,OAAO,oBAAoB;IACd,IAAI,CAAS;IACb,IAAI,GAAoC,EAAE,CAAC;IAE5D,YAAY,OAAe,mBAAmB,EAAE;QAC9C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAmD,CAAC;YACxG,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;gBAC/D,yFAAyF;gBACzF,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;oBAAE,SAAS;gBACtD,MAAM,IAAI,GAAG,GAAG,CAAC,eAAe,CAAC;gBACjC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACpE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;gBACtE,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,yFAAyF;YACzF,uDAAuD;QACzD,CAAC;IACH,CAAC;IAED,uEAAuE;IACvE,GAAG,CAAC,SAAiB;QACnB,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IACpC,CAAC;IAED,8FAA8F;IAC9F,GAAG,CAAC,SAAiB,EAAE,KAAsB;QAC3C,MAAM,IAAI,GAAoB,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC;QACnE,IAAI,IAAI,CAAC,eAAe,KAAK,IAAI,IAAI,IAAI,CAAC,eAAe,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,eAAe,CAAC;QACrG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;QAC5B,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,OAAO;QACb,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,QAAQ,OAAO,CAAC,GAAG,EAAE,CAAC;QAC9C,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC7E,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;CACF"}