@next-open-ai/openbot 0.3.2 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +1 -2
  2. package/apps/desktop/renderer/dist/assets/index-BNuvb6Ay.css +10 -0
  3. package/apps/desktop/renderer/dist/assets/index-DvQjslfT.js +89 -0
  4. package/apps/desktop/renderer/dist/index.html +2 -2
  5. package/dist/core/agent/agent-manager.d.ts +15 -7
  6. package/dist/core/agent/agent-manager.js +52 -21
  7. package/dist/core/agent/run.js +2 -2
  8. package/dist/core/config/desktop-config.d.ts +13 -0
  9. package/dist/core/config/desktop-config.js +19 -1
  10. package/dist/core/session-current-agent.d.ts +34 -0
  11. package/dist/core/session-current-agent.js +32 -0
  12. package/dist/core/tools/create-agent-tool.d.ts +6 -0
  13. package/dist/core/tools/create-agent-tool.js +97 -0
  14. package/dist/core/tools/index.d.ts +3 -0
  15. package/dist/core/tools/index.js +3 -0
  16. package/dist/core/tools/list-agents-tool.d.ts +5 -0
  17. package/dist/core/tools/list-agents-tool.js +45 -0
  18. package/dist/core/tools/switch-agent-tool.d.ts +6 -0
  19. package/dist/core/tools/switch-agent-tool.js +54 -0
  20. package/dist/gateway/channel/adapters/feishu.d.ts +11 -0
  21. package/dist/gateway/channel/adapters/feishu.js +218 -0
  22. package/dist/gateway/channel/channel-core.d.ts +9 -0
  23. package/dist/gateway/channel/channel-core.js +127 -0
  24. package/dist/gateway/channel/registry.d.ts +16 -0
  25. package/dist/gateway/channel/registry.js +54 -0
  26. package/dist/gateway/channel/run-agent.d.ts +26 -0
  27. package/dist/gateway/channel/run-agent.js +137 -0
  28. package/dist/gateway/channel/session-persistence.d.ts +36 -0
  29. package/dist/gateway/channel/session-persistence.js +46 -0
  30. package/dist/gateway/channel/types.d.ts +70 -0
  31. package/dist/gateway/channel/types.js +4 -0
  32. package/dist/gateway/channel-handler.d.ts +3 -4
  33. package/dist/gateway/channel-handler.js +8 -2
  34. package/dist/gateway/methods/agent-chat.js +30 -12
  35. package/dist/gateway/methods/run-scheduled-task.js +4 -2
  36. package/dist/gateway/server.js +50 -1
  37. package/dist/server/agent-config/agent-config.controller.d.ts +6 -1
  38. package/dist/server/agent-config/agent-config.service.d.ts +12 -1
  39. package/dist/server/agent-config/agent-config.service.js +10 -3
  40. package/dist/server/agents/agents.controller.d.ts +10 -0
  41. package/dist/server/agents/agents.controller.js +35 -1
  42. package/dist/server/agents/agents.gateway.js +18 -4
  43. package/dist/server/agents/agents.service.d.ts +4 -0
  44. package/dist/server/agents/agents.service.js +17 -1
  45. package/dist/server/config/config.controller.d.ts +2 -0
  46. package/dist/server/config/config.service.d.ts +3 -0
  47. package/dist/server/config/config.service.js +3 -1
  48. package/dist/server/saved-items/saved-items.controller.d.ts +32 -1
  49. package/dist/server/saved-items/saved-items.controller.js +154 -3
  50. package/dist/server/saved-items/saved-items.module.js +3 -1
  51. package/dist/server/workspace/workspace.service.d.ts +11 -0
  52. package/dist/server/workspace/workspace.service.js +40 -1
  53. package/package.json +2 -1
  54. package/apps/desktop/renderer/dist/assets/index-DKtaRFW4.js +0 -89
  55. package/apps/desktop/renderer/dist/assets/index-QHuqXpWQ.css +0 -10
@@ -1,11 +1,17 @@
1
+ import type { Response } from 'express';
1
2
  import { SavedItemsService } from './saved-items.service.js';
3
+ import { WorkspaceService } from '../workspace/workspace.service.js';
4
+ import { ConfigService } from '../config/config.service.js';
2
5
  export declare class SavedItemsController {
3
6
  private readonly savedItemsService;
4
- constructor(savedItemsService: SavedItemsService);
7
+ private readonly workspaceService;
8
+ private readonly configService;
9
+ constructor(savedItemsService: SavedItemsService, workspaceService: WorkspaceService, configService: ConfigService);
5
10
  list(tagId?: string, workspace?: string): Promise<{
6
11
  success: boolean;
7
12
  data: import("./saved-items.service.js").SavedItem[];
8
13
  }>;
14
+ imageProxy(url: string, res: Response): Promise<void>;
9
15
  get(id: string): Promise<{
10
16
  success: boolean;
11
17
  data: import("./saved-items.service.js").SavedItem;
@@ -23,4 +29,29 @@ export declare class SavedItemsController {
23
29
  delete(id: string): Promise<{
24
30
  success: boolean;
25
31
  }>;
32
+ /**
33
+ * Download the saved item URL to workspace .favorite or to a user-chosen directory.
34
+ * Body: { workspace?: string, targetDir?: string }.
35
+ * If targetDir (absolute path) is provided, file is written there; otherwise to workspace/.favorite.
36
+ */
37
+ downloadToWorkspace(id: string, body: {
38
+ workspace?: string;
39
+ targetDir?: string;
40
+ }): Promise<{
41
+ success: boolean;
42
+ data: {
43
+ absolutePath: string;
44
+ targetDir: string;
45
+ relativePath?: undefined;
46
+ workspace?: undefined;
47
+ };
48
+ } | {
49
+ success: boolean;
50
+ data: {
51
+ relativePath: string;
52
+ workspace: string;
53
+ absolutePath?: undefined;
54
+ targetDir?: undefined;
55
+ };
56
+ }>;
26
57
  }
@@ -10,17 +10,114 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  var __param = (this && this.__param) || function (paramIndex, decorator) {
11
11
  return function (target, key) { decorator(target, key, paramIndex); }
12
12
  };
13
- import { Controller, Get, Post, Delete, Body, Param, Query, HttpException, HttpStatus, } from '@nestjs/common';
13
+ import { Controller, Get, Post, Delete, Body, Param, Query, Res, HttpException, HttpStatus, } from '@nestjs/common';
14
+ import { readFile } from 'fs/promises';
15
+ import { resolve } from 'path';
16
+ import { homedir } from 'os';
17
+ import { existsSync } from 'fs';
14
18
  import { SavedItemsService } from './saved-items.service.js';
19
+ import { WorkspaceService } from '../workspace/workspace.service.js';
20
+ import { ConfigService } from '../config/config.service.js';
21
+ import { getOpenbotWorkspaceDir } from '../../core/agent/agent-dir.js';
22
+ const IMAGE_EXT_MIME = {
23
+ jpg: 'image/jpeg',
24
+ jpeg: 'image/jpeg',
25
+ png: 'image/png',
26
+ gif: 'image/gif',
27
+ webp: 'image/webp',
28
+ svg: 'image/svg+xml',
29
+ bmp: 'image/bmp',
30
+ ico: 'image/x-icon',
31
+ };
32
+ function mimeFromPath(filePath) {
33
+ const ext = filePath.split('.').pop()?.toLowerCase() || '';
34
+ return IMAGE_EXT_MIME[ext] || 'application/octet-stream';
35
+ }
36
+ function safeFilenameFromUrl(url, contentType) {
37
+ try {
38
+ const u = new URL(url);
39
+ const pathname = u.pathname || '';
40
+ const segment = pathname.split('/').filter(Boolean).pop() || '';
41
+ const decoded = decodeURIComponent(segment);
42
+ const base = decoded.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 180);
43
+ if (base)
44
+ return base;
45
+ }
46
+ catch (_) { }
47
+ const ct = (contentType || '').split(';')[0].trim().toLowerCase();
48
+ let ext = 'bin';
49
+ if (ct.includes('image/'))
50
+ ext = ct.replace('image/', '').trim() || 'png';
51
+ else if (ct.includes('text/html'))
52
+ ext = 'html';
53
+ else if (ct.includes('text/plain'))
54
+ ext = 'txt';
55
+ return `download_${Date.now()}.${ext}`;
56
+ }
15
57
  let SavedItemsController = class SavedItemsController {
16
58
  savedItemsService;
17
- constructor(savedItemsService) {
59
+ workspaceService;
60
+ configService;
61
+ constructor(savedItemsService, workspaceService, configService) {
18
62
  this.savedItemsService = savedItemsService;
63
+ this.workspaceService = workspaceService;
64
+ this.configService = configService;
19
65
  }
20
66
  async list(tagId, workspace) {
21
67
  const data = await this.savedItemsService.findAll({ tagId, workspace });
22
68
  return { success: true, data };
23
69
  }
70
+ async imageProxy(url, res) {
71
+ if (!url?.trim())
72
+ throw new HttpException('url is required', HttpStatus.BAD_REQUEST);
73
+ const decoded = decodeURIComponent(url.trim());
74
+ if (decoded.startsWith('file://') || decoded.startsWith('file:/')) {
75
+ try {
76
+ let pathPart;
77
+ try {
78
+ const u = new URL(decoded);
79
+ pathPart = (u.pathname || decoded).replace(/^\/([a-z]:)/i, '$1');
80
+ }
81
+ catch {
82
+ pathPart = decoded.replace(/^file:\/+/, '').replace(/^\/([a-z]:)/i, '$1');
83
+ }
84
+ const absolutePath = resolve(pathPart);
85
+ const home = homedir();
86
+ const workspaceRoot = getOpenbotWorkspaceDir();
87
+ if (!absolutePath.startsWith(resolve(home)) &&
88
+ !absolutePath.startsWith(resolve(workspaceRoot))) {
89
+ throw new HttpException('Local file path not allowed', HttpStatus.FORBIDDEN);
90
+ }
91
+ if (!existsSync(absolutePath)) {
92
+ throw new HttpException('File not found', HttpStatus.NOT_FOUND);
93
+ }
94
+ const buffer = await readFile(absolutePath);
95
+ const contentType = mimeFromPath(absolutePath);
96
+ res.setHeader('Content-Type', contentType);
97
+ res.send(buffer);
98
+ }
99
+ catch (e) {
100
+ if (e instanceof HttpException)
101
+ throw e;
102
+ throw new HttpException('Local file read failed', HttpStatus.BAD_GATEWAY);
103
+ }
104
+ return;
105
+ }
106
+ try {
107
+ const resFetch = await fetch(decoded, { redirect: 'follow' });
108
+ if (!resFetch.ok)
109
+ throw new HttpException('Upstream failed', HttpStatus.BAD_GATEWAY);
110
+ const contentType = resFetch.headers.get('content-type') || 'application/octet-stream';
111
+ res.setHeader('Content-Type', contentType);
112
+ const arr = new Uint8Array(await resFetch.arrayBuffer());
113
+ res.send(Buffer.from(arr));
114
+ }
115
+ catch (e) {
116
+ if (e instanceof HttpException)
117
+ throw e;
118
+ throw new HttpException('Proxy failed', HttpStatus.BAD_GATEWAY);
119
+ }
120
+ }
24
121
  async get(id) {
25
122
  const data = await this.savedItemsService.findById(id);
26
123
  if (!data)
@@ -41,6 +138,42 @@ let SavedItemsController = class SavedItemsController {
41
138
  await this.savedItemsService.delete(id);
42
139
  return { success: true };
43
140
  }
141
+ /**
142
+ * Download the saved item URL to workspace .favorite or to a user-chosen directory.
143
+ * Body: { workspace?: string, targetDir?: string }.
144
+ * If targetDir (absolute path) is provided, file is written there; otherwise to workspace/.favorite.
145
+ */
146
+ async downloadToWorkspace(id, body) {
147
+ const item = await this.savedItemsService.findById(id);
148
+ if (!item)
149
+ throw new HttpException('Saved item not found', HttpStatus.NOT_FOUND);
150
+ const workspace = (body?.workspace ?? '').trim() ||
151
+ this.configService.getDefaultAgentId(await this.configService.getConfig());
152
+ let buffer;
153
+ let contentType;
154
+ try {
155
+ const res = await fetch(item.url, { redirect: 'follow' });
156
+ if (!res.ok) {
157
+ throw new HttpException(`Failed to fetch URL: ${res.status} ${res.statusText}`, HttpStatus.BAD_GATEWAY);
158
+ }
159
+ contentType = res.headers.get('content-type') || undefined;
160
+ const arr = new Uint8Array(await res.arrayBuffer());
161
+ buffer = Buffer.from(arr);
162
+ }
163
+ catch (err) {
164
+ if (err instanceof HttpException)
165
+ throw err;
166
+ const msg = err instanceof Error ? err.message : String(err);
167
+ throw new HttpException(`Download failed: ${msg}`, HttpStatus.BAD_GATEWAY);
168
+ }
169
+ const filename = safeFilenameFromUrl(item.url, contentType);
170
+ if ((body?.targetDir ?? '').trim()) {
171
+ const absolutePath = await this.workspaceService.writeFileToDir(body.targetDir.trim(), filename, buffer);
172
+ return { success: true, data: { absolutePath, targetDir: body.targetDir.trim() } };
173
+ }
174
+ const relativePath = await this.workspaceService.writeFileInFavorite(workspace, filename, buffer);
175
+ return { success: true, data: { relativePath, workspace } };
176
+ }
44
177
  };
45
178
  __decorate([
46
179
  Get(),
@@ -50,6 +183,14 @@ __decorate([
50
183
  __metadata("design:paramtypes", [String, String]),
51
184
  __metadata("design:returntype", Promise)
52
185
  ], SavedItemsController.prototype, "list", null);
186
+ __decorate([
187
+ Get('image-proxy'),
188
+ __param(0, Query('url')),
189
+ __param(1, Res({ passthrough: false })),
190
+ __metadata("design:type", Function),
191
+ __metadata("design:paramtypes", [String, Object]),
192
+ __metadata("design:returntype", Promise)
193
+ ], SavedItemsController.prototype, "imageProxy", null);
53
194
  __decorate([
54
195
  Get(':id'),
55
196
  __param(0, Param('id')),
@@ -71,8 +212,18 @@ __decorate([
71
212
  __metadata("design:paramtypes", [String]),
72
213
  __metadata("design:returntype", Promise)
73
214
  ], SavedItemsController.prototype, "delete", null);
215
+ __decorate([
216
+ Post(':id/download-to-workspace'),
217
+ __param(0, Param('id')),
218
+ __param(1, Body()),
219
+ __metadata("design:type", Function),
220
+ __metadata("design:paramtypes", [String, Object]),
221
+ __metadata("design:returntype", Promise)
222
+ ], SavedItemsController.prototype, "downloadToWorkspace", null);
74
223
  SavedItemsController = __decorate([
75
224
  Controller('saved-items'),
76
- __metadata("design:paramtypes", [SavedItemsService])
225
+ __metadata("design:paramtypes", [SavedItemsService,
226
+ WorkspaceService,
227
+ ConfigService])
77
228
  ], SavedItemsController);
78
229
  export { SavedItemsController };
@@ -6,6 +6,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  };
7
7
  import { Module } from '@nestjs/common';
8
8
  import { DatabaseModule } from '../database/database.module.js';
9
+ import { ConfigModule } from '../config/config.module.js';
10
+ import { WorkspaceModule } from '../workspace/workspace.module.js';
9
11
  import { TagsService } from './tags.service.js';
10
12
  import { TagsController } from './tags.controller.js';
11
13
  import { SavedItemsService } from './saved-items.service.js';
@@ -14,7 +16,7 @@ let SavedItemsModule = class SavedItemsModule {
14
16
  };
15
17
  SavedItemsModule = __decorate([
16
18
  Module({
17
- imports: [DatabaseModule],
19
+ imports: [DatabaseModule, ConfigModule, WorkspaceModule],
18
20
  controllers: [TagsController, SavedItemsController],
19
21
  providers: [TagsService, SavedItemsService],
20
22
  exports: [TagsService, SavedItemsService],
@@ -22,4 +22,15 @@ export declare class WorkspaceService {
22
22
  /** Delete a file or directory (recursive) under workspace. Returns true if deleted. skills 与 .skills 目录禁止删除。 */
23
23
  deletePath(workspaceName: string, relativePath: string): Promise<boolean>;
24
24
  private safeRelativePath;
25
+ /** Ensure workspace/.favorite directory exists. */
26
+ ensureFavoriteDir(workspaceName: string): Promise<string>;
27
+ /**
28
+ * Write buffer to workspace/.favorite/{filename}. Returns relative path (e.g. .favorite/xxx).
29
+ */
30
+ writeFileInFavorite(workspaceName: string, filename: string, buffer: Buffer): Promise<string>;
31
+ /**
32
+ * Write buffer to a user-chosen directory. targetDir must be absolute and under homedir or workspace root.
33
+ * Returns the full path of the written file.
34
+ */
35
+ writeFileToDir(targetDir: string, filename: string, buffer: Buffer): Promise<string>;
25
36
  }
@@ -5,9 +5,10 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { Injectable } from '@nestjs/common';
8
- import { readdir, stat, rm } from 'fs/promises';
8
+ import { readdir, stat, rm, mkdir, writeFile } from 'fs/promises';
9
9
  import { join, resolve, relative } from 'path';
10
10
  import { existsSync } from 'fs';
11
+ import { homedir } from 'os';
11
12
  import { getOpenbotWorkspaceDir } from '../../core/agent/agent-dir.js';
12
13
  let WorkspaceService = class WorkspaceService {
13
14
  getWorkspaceRoot(name) {
@@ -96,6 +97,44 @@ let WorkspaceService = class WorkspaceService {
96
97
  const parts = normalized.split('/').filter((p) => p !== '..' && p !== '.');
97
98
  return parts.join('/');
98
99
  }
100
+ /** Ensure workspace/.favorite directory exists. */
101
+ async ensureFavoriteDir(workspaceName) {
102
+ const root = resolve(this.getWorkspaceRoot(workspaceName));
103
+ const favoriteDir = join(root, '.favorite');
104
+ if (!existsSync(favoriteDir)) {
105
+ await mkdir(favoriteDir, { recursive: true });
106
+ }
107
+ return favoriteDir;
108
+ }
109
+ /**
110
+ * Write buffer to workspace/.favorite/{filename}. Returns relative path (e.g. .favorite/xxx).
111
+ */
112
+ async writeFileInFavorite(workspaceName, filename, buffer) {
113
+ const dir = await this.ensureFavoriteDir(workspaceName);
114
+ const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 200) || 'download';
115
+ const absolutePath = join(dir, safeName);
116
+ await writeFile(absolutePath, buffer);
117
+ return `.favorite/${safeName}`;
118
+ }
119
+ /**
120
+ * Write buffer to a user-chosen directory. targetDir must be absolute and under homedir or workspace root.
121
+ * Returns the full path of the written file.
122
+ */
123
+ async writeFileToDir(targetDir, filename, buffer) {
124
+ const home = homedir();
125
+ const workspaceRoot = getOpenbotWorkspaceDir();
126
+ const normalized = resolve(targetDir);
127
+ if (!normalized.startsWith(resolve(home)) && !normalized.startsWith(resolve(workspaceRoot))) {
128
+ throw new Error('Target directory must be under user home or workspace root');
129
+ }
130
+ if (!existsSync(normalized)) {
131
+ await mkdir(normalized, { recursive: true });
132
+ }
133
+ const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 200) || 'download';
134
+ const absolutePath = join(normalized, safeName);
135
+ await writeFile(absolutePath, buffer);
136
+ return absolutePath;
137
+ }
99
138
  };
100
139
  WorkspaceService = __decorate([
101
140
  Injectable()
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.3.2",
6
+ "version": "0.6.6",
7
7
  "description": "CLI and library to run prompts with skill paths (Agent Skills style). Use as npm package or openbot CLI.",
8
8
  "type": "module",
9
9
  "main": "dist/index.js",
@@ -25,6 +25,7 @@
25
25
  "openbot": "./dist/cli/cli.js"
26
26
  },
27
27
  "dependencies": {
28
+ "@larksuiteoapi/node-sdk": "^1.59.0",
28
29
  "@mariozechner/pi-ai": "^0.51.2",
29
30
  "@mariozechner/pi-coding-agent": "^0.51.2",
30
31
  "@nestjs/common": "^10.3.0",