diff --git a/README.md b/README.md index e4c63e1569..5c9af5e69e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@
-
+
diff --git a/README_CN.md b/README_CN.md
index bf6d49a47e..f868a23bf8 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -15,7 +15,7 @@
-
+
diff --git a/fnos/docker/app/docker/docker-compose.yaml b/fnos/docker/app/docker/docker-compose.yaml
index e7777579ee..ec313a5e2e 100644
--- a/fnos/docker/app/docker/docker-compose.yaml
+++ b/fnos/docker/app/docker/docker-compose.yaml
@@ -1,6 +1,6 @@
services:
octop:
- image: ghcr.io/tencentcloud/octop:1.0.2b6
+ image: ghcr.io/tencentcloud/octop:1.0.2b7
container_name: octop
restart: unless-stopped
pull_policy: missing
diff --git a/fnos/docker/manifest b/fnos/docker/manifest
index 5f8c16888f..21b6ca0f32 100644
--- a/fnos/docker/manifest
+++ b/fnos/docker/manifest
@@ -1,5 +1,5 @@
appname=octop
-version=1.0.2b6
+version=1.0.2b7
display_name=Octop
desc=腾讯开源的多用户多智能体 AI 助手平台,支持 Web 控制台、CLI、IM 渠道与定时任务,数据全部本地存储。
安装后使用:
首次会从 ghcr.io 下载与本包版本对应的镜像(设备需能访问 GitHub Container Registry),请等到应用中心显示「运行中」再点「打开」,或访问 http://设备IP:8088,用安装向导里设置的账号登录。
忘记密码可到数据目录 octop-login.txt 查看备份(仅记录安装或应用「设置」改密;网页改密后不会自动更新)。
首次登录后:
请在控制台「设置 → 模型」配置 API Key,并按需配置 IM 渠道、远程桌面、浏览器等。
注意:Docker 版通过容器部署,会拉取 ghcr.io/tencentcloud/octop 上与本包版本相同的镜像。
source=thirdparty
diff --git a/fnos/native/manifest b/fnos/native/manifest
index 7a8265caf6..f2fe0a538f 100644
--- a/fnos/native/manifest
+++ b/fnos/native/manifest
@@ -1,5 +1,5 @@
appname=octop-native
-version=1.0.2b6
+version=1.0.2b7
display_name=Octop
desc=腾讯开源的多用户多智能体 AI 助手平台,支持 Web 控制台、CLI、IM 渠道与定时任务,数据全部本地存储。
安装后使用:
请等到应用中心显示「运行中」再点「打开」,或访问 http://设备IP:8089,用安装向导里设置的用户名或邮箱登录。
忘记密码时打开飞牛「文件管理」,进入应用共享里的 Octop 数据目录,查看 octop-login.txt(仅备份安装或应用「设置」改密;网页改密后不会自动更新);也可在应用「设置」中改密。
命令行管理:
支持官方 octop CLI(octop provider list / agent list / user list / skills / backup 等,详见 octop --help)。
首次登录后:
请在控制台「设置 → 模型」配置 API Key,并按需配置 IM 渠道、远程桌面、浏览器等。
注意:本地版直接运行在飞牛主机上,安装前会自动关联安装「Python 3.12」开发工具。
source=thirdparty
diff --git a/src/octop/__init__.py b/src/octop/__init__.py
index 847a5c322a..3836b89559 100644
--- a/src/octop/__init__.py
+++ b/src/octop/__init__.py
@@ -4,7 +4,7 @@
from octop.infra.utils.httpx_proxy import install_httpx_cidr_no_proxy
-__version__ = "1.0.2b6"
+__version__ = "1.0.2b7"
# httpx 0.28 treats NO_PROXY CIDR as an exact IP (see issue #1347). Patch once
# at import so CLI, ``octop run``, and in-process harness clients all honor it.
diff --git a/src/octop/infra/gateway/media/backend_files.py b/src/octop/infra/gateway/media/backend_files.py
index 15f63316fe..5e76369a26 100644
--- a/src/octop/infra/gateway/media/backend_files.py
+++ b/src/octop/infra/gateway/media/backend_files.py
@@ -145,7 +145,14 @@ def is_allowed_host_temp_path(resolved: Path) -> bool:
def file_url_to_abs_path(file_url: str) -> str:
parsed = urllib.parse.urlparse(file_url)
+ netloc = parsed.netloc
path = parsed.path
+ # file://C:/… — two slashes, so urlparse puts the drive letter in netloc
+ # and path alone loses it. Without this the result is drive-relative
+ # (D:\Users\… when the workdir is on D:), which both escapes the
+ # containment check and writes somewhere the caller did not name.
+ if len(netloc) == 2 and netloc[1] == ":" and netloc[0].isalpha():
+ return str(Path(f"{netloc}{urllib.parse.unquote(path)}"))
# file:///C:/… — Windows drive in URL path
if len(path) >= 3 and path[0] == "/" and path[2] == ":":
return str(Path(urllib.parse.unquote(path[1:])))
diff --git a/tests/unit/test_host_download_allow.py b/tests/unit/test_host_download_allow.py
index 4add0cf25c..b08cb10daf 100644
--- a/tests/unit/test_host_download_allow.py
+++ b/tests/unit/test_host_download_allow.py
@@ -66,3 +66,28 @@ def test_file_url_windows_drive_decodes_unicode() -> None:
"""file:///C:/… must unquote and match native Path form (Windows CI)."""
url = "file:///C:/Users/me/out/%E4%BF%9D%E6%8A%A4.pptx"
assert file_url_to_abs_path(url) == str(Path("C:/Users/me/out/保护.pptx"))
+
+
+def test_file_url_windows_drive_in_netloc_keeps_drive() -> None:
+ """``file://C:/…`` — two slashes, so the drive lands in netloc, not path.
+
+ Windows CI builds these URLs from ``Path.as_posix()``, which yields
+ ``file://C:/…``. ``urlparse`` puts ``C:`` in netloc, so reading ``path``
+ alone loses the drive and returns ``/Users/…``. On a runner whose workdir
+ is on D: that resolves drive-relative, which both escapes the containment
+ check and writes somewhere the caller did not name — SEC-2's file://
+ write test came back 200 instead of 403.
+ """
+ assert file_url_to_abs_path("file://C:/Users/me/out/a.pptx") == str(
+ Path("C:/Users/me/out/a.pptx")
+ )
+ # Percent-decoding still applies on this branch.
+ assert file_url_to_abs_path("file://C:/Users/me/out/%E4%BF%9D%E6%8A%A4.pptx") == str(
+ Path("C:/Users/me/out/保护.pptx")
+ )
+ # The three-slash form is unchanged by the fix.
+ assert file_url_to_abs_path("file:///C:/Users/me/out/a.pptx") == str(
+ Path("C:/Users/me/out/a.pptx")
+ )
+ # A UNC host is still a host, not a drive letter.
+ assert file_url_to_abs_path("file://server/share/a.pptx") != str(Path("C:/share/a.pptx"))