@appsforgood/next-supabase-kit 0.1.6 → 0.1.8

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/index.js CHANGED
@@ -150,7 +150,7 @@ import { join as join12, normalize } from "path";
150
150
 
151
151
  // src/config/defaults.ts
152
152
  var PACKAGE_NAME = "@appsforgood/next-supabase-kit";
153
- var PACKAGE_VERSION = "0.1.6";
153
+ var PACKAGE_VERSION = "0.1.8";
154
154
  var DEFAULT_CONFIG = {
155
155
  stack: "next-supabase",
156
156
  projectType: "saas",
@@ -362,6 +362,34 @@ function listMarkdown(items) {
362
362
  function unique(values) {
363
363
  return [...new Set(values.filter(Boolean))].sort();
364
364
  }
365
+ function readJsonBody(request) {
366
+ return new Promise((resolve3, reject) => {
367
+ const chunks = [];
368
+ let bodyTooLarge = false;
369
+ request.on("data", (chunk) => {
370
+ if (bodyTooLarge) return;
371
+ chunks.push(chunk);
372
+ if (chunks.reduce((total, item) => total + item.length, 0) > 256e3) {
373
+ bodyTooLarge = true;
374
+ reject(new Error("Request body too large."));
375
+ }
376
+ });
377
+ request.on("end", () => {
378
+ if (bodyTooLarge) return;
379
+ const raw = Buffer.concat(chunks).toString("utf8").trim();
380
+ if (!raw) {
381
+ resolve3({});
382
+ return;
383
+ }
384
+ try {
385
+ resolve3(JSON.parse(raw));
386
+ } catch {
387
+ reject(new Error("Request body must be valid JSON."));
388
+ }
389
+ });
390
+ request.on("error", reject);
391
+ });
392
+ }
365
393
 
366
394
  // src/install/audit.ts
367
395
  import { existsSync as existsSync11, readFileSync as readFileSync9, statSync } from "fs";
@@ -3070,6 +3098,7 @@ function createAuditReport(cwd) {
3070
3098
  // src/install/adapter-validate.ts
3071
3099
  var REQUIRED_COMMANDS = [
3072
3100
  "setup",
3101
+ "spec",
3073
3102
  "audit",
3074
3103
  "plan",
3075
3104
  "handoff",
@@ -3082,6 +3111,8 @@ var REQUIRED_COMMANDS = [
3082
3111
  "distinctiveness-pass",
3083
3112
  "screenshot-critique",
3084
3113
  "browser-qa",
3114
+ "test",
3115
+ "review",
3085
3116
  "security",
3086
3117
  "copy",
3087
3118
  "ship",
@@ -4545,7 +4576,10 @@ function redactEvent(event) {
4545
4576
  };
4546
4577
  }
4547
4578
  function recordNote(cwd, agentId, text) {
4548
- return appendSessionEvent(cwd, getActiveSessionId(cwd), { type: "agent_message", createdAt: nowIso(), agentId, text });
4579
+ return recordSessionNote(cwd, getActiveSessionId(cwd), agentId, text);
4580
+ }
4581
+ function recordSessionNote(cwd, sessionId, agentId, text) {
4582
+ return appendSessionEvent(cwd, sessionId, { type: "agent_message", createdAt: nowIso(), agentId, text });
4549
4583
  }
4550
4584
  function recordDecision(cwd, agentId, decision, risk) {
4551
4585
  return appendSessionEvent(cwd, getActiveSessionId(cwd), {
@@ -5706,7 +5740,7 @@ function renderOfficeHtml(boot, mode) {
5706
5740
  <div id="nameplate-layer" class="nameplate-layer" aria-hidden="true"></div>
5707
5741
  <div id="office-hint" class="office-hint hidden" role="status">${isStudio ? "Watching council session events\u2026" : "Click a desk or zone to brief your agent team."}</div>
5708
5742
  </div>
5709
- ${isStudio ? '<aside class="transcript-panel" id="transcript-panel" aria-label="Session transcript"><h2>Transcript</h2><ol id="transcript-list"></ol></aside>' : ""}
5743
+ ${isStudio ? '<aside class="transcript-panel" id="transcript-panel" aria-label="Session transcript"><div class="studio-controls" id="studio-controls"><label class="studio-label" for="session-picker">Session</label><select id="session-picker" aria-label="Council session"></select><form id="studio-note-form" class="studio-note-form"><select id="studio-note-agent" aria-label="Agent for note"></select><input id="studio-note-text" type="text" maxlength="3999" placeholder="Add council note\u2026" /><button type="submit" class="btn secondary">Add note</button></form><button type="button" class="btn primary" id="studio-render-btn">Render markdown</button></div><h2>Transcript</h2><ol id="transcript-list"></ol></aside>' : ""}
5710
5744
  </main>
5711
5745
  <div id="status" class="status" role="status" aria-live="polite"></div>
5712
5746
  <div id="depth-modal" class="modal modal-blur" hidden>
@@ -6161,31 +6195,6 @@ function invalidateAgenticLevelCache(cwd) {
6161
6195
  // src/studio/setup-server.ts
6162
6196
  var DEFAULT_PORT = 9321;
6163
6197
  var DEFAULT_HOST = "127.0.0.1";
6164
- function readJsonBody(request) {
6165
- return new Promise((resolve3, reject) => {
6166
- const chunks = [];
6167
- request.on("data", (chunk) => {
6168
- chunks.push(chunk);
6169
- if (chunks.reduce((total, item) => total + item.length, 0) > 256e3) {
6170
- reject(new Error("Request body too large."));
6171
- request.destroy();
6172
- }
6173
- });
6174
- request.on("end", () => {
6175
- const raw = Buffer.concat(chunks).toString("utf8").trim();
6176
- if (!raw) {
6177
- resolve3({});
6178
- return;
6179
- }
6180
- try {
6181
- resolve3(JSON.parse(raw));
6182
- } catch {
6183
- reject(new Error("Request body must be valid JSON."));
6184
- }
6185
- });
6186
- request.on("error", reject);
6187
- });
6188
- }
6189
6198
  function sendJson(response, statusCode, payload) {
6190
6199
  response.writeHead(statusCode, {
6191
6200
  "Content-Type": "application/json; charset=utf-8",
@@ -6273,6 +6282,9 @@ async function handleRequest(cwd, request, response) {
6273
6282
  try {
6274
6283
  const body = await readJsonBody(request);
6275
6284
  if (typeof body.completeSection === "string") {
6285
+ if (!(body.completeSection in SECTION_LABELS)) {
6286
+ throw new Error("Invalid section id.");
6287
+ }
6276
6288
  markSectionComplete(cwd, body.completeSection);
6277
6289
  }
6278
6290
  const patch = {};
@@ -6541,6 +6553,11 @@ function safeSessionId(raw) {
6541
6553
  return raw;
6542
6554
  }
6543
6555
  function handleRequest2(cwd, request, response) {
6556
+ handleRequestAsync(cwd, request, response).catch((error) => {
6557
+ sendJson2(response, 500, { error: error instanceof Error ? error.message : String(error) });
6558
+ });
6559
+ }
6560
+ async function handleRequestAsync(cwd, request, response) {
6544
6561
  const url = new URL(request.url ?? "/", "http://127.0.0.1");
6545
6562
  if (request.method === "GET" && (url.pathname === "/" || url.pathname === "/office")) {
6546
6563
  sendHtml2(response, renderLiveStudioHtmlWithContext(cwd));
@@ -6615,6 +6632,57 @@ data: ${JSON.stringify({ sessionId: activeId, events: [] })}
6615
6632
  });
6616
6633
  return;
6617
6634
  }
6635
+ const noteMatch = url.pathname.match(/^\/api\/sessions\/([^/]+)\/note$/);
6636
+ if (request.method === "POST" && noteMatch) {
6637
+ const sessionId = safeSessionId(noteMatch[1] ?? "");
6638
+ if (!sessionId) {
6639
+ sendJson2(response, 400, { error: "Invalid session id." });
6640
+ return;
6641
+ }
6642
+ try {
6643
+ const body = await readJsonBody(request);
6644
+ const agent = typeof body.agent === "string" ? body.agent.trim() : "";
6645
+ const text = typeof body.text === "string" ? body.text.trim() : "";
6646
+ if (!agent) {
6647
+ sendJson2(response, 400, { error: "agent is required." });
6648
+ return;
6649
+ }
6650
+ if (!text) {
6651
+ sendJson2(response, 400, { error: "text is required." });
6652
+ return;
6653
+ }
6654
+ if (text.length >= 4e3) {
6655
+ sendJson2(response, 400, { error: "text must be under 4000 characters." });
6656
+ return;
6657
+ }
6658
+ const event = recordSessionNote(cwd, sessionId, agent, text);
6659
+ broadcastSse("event", { sessionId, event, total: readSessionEvents(cwd, sessionId).length });
6660
+ sendJson2(response, 200, { event });
6661
+ } catch (error) {
6662
+ sendJson2(response, 404, { error: error instanceof Error ? error.message : String(error) });
6663
+ }
6664
+ return;
6665
+ }
6666
+ const renderMatch = url.pathname.match(/^\/api\/sessions\/([^/]+)\/render$/);
6667
+ if (request.method === "POST" && renderMatch) {
6668
+ const sessionId = safeSessionId(renderMatch[1] ?? "");
6669
+ if (!sessionId) {
6670
+ sendJson2(response, 400, { error: "Invalid session id." });
6671
+ return;
6672
+ }
6673
+ try {
6674
+ const result = renderSession(cwd, sessionId);
6675
+ sendJson2(response, 200, {
6676
+ rendered: true,
6677
+ sessionId: result.sessionId,
6678
+ sessionPath: result.sessionPath,
6679
+ files: [`${result.sessionPath}/index.md`, `${result.sessionPath}/transcript.md`]
6680
+ });
6681
+ } catch (error) {
6682
+ sendJson2(response, 404, { error: error instanceof Error ? error.message : String(error) });
6683
+ }
6684
+ return;
6685
+ }
6618
6686
  sendJson2(response, 404, { error: "Not found." });
6619
6687
  }
6620
6688
  function listen2(server, host, port) {