Skip to content

Commit eb27faf

Browse files
authored
fix(sandbox): resolve inotify infinite loops & proxy loopback connection (#7129)
* Update repository.ts * refactor(fastgpt): update default fastgpt-ide-agent image to ghcr.io
1 parent a1674a5 commit eb27faf

13 files changed

Lines changed: 364 additions & 98 deletions

File tree

deploy/dev/docker-compose.cn.yml

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,21 @@
99
# - fastgpt-mcp-server: 3003
1010
# - fastgpt-plugin: 3004
1111
# - fastgpt-volume-manager: 3005
12+
# - fastgpt-agent-sandbox-proxy: 1006
1213
# - opensandbox-server: 8090
1314
# - fastgpt-aiproxy: 3010
1415
# - fastgpt-aiproxy-pg: 5432
1516
# - 使用 pgvector 作为默认的向量库
16-
# - 配置 opensandbox-config 的 network_mode 为 docker 网络,如 dev_fastgpt
17-
# - 配置 opensandbox-config 的 host_ip 为宿主机 LAN IP,如 192.168.1.100
17+
# - fastgpt-agent-sandbox-proxy 通过端口映射暴露给浏览器,内部回源宿主机服务
1818

1919
# plugin auth token
2020
x-plugin-auth-token: &x-plugin-auth-token 'token'
2121
# code sandbox token
2222
x-code-sandbox-token: &x-code-sandbox-token 'codesandbox'
2323
# volume manager auth token
2424
x-volume-manager-auth-token: &x-volume-manager-auth-token 'vmtoken'
25+
# agent sandbox proxy secret, must match AGENT_SANDBOX_PROXY_SECRET in local FastGPT app
26+
x-agent-sandbox-proxy-secret: &x-agent-sandbox-proxy-secret 'default_fastgpt_agent_sandbox_proxy_secret'
2527
# aiproxy token
2628
x-aiproxy-token: &x-aiproxy-token 'token'
2729
# 数据库连接相关配置
@@ -50,8 +52,8 @@ x-log-config: &x-log-config
5052
# 容器运行环境可能会自动注入 HTTP_PROXY/HTTPS_PROXY。
5153
# 明确绕过 compose 内部服务,避免内部请求被代理劫持。
5254
x-no-proxy-config: &x-no-proxy-config
53-
NO_PROXY: localhost,127.0.0.1,::1,fastgpt,fastgpt-plugin,fastgpt-code-sandbox,fastgpt-aiproxy,fastgpt-aiproxy-pg,fastgpt-minio,fastgpt-mongo,fastgpt-redis,fastgpt-pg,fastgpt-mcp-server,opensandbox-server,fastgpt-volume-manager,host.docker.internal,*.orb.internal,*.orb.local
54-
no_proxy: localhost,127.0.0.1,::1,fastgpt,fastgpt-plugin,fastgpt-code-sandbox,fastgpt-aiproxy,fastgpt-aiproxy-pg,fastgpt-minio,fastgpt-mongo,fastgpt-redis,fastgpt-pg,fastgpt-mcp-server,opensandbox-server,fastgpt-volume-manager,host.docker.internal,*.orb.internal,*.orb.local
55+
NO_PROXY: localhost,127.0.0.1,::1,fastgpt,fastgpt-plugin,fastgpt-code-sandbox,fastgpt-agent-sandbox-proxy,fastgpt-aiproxy,fastgpt-aiproxy-pg,fastgpt-minio,fastgpt-mongo,fastgpt-redis,fastgpt-pg,fastgpt-mcp-server,opensandbox-server,fastgpt-volume-manager,host.docker.internal,*.orb.internal,*.orb.local
56+
no_proxy: localhost,127.0.0.1,::1,fastgpt,fastgpt-plugin,fastgpt-code-sandbox,fastgpt-agent-sandbox-proxy,fastgpt-aiproxy,fastgpt-aiproxy-pg,fastgpt-minio,fastgpt-mongo,fastgpt-redis,fastgpt-pg,fastgpt-mcp-server,opensandbox-server,fastgpt-volume-manager,host.docker.internal,*.orb.internal,*.orb.local
5557

5658
services:
5759
# Vector DB
@@ -233,6 +235,24 @@ services:
233235
interval: 30s
234236
timeout: 20s
235237
retries: 3
238+
fastgpt-agent-sandbox-proxy:
239+
container_name: fastgpt-agent-sandbox-proxy
240+
image: ghcr.io/labring/fastgpt-agent-sandbox-proxy:v0.2.0-beta1
241+
ports:
242+
- 1006:1006
243+
networks:
244+
- fastgpt
245+
restart: always
246+
extra_hosts:
247+
- 'host.docker.internal:host-gateway'
248+
environment:
249+
<<: [*x-no-proxy-config]
250+
PORT: 1006
251+
# dev compose 不包含 FastGPT 主站,默认回调宿主机本地启动的 app。
252+
FASTGPT_APP_URL: http://host.docker.internal:3000
253+
# 主站返回 localhost upstream endpoint 时,proxy 容器需要改写为宿主机别名再连接。
254+
AGENT_SANDBOX_PROXY_REWRITE_HOST: host.docker.internal
255+
AGENT_SANDBOX_PROXY_SECRET: *x-agent-sandbox-proxy-secret
236256
fastgpt-mcp-server:
237257
container_name: fastgpt-mcp-server
238258
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.15.0-beta1
@@ -298,7 +318,7 @@ services:
298318
retries: 5
299319
# Pre-pull only: not started by `docker compose up` (uses profile `prepull`).
300320
opensandbox-agent-sandbox-image:
301-
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-agent-sandbox:v0.2.0
321+
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-agent-sandbox:v0.2.0-beta1
302322
profiles:
303323
- prepull
304324
opensandbox-execd-image:

deploy/dev/docker-compose.yml

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,21 @@
99
# - fastgpt-mcp-server: 3003
1010
# - fastgpt-plugin: 3004
1111
# - fastgpt-volume-manager: 3005
12+
# - fastgpt-agent-sandbox-proxy: 1006
1213
# - opensandbox-server: 8090
1314
# - fastgpt-aiproxy: 3010
1415
# - fastgpt-aiproxy-pg: 5432
1516
# - 使用 pgvector 作为默认的向量库
16-
# - 配置 opensandbox-config 的 network_mode 为 docker 网络,如 dev_fastgpt
17-
# - 配置 opensandbox-config 的 host_ip 为宿主机 LAN IP,如 192.168.1.100
17+
# - fastgpt-agent-sandbox-proxy 通过端口映射暴露给浏览器,内部回源宿主机服务
1818

1919
# plugin auth token
2020
x-plugin-auth-token: &x-plugin-auth-token 'token'
2121
# code sandbox token
2222
x-code-sandbox-token: &x-code-sandbox-token 'codesandbox'
2323
# volume manager auth token
2424
x-volume-manager-auth-token: &x-volume-manager-auth-token 'vmtoken'
25+
# agent sandbox proxy secret, must match AGENT_SANDBOX_PROXY_SECRET in local FastGPT app
26+
x-agent-sandbox-proxy-secret: &x-agent-sandbox-proxy-secret 'default_fastgpt_agent_sandbox_proxy_secret'
2527
# aiproxy token
2628
x-aiproxy-token: &x-aiproxy-token 'token'
2729
# 数据库连接相关配置
@@ -50,8 +52,8 @@ x-log-config: &x-log-config
5052
# 容器运行环境可能会自动注入 HTTP_PROXY/HTTPS_PROXY。
5153
# 明确绕过 compose 内部服务,避免内部请求被代理劫持。
5254
x-no-proxy-config: &x-no-proxy-config
53-
NO_PROXY: localhost,127.0.0.1,::1,fastgpt,fastgpt-plugin,fastgpt-code-sandbox,fastgpt-aiproxy,fastgpt-aiproxy-pg,fastgpt-minio,fastgpt-mongo,fastgpt-redis,fastgpt-pg,fastgpt-mcp-server,opensandbox-server,fastgpt-volume-manager,host.docker.internal,*.orb.internal,*.orb.local
54-
no_proxy: localhost,127.0.0.1,::1,fastgpt,fastgpt-plugin,fastgpt-code-sandbox,fastgpt-aiproxy,fastgpt-aiproxy-pg,fastgpt-minio,fastgpt-mongo,fastgpt-redis,fastgpt-pg,fastgpt-mcp-server,opensandbox-server,fastgpt-volume-manager,host.docker.internal,*.orb.internal,*.orb.local
55+
NO_PROXY: localhost,127.0.0.1,::1,fastgpt,fastgpt-plugin,fastgpt-code-sandbox,fastgpt-agent-sandbox-proxy,fastgpt-aiproxy,fastgpt-aiproxy-pg,fastgpt-minio,fastgpt-mongo,fastgpt-redis,fastgpt-pg,fastgpt-mcp-server,opensandbox-server,fastgpt-volume-manager,host.docker.internal,*.orb.internal,*.orb.local
56+
no_proxy: localhost,127.0.0.1,::1,fastgpt,fastgpt-plugin,fastgpt-code-sandbox,fastgpt-agent-sandbox-proxy,fastgpt-aiproxy,fastgpt-aiproxy-pg,fastgpt-minio,fastgpt-mongo,fastgpt-redis,fastgpt-pg,fastgpt-mcp-server,opensandbox-server,fastgpt-volume-manager,host.docker.internal,*.orb.internal,*.orb.local
5557

5658
services:
5759
# Vector DB
@@ -233,6 +235,24 @@ services:
233235
interval: 30s
234236
timeout: 20s
235237
retries: 3
238+
fastgpt-agent-sandbox-proxy:
239+
container_name: fastgpt-agent-sandbox-proxy
240+
image: ghcr.io/labring/fastgpt-agent-sandbox-proxy:v0.2.0-beta1
241+
ports:
242+
- 1006:1006
243+
networks:
244+
- fastgpt
245+
restart: always
246+
extra_hosts:
247+
- 'host.docker.internal:host-gateway'
248+
environment:
249+
<<: [*x-no-proxy-config]
250+
PORT: 1006
251+
# dev compose 不包含 FastGPT 主站,默认回调宿主机本地启动的 app。
252+
FASTGPT_APP_URL: http://host.docker.internal:3000
253+
# 主站返回 localhost upstream endpoint 时,proxy 容器需要改写为宿主机别名再连接。
254+
AGENT_SANDBOX_PROXY_REWRITE_HOST: host.docker.internal
255+
AGENT_SANDBOX_PROXY_SECRET: *x-agent-sandbox-proxy-secret
236256
fastgpt-mcp-server:
237257
container_name: fastgpt-mcp-server
238258
image: ghcr.io/labring/fastgpt-mcp_server:v4.15.0-beta1
@@ -298,7 +318,7 @@ services:
298318
retries: 5
299319
# Pre-pull only: not started by `docker compose up` (uses profile `prepull`).
300320
opensandbox-agent-sandbox-image:
301-
image: ghcr.io/labring/fastgpt-agent-sandbox:v0.2.0
321+
image: ghcr.io/labring/fastgpt-agent-sandbox:v0.2.0-beta1
302322
profiles:
303323
- prepull
304324
opensandbox-execd-image:

packages/service/core/ai/sandbox/instance/repository.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const buildSandboxResourcesToArchiveQuery = (params: {
4949
status: SandboxStatusEnum.stopped,
5050
lastActiveAt: { $lt: inactiveBefore },
5151
'metadata.archive.state': { $exists: false }
52-
}).sort({ lastActiveAt: 1 });
52+
}).sort({ lastActiveAt: -1 });
5353
};
5454

5555
/**

packages/service/test/core/ai/sandbox/instance/repository.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,65 @@ describe('sandbox instance helpers', () => {
449449
expect(stored?.storage).toBeUndefined();
450450
});
451451

452+
it('streams archive candidates by lastActiveAt descending', async () => {
453+
const inactiveBefore = new Date('2026-02-01T00:00:00.000Z');
454+
const appId = `instance-helper-${getNanoid()}`;
455+
const [olderDoc, newerDoc] = await MongoSandboxInstance.create([
456+
{
457+
provider: 'opensandbox',
458+
sandboxId: `instance-helper-${getNanoid()}`,
459+
appId,
460+
userId: 'user-1',
461+
chatId: 'archive-old-chat',
462+
type: SandboxTypeEnum.sessionRuntime,
463+
status: SandboxStatusEnum.stopped,
464+
lastActiveAt: new Date('2026-01-01T00:00:00.000Z'),
465+
createdAt: new Date(),
466+
metadata: {
467+
image: { repository: 'image' }
468+
}
469+
},
470+
{
471+
provider: 'opensandbox',
472+
sandboxId: `instance-helper-${getNanoid()}`,
473+
appId,
474+
userId: 'user-1',
475+
chatId: 'archive-new-chat',
476+
type: SandboxTypeEnum.sessionRuntime,
477+
status: SandboxStatusEnum.stopped,
478+
lastActiveAt: new Date('2026-01-20T00:00:00.000Z'),
479+
createdAt: new Date(),
480+
metadata: {
481+
image: { repository: 'image' }
482+
}
483+
},
484+
{
485+
provider: 'opensandbox',
486+
sandboxId: `instance-helper-${getNanoid()}`,
487+
appId,
488+
userId: 'user-1',
489+
chatId: 'archive-active-chat',
490+
type: SandboxTypeEnum.sessionRuntime,
491+
status: SandboxStatusEnum.stopped,
492+
lastActiveAt: new Date('2026-02-20T00:00:00.000Z'),
493+
createdAt: new Date(),
494+
metadata: {
495+
image: { repository: 'image' }
496+
}
497+
}
498+
]);
499+
500+
const resources = await collectArchiveCursor({
501+
inactiveBefore,
502+
providers: ['opensandbox']
503+
});
504+
505+
expect(resources.map((item) => item.sandboxId)).toEqual([
506+
newerDoc.sandboxId,
507+
olderDoc.sandboxId
508+
]);
509+
});
510+
452511
it('supports repository optional provider and update branches', async () => {
453512
const appId = `instance-helper-${getNanoid()}`;
454513
const chatId = `chat-${getNanoid()}`;

projects/agent-sandbox-proxy/.env.template

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,7 @@ FASTGPT_APP_URL=http://localhost:3000
1717

1818
# 5. Proxy 回源主站请求超时时间(秒)。需覆盖主站冷启动沙盒、读取 agent password 和 endpoint 查询。
1919
FASTGPT_APP_REQUEST_TIMEOUT_SECS=10
20+
21+
# 6. 将主站返回的 localhost 上游地址改写为容器可访问的 Host。
22+
# dev compose 通常填 host.docker.internal;生产环境一般留空。
23+
AGENT_SANDBOX_PROXY_REWRITE_HOST=

0 commit comments

Comments
 (0)