Initialize repository with harness code and assets

Initial import of all source code, config, and README assets: the
packages workspace (cli, core, server, web, docs, landing, skills),
build scripts, tooling config, and CI workflows.

Includes the data-layout revision made on this branch: the local data
root defaults to ~/.penguin/data (PENGUIN_HOME still overrides; the
installer keeps its binaries in ~/.penguin), and every Agent lives
under <project>/agents/<agent>/ — path helpers, the three
agent-enumeration scans, the system prompt, built-in Skills, tests
and docs all follow the new layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ihk8iQuo3kv2aPjAYEPuR
This commit is contained in:
Yaowei ZhengandClaude Opus 4.8 authored and GitHub committed 2026-07-19 14:06:53 +08:00
1 parent 056bed7aeb
commit 45bfae6e94
543 files changed
+92949

No files matched your search

+65
View File
@@ -0,0 +1,65 @@
/**
* Current-user routes: GET /api/me, PUT /api/me/password, GET|PUT /api/me/prefs.
* ui_prefs is free-form JSON (theme / lastProjectId / credentialGuideSeen, etc.): GET reads
* it whole, PUT shallow-merges (PATCH semantics) — several independent writers each write
* their own fields without clobbering each other.
*/
import { Hono } from "hono";
import type { MeResponse, PrefsResponse, UiPrefs } from "../../api/types.js";
import { toUserInfo } from "../../auth/service.js";
import type { AppEnv } from "../../auth/middleware.js";
import { readJson, requireString } from "../validate.js";
import type { AppDeps } from "../../app.js";
export function meRoutes(deps: AppDeps): Hono<AppEnv> {
const app = new Hono<AppEnv>();
app.get("/", (c) => {
return c.json({ user: toUserInfo(c.var.user) } satisfies MeResponse);
});
// Self-service password change (user settings): validates the old password; on success, the initial-password prompt disappears from GET /api/me.
app.put("/password", async (c) => {
const body = await readJson(c);
const oldPassword = requireString(body, "oldPassword", { label: "oldPassword" });
const newPassword = requireString(body, "newPassword", { label: "newPassword" });
await deps.authService.changePassword(c.var.user.userId, oldPassword, newPassword);
return c.body(null, 204);
});
app.get("/prefs", (c) => {
const raw = deps.prefsRepo.get(c.var.user.userId);
let prefs: UiPrefs = {};
if (raw !== null) {
try {
prefs = JSON.parse(raw) as UiPrefs;
} catch {
prefs = {}; // Corrupted prefs fall back to an empty object
}
}
return c.json({ prefs } satisfies PrefsResponse);
});
// PATCH semantics: the request body is **shallow-merged** into existing prefs, not a
// full replace. prefs has several independent writers (lastProjectId /
// credentialGuideSeen, etc., each writing their own field); a full replace would wipe
// out each other's fields — e.g. writing lastProjectId when switching Projects would
// clear credentialGuideSeen, breaking the "show onboarding once ever" guarantee.
app.put("/prefs", async (c) => {
const body = await readJson(c);
const raw = deps.prefsRepo.get(c.var.user.userId);
let current: UiPrefs = {};
if (raw !== null) {
try {
current = JSON.parse(raw) as UiPrefs;
} catch {
current = {}; // Corrupted prefs fall back to an empty object (consistent with GET).
}
}
const merged = { ...current, ...(body as UiPrefs) };
deps.prefsRepo.set(c.var.user.userId, JSON.stringify(merged));
return c.json({ prefs: merged } satisfies PrefsResponse);
});
return app;
}