623 lines
22 KiB
TypeScript
623 lines
22 KiB
TypeScript
import { extname, join, normalize } from "jsr:@std/path@1.1.4";
|
|
import { journalBrowserFixture } from "./journal_browser_fixture.ts";
|
|
import {
|
|
createSubjectPermissionsFixture,
|
|
permissionFixtureSubjects,
|
|
} from "./subject_permissions_fixture.ts";
|
|
|
|
const port = Number(Deno.args[0]);
|
|
const buildRoot = Deno.args[1];
|
|
const sectionCount = Deno.args[2] === undefined ? 90 : Number(Deno.args[2]);
|
|
if (
|
|
!Number.isInteger(port) || !buildRoot || !Number.isInteger(sectionCount) || sectionCount < 3 ||
|
|
sectionCount > 100
|
|
) {
|
|
throw new Error("usage: server.ts <port> <build-root> [section-count: 3..100]");
|
|
}
|
|
|
|
// 既存Memory scenarioとpaginationを変えないよう、明示選択時だけ有効にする。
|
|
const permissionsMode = Deno.args[3] === "permissions";
|
|
if (permissionsMode) {
|
|
// 権限の証拠は親から渡された実製品buildを使って採取する。
|
|
// build欠落時に/healthを成功させたり、fixture HTMLで代替したりしない。
|
|
await Deno.stat(join(buildRoot, "index.html"));
|
|
await Deno.stat(join(buildRoot, "_app/immutable"));
|
|
}
|
|
const permissionsFixture = createSubjectPermissionsFixture();
|
|
const workspaceId = "memory-review";
|
|
const representativeSubjectId =
|
|
"release-coordination-subject-with-a-deliberately-long-stable-identity-for-responsive-review";
|
|
const representativeMemoryId =
|
|
"memory-decision-with-a-deliberately-long-stable-identity-for-overflow-review-0000000001";
|
|
const derivedMemoryId = "memory-derived-source-0002";
|
|
const emptySubjectId = "empty-subject";
|
|
const staleSubjectId = "stale-subject";
|
|
const failedSubjectId = "failed-subject";
|
|
const ungeneratedSubjectId = "ungenerated-subject";
|
|
const errorSubjectId = "error-subject";
|
|
const pagedSubjectId = "paged-subject";
|
|
const subjectPageCursor = "fixture-subject-page-2";
|
|
const emptySubjectPageCursor = "fixture-empty-subject-page";
|
|
const errorSubjectPageCursor = "fixture-error-subject-page";
|
|
const memoryPageCursor = "fixture-memory-page-2";
|
|
const changePageCursor = "fixture-change-page-2";
|
|
const createdSubjectIds = new Set<string>();
|
|
const subjectCreateRequests: unknown[] = [];
|
|
let createdSubjectCount = 0;
|
|
|
|
const paragraphs = Array.from(
|
|
{ length: sectionCount },
|
|
(_, index) =>
|
|
`## Section ${
|
|
index + 1
|
|
}\n\nThis is representative resident Memory context with **important context**, an identifier \`memory-item-${
|
|
index + 1
|
|
}\`, and a long URL https://example.com/${"long-segment-".repeat(12)}${
|
|
index + 1
|
|
}.\n\n> A durable note for section ${index + 1}.\n\n- first detail\n- second detail`,
|
|
).join("\n\n");
|
|
const surfaceBodyMd =
|
|
`# Resident subject context\n\nThis generated surface is read-only.\n\n| Very wide heading | Another heading | Decision |\n| --- | --- | --- |\n| ${
|
|
"wide-value-".repeat(16)
|
|
} | stable | keep scrolling local |\n\n##### Deep surface heading\n\n###### Deepest surface heading\n\n\`\`\`rust\nfn example() { println!("${
|
|
"wide-code-".repeat(18)
|
|
}"); }\n\`\`\`\n\n[Safe link](https://example.com) [Unsafe link](javascript:alert(1))\n\n<img src=x onerror=alert(1)>\n\n${paragraphs}\n\n## Surface content complete`;
|
|
|
|
const detailBodyMd = `# Committed Memory detail
|
|
|
|
This immutable change preserves its candidate evidence, source refs, and derivation refs.
|
|
|
|
| Local table overflow | Value |
|
|
| --- | --- |
|
|
| ${"detail-wide-value-".repeat(14)} | stable |
|
|
|
|
\`\`\`text
|
|
${"detail-wide-code-".repeat(18)}
|
|
\`\`\`
|
|
|
|
[Safe evidence link](https://example.com/evidence) [Unsafe evidence link](javascript:alert(2))
|
|
|
|
<script>alert("not rendered")</script>`;
|
|
|
|
const subjects = [
|
|
subject(
|
|
representativeSubjectId,
|
|
"Release coordination",
|
|
"active",
|
|
"fingerprint-current",
|
|
"Prefer explicit evidence over assumptions.\nAsk before irreversible actions, and state uncertainty plainly.",
|
|
connectedWorker("Release coordination Worker with a deliberately long display name"),
|
|
),
|
|
subject(emptySubjectId, "Empty ready subject", "active", "fingerprint-0"),
|
|
subject(staleSubjectId, "Stale surface subject", "active", "fingerprint-19"),
|
|
subject(failedSubjectId, "Failed surface subject", "active", "fingerprint-8"),
|
|
subject(ungeneratedSubjectId, "Ungenerated subject", "active", "fingerprint-0"),
|
|
subject(errorSubjectId, "Unavailable subject", "retired", "fingerprint-3"),
|
|
];
|
|
if (permissionsMode) {
|
|
subjects.push(
|
|
...permissionFixtureSubjects.map(([id, role]) => subject(id, role, "active", "fingerprint-0")),
|
|
);
|
|
}
|
|
const pagedSubject = subject(pagedSubjectId, "Subject on the next page", "active", "fingerprint-1");
|
|
|
|
function connectedWorker(displayName: string) {
|
|
return {
|
|
runtime_id: "embedded-worker-runtime",
|
|
worker_id: "release-coordination-worker",
|
|
host_id: "fixture-host",
|
|
display_name: displayName,
|
|
label: displayName,
|
|
profile: "builtin:companion",
|
|
singleton_key: `subjektiv:${representativeSubjectId}`,
|
|
tags: [],
|
|
workspace: {
|
|
visibility: "workspace",
|
|
identity: workspaceId,
|
|
workspace_id: workspaceId,
|
|
},
|
|
state: "idle",
|
|
last_seen_at: "2026-01-02T03:04:05Z",
|
|
pinned: false,
|
|
retention_state: "retained",
|
|
implementation: { kind: "embedded", display_hint: "Fixture Worker" },
|
|
diagnostics: [],
|
|
};
|
|
}
|
|
|
|
function subject(
|
|
id: string,
|
|
role: string,
|
|
state: "active" | "retired",
|
|
memoryFingerprint: string,
|
|
behaviorMd = "",
|
|
currentWorker?: ReturnType<typeof connectedWorker>,
|
|
) {
|
|
return {
|
|
id,
|
|
role,
|
|
behavior_md: behaviorMd,
|
|
state,
|
|
memory_fingerprint: memoryFingerprint,
|
|
created_at: "2026-01-01T00:00:00Z",
|
|
updated_at: "2026-01-02T03:04:05Z",
|
|
...(currentWorker ? { current_worker: currentWorker } : {}),
|
|
};
|
|
}
|
|
|
|
function memoriesFor(subjectId: string) {
|
|
if (createdSubjectIds.has(subjectId)) return [];
|
|
if (subjectId === emptySubjectId || subjectId === ungeneratedSubjectId) return [];
|
|
if (subjectId === staleSubjectId) {
|
|
return [
|
|
memorySummary(
|
|
"stale-memory-1",
|
|
"change-earlier",
|
|
"lesson",
|
|
"resolved",
|
|
"A resolved stale-surface lesson",
|
|
),
|
|
];
|
|
}
|
|
if (subjectId === failedSubjectId) {
|
|
return [
|
|
memorySummary(
|
|
"failed-memory-1",
|
|
"change-original",
|
|
"constraint",
|
|
"retracted",
|
|
"A retracted failed-surface constraint",
|
|
),
|
|
];
|
|
}
|
|
return [
|
|
memorySummary(
|
|
representativeMemoryId,
|
|
"change-current",
|
|
"decision",
|
|
"active",
|
|
"Keep subject Memory provenance typed and visible",
|
|
),
|
|
memorySummary(
|
|
"memory-resolved-0002",
|
|
"change-earlier",
|
|
"lesson",
|
|
"resolved",
|
|
"The bounded reader must preserve continuations",
|
|
),
|
|
memorySummary(
|
|
"memory-retracted-0003",
|
|
"change-original",
|
|
"working_assumption",
|
|
"retracted",
|
|
"Memory list end marker for scroll validation",
|
|
),
|
|
];
|
|
}
|
|
|
|
function memorySummary(
|
|
id: string,
|
|
changeId: string,
|
|
kind: string,
|
|
state: string,
|
|
claim: string,
|
|
) {
|
|
return {
|
|
id,
|
|
change_id: changeId,
|
|
kind,
|
|
state,
|
|
claim,
|
|
excerpt: `Read-only ${state} Memory with exact change ID and provenance metadata.`,
|
|
updated_at: "2026-01-02T03:04:05Z",
|
|
};
|
|
}
|
|
|
|
function surfaceFor(subjectId: string) {
|
|
if (subjectId === representativeSubjectId) {
|
|
return {
|
|
subject_id: subjectId,
|
|
availability: "ready",
|
|
snapshot: {
|
|
snapshot_id: "surface-snapshot-0042",
|
|
body_md: surfaceBodyMd,
|
|
memory_refs: [
|
|
{ memory_id: representativeMemoryId, change_id: "change-current" },
|
|
{ memory_id: derivedMemoryId, change_id: "change-parent" },
|
|
],
|
|
built_from_memory_fingerprint: "fingerprint-current",
|
|
created_at: "2026-01-02T03:04:05Z",
|
|
},
|
|
};
|
|
}
|
|
if (subjectId === emptySubjectId) {
|
|
return {
|
|
subject_id: subjectId,
|
|
availability: "ready",
|
|
snapshot: {
|
|
snapshot_id: "surface-snapshot-empty",
|
|
body_md: "",
|
|
memory_refs: [],
|
|
built_from_memory_fingerprint: "fingerprint-0",
|
|
created_at: "2026-01-02T03:04:05Z",
|
|
},
|
|
};
|
|
}
|
|
if (subjectId === staleSubjectId) return { subject_id: subjectId, availability: "stale" };
|
|
if (subjectId === failedSubjectId) return { subject_id: subjectId, availability: "failed" };
|
|
return { subject_id: subjectId, availability: "ungenerated" };
|
|
}
|
|
|
|
function memoryDetail(memoryId: string, requestedChangeId: string | null) {
|
|
const currentChangeId = memoryId === derivedMemoryId ? "change-parent" : "change-current";
|
|
const changeId = requestedChangeId ?? currentChangeId;
|
|
const states: Record<string, string> = {
|
|
"change-original": "retracted",
|
|
"change-earlier": "resolved",
|
|
"change-current": "active",
|
|
"change-parent": "active",
|
|
};
|
|
return {
|
|
memory_id: memoryId,
|
|
change_id: changeId,
|
|
current_change_id: currentChangeId,
|
|
kind: "decision",
|
|
state: states[changeId],
|
|
claim: changeId === "change-current"
|
|
? "Keep subject Memory provenance typed and visible"
|
|
: `Historical provenance decision, change ${changeId}`,
|
|
body_md: detailBodyMd,
|
|
why_useful:
|
|
"Future readers can audit why a committed Memory exists without exposing unbounded session content.",
|
|
staleness: null,
|
|
change_reason: changeId === "change-current"
|
|
? "Clarified the durable read contract."
|
|
: "Immutable historical change.",
|
|
created_at: "2026-01-01T00:00:00Z",
|
|
updated_at: "2026-01-02T03:04:05Z",
|
|
body_offset: 0,
|
|
body_byte_offset: 0,
|
|
body_truncated: false,
|
|
source_candidate_ids: ["candidate-release-decision-0001"],
|
|
source_candidates: [{
|
|
candidate_id: "candidate-release-decision-0001",
|
|
evidence: [{
|
|
id: "evidence-message-0001",
|
|
kind: "message",
|
|
entry_range: [12, 14],
|
|
origin: {
|
|
kind: "flow_instruction",
|
|
workspace_id: workspaceId,
|
|
runtime_id: "runtime-fixture",
|
|
worker_id: "worker-fixture",
|
|
flow_selector: "builtin:coder-review",
|
|
flow_definition_id: "flow-definition-fixture",
|
|
},
|
|
excerpt: "Use normal explicit subject routes and preserve provenance.",
|
|
summary: "The product contract was explicitly requested.",
|
|
}],
|
|
evidence_total: 1,
|
|
evidence_truncated: false,
|
|
source_refs: [{
|
|
session_id: "session-fixture-0001",
|
|
segment_id: "segment-fixture-0001",
|
|
entry_range: [12, 14],
|
|
evidence_id: "evidence-message-0001",
|
|
origin: { kind: "human_input", account_id: "account-fixture" },
|
|
evidence_kind: "message",
|
|
label: "Memory information design source",
|
|
summary: "Bounded source reference retained with the committed change.",
|
|
}],
|
|
source_refs_total: 1,
|
|
source_refs_truncated: false,
|
|
}],
|
|
derived_from: [{ memory_id: derivedMemoryId, change_id: "change-parent" }],
|
|
evidence_has_more: false,
|
|
};
|
|
}
|
|
|
|
function changes(memoryId: string, cursor: string | null) {
|
|
const all = [
|
|
{
|
|
change_id: "change-current",
|
|
kind: "decision",
|
|
state: "active",
|
|
claim: "Keep subject Memory provenance typed and visible",
|
|
change_reason: "Clarified the durable read contract.",
|
|
updated_at: "2026-01-03T03:04:05Z",
|
|
},
|
|
{
|
|
change_id: "change-earlier",
|
|
kind: "decision",
|
|
state: "resolved",
|
|
claim: "Historical provenance decision, change change-earlier",
|
|
change_reason: "Resolved after endpoint integration.",
|
|
updated_at: "2026-01-02T03:04:05Z",
|
|
},
|
|
{
|
|
change_id: "change-original",
|
|
kind: "decision",
|
|
state: "retracted",
|
|
claim: "Historical provenance decision, change change-original",
|
|
change_reason: "Retracted the legacy shape.",
|
|
updated_at: "2026-01-01T03:04:05Z",
|
|
},
|
|
];
|
|
const continued = cursor === changePageCursor;
|
|
return {
|
|
memory_id: memoryId,
|
|
current_change_id: "change-current",
|
|
items: continued ? all.slice(2) : all.slice(0, 2),
|
|
...(continued ? {} : { next_cursor: changePageCursor }),
|
|
has_more: !continued,
|
|
};
|
|
}
|
|
|
|
const json = (value: unknown, status = 200) =>
|
|
new Response(JSON.stringify(value), {
|
|
status,
|
|
headers: { "content-type": "application/json; charset=utf-8" },
|
|
});
|
|
|
|
const mime: Record<string, string> = {
|
|
".html": "text/html; charset=utf-8",
|
|
".js": "text/javascript; charset=utf-8",
|
|
".css": "text/css; charset=utf-8",
|
|
".json": "application/json; charset=utf-8",
|
|
".svg": "image/svg+xml",
|
|
".png": "image/png",
|
|
".woff2": "font/woff2",
|
|
};
|
|
|
|
async function staticResponse(pathname: string): Promise<Response> {
|
|
const relative = pathname === "/" ? "index.html" : pathname.replace(/^\/+/, "");
|
|
const normalized = normalize(relative);
|
|
if (normalized.startsWith("..")) return new Response("not found", { status: 404 });
|
|
let filePath = join(buildRoot, normalized);
|
|
try {
|
|
const stat = await Deno.stat(filePath);
|
|
if (stat.isDirectory) filePath = join(filePath, "index.html");
|
|
return new Response(await Deno.readFile(filePath), {
|
|
headers: { "content-type": mime[extname(filePath)] ?? "application/octet-stream" },
|
|
});
|
|
} catch {
|
|
return new Response(await Deno.readFile(join(buildRoot, "index.html")), {
|
|
headers: { "content-type": "text/html; charset=utf-8" },
|
|
});
|
|
}
|
|
}
|
|
|
|
Deno.serve({ hostname: "127.0.0.1", port }, async (request) => {
|
|
const url = new URL(request.url);
|
|
if (url.pathname === "/health") return new Response("ok");
|
|
if (permissionsMode) {
|
|
const response = await permissionsFixture(request);
|
|
if (response) return response;
|
|
}
|
|
if (url.pathname === "/fixture/subject-create-requests") {
|
|
return json({ count: subjectCreateRequests.length, requests: subjectCreateRequests });
|
|
}
|
|
if (url.pathname === "/api/workspaces") {
|
|
return json([{
|
|
workspace_id: workspaceId,
|
|
owner_account_id: "owner",
|
|
display_name: "Memory Review",
|
|
state: "active",
|
|
created_at: "2026-01-01T00:00:00Z",
|
|
updated_at: "2026-01-02T00:00:00Z",
|
|
}]);
|
|
}
|
|
if (url.pathname === `/api/w/${workspaceId}/workspace`) {
|
|
return json({
|
|
workspace_id: workspaceId,
|
|
display_name: "Memory Review",
|
|
record_authority: "fixture",
|
|
schema_version: 1,
|
|
auth: {
|
|
Passkey: {
|
|
rp_id: "127.0.0.1",
|
|
origin: `http://127.0.0.1:${port}`,
|
|
public_base_url: `http://127.0.0.1:${port}`,
|
|
cookie_name: "fixture",
|
|
},
|
|
},
|
|
permissions: {
|
|
manage_repositories: false,
|
|
manage_secrets: false,
|
|
manage_runtimes: false,
|
|
delete_workspace: false,
|
|
delete_subjects: false,
|
|
},
|
|
extension_points: {
|
|
store: "fixture",
|
|
event_stream: { status: "ready", note: "fixture", diagnostics: [] },
|
|
host_worker_bridge: { status: "ready", note: "fixture", diagnostics: [] },
|
|
companion_console: { status: "ready", note: "fixture", diagnostics: [] },
|
|
},
|
|
});
|
|
}
|
|
if (url.pathname === `/api/w/${workspaceId}/repositories`) {
|
|
return json({ workspace_id: workspaceId, items: [], source: "fixture", diagnostics: [] });
|
|
}
|
|
if (url.pathname === `/api/w/${workspaceId}/working-directories`) {
|
|
return json({ workspace_id: workspaceId, items: [], diagnostics: [] });
|
|
}
|
|
if (url.pathname === `/api/w/${workspaceId}/workers`) {
|
|
return json({
|
|
workspace_id: workspaceId,
|
|
limit: 200,
|
|
items: [],
|
|
source: "fixture",
|
|
diagnostics: [],
|
|
});
|
|
}
|
|
if (url.pathname === `/api/w/${workspaceId}/workers/launch-options`) {
|
|
return json({
|
|
workspace_id: workspaceId,
|
|
runtimes: [{
|
|
runtime_id: "embedded-worker-runtime",
|
|
display_name: "Embedded Runtime",
|
|
built_in: true,
|
|
worker_creation_available: true,
|
|
supports_workdir_attachments: false,
|
|
status: "active",
|
|
diagnostics: [],
|
|
}],
|
|
default_profile: "builtin:companion",
|
|
profiles: [
|
|
{
|
|
id: "builtin:companion",
|
|
label: "Companion",
|
|
description: "General Workspace assistance with explicit subject Memory support.",
|
|
feature_connections: { subjektiv: true },
|
|
},
|
|
{
|
|
id: "builtin:standalone",
|
|
label: "Standalone",
|
|
description: "Independent Worker without Workspace subject Memory support.",
|
|
feature_connections: { subjektiv: false },
|
|
},
|
|
],
|
|
repositories: [],
|
|
working_directories: [],
|
|
diagnostics: [],
|
|
});
|
|
}
|
|
|
|
const subjectsPath = `/api/w/${workspaceId}/subjektiv/subjects`;
|
|
if (url.pathname === subjectsPath) {
|
|
if (request.method === "POST") {
|
|
const payload = await request.json().catch(() => null) as {
|
|
role?: unknown;
|
|
behavior_md?: unknown;
|
|
} | null;
|
|
subjectCreateRequests.push(payload);
|
|
if (!payload || typeof payload.role !== "string" || payload.role.trim().length === 0) {
|
|
return json({ error: "Bad Request", message: "subject role must not be empty" }, 400);
|
|
}
|
|
if (payload.role === "Permission denied") {
|
|
return json({ error: "Forbidden", message: "workspace permission denied" }, 403);
|
|
}
|
|
if (typeof payload.behavior_md !== "string") {
|
|
return json({ error: "Bad Request", message: "subject behavior must be a string" }, 400);
|
|
}
|
|
createdSubjectCount += 1;
|
|
const id = `created-subject-${String(createdSubjectCount).padStart(4, "0")}`;
|
|
const created = subject(id, payload.role, "active", "fingerprint-empty", payload.behavior_md);
|
|
createdSubjectIds.add(id);
|
|
subjects.unshift(created);
|
|
return json(created, 201);
|
|
}
|
|
if (request.method !== "GET") return new Response("method not allowed", { status: 405 });
|
|
const cursor = url.searchParams.get("cursor");
|
|
if (cursor === errorSubjectPageCursor) {
|
|
return json({ error: "Service Unavailable", message: "subject list unavailable" }, 503);
|
|
}
|
|
if (cursor === emptySubjectPageCursor) {
|
|
return json({ limit: 100, items: [], has_more: false });
|
|
}
|
|
const continued = cursor === subjectPageCursor;
|
|
return json({
|
|
limit: 100,
|
|
items: continued ? [pagedSubject] : subjects,
|
|
...(continued ? {} : { next_cursor: subjectPageCursor }),
|
|
has_more: !continued,
|
|
});
|
|
}
|
|
if (url.pathname.startsWith(`${subjectsPath}/`)) {
|
|
const suffix = url.pathname.slice(subjectsPath.length + 1);
|
|
const parts = suffix.split("/").map(decodeURIComponent);
|
|
const subjectId = parts[0];
|
|
if (subjectId === errorSubjectId) return json({}, 503);
|
|
const foundSubject = subjects.find((item) => item.id === subjectId);
|
|
if (!foundSubject) return json({}, 404);
|
|
|
|
if (parts.length === 2 && parts[1] === "journal-browser") {
|
|
return await journalBrowserFixture(request, subjectId);
|
|
}
|
|
if (parts.length === 2 && parts[1] === "behavior" && request.method === "PATCH") {
|
|
const payload = await request.json().catch(() => null) as {
|
|
expected_behavior_md?: unknown;
|
|
behavior_md?: unknown;
|
|
} | null;
|
|
if (
|
|
!payload ||
|
|
typeof payload.expected_behavior_md !== "string" ||
|
|
typeof payload.behavior_md !== "string"
|
|
) {
|
|
return json({ error: "Bad Request", message: "invalid behavior update" }, 400);
|
|
}
|
|
if (payload.expected_behavior_md !== foundSubject.behavior_md) {
|
|
return json({ error: "Conflict", message: "subject behavior content conflict" }, 409);
|
|
}
|
|
foundSubject.behavior_md = payload.behavior_md;
|
|
foundSubject.updated_at = "2026-01-03T04:05:06Z";
|
|
return json(foundSubject);
|
|
}
|
|
if (request.method !== "GET") return new Response("method not allowed", { status: 405 });
|
|
if (parts.length === 1) return json(foundSubject);
|
|
if (parts.length === 2 && parts[1] === "surface") return json(surfaceFor(subjectId));
|
|
if (parts.length === 2 && parts[1] === "memories") {
|
|
const paginated = subjectId === representativeSubjectId;
|
|
const continued = paginated && url.searchParams.get("cursor") === memoryPageCursor;
|
|
const items = continued
|
|
? [
|
|
memorySummary(
|
|
"memory-page-two",
|
|
"change-original",
|
|
"lesson",
|
|
"active",
|
|
"Memory on the next page",
|
|
),
|
|
]
|
|
: memoriesFor(subjectId);
|
|
return json({
|
|
items,
|
|
...(paginated && !continued ? { next_cursor: memoryPageCursor } : {}),
|
|
has_more: paginated && !continued,
|
|
});
|
|
}
|
|
if (parts.length >= 3 && parts[1] === "memories") {
|
|
const memoryId = parts[2];
|
|
if (parts.length === 4 && parts[3] === "changes") {
|
|
return json(changes(memoryId, url.searchParams.get("cursor")));
|
|
}
|
|
if (parts.length === 3) {
|
|
const requested = url.searchParams.get("change_id");
|
|
const knownChanges = memoryId === derivedMemoryId
|
|
? ["change-parent"]
|
|
: ["change-original", "change-earlier", "change-current"];
|
|
if (requested !== null && !knownChanges.includes(requested)) return json({}, 404);
|
|
return json(memoryDetail(memoryId, requested));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (
|
|
url.pathname === `/api/w/${workspaceId}/protocol/ws` &&
|
|
request.headers.get("upgrade") === "websocket"
|
|
) {
|
|
const { socket, response } = Deno.upgradeWebSocket(request);
|
|
socket.onmessage = (event) => {
|
|
const frame = JSON.parse(String(event.data));
|
|
if (frame?.message?.method !== "subscribe_events") return;
|
|
const requestId = frame.message.params.request_id;
|
|
socket.send(JSON.stringify({
|
|
protocol_version: 2,
|
|
frame: "response",
|
|
message: {
|
|
result: "subscribed",
|
|
payload: {
|
|
request_id: requestId,
|
|
subscription_id: "fixture-workers",
|
|
selector: { topic: "workspace_workers" },
|
|
snapshot: { topic: "workers", data: { workers: [] } },
|
|
},
|
|
},
|
|
}));
|
|
};
|
|
return response;
|
|
}
|
|
return await staticResponse(url.pathname);
|
|
});
|