chore: v2.8.15 — Oh My Pi login sharing, omp --allow-home, Markdown Preview default, first-login password
- omp-share.js: an admin's switch (S3) runs omp's own auth broker + gateway as the host on loopback; a member's omp pane reaches it as a scoped client keyed by its pane token (clients file = live omp panes only), runs on --model agenthub/<model>, never holds a credential. - omp catalog: --allow-home, so a workspace at the account's home keeps its sessions there (they were recorded under a temp dir and dropped). - Settings → Appearance → Open Markdown as: Preview (default) or Raw, for Files/Changes, the config viewer and a chat's file panel. - First-login password change and password rules, avatar URLs, Artifacts "Mine" includes installed shared skills/plugins, a member's own hook pages.
This commit is contained in:
@@ -733,6 +733,8 @@ export type AuthUser = {
|
||||
/** When the account's avatar was last set (null = none) — versions its URL. */
|
||||
avatarAt?: string | null;
|
||||
createdAt?: string;
|
||||
/** An admin gave this password: the user must choose their own before anything else. */
|
||||
mustChangePassword?: boolean;
|
||||
};
|
||||
export type AuthState = { mode: "local" } | { mode: "server"; user: AuthUser | null };
|
||||
/** GET /api/me/prefs — the signed-in account's own UI preferences (localStorage keys). */
|
||||
@@ -1007,6 +1009,9 @@ export const api = {
|
||||
body: file,
|
||||
headers: { "Content-Type": "application/octet-stream" },
|
||||
}),
|
||||
/** POST /api/me/avatar/url — the server downloads the picture from an https link. */
|
||||
setAvatarFromUrl: (url: string) =>
|
||||
request<MyAccount>("/me/avatar/url", { method: "POST", body: JSON.stringify({ url }) }),
|
||||
/** DELETE /api/me/avatar — back to the initials. */
|
||||
removeAvatar: () => request<MyAccount>("/me/avatar", { method: "DELETE" }),
|
||||
},
|
||||
@@ -3051,6 +3056,14 @@ export const api = {
|
||||
aiTools: {
|
||||
/** GET /api/ai-tools - the catalog, what is installed, enabled, and the default. */
|
||||
list: () => request<AiToolsView>("/ai-tools"),
|
||||
/** GET /api/ai-tools/omp-sharing - the host's Oh My Pi login shared with members (S3). */
|
||||
ompSharing: () => request<OmpSharingState>("/ai-tools/omp-sharing"),
|
||||
/** PUT /api/ai-tools/omp-sharing - an admin's switch. */
|
||||
setOmpSharing: (enabled: boolean) =>
|
||||
request<OmpSharingState>("/ai-tools/omp-sharing", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({ enabled }),
|
||||
}),
|
||||
/** POST /api/ai-tools/refresh - re-read the login shell's PATH and re-detect. */
|
||||
refresh: () => request<AiToolsView>("/ai-tools/refresh", { method: "POST" }),
|
||||
/** PUT /api/ai-tools/:id - enable/disable a tool, make it the default, or set its default model ("" clears). */
|
||||
@@ -4307,6 +4320,16 @@ export interface ArtifactCandidate {
|
||||
pinned: boolean;
|
||||
}
|
||||
|
||||
/** `GET /api/ai-tools/omp-sharing` — never a token. */
|
||||
export interface OmpSharingState {
|
||||
/** Server mode with per-user Linux accounts (S3): the only place it applies. */
|
||||
available: boolean;
|
||||
enabled: boolean;
|
||||
installed: boolean;
|
||||
broker: { running: boolean; error?: string | null };
|
||||
gateway: { running: boolean; port?: number; error?: string | null };
|
||||
}
|
||||
|
||||
export interface ArtifactSettings {
|
||||
serverMode: boolean;
|
||||
canShare: boolean;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @file markdownView — Settings → Appearance → "Open Markdown as": a viewer
|
||||
* preference in `localStorage` (`agenthub.markdown.view`, synced to the
|
||||
* account through `lib/userPrefs.ts`). Preview (the default) opens a `.md`
|
||||
* file rendered; Raw opens its source. Read each time a viewer opens a file
|
||||
* (the Files/Changes panel, the config file viewer, a chat artifact); the
|
||||
* viewer's own Preview/Raw toggle still flips that one file.
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
|
||||
export const MARKDOWN_VIEW_KEY = "agenthub.markdown.view";
|
||||
|
||||
/** True when a Markdown file opens rendered (the default). */
|
||||
export function readMarkdownPreview(): boolean {
|
||||
try {
|
||||
return window.localStorage.getItem(MARKDOWN_VIEW_KEY) !== "raw";
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export function writeMarkdownPreview(on: boolean) {
|
||||
try {
|
||||
if (on) window.localStorage.removeItem(MARKDOWN_VIEW_KEY);
|
||||
else window.localStorage.setItem(MARKDOWN_VIEW_KEY, "raw");
|
||||
} catch {
|
||||
/* storage blocked — the default */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @file passwordRules.ts
|
||||
* @description The rules a password an account picks for ITSELF must pass —
|
||||
* a copy of `passwordProblems` in server/lib/auth.js, so the checklist can
|
||||
* tick each one as it is typed. The server's copy is the one that decides
|
||||
* (EWEAKPASSWORD); a password an admin gives someone else has no rules.
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
|
||||
export const PASSWORD_MIN_LENGTH = 8;
|
||||
|
||||
export const PASSWORD_RULES = ["length", "lower", "upper", "digit", "symbol", "username"] as const;
|
||||
export type PasswordRule = (typeof PASSWORD_RULES)[number];
|
||||
|
||||
/** The rules `password` fails (empty = it passes every one). */
|
||||
export function passwordProblems(password: string, username?: string): PasswordRule[] {
|
||||
const out: PasswordRule[] = [];
|
||||
if ([...password].length < PASSWORD_MIN_LENGTH) out.push("length");
|
||||
if (!/\p{Ll}/u.test(password)) out.push("lower");
|
||||
if (!/\p{Lu}/u.test(password)) out.push("upper");
|
||||
if (!/\p{N}/u.test(password)) out.push("digit");
|
||||
if (!/[^\p{L}\p{N}]/u.test(password)) out.push("symbol");
|
||||
const name = (username || "").toLowerCase();
|
||||
if (name && password.toLowerCase().includes(name)) out.push("username");
|
||||
return out;
|
||||
}
|
||||
@@ -43,6 +43,7 @@ export const PREF_KEYS = [
|
||||
"agenthub.codex.bypass",
|
||||
"agenthub.tour",
|
||||
"agenthub.workspace.allTabs",
|
||||
"agenthub.markdown.view",
|
||||
] as const;
|
||||
const SYNCED = new Set<string>(PREF_KEYS);
|
||||
const PUSH_DELAY_MS = 400;
|
||||
|
||||
Reference in New Issue
Block a user