@lcap/wave-sandbox-sdk 0.0.4 → 0.0.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.
package/dist/client.js CHANGED
@@ -13,14 +13,14 @@ export class WaveSandboxClient {
13
13
  return this.fileSystem;
14
14
  }
15
15
  constructor(options) {
16
- const { url, projectId, options: socketOptions } = options;
16
+ const { url, projectId, authToken, options: socketOptions } = options;
17
17
  const uri = new URL(url);
18
18
  let pathname = uri.pathname;
19
19
  if (pathname.endsWith('/')) {
20
20
  pathname = pathname.slice(0, pathname.length - 1);
21
21
  }
22
22
  pathname = pathname + '/socket.io/';
23
- // 构建 Socket.IO 连接选项
23
+ // 构建 Socket.IO 连接选项(Authorization 供服务端 auth 中间件校验)
24
24
  const connectOptions = {
25
25
  autoConnect: socketOptions?.autoConnect ?? true,
26
26
  reconnection: socketOptions?.reconnection ?? true,
@@ -28,15 +28,25 @@ export class WaveSandboxClient {
28
28
  reconnectionAttempts: socketOptions?.reconnectionAttempts ?? 5,
29
29
  query: projectId ? { projectId } : undefined,
30
30
  path: pathname,
31
+ ...(authToken
32
+ ? {
33
+ extraHeaders: { Authorization: authToken },
34
+ transportOptions: {
35
+ polling: {
36
+ extraHeaders: { Authorization: authToken },
37
+ },
38
+ },
39
+ }
40
+ : {}),
31
41
  };
32
42
  // 创建 Socket.IO 连接
33
43
  this.socket = io(uri.host, connectOptions);
34
44
  // 初始化各个模块
35
45
  this.exec = new ExecModule(this.socket);
36
- this.fileSystem = new FileSystemModule(this.socket, url, projectId);
46
+ this.fileSystem = new FileSystemModule(this.socket, url, projectId, authToken);
37
47
  this.port = new PortModule(this.socket);
38
- this.project = new ProjectModule(this.socket, url, projectId);
39
- this.agent = new AgentModule(this.socket, url, projectId);
48
+ this.project = new ProjectModule(this.socket, url, projectId, authToken);
49
+ this.agent = new AgentModule(this.socket, url, projectId, authToken);
40
50
  }
41
51
  /**
42
52
  * 连接到服务器
@@ -253,13 +253,6 @@ export interface AskUserQuestion {
253
253
  options: AskUserQuestionOption[];
254
254
  multiSelect?: boolean;
255
255
  }
256
- /**
257
- * 询问用户问题事件数据
258
- */
259
- export interface AskUserQuestionEvent {
260
- questionId: string;
261
- questions: AskUserQuestion[];
262
- }
263
256
  /**
264
257
  * Ask 信息状态
265
258
  */
@@ -271,6 +264,10 @@ export interface AskInfo {
271
264
  answered: boolean;
272
265
  answer?: string;
273
266
  }
267
+ /**
268
+ * 询问用户问题事件数据(与 AskInfo 相同)
269
+ */
270
+ export type AskUserQuestionEvent = AskInfo;
274
271
  /**
275
272
  * 客户端回调选项
276
273
  */
@@ -317,7 +314,8 @@ export declare class AgentModule {
317
314
  private socket;
318
315
  private baseUrl;
319
316
  private projectId?;
320
- constructor(socket: Socket, baseUrl: string, projectId?: string | undefined);
317
+ private authToken?;
318
+ constructor(socket: Socket, baseUrl: string, projectId?: string | undefined, authToken?: string | undefined);
321
319
  /**
322
320
  * 获取查询参数
323
321
  */
@@ -1,12 +1,14 @@
1
1
  import { createSocketHandler, onSocketEvent } from '../utils/socket.js';
2
+ import { fetchWithAuth } from '../utils/fetch-auth.js';
2
3
  /**
3
4
  * Agent 模块
4
5
  */
5
6
  export class AgentModule {
6
- constructor(socket, baseUrl, projectId) {
7
+ constructor(socket, baseUrl, projectId, authToken) {
7
8
  this.socket = socket;
8
9
  this.baseUrl = baseUrl;
9
10
  this.projectId = projectId;
11
+ this.authToken = authToken;
10
12
  }
11
13
  /**
12
14
  * 获取查询参数
@@ -61,7 +63,7 @@ export class AgentModule {
61
63
  async getCurrentState() {
62
64
  const queryParams = this.getQueryParams();
63
65
  const url = `${this.baseUrl}/api/agent/currentState${queryParams ? `?${queryParams}` : ''}`;
64
- const response = await fetch(url);
66
+ const response = await fetchWithAuth(url, this.authToken);
65
67
  const result = await response.json();
66
68
  if (result.status === 'error') {
67
69
  throw new Error(result.message);
@@ -78,7 +80,7 @@ export class AgentModule {
78
80
  const sessionIdParam = `sessionId=${encodeURIComponent(sessionId)}`;
79
81
  const params = queryParams ? `${queryParams}&${sessionIdParam}` : sessionIdParam;
80
82
  const url = `${this.baseUrl}/api/agent/askInfo?${params}`;
81
- const response = await fetch(url);
83
+ const response = await fetchWithAuth(url, this.authToken);
82
84
  const result = await response.json();
83
85
  if (result.status === 'error') {
84
86
  throw new Error(result.message);
@@ -7,7 +7,8 @@ export declare class FileSystemModule {
7
7
  private socket;
8
8
  private baseUrl;
9
9
  private projectId?;
10
- constructor(socket: Socket, baseUrl: string, projectId?: string | undefined);
10
+ private authToken?;
11
+ constructor(socket: Socket, baseUrl: string, projectId?: string | undefined, authToken?: string | undefined);
11
12
  /**
12
13
  * 获取查询参数
13
14
  */
@@ -1,12 +1,14 @@
1
1
  import { createSocketHandler, onSocketEvent } from '../utils/socket.js';
2
+ import { fetchWithAuth } from '../utils/fetch-auth.js';
2
3
  /**
3
4
  * 文件系统模块
4
5
  */
5
6
  export class FileSystemModule {
6
- constructor(socket, baseUrl, projectId) {
7
+ constructor(socket, baseUrl, projectId, authToken) {
7
8
  this.socket = socket;
8
9
  this.baseUrl = baseUrl;
9
10
  this.projectId = projectId;
11
+ this.authToken = authToken;
10
12
  }
11
13
  /**
12
14
  * 获取查询参数
@@ -33,7 +35,7 @@ export class FileSystemModule {
33
35
  */
34
36
  async stat(filePath) {
35
37
  const url = `${this.baseUrl}/api/fs/stat?${this.getQueryParams(filePath)}`;
36
- const response = await fetch(url);
38
+ const response = await fetchWithAuth(url, this.authToken);
37
39
  const result = await response.json();
38
40
  if (result.status === 'error') {
39
41
  throw new Error(result.message);
@@ -44,7 +46,7 @@ export class FileSystemModule {
44
46
  if (asText) {
45
47
  // 使用 readFile 端点获取文本内容
46
48
  const url = `${this.baseUrl}/api/fs/readFile?${this.getQueryParams(filePath)}`;
47
- const response = await fetch(url);
49
+ const response = await fetchWithAuth(url, this.authToken);
48
50
  const result = await response.json();
49
51
  if (result.status === 'error') {
50
52
  throw new Error(result.message);
@@ -54,7 +56,7 @@ export class FileSystemModule {
54
56
  else {
55
57
  // 使用 read 端点获取原始文件(支持二进制)
56
58
  const url = `${this.baseUrl}/api/fs/read?${this.getQueryParams(filePath)}`;
57
- const response = await fetch(url);
59
+ const response = await fetchWithAuth(url, this.authToken);
58
60
  if (!response.ok) {
59
61
  throw new Error(`Failed to read file: ${response.statusText}`);
60
62
  }
@@ -74,7 +76,7 @@ export class FileSystemModule {
74
76
  */
75
77
  async readdir(filePath) {
76
78
  const url = `${this.baseUrl}/api/fs/readdir?${this.getQueryParams(filePath)}`;
77
- const response = await fetch(url);
79
+ const response = await fetchWithAuth(url, this.authToken);
78
80
  const result = await response.json();
79
81
  if (result.status === 'error') {
80
82
  throw new Error(result.message);
@@ -88,7 +90,7 @@ export class FileSystemModule {
88
90
  */
89
91
  async files(dirPath) {
90
92
  const url = `${this.baseUrl}/api/fs/files?${this.getDirQueryParams(dirPath)}`;
91
- const response = await fetch(url);
93
+ const response = await fetchWithAuth(url, this.authToken);
92
94
  const result = await response.json();
93
95
  if (result.status === 'error') {
94
96
  throw new Error(result.message);
@@ -7,7 +7,8 @@ export declare class ProjectModule {
7
7
  private socket;
8
8
  private baseUrl;
9
9
  private projectId?;
10
- constructor(socket: Socket, baseUrl: string, projectId?: string | undefined);
10
+ private authToken?;
11
+ constructor(socket: Socket, baseUrl: string, projectId?: string | undefined, authToken?: string | undefined);
11
12
  /**
12
13
  * 获取查询参数
13
14
  */
@@ -1,12 +1,14 @@
1
1
  import { createSocketHandler, onSocketEvent } from '../utils/socket.js';
2
+ import { fetchWithAuth } from '../utils/fetch-auth.js';
2
3
  /**
3
4
  * 项目模块
4
5
  */
5
6
  export class ProjectModule {
6
- constructor(socket, baseUrl, projectId) {
7
+ constructor(socket, baseUrl, projectId, authToken) {
7
8
  this.socket = socket;
8
9
  this.baseUrl = baseUrl;
9
10
  this.projectId = projectId;
11
+ this.authToken = authToken;
10
12
  }
11
13
  /**
12
14
  * 获取查询参数
@@ -22,7 +24,7 @@ export class ProjectModule {
22
24
  */
23
25
  async getInfo() {
24
26
  const url = `${this.baseUrl}/project/info${this.projectId ? `?${this.getQueryParams()}` : ''}`;
25
- const response = await fetch(url);
27
+ const response = await fetchWithAuth(url, this.authToken);
26
28
  const result = await response.json();
27
29
  if (result.status === 'error') {
28
30
  throw new Error(result.message);
@@ -34,7 +36,7 @@ export class ProjectModule {
34
36
  */
35
37
  async getFiles() {
36
38
  const url = `${this.baseUrl}/project/files${this.projectId ? `?${this.getQueryParams()}` : ''}`;
37
- const response = await fetch(url);
39
+ const response = await fetchWithAuth(url, this.authToken);
38
40
  const result = await response.json();
39
41
  if (result.status === 'error') {
40
42
  throw new Error(result.message);
@@ -99,6 +99,11 @@ export interface SDKOptions {
99
99
  url: string;
100
100
  /** 项目 ID(可选,用于工作空间模式) */
101
101
  projectId?: string;
102
+ /**
103
+ * 与服务器 `authToken` 一致时启用;HTTP 与 Socket 握手会携带 `Authorization` 头
104
+ *(例如 spec-server:`[naslStorageAppId, SBX_SANDBOX_ID].join('|')`)
105
+ */
106
+ authToken?: string;
102
107
  /** 连接选项 */
103
108
  options?: {
104
109
  /** 自动连接 */
@@ -0,0 +1,4 @@
1
+ /**
2
+ * 带可选 Authorization 的 fetch,与 wave-sandbox-server 的 authToken 鉴权一致
3
+ */
4
+ export declare function fetchWithAuth(url: string | URL, authToken: string | undefined, init?: RequestInit): Promise<Response>;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 带可选 Authorization 的 fetch,与 wave-sandbox-server 的 authToken 鉴权一致
3
+ */
4
+ export function fetchWithAuth(url, authToken, init) {
5
+ if (!authToken) {
6
+ return fetch(url, init);
7
+ }
8
+ const headers = new Headers(init?.headers);
9
+ headers.set('Authorization', authToken);
10
+ return fetch(url, { ...init, headers });
11
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lcap/wave-sandbox-sdk",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "SDK for Wave Sandbox Client",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,6 +10,15 @@
10
10
  "src",
11
11
  "README.md"
12
12
  ],
13
+ "scripts": {
14
+ "build": "rimraf dist && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
15
+ "type-check": "tsc --noEmit",
16
+ "dev": "tsc -p tsconfig.build.json --watch & tsc-alias -p tsconfig.build.json --watch",
17
+ "test": "vitest run",
18
+ "lint": "eslint --cache",
19
+ "format": "prettier --write .",
20
+ "prepublishOnly": "pnpm run build"
21
+ },
13
22
  "dependencies": {
14
23
  "socket.io-client": "^4.8.3"
15
24
  },
@@ -22,13 +31,5 @@
22
31
  "engines": {
23
32
  "node": ">=16.0.0"
24
33
  },
25
- "license": "MIT",
26
- "scripts": {
27
- "build": "rimraf dist && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
28
- "type-check": "tsc --noEmit",
29
- "dev": "tsc -p tsconfig.build.json --watch & tsc-alias -p tsconfig.build.json --watch",
30
- "test": "vitest run",
31
- "lint": "eslint --cache",
32
- "format": "prettier --write ."
33
- }
34
- }
34
+ "license": "MIT"
35
+ }
package/src/client.ts CHANGED
@@ -22,7 +22,7 @@ export class WaveSandboxClient {
22
22
  public readonly agent: AgentModule;
23
23
 
24
24
  constructor(options: SDKOptions) {
25
- const { url, projectId, options: socketOptions } = options;
25
+ const { url, projectId, authToken, options: socketOptions } = options;
26
26
 
27
27
  const uri = new URL(url);
28
28
  let pathname = uri.pathname;
@@ -33,7 +33,7 @@ export class WaveSandboxClient {
33
33
 
34
34
  pathname = pathname + '/socket.io/';
35
35
 
36
- // 构建 Socket.IO 连接选项
36
+ // 构建 Socket.IO 连接选项(Authorization 供服务端 auth 中间件校验)
37
37
  const connectOptions = {
38
38
  autoConnect: socketOptions?.autoConnect ?? true,
39
39
  reconnection: socketOptions?.reconnection ?? true,
@@ -41,6 +41,16 @@ export class WaveSandboxClient {
41
41
  reconnectionAttempts: socketOptions?.reconnectionAttempts ?? 5,
42
42
  query: projectId ? { projectId } : undefined,
43
43
  path: pathname,
44
+ ...(authToken
45
+ ? {
46
+ extraHeaders: { Authorization: authToken },
47
+ transportOptions: {
48
+ polling: {
49
+ extraHeaders: { Authorization: authToken },
50
+ },
51
+ },
52
+ }
53
+ : {}),
44
54
  };
45
55
 
46
56
  // 创建 Socket.IO 连接
@@ -48,10 +58,10 @@ export class WaveSandboxClient {
48
58
 
49
59
  // 初始化各个模块
50
60
  this.exec = new ExecModule(this.socket);
51
- this.fileSystem = new FileSystemModule(this.socket, url, projectId);
61
+ this.fileSystem = new FileSystemModule(this.socket, url, projectId, authToken);
52
62
  this.port = new PortModule(this.socket);
53
- this.project = new ProjectModule(this.socket, url, projectId);
54
- this.agent = new AgentModule(this.socket, url, projectId);
63
+ this.project = new ProjectModule(this.socket, url, projectId, authToken);
64
+ this.agent = new AgentModule(this.socket, url, projectId, authToken);
55
65
  }
56
66
 
57
67
  /**
@@ -9,6 +9,7 @@ import type {
9
9
  } from '../types/agent-types.js';
10
10
  import type { HttpResponse } from '../types/index.js';
11
11
  import { createSocketHandler, onSocketEvent } from '../utils/socket.js';
12
+ import { fetchWithAuth } from '../utils/fetch-auth.js';
12
13
 
13
14
  /**
14
15
  * 附件接口
@@ -289,14 +290,6 @@ export interface AskUserQuestion {
289
290
  multiSelect?: boolean;
290
291
  }
291
292
 
292
- /**
293
- * 询问用户问题事件数据
294
- */
295
- export interface AskUserQuestionEvent {
296
- questionId: string;
297
- questions: AskUserQuestion[];
298
- }
299
-
300
293
  /**
301
294
  * Ask 信息状态
302
295
  */
@@ -309,6 +302,11 @@ export interface AskInfo {
309
302
  answer?: string;
310
303
  }
311
304
 
305
+ /**
306
+ * 询问用户问题事件数据(与 AskInfo 相同)
307
+ */
308
+ export type AskUserQuestionEvent = AskInfo;
309
+
312
310
  /**
313
311
  * 客户端回调选项
314
312
  */
@@ -358,7 +356,8 @@ export class AgentModule {
358
356
  constructor(
359
357
  private socket: Socket,
360
358
  private baseUrl: string,
361
- private projectId?: string
359
+ private projectId?: string,
360
+ private authToken?: string
362
361
  ) {}
363
362
 
364
363
  /**
@@ -427,7 +426,7 @@ export class AgentModule {
427
426
  async getCurrentState(): Promise<MessageQueueChangedEvent | null> {
428
427
  const queryParams = this.getQueryParams();
429
428
  const url = `${this.baseUrl}/api/agent/currentState${queryParams ? `?${queryParams}` : ''}`;
430
- const response = await fetch(url);
429
+ const response = await fetchWithAuth(url, this.authToken);
431
430
  const result: HttpResponse<MessageQueueChangedEvent | null> = await response.json();
432
431
 
433
432
  if (result.status === 'error') {
@@ -447,7 +446,7 @@ export class AgentModule {
447
446
  const sessionIdParam = `sessionId=${encodeURIComponent(sessionId)}`;
448
447
  const params = queryParams ? `${queryParams}&${sessionIdParam}` : sessionIdParam;
449
448
  const url = `${this.baseUrl}/api/agent/askInfo?${params}`;
450
- const response = await fetch(url);
449
+ const response = await fetchWithAuth(url, this.authToken);
451
450
  const result: HttpResponse<AskInfo | null> = await response.json();
452
451
 
453
452
  if (result.status === 'error') {
@@ -1,5 +1,6 @@
1
1
  import type { Socket } from 'socket.io-client';
2
2
  import { createSocketHandler, onSocketEvent } from '../utils/socket.js';
3
+ import { fetchWithAuth } from '../utils/fetch-auth.js';
3
4
  import type { FileStat, DirectoryItem, FileChangeEvent, HttpResponse, ProjectFile } from '../types/index.js';
4
5
 
5
6
  /**
@@ -9,7 +10,8 @@ export class FileSystemModule {
9
10
  constructor(
10
11
  private socket: Socket,
11
12
  private baseUrl: string,
12
- private projectId?: string
13
+ private projectId?: string,
14
+ private authToken?: string
13
15
  ) {}
14
16
 
15
17
  /**
@@ -39,7 +41,7 @@ export class FileSystemModule {
39
41
  */
40
42
  async stat(filePath: string): Promise<FileStat> {
41
43
  const url = `${this.baseUrl}/api/fs/stat?${this.getQueryParams(filePath)}`;
42
- const response = await fetch(url);
44
+ const response = await fetchWithAuth(url, this.authToken);
43
45
  const result: HttpResponse<FileStat> = await response.json();
44
46
 
45
47
  if (result.status === 'error') {
@@ -61,7 +63,7 @@ export class FileSystemModule {
61
63
  if (asText) {
62
64
  // 使用 readFile 端点获取文本内容
63
65
  const url = `${this.baseUrl}/api/fs/readFile?${this.getQueryParams(filePath)}`;
64
- const response = await fetch(url);
66
+ const response = await fetchWithAuth(url, this.authToken);
65
67
  const result: HttpResponse<string> = await response.json();
66
68
 
67
69
  if (result.status === 'error') {
@@ -72,7 +74,7 @@ export class FileSystemModule {
72
74
  } else {
73
75
  // 使用 read 端点获取原始文件(支持二进制)
74
76
  const url = `${this.baseUrl}/api/fs/read?${this.getQueryParams(filePath)}`;
75
- const response = await fetch(url);
77
+ const response = await fetchWithAuth(url, this.authToken);
76
78
  if (!response.ok) {
77
79
  throw new Error(`Failed to read file: ${response.statusText}`);
78
80
  }
@@ -94,7 +96,7 @@ export class FileSystemModule {
94
96
  */
95
97
  async readdir(filePath: string): Promise<DirectoryItem[]> {
96
98
  const url = `${this.baseUrl}/api/fs/readdir?${this.getQueryParams(filePath)}`;
97
- const response = await fetch(url);
99
+ const response = await fetchWithAuth(url, this.authToken);
98
100
  const result: HttpResponse<DirectoryItem[]> = await response.json();
99
101
 
100
102
  if (result.status === 'error') {
@@ -111,7 +113,7 @@ export class FileSystemModule {
111
113
  */
112
114
  async files(dirPath: string): Promise<Record<string, ProjectFile>> {
113
115
  const url = `${this.baseUrl}/api/fs/files?${this.getDirQueryParams(dirPath)}`;
114
- const response = await fetch(url);
116
+ const response = await fetchWithAuth(url, this.authToken);
115
117
  const result: HttpResponse<Record<string, ProjectFile>> = await response.json();
116
118
 
117
119
  if (result.status === 'error') {
@@ -1,5 +1,6 @@
1
1
  import type { Socket } from 'socket.io-client';
2
2
  import { createSocketHandler, onSocketEvent } from '../utils/socket.js';
3
+ import { fetchWithAuth } from '../utils/fetch-auth.js';
3
4
  import type { ProjectInfo, ProjectFile, HttpResponse, FileChangeEvent } from '../types/index.js';
4
5
 
5
6
  /**
@@ -9,7 +10,8 @@ export class ProjectModule {
9
10
  constructor(
10
11
  private socket: Socket,
11
12
  private baseUrl: string,
12
- private projectId?: string
13
+ private projectId?: string,
14
+ private authToken?: string
13
15
  ) {}
14
16
 
15
17
  /**
@@ -27,7 +29,7 @@ export class ProjectModule {
27
29
  */
28
30
  async getInfo(): Promise<ProjectInfo> {
29
31
  const url = `${this.baseUrl}/project/info${this.projectId ? `?${this.getQueryParams()}` : ''}`;
30
- const response = await fetch(url);
32
+ const response = await fetchWithAuth(url, this.authToken);
31
33
  const result: HttpResponse<ProjectInfo> = await response.json();
32
34
 
33
35
  if (result.status === 'error') {
@@ -42,7 +44,7 @@ export class ProjectModule {
42
44
  */
43
45
  async getFiles(): Promise<ProjectFile[]> {
44
46
  const url = `${this.baseUrl}/project/files${this.projectId ? `?${this.getQueryParams()}` : ''}`;
45
- const response = await fetch(url);
47
+ const response = await fetchWithAuth(url, this.authToken);
46
48
  const result: HttpResponse<{ files: ProjectFile[] }> = await response.json();
47
49
 
48
50
  if (result.status === 'error') {
@@ -114,6 +114,11 @@ export interface SDKOptions {
114
114
  url: string;
115
115
  /** 项目 ID(可选,用于工作空间模式) */
116
116
  projectId?: string;
117
+ /**
118
+ * 与服务器 `authToken` 一致时启用;HTTP 与 Socket 握手会携带 `Authorization` 头
119
+ *(例如 spec-server:`[naslStorageAppId, SBX_SANDBOX_ID].join('|')`)
120
+ */
121
+ authToken?: string;
117
122
  /** 连接选项 */
118
123
  options?: {
119
124
  /** 自动连接 */
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 带可选 Authorization 的 fetch,与 wave-sandbox-server 的 authToken 鉴权一致
3
+ */
4
+ export function fetchWithAuth(url: string | URL, authToken: string | undefined, init?: RequestInit): Promise<Response> {
5
+ if (!authToken) {
6
+ return fetch(url, init);
7
+ }
8
+ const headers = new Headers(init?.headers);
9
+ headers.set('Authorization', authToken);
10
+ return fetch(url, { ...init, headers });
11
+ }