@lcap/wave-sandbox-sdk 0.0.5 → 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
  * 连接到服务器
@@ -314,7 +314,8 @@ export declare class AgentModule {
314
314
  private socket;
315
315
  private baseUrl;
316
316
  private projectId?;
317
- constructor(socket: Socket, baseUrl: string, projectId?: string | undefined);
317
+ private authToken?;
318
+ constructor(socket: Socket, baseUrl: string, projectId?: string | undefined, authToken?: string | undefined);
318
319
  /**
319
320
  * 获取查询参数
320
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.5",
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
  * 附件接口
@@ -355,7 +356,8 @@ export class AgentModule {
355
356
  constructor(
356
357
  private socket: Socket,
357
358
  private baseUrl: string,
358
- private projectId?: string
359
+ private projectId?: string,
360
+ private authToken?: string
359
361
  ) {}
360
362
 
361
363
  /**
@@ -424,7 +426,7 @@ export class AgentModule {
424
426
  async getCurrentState(): Promise<MessageQueueChangedEvent | null> {
425
427
  const queryParams = this.getQueryParams();
426
428
  const url = `${this.baseUrl}/api/agent/currentState${queryParams ? `?${queryParams}` : ''}`;
427
- const response = await fetch(url);
429
+ const response = await fetchWithAuth(url, this.authToken);
428
430
  const result: HttpResponse<MessageQueueChangedEvent | null> = await response.json();
429
431
 
430
432
  if (result.status === 'error') {
@@ -444,7 +446,7 @@ export class AgentModule {
444
446
  const sessionIdParam = `sessionId=${encodeURIComponent(sessionId)}`;
445
447
  const params = queryParams ? `${queryParams}&${sessionIdParam}` : sessionIdParam;
446
448
  const url = `${this.baseUrl}/api/agent/askInfo?${params}`;
447
- const response = await fetch(url);
449
+ const response = await fetchWithAuth(url, this.authToken);
448
450
  const result: HttpResponse<AskInfo | null> = await response.json();
449
451
 
450
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
+ }