@opengsd/gsd-pi 1.1.1-dev.154fd443 → 1.1.1-dev.1854a79a
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/project-sessions.js +4 -2
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/browser-tools/index.js +39 -22
- package/dist/resources/extensions/browser-tools/state.js +12 -0
- package/dist/resources/extensions/browser-tools/tools/session.js +3 -2
- package/dist/resources/extensions/browser-tools/utils.js +3 -3
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +17 -9
- package/dist/resources/extensions/gsd/auto/contracts.js +8 -1
- package/dist/resources/extensions/gsd/auto/orchestrator.js +659 -57
- package/dist/resources/extensions/gsd/auto-prompts.js +14 -1
- package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +5 -0
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +29 -0
- package/dist/resources/extensions/gsd/auto.js +62 -464
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +2 -1
- package/dist/resources/extensions/gsd/debug-logger.js +10 -0
- package/dist/resources/extensions/gsd/doctor-proactive.js +7 -2
- package/dist/resources/extensions/gsd/markdown-renderer.js +31 -32
- package/dist/resources/extensions/gsd/mcp-filter.js +6 -0
- package/dist/resources/extensions/gsd/native-git-bridge.js +9 -0
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/schemas/parsers.js +6 -1
- package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +21 -1
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +169 -20
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
- package/dist/web/standalone/.next/server/chunks/5047.js +2 -0
- package/dist/web/standalone/.next/server/chunks/5124.js +1 -0
- package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/package.json +6 -4
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
- package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +0 -34
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +0 -34
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +11 -3
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/browser-tools/index.ts +39 -22
- package/src/resources/extensions/browser-tools/state.ts +13 -0
- package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
- package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
- package/src/resources/extensions/browser-tools/utils.ts +3 -3
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +18 -8
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +2 -2
- package/src/resources/extensions/gsd/auto/contracts.ts +8 -119
- package/src/resources/extensions/gsd/auto/orchestrator.ts +794 -58
- package/src/resources/extensions/gsd/auto-prompts.ts +21 -1
- package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +5 -0
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +33 -0
- package/src/resources/extensions/gsd/auto.ts +81 -500
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +2 -0
- package/src/resources/extensions/gsd/debug-logger.ts +11 -0
- package/src/resources/extensions/gsd/doctor-proactive.ts +8 -2
- package/src/resources/extensions/gsd/markdown-renderer.ts +38 -19
- package/src/resources/extensions/gsd/mcp-filter.ts +7 -0
- package/src/resources/extensions/gsd/native-git-bridge.ts +9 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/schemas/parsers.ts +6 -1
- package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +31 -10
- package/src/resources/extensions/gsd/tests/artifact-db-drift-memo.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/auto-dispatch-baseline-harness.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +590 -855
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +38 -10
- package/src/resources/extensions/gsd/tests/debug-logger.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/execute-summary-save-empty-project.test.ts +64 -1
- package/src/resources/extensions/gsd/tests/markdown-renderer-parse-cache.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/orchestrator-legacy-parity.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/parse-project-milestone-bridge.test.ts +77 -0
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +19 -5
- package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -3
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +183 -21
- package/dist/web/standalone/.next/server/chunks/678.js +0 -2
- /package/dist/web/standalone/.next/static/{vAecbJ3K9eO213bAxU8Mi → h38jfi0dnRY0y3hbyBszg}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{vAecbJ3K9eO213bAxU8Mi → h38jfi0dnRY0y3hbyBszg}/_ssgManifest.js +0 -0
|
@@ -205,12 +205,17 @@ export async function preDispatchHealthGate(basePath) {
|
|
|
205
205
|
catch {
|
|
206
206
|
// Non-fatal — dispatch continues without STATE.md if rebuild fails
|
|
207
207
|
}
|
|
208
|
+
// #442 Phase 1.7: resolve repo-ness once per gate. ensureWorkspaceGitReady
|
|
209
|
+
// has already run, and nothing below initializes/deinitializes the repo, so
|
|
210
|
+
// the two downstream blocks (integration-branch check, stale-changes
|
|
211
|
+
// snapshot) can share one nativeIsRepo result instead of querying twice.
|
|
212
|
+
const isRepo = nativeIsRepo(basePath);
|
|
208
213
|
// ── Integration branch existence check ──
|
|
209
214
|
// If the active milestone's recorded integration branch no longer exists in
|
|
210
215
|
// git, the merge-back at the end of the milestone will fail. Block dispatch
|
|
211
216
|
// now to surface this before work is lost.
|
|
212
217
|
try {
|
|
213
|
-
if (
|
|
218
|
+
if (isRepo) {
|
|
214
219
|
const state = await deriveState(basePath);
|
|
215
220
|
if (state.activeMilestone) {
|
|
216
221
|
const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git ?? {};
|
|
@@ -231,7 +236,7 @@ export async function preDispatchHealthGate(basePath) {
|
|
|
231
236
|
// If the working tree is dirty and no commit has happened recently,
|
|
232
237
|
// create a safety snapshot so work isn't lost if the next unit crashes.
|
|
233
238
|
try {
|
|
234
|
-
if (
|
|
239
|
+
if (isRepo) {
|
|
235
240
|
const prefs = loadEffectiveGSDPreferences()?.preferences ?? {};
|
|
236
241
|
// `git.snapshots: false` is the canonical toggle that disables WIP
|
|
237
242
|
// snapshot commits — honour it before touching the threshold path (#4420).
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
//
|
|
9
9
|
// Critical invariant: rendered markdown must round-trip through
|
|
10
10
|
// parseRoadmap(), parsePlan(), parseSummary() in files.ts.
|
|
11
|
-
import { readFileSync, existsSync, mkdirSync } from "node:fs";
|
|
11
|
+
import { readFileSync, existsSync, mkdirSync, statSync } from "node:fs";
|
|
12
12
|
import { logWarning } from "./workflow-logger.js";
|
|
13
13
|
import { isClosedStatus } from "./status-guards.js";
|
|
14
14
|
import { dirname, join, relative } from "node:path";
|
|
15
|
-
import { createRequire } from "node:module";
|
|
16
15
|
import { getAllMilestones, getMilestone, getMilestoneSlices, getSliceTasks, getTask, getSlice, insertArtifact, getGateResults, } from "./gsd-db.js";
|
|
17
16
|
import { resolveDir, resolveFile, resolveSliceFile, resolveSlicePath, gsdProjectionRoot, gsdRoot, buildMilestoneFileName, buildTaskFileName, buildSliceFileName, } from "./paths.js";
|
|
18
|
-
import { saveFile, clearParseCache } from "./files.js";
|
|
17
|
+
import { saveFile, clearParseCache, registerCacheClearCallback } from "./files.js";
|
|
18
|
+
import { parseRoadmap, parsePlan } from "./parsers-legacy.js";
|
|
19
19
|
import { invalidateStateCache } from "./state.js";
|
|
20
20
|
import { clearPathCache } from "./paths.js";
|
|
21
21
|
// ─── Helpers ──────────────────────────────────────────────────────────────
|
|
@@ -562,34 +562,35 @@ export async function renderAllFromDb(basePath) {
|
|
|
562
562
|
}
|
|
563
563
|
return result;
|
|
564
564
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
* 1. Roadmap checkbox states vs DB slice statuses
|
|
570
|
-
* 2. Plan checkbox states vs DB task statuses
|
|
571
|
-
* 3. Missing SUMMARY.md files for complete tasks with full_summary_md
|
|
572
|
-
* 4. Missing SUMMARY.md/UAT.md files for complete slices with content
|
|
573
|
-
*
|
|
574
|
-
* Returns a list of stale entries with file path and reason.
|
|
575
|
-
* Logs to stderr when stale files are detected.
|
|
576
|
-
*/
|
|
577
|
-
export function detectStaleRenders(basePath) {
|
|
578
|
-
// Lazy-load parsers — intentional disk-vs-DB comparison requires parsers
|
|
579
|
-
const _require = createRequire(import.meta.url);
|
|
580
|
-
let parseRoadmap, parsePlan;
|
|
565
|
+
const _projectionParseCache = new Map();
|
|
566
|
+
registerCacheClearCallback(() => _projectionParseCache.clear());
|
|
567
|
+
function parseProjectionByIdentity(path, parse) {
|
|
568
|
+
let st = null;
|
|
581
569
|
try {
|
|
582
|
-
|
|
583
|
-
const m = _require("./parsers-legacy.js");
|
|
584
|
-
parseRoadmap = m.parseRoadmap;
|
|
585
|
-
parsePlan = m.parsePlan;
|
|
570
|
+
st = statSync(path);
|
|
586
571
|
}
|
|
587
|
-
catch
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
572
|
+
catch {
|
|
573
|
+
st = null;
|
|
574
|
+
}
|
|
575
|
+
if (st) {
|
|
576
|
+
const hit = _projectionParseCache.get(path);
|
|
577
|
+
if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size) {
|
|
578
|
+
return hit.parsed;
|
|
579
|
+
}
|
|
580
|
+
const parsed = parse(readFileSync(path, "utf-8"));
|
|
581
|
+
_projectionParseCache.set(path, { mtimeMs: st.mtimeMs, size: st.size, parsed });
|
|
582
|
+
return parsed;
|
|
592
583
|
}
|
|
584
|
+
// stat failed (e.g. file vanished between existsSync and here) — fall back to
|
|
585
|
+
// the original plain read+parse so error handling is unchanged.
|
|
586
|
+
return parse(readFileSync(path, "utf-8"));
|
|
587
|
+
}
|
|
588
|
+
export function detectStaleRenders(basePath) {
|
|
589
|
+
// parseRoadmap/parsePlan are statically imported (#442 Phase 1.4): the
|
|
590
|
+
// per-call createRequire("./parsers-legacy") that used to live here ran on
|
|
591
|
+
// every dispatch. The static `./parsers-legacy.js` specifier resolves in
|
|
592
|
+
// both packaged (.js) and source (.ts via the strip-types loader) contexts —
|
|
593
|
+
// the same form a dozen other modules already use.
|
|
593
594
|
const stale = [];
|
|
594
595
|
const milestones = getAllMilestones();
|
|
595
596
|
for (const milestone of milestones) {
|
|
@@ -598,8 +599,7 @@ export function detectStaleRenders(basePath) {
|
|
|
598
599
|
const roadmapPath = resolveRoadmapProjectionPath(basePath, milestone.id);
|
|
599
600
|
if (existsSync(roadmapPath)) {
|
|
600
601
|
try {
|
|
601
|
-
const
|
|
602
|
-
const parsed = parseRoadmap(content);
|
|
602
|
+
const parsed = parseProjectionByIdentity(roadmapPath, parseRoadmap);
|
|
603
603
|
for (const slice of slices) {
|
|
604
604
|
const isCompleteInDb = isClosedStatus(slice.status);
|
|
605
605
|
const roadmapSlice = parsed.slices.find((s) => s.id === slice.id);
|
|
@@ -630,8 +630,7 @@ export function detectStaleRenders(basePath) {
|
|
|
630
630
|
const planPath = resolveSliceFile(basePath, milestone.id, slice.id, "PLAN");
|
|
631
631
|
if (planPath && existsSync(planPath)) {
|
|
632
632
|
try {
|
|
633
|
-
const
|
|
634
|
-
const parsed = parsePlan(content);
|
|
633
|
+
const parsed = parseProjectionByIdentity(planPath, parsePlan);
|
|
635
634
|
for (const task of tasks) {
|
|
636
635
|
const isDoneInDb = isClosedStatus(task.status);
|
|
637
636
|
const planTask = parsed.tasks.find((t) => t.id === task.id);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
2
3
|
import { resolve } from "node:path";
|
|
3
4
|
import { resolveModelMcpConfig } from "./preferences-mcp.js";
|
|
4
5
|
function isRecord(value) {
|
|
@@ -88,6 +89,11 @@ export function discoverBrowserMcpServerName(projectDir) {
|
|
|
88
89
|
export function discoverMcpServerNames(projectDir) {
|
|
89
90
|
return discoverMcpServers(projectDir).map((server) => server.name);
|
|
90
91
|
}
|
|
92
|
+
export function discoverUserMcpServerNames() {
|
|
93
|
+
const userSettingsPath = resolve(homedir(), ".claude", "settings.json");
|
|
94
|
+
const userSettings = readJsonFile(userSettingsPath, true);
|
|
95
|
+
return collectServerEntries(userSettings?.mcpServers).map((s) => s.name);
|
|
96
|
+
}
|
|
91
97
|
export function computeMcpDisallowedTools(modelId, mcpConfig, discoveredServers, workflowServerName) {
|
|
92
98
|
if (!mcpConfig)
|
|
93
99
|
return [];
|
|
@@ -11,6 +11,7 @@ import { GSDError, GSD_GIT_ERROR } from "./errors.js";
|
|
|
11
11
|
import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
|
|
12
12
|
import { getErrorMessage } from "./error-utils.js";
|
|
13
13
|
import { isInfrastructureError } from "./auto/infra-errors.js";
|
|
14
|
+
import { debugCount } from "./debug-logger.js";
|
|
14
15
|
// Issue #453: keep auto-mode bookkeeping on the stable git CLI path unless a
|
|
15
16
|
// caller explicitly opts into the native helper.
|
|
16
17
|
const NATIVE_GSD_GIT_ENABLED = process.env.GSD_ENABLE_NATIVE_GSD_GIT === "1";
|
|
@@ -40,6 +41,8 @@ function loadNative() {
|
|
|
40
41
|
// ─── Fallback Helpers ──────────────────────────────────────────────────────
|
|
41
42
|
/** Run a git command via execFileSync. Returns trimmed stdout. */
|
|
42
43
|
function gitExec(basePath, args, allowFailure = false) {
|
|
44
|
+
// Counts git CLI shell-outs only (native libgit2 paths bypass this helper).
|
|
45
|
+
debugCount("gitInvocations");
|
|
43
46
|
try {
|
|
44
47
|
return execFileSync("git", args, {
|
|
45
48
|
cwd: basePath,
|
|
@@ -64,6 +67,8 @@ function isRetryableGitError(err) {
|
|
|
64
67
|
return code !== null && TRANSIENT_GIT_RETRY_CODES.has(code);
|
|
65
68
|
}
|
|
66
69
|
function execGitFileSyncWithRetry(basePath, args, options) {
|
|
70
|
+
// Counts git CLI shell-outs only (native libgit2 paths bypass this helper).
|
|
71
|
+
debugCount("gitInvocations");
|
|
67
72
|
try {
|
|
68
73
|
return execFileSync("git", args, {
|
|
69
74
|
cwd: basePath,
|
|
@@ -77,6 +82,8 @@ function execGitFileSyncWithRetry(basePath, args, options) {
|
|
|
77
82
|
if (!isRetryableGitError(err))
|
|
78
83
|
throw err;
|
|
79
84
|
sleepSync(GIT_RETRY_DELAY_MS);
|
|
85
|
+
// Retry is a second physical shell-out — count it too.
|
|
86
|
+
debugCount("gitInvocations");
|
|
80
87
|
return execFileSync("git", args, {
|
|
81
88
|
cwd: basePath,
|
|
82
89
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -88,6 +95,8 @@ function execGitFileSyncWithRetry(basePath, args, options) {
|
|
|
88
95
|
}
|
|
89
96
|
/** Run a git command via execFileSync. Returns trimmed stdout. */
|
|
90
97
|
function gitFileExec(basePath, args, allowFailure = false) {
|
|
98
|
+
// Counts git CLI shell-outs only (native libgit2 paths bypass this helper).
|
|
99
|
+
debugCount("gitInvocations");
|
|
91
100
|
try {
|
|
92
101
|
return execFileSync("git", args, {
|
|
93
102
|
cwd: basePath,
|
|
@@ -38,7 +38,7 @@ If slice research is inlined, trust its architectural findings, but verify every
|
|
|
38
38
|
|
|
39
39
|
1. If requirements are preloaded, identify owned and supporting Active requirements.
|
|
40
40
|
2. Call `memory_query` with keywords from the slice title and source files.
|
|
41
|
-
3.
|
|
41
|
+
3. Use the inlined Output Template sections already present in this prompt. Do not read template files from disk.
|
|
42
42
|
4. {{skillActivation}} Record expected executor skills in each task plan's `skills_used` frontmatter.
|
|
43
43
|
5. Define slice verification before tasks. Non-trivial slices need real tests or executable assertions; boundary contracts need contract-exercising checks. Tests must not read .gitignore/gitignored paths such as `.gsd/`, `.planning/`, or `.audits/`.
|
|
44
44
|
6. Include Threat Surface (Q3), Requirement Impact (Q4), proof level, observability, integration closure, Failure Modes (Q5), Load Profile (Q6), and Negative Tests (Q7) only where applicable.
|
|
@@ -6,7 +6,12 @@
|
|
|
6
6
|
const TEMPLATE_TOKEN_RE = /\{\{[^}]+\}\}/;
|
|
7
7
|
const H2_RE = /^##\s+(.+)$/gm;
|
|
8
8
|
const H3_RE = /^###\s+(.+)$/gm;
|
|
9
|
-
|
|
9
|
+
// A milestone line is single-line by construction. Every inter-token gap uses
|
|
10
|
+
// horizontal-whitespace classes (`[^\S\n]`) rather than `\s`, because `\s`
|
|
11
|
+
// matches newlines: a line missing a valid separator would otherwise let the
|
|
12
|
+
// `\s+(?:—|--|-)\s+` clause "bridge" onto the NEXT bullet's `- `, consuming it
|
|
13
|
+
// as the separator and silently swallowing the following well-formed milestone.
|
|
14
|
+
const MILESTONE_LINE_RE = /^-[^\S\n]+\[([ x])\][^\S\n]+(M\d{3}):[^\S\n]+(.+?)[^\S\n]+(?:—|--|-)[^\S\n]+(.+)$/gm;
|
|
10
15
|
const SLICE_HEADER_RE = /^###\s+(S\d{2})\s*(?:—|--|-)\s+(.+)$/m;
|
|
11
16
|
const REQUIREMENT_HEADER_RE = /^###\s+(R\d{3})\s*(?:—|--|-)\s+(.+)$/m;
|
|
12
17
|
function splitH2Sections(content) {
|
|
@@ -250,7 +250,27 @@ function detectDiskSliceIdDivergenceForMilestone(basePath, milestoneId) {
|
|
|
250
250
|
}
|
|
251
251
|
return drifts;
|
|
252
252
|
}
|
|
253
|
-
|
|
253
|
+
// #442 Phase 1.6: the three artifact/DB drift handlers (disk-slice-id,
|
|
254
|
+
// artifact-db-status, completed-milestone-reopened) each call
|
|
255
|
+
// detectArtifactDbDrift and then filter for their own kind — so the full
|
|
256
|
+
// milestone→slice→task walk + artifact SQL + disk scan would run THREE times
|
|
257
|
+
// per detection pass and discard 2/3 of the work. Memoize the result per
|
|
258
|
+
// DriftContext so the three handlers share one computation. The key is the
|
|
259
|
+
// ctx object, which detectAllDrift rebuilds for every pass (and which is
|
|
260
|
+
// unreferenced once the pass ends, so the WeakMap entry is GC'd) — DB/disk
|
|
261
|
+
// state is immutable within a single pass (repairs run only after detection),
|
|
262
|
+
// so this is behavior-preserving. A fresh ctx (e.g. the maintenance command's
|
|
263
|
+
// inline { basePath, state }) always recomputes.
|
|
264
|
+
const _artifactDbDriftMemo = new WeakMap();
|
|
265
|
+
export function detectArtifactDbDrift(state, ctx) {
|
|
266
|
+
const cached = _artifactDbDriftMemo.get(ctx);
|
|
267
|
+
if (cached)
|
|
268
|
+
return cached;
|
|
269
|
+
const computed = computeArtifactDbDrift(state, ctx);
|
|
270
|
+
_artifactDbDriftMemo.set(ctx, computed);
|
|
271
|
+
return computed;
|
|
272
|
+
}
|
|
273
|
+
function computeArtifactDbDrift(_state, ctx) {
|
|
254
274
|
if (!isDbAvailable())
|
|
255
275
|
return [];
|
|
256
276
|
const drifts = [];
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { ensureDbOpen } from "../bootstrap/dynamic-tools.js";
|
|
4
4
|
import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-milestone.js";
|
|
5
5
|
import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot, shouldBlockRootArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
|
|
6
|
-
import { getActiveRequirements,
|
|
6
|
+
import { getActiveRequirements, getAllMilestones, getMilestone, getSliceStatusSummary, getSliceTaskCounts, insertMilestone, insertAssessment, insertGateRun, readTransaction, saveGateResult, upsertQualityGate, } from "../gsd-db.js";
|
|
7
7
|
import { GATE_REGISTRY } from "../gate-registry.js";
|
|
8
8
|
import { generateRequirementsMd, saveArtifactToDb } from "../db-writer.js";
|
|
9
9
|
import { clearPathCache, relSliceFile, resolveGsdPathContract, resolveMilestoneFile, resolveSliceFile } from "../paths.js";
|
|
@@ -74,6 +74,103 @@ function registerProjectMilestoneSequence(content) {
|
|
|
74
74
|
}
|
|
75
75
|
return registered;
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Best-effort recovery of the human one-liner for each milestone id from a
|
|
79
|
+
* (possibly malformed) Milestone Sequence body. Deliberately lenient: tolerates
|
|
80
|
+
* any separator the canonical MILESTONE_LINE_RE rejects (en-dash, " : ", a
|
|
81
|
+
* missing checkbox, etc.) so a model formatting slip does not discard the prose.
|
|
82
|
+
*/
|
|
83
|
+
function recoverMilestoneTails(sequenceBody) {
|
|
84
|
+
const out = new Map();
|
|
85
|
+
const lenient = /^\s*(?:-\s*)?(?:\[[ xX]\]\s*)?(M\d{3})\b\s*[:.\-–—]*\s*(.*)$/;
|
|
86
|
+
for (const rawLine of sequenceBody.split("\n")) {
|
|
87
|
+
const m = rawLine.match(lenient);
|
|
88
|
+
if (m)
|
|
89
|
+
out.set(m[1], m[2].trim());
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
function firstSentence(text) {
|
|
94
|
+
const trimmed = text.trim();
|
|
95
|
+
if (!trimmed)
|
|
96
|
+
return "";
|
|
97
|
+
const idx = trimmed.search(/[.!?](\s|$)/);
|
|
98
|
+
return (idx >= 0 ? trimmed.slice(0, idx + 1) : trimmed).trim();
|
|
99
|
+
}
|
|
100
|
+
/** Render one canonical, parseable milestone line for the given DB row. */
|
|
101
|
+
function renderMilestoneLine(m, recoveredTail) {
|
|
102
|
+
const done = m.status === "complete";
|
|
103
|
+
let oneLiner = recoveredTail;
|
|
104
|
+
// The recovered tail often still carries the title (e.g. "Foo — bar" or
|
|
105
|
+
// "Foo : bar"). Strip a leading repetition of the title, then any separator.
|
|
106
|
+
if (oneLiner.toLowerCase().startsWith(m.title.toLowerCase())) {
|
|
107
|
+
oneLiner = oneLiner.slice(m.title.length).replace(/^\s*[:.\-–—]+\s*/, "").trim();
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
const sep = oneLiner.match(/\s+(?:—|–|--|-|:)\s+/);
|
|
111
|
+
if (sep && sep.index !== undefined)
|
|
112
|
+
oneLiner = oneLiner.slice(sep.index + sep[0].length).trim();
|
|
113
|
+
}
|
|
114
|
+
// MILESTONE_LINE_RE requires non-empty prose after the separator.
|
|
115
|
+
if (!oneLiner)
|
|
116
|
+
oneLiner = firstSentence(m.vision) || (done ? "Completed." : "Planned.");
|
|
117
|
+
return `- [${done ? "x" : " "}] ${m.id}: ${m.title} — ${oneLiner}`;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Rebuild the "## Milestone Sequence" section from authoritative DB rows when a
|
|
121
|
+
* model-authored PROJECT.md projection parsed to zero milestone lines but the DB
|
|
122
|
+
* already holds milestones. The DB is the source of truth (markdown is a
|
|
123
|
+
* projection), so this repairs the projection rather than failing the save.
|
|
124
|
+
* Preserves a leading HTML comment in the section and recovers one-liners
|
|
125
|
+
* best-effort. The returned content parses cleanly under MILESTONE_LINE_RE.
|
|
126
|
+
*/
|
|
127
|
+
function rebuildMilestoneSequenceSection(content, milestones) {
|
|
128
|
+
const lines = content.split("\n");
|
|
129
|
+
const headerIdx = lines.findIndex(l => /^##\s+Milestone Sequence\s*$/.test(l));
|
|
130
|
+
const canonicalLines = (() => {
|
|
131
|
+
// Recover tails from the existing (malformed) body when the section exists.
|
|
132
|
+
let body = "";
|
|
133
|
+
if (headerIdx !== -1) {
|
|
134
|
+
let end = headerIdx + 1;
|
|
135
|
+
while (end < lines.length && !/^##\s+/.test(lines[end]))
|
|
136
|
+
end++;
|
|
137
|
+
body = lines.slice(headerIdx + 1, end).join("\n");
|
|
138
|
+
}
|
|
139
|
+
const tails = recoverMilestoneTails(body);
|
|
140
|
+
return milestones.map(m => renderMilestoneLine(m, tails.get(m.id) ?? ""));
|
|
141
|
+
})();
|
|
142
|
+
if (headerIdx === -1) {
|
|
143
|
+
// No section at all — append a fresh, canonical one.
|
|
144
|
+
const sep = content.endsWith("\n") ? "" : "\n";
|
|
145
|
+
return `${content}${sep}\n## Milestone Sequence\n\n${canonicalLines.join("\n")}\n`;
|
|
146
|
+
}
|
|
147
|
+
let bodyEnd = headerIdx + 1;
|
|
148
|
+
while (bodyEnd < lines.length && !/^##\s+/.test(lines[bodyEnd]))
|
|
149
|
+
bodyEnd++;
|
|
150
|
+
const existingBody = lines.slice(headerIdx + 1, bodyEnd);
|
|
151
|
+
// Preserve a contiguous leading HTML comment block (the "Check off…" hint).
|
|
152
|
+
let i = 0;
|
|
153
|
+
while (i < existingBody.length && existingBody[i].trim() === "")
|
|
154
|
+
i++;
|
|
155
|
+
const preserved = [];
|
|
156
|
+
if (i < existingBody.length && existingBody[i].trim().startsWith("<!--")) {
|
|
157
|
+
while (i < existingBody.length) {
|
|
158
|
+
preserved.push(existingBody[i]);
|
|
159
|
+
const closed = existingBody[i].includes("-->");
|
|
160
|
+
i++;
|
|
161
|
+
if (closed)
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return [
|
|
166
|
+
...lines.slice(0, headerIdx + 1),
|
|
167
|
+
"",
|
|
168
|
+
...(preserved.length ? [...preserved, ""] : []),
|
|
169
|
+
...canonicalLines,
|
|
170
|
+
"",
|
|
171
|
+
...lines.slice(bodyEnd),
|
|
172
|
+
].join("\n");
|
|
173
|
+
}
|
|
77
174
|
async function mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, content) {
|
|
78
175
|
const contract = resolveGsdPathContract(basePath);
|
|
79
176
|
if (!contract.worktreeGsd)
|
|
@@ -192,6 +289,7 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
|
192
289
|
}, basePath);
|
|
193
290
|
await mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, contentToSave);
|
|
194
291
|
let registeredMilestones = [];
|
|
292
|
+
let milestoneSequenceSelfHealed = false;
|
|
195
293
|
if (params.artifact_type === "PROJECT") {
|
|
196
294
|
try {
|
|
197
295
|
registeredMilestones = registerProjectMilestoneSequence(contentToSave);
|
|
@@ -227,28 +325,78 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
|
227
325
|
};
|
|
228
326
|
}
|
|
229
327
|
if (registeredMilestones.length === 0) {
|
|
230
|
-
|
|
328
|
+
const existingMilestones = getAllMilestones();
|
|
329
|
+
if (existingMilestones.length === 0) {
|
|
330
|
+
// Genuine first-save failure: no milestones parsed AND none in the DB.
|
|
331
|
+
// /gsd really would report "No Active Milestone" — hard-fail so the
|
|
332
|
+
// caller rewrites the sequence before proceeding.
|
|
333
|
+
logError("tool", `gsd_summary_save: PROJECT.md saved to ${relativePath} but parsed zero milestones — registration produced no DB rows`, {
|
|
334
|
+
tool: "gsd_summary_save",
|
|
335
|
+
});
|
|
336
|
+
// PROJECT.md was persisted; invalidate so subsequent reads see the new
|
|
337
|
+
// artifacts row even though no milestones registered.
|
|
338
|
+
invalidateStateCache();
|
|
339
|
+
return {
|
|
340
|
+
content: [{
|
|
341
|
+
type: "text",
|
|
342
|
+
text: `Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
|
|
343
|
+
`so no milestones were registered in the DB. /gsd will report "No Active Milestone". ` +
|
|
344
|
+
`Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
|
|
345
|
+
`\`- [ ] M001: <Title> — <One-liner>\` (em-dash, double-dash \`--\`, or single-dash \`-\` separator), then re-call gsd_summary_save(PROJECT).`,
|
|
346
|
+
}],
|
|
347
|
+
details: {
|
|
348
|
+
operation: "save_summary",
|
|
349
|
+
path: relativePath,
|
|
350
|
+
artifact_type: params.artifact_type,
|
|
351
|
+
error: "milestone_registration_empty_parse",
|
|
352
|
+
},
|
|
353
|
+
isError: true,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
// Existing DB rows mean this is projection drift, not data loss. Rebuild
|
|
357
|
+
// the section from DB state and re-persist a parseable projection.
|
|
358
|
+
logWarning("tool", `gsd_summary_save: PROJECT.md parsed zero milestone lines but DB has ${existingMilestones.length} — rebuilding Milestone Sequence from DB (projection self-heal)`, {
|
|
231
359
|
tool: "gsd_summary_save",
|
|
360
|
+
path: relativePath,
|
|
232
361
|
});
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
362
|
+
try {
|
|
363
|
+
const healed = rebuildMilestoneSequenceSection(contentToSave, existingMilestones);
|
|
364
|
+
await saveArtifactToDb({ path: relativePath, artifact_type: params.artifact_type, content: healed }, basePath);
|
|
365
|
+
await mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, healed);
|
|
366
|
+
const healedRegisteredMilestones = registerProjectMilestoneSequence(healed);
|
|
367
|
+
if (healedRegisteredMilestones.length === 0) {
|
|
368
|
+
throw new Error("self-healed PROJECT.md still parsed zero milestone lines");
|
|
369
|
+
}
|
|
370
|
+
registeredMilestones = healedRegisteredMilestones;
|
|
371
|
+
milestoneSequenceSelfHealed = true;
|
|
372
|
+
}
|
|
373
|
+
catch (healErr) {
|
|
374
|
+
const msg = healErr instanceof Error ? healErr.message : String(healErr);
|
|
375
|
+
logError("tool", `gsd_summary_save: Milestone Sequence self-heal failed: ${msg}`, {
|
|
376
|
+
tool: "gsd_summary_save",
|
|
246
377
|
path: relativePath,
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
378
|
+
error: msg,
|
|
379
|
+
});
|
|
380
|
+
invalidateStateCache();
|
|
381
|
+
return {
|
|
382
|
+
content: [{
|
|
383
|
+
type: "text",
|
|
384
|
+
text: `Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
|
|
385
|
+
`and automatic DB-backed Milestone Sequence repair failed: ${msg}. ` +
|
|
386
|
+
`Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
|
|
387
|
+
`\`- [ ] M001: <Title> — <One-liner>\`, then re-call gsd_summary_save(PROJECT).`,
|
|
388
|
+
}],
|
|
389
|
+
details: {
|
|
390
|
+
operation: "save_summary",
|
|
391
|
+
path: relativePath,
|
|
392
|
+
artifact_type: params.artifact_type,
|
|
393
|
+
error: "milestone_sequence_self_heal_failed",
|
|
394
|
+
self_heal_error: msg,
|
|
395
|
+
},
|
|
396
|
+
isError: true,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
invalidateStateCache();
|
|
252
400
|
}
|
|
253
401
|
}
|
|
254
402
|
if (params.artifact_type === "CONTEXT" && !params.task_id) {
|
|
@@ -271,6 +419,7 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
|
271
419
|
artifact_type: params.artifact_type,
|
|
272
420
|
content_source: contentSource,
|
|
273
421
|
...(registeredMilestones.length > 0 ? { registeredMilestones } : {}),
|
|
422
|
+
...(milestoneSequenceSelfHealed ? { milestoneSequenceSelfHealed: true } : {}),
|
|
274
423
|
},
|
|
275
424
|
};
|
|
276
425
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
h38jfi0dnRY0y3hbyBszg
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
"/api/boot/route": "/api/boot",
|
|
5
5
|
"/api/bridge-terminal/input/route": "/api/bridge-terminal/input",
|
|
6
6
|
"/api/bridge-terminal/resize/route": "/api/bridge-terminal/resize",
|
|
7
|
-
"/api/bridge-terminal/stream/route": "/api/bridge-terminal/stream",
|
|
8
7
|
"/api/browse-directories/route": "/api/browse-directories",
|
|
9
|
-
"/api/captures/route": "/api/captures",
|
|
10
8
|
"/api/dev-mode/route": "/api/dev-mode",
|
|
9
|
+
"/api/bridge-terminal/stream/route": "/api/bridge-terminal/stream",
|
|
11
10
|
"/api/cleanup/route": "/api/cleanup",
|
|
12
11
|
"/api/doctor/route": "/api/doctor",
|
|
12
|
+
"/api/captures/route": "/api/captures",
|
|
13
13
|
"/api/export-data/route": "/api/export-data",
|
|
14
14
|
"/api/experimental/route": "/api/experimental",
|
|
15
15
|
"/api/forensics/route": "/api/forensics",
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
"/api/inspect/route": "/api/inspect",
|
|
20
20
|
"/api/knowledge/route": "/api/knowledge",
|
|
21
21
|
"/api/live-state/route": "/api/live-state",
|
|
22
|
-
"/api/files/route": "/api/files",
|
|
23
22
|
"/api/mcp-connections/route": "/api/mcp-connections",
|
|
24
23
|
"/api/notifications/route": "/api/notifications",
|
|
25
|
-
"/api/
|
|
24
|
+
"/api/files/route": "/api/files",
|
|
26
25
|
"/api/onboarding/route": "/api/onboarding",
|
|
26
|
+
"/api/preferences/route": "/api/preferences",
|
|
27
27
|
"/api/recovery/route": "/api/recovery",
|
|
28
28
|
"/api/projects/route": "/api/projects",
|
|
29
29
|
"/api/session/browser/route": "/api/session/browser",
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"/api/settings-data/route": "/api/settings-data",
|
|
33
33
|
"/api/session/manage/route": "/api/session/manage",
|
|
34
34
|
"/api/shutdown/route": "/api/shutdown",
|
|
35
|
-
"/api/skill-health/route": "/api/skill-health",
|
|
36
35
|
"/api/remote-questions/route": "/api/remote-questions",
|
|
36
|
+
"/api/skill-health/route": "/api/skill-health",
|
|
37
37
|
"/api/steer/route": "/api/steer",
|
|
38
|
-
"/api/switch-root/route": "/api/switch-root",
|
|
39
38
|
"/api/terminal/input/route": "/api/terminal/input",
|
|
39
|
+
"/api/switch-root/route": "/api/switch-root",
|
|
40
40
|
"/api/terminal/resize/route": "/api/terminal/resize",
|
|
41
41
|
"/api/terminal/sessions/route": "/api/terminal/sessions",
|
|
42
42
|
"/api/terminal/stream/route": "/api/terminal/stream",
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
],
|
|
5
5
|
"devFiles": [],
|
|
6
6
|
"lowPriorityFiles": [
|
|
7
|
-
"static/
|
|
8
|
-
"static/
|
|
7
|
+
"static/h38jfi0dnRY0y3hbyBszg/_buildManifest.js",
|
|
8
|
+
"static/h38jfi0dnRY0y3hbyBszg/_ssgManifest.js"
|
|
9
9
|
],
|
|
10
10
|
"rootMainFiles": [
|
|
11
11
|
"static/chunks/webpack-dda80a1ef5587410.js",
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
"dynamicRoutes": {},
|
|
79
79
|
"notFoundRoutes": [],
|
|
80
80
|
"preview": {
|
|
81
|
-
"previewModeId": "
|
|
82
|
-
"previewModeSigningKey": "
|
|
83
|
-
"previewModeEncryptionKey": "
|
|
81
|
+
"previewModeId": "30286f2ea254c7b62e2eccbd95bda1f9",
|
|
82
|
+
"previewModeSigningKey": "b6ad03023e97ec79fc6584ed47293aabc27d15552a29811540288010912ec929",
|
|
83
|
+
"previewModeEncryptionKey": "fbee4e739613703bdcbfc9a2d5bdb491f48806955998ccfcf5a201924ea58a34"
|
|
84
84
|
}
|
|
85
85
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-dda80a1ef5587410.js"/><script src="/_next/static/chunks/51aff721-df603e9695992f6b.js" async=""></script><script src="/_next/static/chunks/686-9424f271d9ce5d07.js" async=""></script><script src="/_next/static/chunks/main-app-90d1d8d5e5d2dc6b.js" async=""></script><title>500: This page couldn’t load</title><style>:root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }</style><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;display:flex;align-items:center;justify-content:center"><div style="margin-top:-32px;max-width:325px;padding:32px 28px;text-align:left"><svg width="32" height="32" viewBox="-0.2 -1.5 32 32" fill="none" style="margin-bottom:24px"><path d="M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z" fill="var(--next-error-title)"></path></svg><h1 style="font-size:24px;font-weight:500;letter-spacing:-0.02em;line-height:32px;margin:0 0 12px 0;color:var(--next-error-title)">This page couldn’t load</h1><p style="font-size:14px;font-weight:400;line-height:21px;margin:0 0 20px 0;color:var(--next-error-message)">A server error occurred. Reload to try again.</p><form style="margin:0"><button type="submit" style="display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 12px;font-size:14px;font-weight:500;line-height:20px;border-radius:6px;cursor:pointer;color:var(--next-error-btn-text);background:var(--next-error-btn-bg);border:var(--next-error-btn-border)">Reload</button></form></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-dda80a1ef5587410.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[25020,[],\"\"]\n3:I[81130,[],\"\"]\n4:I[84649,[],\"OutletBoundary\"]\n5:\"$Sreact.suspense\"\n8:I[84649,[],\"ViewportBoundary\"]\na:I[84649,[],\"MetadataBoundary\"]\nc:I[45336,[],\"default\",1]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"_global-error\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"_global-error\",{\"children\":[\"__PAGE__\",{}]}]}],[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"html\",null,{\"id\":\"__next_error__\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"title\",null,{\"children\":\"500: This page couldn’t load\"}],[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\":root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }\"}}]]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"display\":\"flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"style\":{\"marginTop\":\"-32px\",\"maxWidth\":\"325px\",\"padding\":\"32px 28px\",\"textAlign\":\"left\"},\"children\":[[\"$\",\"svg\",null,{\"width\":\"32\",\"height\":\"32\",\"viewBox\":\"-0.2 -1.5 32 32\",\"fill\":\"none\",\"style\":{\"marginBottom\":\"24px\"},\"children\":[\"$\",\"path\",null,{\"d\":\"M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z\",\"fill\":\"var(--next-error-title)\"}]}],[\"$\",\"h1\",null,{\"style\":{\"fontSize\":\"24px\",\"fontWeight\":500,\"letterSpacing\":\"-0.02em\",\"lineHeight\":\"32px\",\"margin\":\"0 0 12px 0\",\"color\":\"var(--next-error-title)\"},\"children\":\"This page couldn’t load\"}],[\"$\",\"p\",null,{\"style\":{\"fontSize\":\"14px\",\"fontWeight\":400,\"lineHeight\":\"21px\",\"margin\":\"0 0 20px 0\",\"color\":\"var(--next-error-message)\"},\"children\":\"A server error occurred. Reload to try again.\"}],[\"$\",\"form\",null,{\"style\":{\"margin\":0},\"children\":[\"$\",\"button\",null,{\"type\":\"submit\",\"style\":{\"display\":\"inline-flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\",\"height\":\"32px\",\"padding\":\"0 12px\",\"fontSize\":\"14px\",\"fontWeight\":500,\"lineHeight\":\"20px\",\"borderRadius\":\"6px\",\"cursor\":\"pointer\",\"color\":\"var(--next-error-btn-text)\",\"background\":\"var(--next-error-btn-bg)\",\"border\":\"var(--next-error-btn-border)\"},\"children\":\"Reload\"}]}]]}]}]}]]}],null,[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@6\"}]}]]}],{},null,false,null]},null,false,\"$@7\"]},null,false,\"$@7\"],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L8\",null,{\"children\":\"$L9\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$La\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Lb\"}]}]}],null]}],false]],\"m\":\"$undefined\",\"G\":[\"$c\",[]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"
|
|
1
|
+
<!DOCTYPE html><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-dda80a1ef5587410.js"/><script src="/_next/static/chunks/51aff721-df603e9695992f6b.js" async=""></script><script src="/_next/static/chunks/686-9424f271d9ce5d07.js" async=""></script><script src="/_next/static/chunks/main-app-90d1d8d5e5d2dc6b.js" async=""></script><title>500: This page couldn’t load</title><style>:root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }</style><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;display:flex;align-items:center;justify-content:center"><div style="margin-top:-32px;max-width:325px;padding:32px 28px;text-align:left"><svg width="32" height="32" viewBox="-0.2 -1.5 32 32" fill="none" style="margin-bottom:24px"><path d="M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z" fill="var(--next-error-title)"></path></svg><h1 style="font-size:24px;font-weight:500;letter-spacing:-0.02em;line-height:32px;margin:0 0 12px 0;color:var(--next-error-title)">This page couldn’t load</h1><p style="font-size:14px;font-weight:400;line-height:21px;margin:0 0 20px 0;color:var(--next-error-message)">A server error occurred. Reload to try again.</p><form style="margin:0"><button type="submit" style="display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 12px;font-size:14px;font-weight:500;line-height:20px;border-radius:6px;cursor:pointer;color:var(--next-error-btn-text);background:var(--next-error-btn-bg);border:var(--next-error-btn-border)">Reload</button></form></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-dda80a1ef5587410.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[25020,[],\"\"]\n3:I[81130,[],\"\"]\n4:I[84649,[],\"OutletBoundary\"]\n5:\"$Sreact.suspense\"\n8:I[84649,[],\"ViewportBoundary\"]\na:I[84649,[],\"MetadataBoundary\"]\nc:I[45336,[],\"default\",1]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"_global-error\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"_global-error\",{\"children\":[\"__PAGE__\",{}]}]}],[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"html\",null,{\"id\":\"__next_error__\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"title\",null,{\"children\":\"500: This page couldn’t load\"}],[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\":root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }\"}}]]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"display\":\"flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"style\":{\"marginTop\":\"-32px\",\"maxWidth\":\"325px\",\"padding\":\"32px 28px\",\"textAlign\":\"left\"},\"children\":[[\"$\",\"svg\",null,{\"width\":\"32\",\"height\":\"32\",\"viewBox\":\"-0.2 -1.5 32 32\",\"fill\":\"none\",\"style\":{\"marginBottom\":\"24px\"},\"children\":[\"$\",\"path\",null,{\"d\":\"M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z\",\"fill\":\"var(--next-error-title)\"}]}],[\"$\",\"h1\",null,{\"style\":{\"fontSize\":\"24px\",\"fontWeight\":500,\"letterSpacing\":\"-0.02em\",\"lineHeight\":\"32px\",\"margin\":\"0 0 12px 0\",\"color\":\"var(--next-error-title)\"},\"children\":\"This page couldn’t load\"}],[\"$\",\"p\",null,{\"style\":{\"fontSize\":\"14px\",\"fontWeight\":400,\"lineHeight\":\"21px\",\"margin\":\"0 0 20px 0\",\"color\":\"var(--next-error-message)\"},\"children\":\"A server error occurred. Reload to try again.\"}],[\"$\",\"form\",null,{\"style\":{\"margin\":0},\"children\":[\"$\",\"button\",null,{\"type\":\"submit\",\"style\":{\"display\":\"inline-flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\",\"height\":\"32px\",\"padding\":\"0 12px\",\"fontSize\":\"14px\",\"fontWeight\":500,\"lineHeight\":\"20px\",\"borderRadius\":\"6px\",\"cursor\":\"pointer\",\"color\":\"var(--next-error-btn-text)\",\"background\":\"var(--next-error-btn-bg)\",\"border\":\"var(--next-error-btn-border)\"},\"children\":\"Reload\"}]}]]}]}]}]]}],null,[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@6\"}]}]]}],{},null,false,null]},null,false,\"$@7\"]},null,false,\"$@7\"],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L8\",null,{\"children\":\"$L9\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$La\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Lb\"}]}]}],null]}],false]],\"m\":\"$undefined\",\"G\":[\"$c\",[]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"h38jfi0dnRY0y3hbyBszg\"}\n"])</script><script>self.__next_f.push([1,"d:[]\n7:\"$Wd\"\n"])</script><script>self.__next_f.push([1,"9:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"6:null\nb:[]\n"])</script></body></html>
|