Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b07594e
Merge pull request #307 from TencentCloud/chore/sync-develop-after-ma…
github-actions[bot] Aug 15, 2026
07259e4
feat(skills): allow Unicode skill names with filesystem-safe validation
chujieHong Aug 16, 2026
0f8e7b4
fix(chat): strip media-offload placeholder text from image history
chujieHong Aug 14, 2026
420d5e2
fix(dashboard): prevent AGENT_NOT_RUNNING race when saving page confi…
chujieHong Aug 17, 2026
961b572
fix(chat): stop message-list jitter while typing during streaming (#303)
chujieHong Aug 17, 2026
f3e2415
fix(plugins): sanitize non-ASCII tool names for strict LLM tool-name …
chujieHong Aug 17, 2026
10012e1
Feat/自定义主题色 (#334)
miaowmint Aug 19, 2026
aa8759e
fix(voice): MiMo TTS low-latency streaming, voice preset fix, TTS aut…
miaowmint Aug 19, 2026
37f612e
Feat/meituan living assistant expert 内置美团生活助手专家 (#328)
miaowmint Aug 19, 2026
1b5f18c
fix(dashboard): adjust CronJobs table layout for better content displ…
liukewia Aug 19, 2026
89f6188
fix(dashboard): move update progress/result prompts above release not…
miaowmint Aug 19, 2026
90621a8
feat: show memory slimming progress on chat and agent status (#321)
Bosheng0422 Aug 19, 2026
1829098
fix(chat): hide stale scroll-to-bottom button after dock close (#304)
chujieHong Aug 19, 2026
94cf7bd
feat(experts): allow custom agent id when creating experts (#333)
miaowmint Aug 19, 2026
8fc95fa
fix(channels): add missing internal scroll to channels panel in fill …
chujieHong Aug 19, 2026
de606bb
feat(expert): extend meituan-living-assistant to 6 quick prompts (#344)
miaowmint Aug 19, 2026
0dce85c
fix: inject Admin env into running agents and sum turn token usage (#…
jubaoliang Aug 19, 2026
9b3aeea
fix(dashboard): hide cron session routing when using a fresh thread (…
jubaoliang Aug 19, 2026
e07824e
Fix/agent env usage workspace (#351)
jubaoliang Aug 20, 2026
454048b
fix(gateway): upgrade to 0.9.3 (#353)
Pandakingxbc Aug 20, 2026
8c21334
fix(usage): add cache-aware token accounting (#352)
Pandakingxbc Aug 20, 2026
afd1857
fix(cli): force utf-8 stdio so octop init does not crash on GBK conso…
c020627 Aug 20, 2026
248dc4d
修复onnx向量模型调用 (#360)
Bosheng0422 Aug 20, 2026
58356a9
fix(skills): keep sibling files when editing a skill's SKILL.md (#365)
chujieHong Aug 20, 2026
389df18
Feat/tencentcloud api expert (#370)
miaowmint Aug 21, 2026
7abaf6b
feat(experts): add clinical learning subscription assistant (#369)
Pandakingxbc Aug 21, 2026
18ab9f6
feat: workspace backend sandboxing, execute-env injection, and epheme…
jubaoliang Aug 21, 2026
30df769
chore: sync develop onto main after manual
github-actions[bot] Aug 21, 2026
9ba89e5
Merge branch 'develop' into chore/sync-develop-after-manual
jubaoliang Aug 22, 2026
10b9d59
Merge pull request #378 from TencentCloud/chore/sync-develop-after-ma…
jubaoliang Aug 22, 2026
98e2ed1
feat(mobile): Remote Phone (adb stream, dashboard, agent tools) (#354)
huangcheng Aug 22, 2026
0ce9cf4
feat(dashboard): layout refactor, email invites, KB text editor, remo…
jubaoliang Aug 22, 2026
c76abfd
fix(dashboard): polish invite flow, boot splash, and chat dock (#381)
jubaoliang Aug 22, 2026
be83961
fix(dashboard): harden auth/request, login polish, mobile gating, hub…
jubaoliang-tencent Aug 22, 2026
8466666
chore(dashboard): rename "invite new user" to "invite user" in zh bun…
jubaoliang Aug 23, 2026
469e37e
chore: release 0.9.26
jubaoliang-tencent Aug 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,7 @@ src/octop/dashboard/assets/*
!src/octop/dashboard/__init__.py
!src/octop/dashboard/.gitkeep

# 美团生活助手专家:随包分发 pt-passport 运行时(Docker 镜像无 npm,
# 依赖此预装产物执行认证;包内文件由来源 tgz 固定,不会被误提交污染)
# 注意:git 不会进入被忽略的目录,因此必须先反排除 node_modules 目录本身
!src/octop/infra/agents/experts/library/meituan-living-assistant/
!src/octop/infra/agents/experts/library/meituan-living-assistant/skills/
!src/octop/infra/agents/experts/library/meituan-living-assistant/skills/meituan-deals/
!src/octop/infra/agents/experts/library/meituan-living-assistant/skills/meituan-deals/scripts/
# Ship Meituan pt-passport runtime; override global node_modules/ ignore.
!src/octop/infra/agents/experts/library/meituan-living-assistant/skills/meituan-deals/scripts/node_modules/
!src/octop/infra/agents/experts/library/meituan-living-assistant/skills/meituan-deals/scripts/node_modules/**

Expand All @@ -98,6 +92,10 @@ logs/
# Local monorepo dev helpers (not for upstream)
scripts/dev-local-link.sh

# Local downloaded platform-tools for mobile DinD testing
.tools/
.tools-sdk/

# Local agent planning docs (not for upstream)
docs/superpowers/
scripts/lh_image_install_octop_tencentos.sh
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@

## [Unreleased]

## [0.9.26] - 2026-08-23

### 新增

- 远程手机(实验性):安装时探测主机移动能力(`capabilities.mobile`,物理机 / Redroid / KVM);能力开启后开放 `GET /api/settings/capabilities` 与 `/api/mobile/*`。控制台「远程手机」支持 adb H.264/JPEG 推流、触控、画质预设、设备信息与 AI 助手面板;智能体移动工具绑定当前远程手机会话
- 控制台布局支持经典 / 极简模式,聊天记录统一承载;用户可选填邮箱(邀请 / 登录);知识库支持应用内编辑 markdown / txt;远程桌面与远程手机合并为统一控制入口

### 修复

- 邀请链接统一为 `/invite?code=`,修复邀请页居中与移动端在 overflow-hidden 壳下的滚动;启动前显示 logo 加载动画;设置页邮箱输入图标对齐;移除聊天坞中的远程手机入口
- 加固 dashboard 鉴权与请求层(setup 锁定 503、401 刷新)、登录页与 AuthGuard 体验;按服务器能力门控移动端功能;远程控制中枢页签文案缩短为「服务器 / 手机」

## [0.9.25] - 2026-08-21

### 新增
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<p align="center">
<a href="https://www.python.org/downloads/"><img alt="Python 3.12+" src="https://img.shields.io/badge/python-3.12%2B-blue?logo=python&logoColor=white" /></a>
<a href="https://github.com/TencentCloud/Octop/blob/main/LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-green" /></a>
<a href="https://github.com/TencentCloud/Octop/releases"><img alt="Version" src="https://img.shields.io/badge/version-0.9.25-orange" /></a>
<a href="https://github.com/TencentCloud/Octop/releases"><img alt="Version" src="https://img.shields.io/badge/version-0.9.26-orange" /></a>
<a href="https://pypi.org/project/octop/"><img src="https://img.shields.io/pypi/v/octop" alt="PyPI" /></a>
<a href="https://github.com/astral-sh/ruff"><img alt="Code Style: Ruff" src="https://img.shields.io/badge/code%20style-ruff-000000?logo=ruff&logoColor=white" /></a>
<a href="https://github.com/TencentCloud/Octop"><img alt="GitHub stars" src="https://img.shields.io/github/stars/TencentCloud/Octop?style=social" /></a>
Expand Down
133 changes: 132 additions & 1 deletion dashboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,109 @@
<!-- Web App Manifest -->
<link rel="manifest" href="/manifest.json" />
<title>Octop</title>
<!--
First-paint boot splash: visible before JS chunks parse. React.createRoot
replaces #root contents on mount, so this disappears automatically.
-->
<style>
html,
body {
height: 100%;
margin: 0;
background: #f7f8fa;
}
#root {
min-height: 100%;
min-height: 100dvh;
}
#octop-boot {
/* fixed + inset: always viewport-centered before app CSS sets #root height */
position: fixed;
inset: 0;
z-index: 2147483646;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
padding: 24px;
margin: 0;
background: #f7f8fa;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
"Noto Sans SC", sans-serif;
color: #8b8fa3;
}
@media (prefers-color-scheme: dark) {
html,
body {
background: #12141c;
}
#octop-boot {
background: #12141c;
color: rgba(255, 255, 255, 0.55);
}
}
#octop-boot-mark {
position: relative;
width: 88px;
height: 88px;
flex: none;
}
#octop-boot-ring {
box-sizing: border-box;
position: absolute;
inset: 0;
border-radius: 50%;
border: 3px solid rgba(232, 93, 117, 0.18);
border-top-color: #e85d75;
animation: octop-boot-spin 0.85s linear infinite;
}
@media (prefers-color-scheme: dark) {
#octop-boot-ring {
border-color: rgba(240, 139, 154, 0.22);
border-top-color: #f08b9a;
}
}
#octop-boot-logo {
position: absolute;
inset: 14px;
width: 60px;
height: 60px;
border-radius: 14px;
object-fit: contain;
display: block;
animation: octop-boot-breathe 1.6s ease-in-out infinite;
}
#octop-boot-text {
font-size: 14px;
font-weight: 500;
letter-spacing: 0.04em;
user-select: none;
}
@keyframes octop-boot-spin {
to {
transform: rotate(360deg);
}
}
@keyframes octop-boot-breathe {
0%,
100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(0.94);
opacity: 0.88;
}
}
@media (prefers-reduced-motion: reduce) {
#octop-boot-ring,
#octop-boot-logo {
animation: none;
}
}
</style>
<!--
Classic script (not type=module) so it runs even when hashed chunks 404.
Shares octop:chunk-reload with reloadOnStaleChunk.ts.
Expand Down Expand Up @@ -90,7 +193,35 @@
</script>
</head>
<body>
<div id="root"></div>
<div id="root">
<div id="octop-boot" role="status" aria-live="polite">
<div id="octop-boot-mark" aria-hidden="true">
<div id="octop-boot-ring"></div>
<img
id="octop-boot-logo"
src="/logo.svg"
alt=""
width="60"
height="60"
/>
</div>
<div id="octop-boot-text">Loading…</div>
</div>
</div>
<script>
(function () {
var el = document.getElementById("octop-boot-text");
if (!el) return;
try {
var lang = (
navigator.language ||
navigator.userLanguage ||
""
).toLowerCase();
if (lang.indexOf("zh") === 0) el.textContent = "加载中…";
} catch (e) {}
})();
</script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
9 changes: 6 additions & 3 deletions dashboard/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { AntdAppProvider } from "./components/AntdAppProvider";
import GlobalErrorBoundary from "./components/ErrorBoundary";
import { ThemeProvider, useTheme } from "./context/ThemeContext";
import { AgentProvider } from "./context/AgentContext";
import { LayoutModeProvider } from "./context/LayoutModeContext";
import { VoiceOutputProvider } from "./context/VoiceOutputContext";
import { useIsMobile } from "./hooks/useIsMobile";
import { useUnauthorizedRedirect } from "./hooks/useUnauthorizedRedirect";
Expand Down Expand Up @@ -109,9 +110,11 @@ function ThemedApp() {
element={
<AuthGuard>
<AgentProvider>
<VoiceOutputProvider>
<MainLayout />
</VoiceOutputProvider>
<LayoutModeProvider>
<VoiceOutputProvider>
<MainLayout />
</VoiceOutputProvider>
</LayoutModeProvider>
</AgentProvider>
</AuthGuard>
}
Expand Down
11 changes: 10 additions & 1 deletion dashboard/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
export * from "./types";

export { request, setAuthToken, getAuthToken, clearAuthToken } from "./request";
export {
request,
setAuthToken,
getAuthToken,
clearAuthToken,
markSetupRequired,
clearSetupRequired,
isSetupRequiredKnown,
SetupRequiredError,
} from "./request";

export { getApiUrl, getWsUrl } from "./config";

Expand Down
75 changes: 54 additions & 21 deletions dashboard/src/api/modules/auth.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { request } from "../request";
import { clearSetupRequired, markSetupRequired, request } from "../request";

/**
* Auth + setup module — adapted to octop's multi-user backend.
Expand Down Expand Up @@ -84,32 +84,65 @@ interface RawLoginResponse {
user: OctopUser;
}

/** Coalesce AuthGuard / Login / Setup probing the same endpoint in parallel. */
let statusInFlight: Promise<AuthStatus> | null = null;

/** Drop any in-flight setup-status probe (e.g. after creating the admin). */
export function invalidateAuthStatusCache(): void {
statusInFlight = null;
}

function applySetupFlags(status: AuthStatus): AuthStatus {
if (status.setup_required) {
markSetupRequired();
} else {
clearSetupRequired();
}
return status;
}

export const authApi = {
/** Probe whether the initial admin has been created. */
getAuthStatus: async (): Promise<AuthStatus> => {
const raw = await request<RawSetupStatus>("/setup/status");
return {
setup_required: raw.setup_required,
setup_done: !raw.setup_required,
enabled: true,
has_password: true,
wizard_password_exists: raw.wizard_password_exists ?? false,
wizard_password_required: raw.wizard_password_required ?? true,
wizard_password_path: raw.wizard_password_path,
database_bound: raw.database_bound ?? false,
database_driver: raw.database_driver ?? null,
};
if (statusInFlight) return statusInFlight;

statusInFlight = (async () => {
try {
const raw = await request<RawSetupStatus>("/setup/status");
const value: AuthStatus = {
setup_required: raw.setup_required,
setup_done: !raw.setup_required,
enabled: true,
has_password: true,
wizard_password_exists: raw.wizard_password_exists ?? false,
wizard_password_required: raw.wizard_password_required ?? true,
wizard_password_path: raw.wizard_password_path,
database_bound: raw.database_bound ?? false,
database_driver: raw.database_driver ?? null,
};
return applySetupFlags(value);
} finally {
statusInFlight = null;
}
})();

return statusInFlight;
},

/** Bootstrap the first admin (only succeeds while user_manager is empty). */
createInitialAdmin: (body: SetupBody) =>
request<{ id: number; username: string; role: string }>(
"/setup/initial-admin",
{
method: "POST",
body: JSON.stringify(body),
},
),
createInitialAdmin: async (body: SetupBody) => {
const result = await request<{
id: number;
username: string;
role: string;
}>("/setup/initial-admin", {
method: "POST",
body: JSON.stringify(body),
});
invalidateAuthStatusCache();
clearSetupRequired();
return result;
},

/** Login — octop uses username + password. */
login: async (username: string, password: string): Promise<LoginResponse> => {
Expand Down
3 changes: 2 additions & 1 deletion dashboard/src/api/modules/invites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface InviteRedeemBody {
username: string;
password: string;
display_name?: string | null;
email?: string | null;
}

export interface InviteRedeemResponse {
Expand All @@ -46,7 +47,7 @@ export interface InviteRedeemResponse {
/** Prefer the browser origin so copied links match how admins reach the UI. */
export function localInviteUrl(code: string): string {
const origin = typeof window !== "undefined" ? window.location.origin : "";
return `${origin}/?invite=${encodeURIComponent(code)}`;
return `${origin}/invite?code=${encodeURIComponent(code)}`;
}

export const invitesApi = {
Expand Down
31 changes: 31 additions & 0 deletions dashboard/src/api/modules/knowledgeBases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,37 @@ export const knowledgeBasesApi = {
body: JSON.stringify({ path }),
}),

createTextDocument: (
id: string,
body: {
name: string;
format: "md" | "txt";
content?: string;
path?: string;
},
) =>
request<KnowledgeDocument>(`/knowledge-bases/${id}/documents/text`, {
method: "POST",
body: JSON.stringify(body),
}),

getTextDocument: (id: string, documentId: string) =>
request<{
id: string;
filename: string;
content_type: string;
text: string;
}>(`/knowledge-bases/${id}/documents/${documentId}/content`),

updateTextDocument: (id: string, documentId: string, content: string) =>
request<KnowledgeDocument>(
`/knowledge-bases/${id}/documents/${documentId}/content`,
{
method: "PUT",
body: JSON.stringify({ content }),
},
),

uploadDocument: (id: string, file: File, relativePath?: string) => {
const body = new FormData();
body.append("upload", file);
Expand Down
Loading
Loading