lexmount 0.2.4 → 0.2.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/README.md +34 -0
- package/dist/index.d.mts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +103 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +103 -25
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -149,6 +149,14 @@ const details = await client.contexts.get(context.id);
|
|
|
149
149
|
console.log(details.status);
|
|
150
150
|
```
|
|
151
151
|
|
|
152
|
+
Fork an available context into a new context:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
const source = await client.contexts.create();
|
|
156
|
+
const forked = await client.contexts.fork(source.id);
|
|
157
|
+
console.log(forked.id);
|
|
158
|
+
```
|
|
159
|
+
|
|
152
160
|
List contexts:
|
|
153
161
|
|
|
154
162
|
```ts
|
|
@@ -164,6 +172,13 @@ Force release a stuck lock:
|
|
|
164
172
|
await client.contexts.forceRelease('context-id');
|
|
165
173
|
```
|
|
166
174
|
|
|
175
|
+
Current `context fork` limitations:
|
|
176
|
+
|
|
177
|
+
- Only supports offline snapshot copy from an `available` context
|
|
178
|
+
- Does not support hot fork from a `locked` context
|
|
179
|
+
- Does not support copy-on-write
|
|
180
|
+
- Does not support parent/child lineage tracking
|
|
181
|
+
|
|
167
182
|
### Extensions
|
|
168
183
|
|
|
169
184
|
Upload an extension archive:
|
|
@@ -271,6 +286,7 @@ Example scripts live in [examples](./examples):
|
|
|
271
286
|
- `playwright-basic.ts`
|
|
272
287
|
- `session-management.ts`
|
|
273
288
|
- `context-basic.ts`
|
|
289
|
+
- `context-fork.ts`
|
|
274
290
|
- `context-list-get.ts`
|
|
275
291
|
- `context-modes.ts`
|
|
276
292
|
- `context-lock-handling.ts`
|
|
@@ -283,6 +299,7 @@ Run them with:
|
|
|
283
299
|
```bash
|
|
284
300
|
cd examples
|
|
285
301
|
npm install
|
|
302
|
+
npm run context-fork
|
|
286
303
|
npm run playwright-basic
|
|
287
304
|
npm run session-management
|
|
288
305
|
npm run extension-basic
|
|
@@ -450,6 +467,14 @@ const details = await client.contexts.get(context.id);
|
|
|
450
467
|
console.log(details.status);
|
|
451
468
|
```
|
|
452
469
|
|
|
470
|
+
基于一个 `available` context 创建新的 fork:
|
|
471
|
+
|
|
472
|
+
```ts
|
|
473
|
+
const source = await client.contexts.create();
|
|
474
|
+
const forked = await client.contexts.fork(source.id);
|
|
475
|
+
console.log(forked.id);
|
|
476
|
+
```
|
|
477
|
+
|
|
453
478
|
列出 context:
|
|
454
479
|
|
|
455
480
|
```ts
|
|
@@ -465,6 +490,13 @@ const contexts = await client.contexts.list({
|
|
|
465
490
|
await client.contexts.forceRelease('context-id');
|
|
466
491
|
```
|
|
467
492
|
|
|
493
|
+
当前 `context fork` 第一版限制:
|
|
494
|
+
|
|
495
|
+
- 只支持对 `available context` 做离线快照复制
|
|
496
|
+
- 不支持 `locked` context 的热 fork
|
|
497
|
+
- 不支持 copy-on-write
|
|
498
|
+
- 不支持父子链追踪
|
|
499
|
+
|
|
468
500
|
### Extensions
|
|
469
501
|
|
|
470
502
|
上传扩展压缩包:
|
|
@@ -572,6 +604,7 @@ npm run docs:html
|
|
|
572
604
|
- `playwright-basic.ts`
|
|
573
605
|
- `session-management.ts`
|
|
574
606
|
- `context-basic.ts`
|
|
607
|
+
- `context-fork.ts`
|
|
575
608
|
- `context-list-get.ts`
|
|
576
609
|
- `context-modes.ts`
|
|
577
610
|
- `context-lock-handling.ts`
|
|
@@ -584,6 +617,7 @@ npm run docs:html
|
|
|
584
617
|
```bash
|
|
585
618
|
cd examples
|
|
586
619
|
npm install
|
|
620
|
+
npm run context-fork
|
|
587
621
|
npm run playwright-basic
|
|
588
622
|
npm run session-management
|
|
589
623
|
npm run extension-basic
|
package/dist/index.d.mts
CHANGED
|
@@ -334,6 +334,10 @@ declare class ContextsResource {
|
|
|
334
334
|
* Fetch a single context.
|
|
335
335
|
*/
|
|
336
336
|
get(contextId: string): Promise<ContextInfo>;
|
|
337
|
+
/**
|
|
338
|
+
* Fork an existing persistent context into a new context.
|
|
339
|
+
*/
|
|
340
|
+
fork(contextId: string): Promise<ContextInfo>;
|
|
337
341
|
/**
|
|
338
342
|
* Delete a persistent context.
|
|
339
343
|
*/
|
|
@@ -505,6 +509,13 @@ declare class SessionsResource {
|
|
|
505
509
|
* Create a new browser session.
|
|
506
510
|
*/
|
|
507
511
|
create(options?: SessionCreateOptions): Promise<SessionCreateResponse>;
|
|
512
|
+
/**
|
|
513
|
+
* Fetch one session by id.
|
|
514
|
+
*
|
|
515
|
+
* The upstream API uses POST /instance/session for this lookup, so this SDK method
|
|
516
|
+
* intentionally uses POST even though the operation is read-only.
|
|
517
|
+
*/
|
|
518
|
+
get(sessionId: string, projectId?: string): Promise<SessionInfo>;
|
|
508
519
|
/**
|
|
509
520
|
* List sessions for the current project.
|
|
510
521
|
*/
|
|
@@ -521,8 +532,11 @@ declare class SessionsResource {
|
|
|
521
532
|
_getWebSocketDebuggerUrl(sessionId: string): Promise<string | null>;
|
|
522
533
|
private normalizeProxy;
|
|
523
534
|
private handleCreateError;
|
|
535
|
+
private handleGetError;
|
|
524
536
|
private handleListError;
|
|
525
537
|
private handleDeleteError;
|
|
538
|
+
private mapSessionInfo;
|
|
539
|
+
private _getCreatedSession;
|
|
526
540
|
}
|
|
527
541
|
|
|
528
542
|
/**
|
|
@@ -657,6 +671,6 @@ declare class TimeoutError extends LexmountError {
|
|
|
657
671
|
/**
|
|
658
672
|
* Lexmount Node.js SDK.
|
|
659
673
|
*/
|
|
660
|
-
declare const VERSION = "0.2.
|
|
674
|
+
declare const VERSION = "0.2.5";
|
|
661
675
|
|
|
662
676
|
export { APIError, AuthenticationError, type BrowserMode, type ContextAccessMode, type ContextCreateOptions, ContextInfo, type ContextListOptions, ContextListResponse, ContextLockedError, type ContextMetadata, ContextNotFoundError, type ContextStatus, ContextsResource, ExtensionInfo, type ExtensionInfoShape, type ExtensionListOptions, type ExtensionUploadOptions, ExtensionsResource, type ForceReleaseResponse, Lexmount, type LexmountConfig, LexmountError, LexmountLogger, type LogLevel, NetworkError, PaginationInfo, type PaginationInfoShape, type SessionContext, type SessionCreateOptions, type SessionCreateResponse, type SessionDeleteOptions, SessionDownloadInfo, type SessionDownloadInfoShape, SessionDownloadsDeleteResponse, type SessionDownloadsDeleteResponseShape, SessionDownloadsListResponse, type SessionDownloadsListResponseShape, SessionDownloadsResource, SessionInfo, type SessionListOptions, SessionListResponse, SessionNotFoundError, type SessionProxyConfig, type SessionStatus, SessionsResource, TimeoutError, VERSION, ValidationError, Lexmount as default, disableLogging, enableLogging, getLogger, setLogLevel };
|
package/dist/index.d.ts
CHANGED
|
@@ -334,6 +334,10 @@ declare class ContextsResource {
|
|
|
334
334
|
* Fetch a single context.
|
|
335
335
|
*/
|
|
336
336
|
get(contextId: string): Promise<ContextInfo>;
|
|
337
|
+
/**
|
|
338
|
+
* Fork an existing persistent context into a new context.
|
|
339
|
+
*/
|
|
340
|
+
fork(contextId: string): Promise<ContextInfo>;
|
|
337
341
|
/**
|
|
338
342
|
* Delete a persistent context.
|
|
339
343
|
*/
|
|
@@ -505,6 +509,13 @@ declare class SessionsResource {
|
|
|
505
509
|
* Create a new browser session.
|
|
506
510
|
*/
|
|
507
511
|
create(options?: SessionCreateOptions): Promise<SessionCreateResponse>;
|
|
512
|
+
/**
|
|
513
|
+
* Fetch one session by id.
|
|
514
|
+
*
|
|
515
|
+
* The upstream API uses POST /instance/session for this lookup, so this SDK method
|
|
516
|
+
* intentionally uses POST even though the operation is read-only.
|
|
517
|
+
*/
|
|
518
|
+
get(sessionId: string, projectId?: string): Promise<SessionInfo>;
|
|
508
519
|
/**
|
|
509
520
|
* List sessions for the current project.
|
|
510
521
|
*/
|
|
@@ -521,8 +532,11 @@ declare class SessionsResource {
|
|
|
521
532
|
_getWebSocketDebuggerUrl(sessionId: string): Promise<string | null>;
|
|
522
533
|
private normalizeProxy;
|
|
523
534
|
private handleCreateError;
|
|
535
|
+
private handleGetError;
|
|
524
536
|
private handleListError;
|
|
525
537
|
private handleDeleteError;
|
|
538
|
+
private mapSessionInfo;
|
|
539
|
+
private _getCreatedSession;
|
|
526
540
|
}
|
|
527
541
|
|
|
528
542
|
/**
|
|
@@ -657,6 +671,6 @@ declare class TimeoutError extends LexmountError {
|
|
|
657
671
|
/**
|
|
658
672
|
* Lexmount Node.js SDK.
|
|
659
673
|
*/
|
|
660
|
-
declare const VERSION = "0.2.
|
|
674
|
+
declare const VERSION = "0.2.5";
|
|
661
675
|
|
|
662
676
|
export { APIError, AuthenticationError, type BrowserMode, type ContextAccessMode, type ContextCreateOptions, ContextInfo, type ContextListOptions, ContextListResponse, ContextLockedError, type ContextMetadata, ContextNotFoundError, type ContextStatus, ContextsResource, ExtensionInfo, type ExtensionInfoShape, type ExtensionListOptions, type ExtensionUploadOptions, ExtensionsResource, type ForceReleaseResponse, Lexmount, type LexmountConfig, LexmountError, LexmountLogger, type LogLevel, NetworkError, PaginationInfo, type PaginationInfoShape, type SessionContext, type SessionCreateOptions, type SessionCreateResponse, type SessionDeleteOptions, SessionDownloadInfo, type SessionDownloadInfoShape, SessionDownloadsDeleteResponse, type SessionDownloadsDeleteResponseShape, SessionDownloadsListResponse, type SessionDownloadsListResponseShape, SessionDownloadsResource, SessionInfo, type SessionListOptions, SessionListResponse, SessionNotFoundError, type SessionProxyConfig, type SessionStatus, SessionsResource, TimeoutError, VERSION, ValidationError, Lexmount as default, disableLogging, enableLogging, getLogger, setLogLevel };
|
package/dist/index.js
CHANGED
|
@@ -360,6 +360,37 @@ var ContextsResource = class {
|
|
|
360
360
|
getLogger().info(`Retrieved context '${contextInfo.id}' (status: ${contextInfo.status})`);
|
|
361
361
|
return contextInfo;
|
|
362
362
|
}
|
|
363
|
+
/**
|
|
364
|
+
* Fork an existing persistent context into a new context.
|
|
365
|
+
*/
|
|
366
|
+
async fork(contextId) {
|
|
367
|
+
const url = `${this.client.baseUrl}/instance/v1/contexts/${contextId}/fork`;
|
|
368
|
+
const payload = {
|
|
369
|
+
api_key: this.client.apiKey,
|
|
370
|
+
project_id: this.client.projectId
|
|
371
|
+
};
|
|
372
|
+
const response = await this.client._post(url, payload);
|
|
373
|
+
if (response.status >= 400) {
|
|
374
|
+
this.handleError(response, "fork", contextId);
|
|
375
|
+
}
|
|
376
|
+
const result = asRecord(response.data);
|
|
377
|
+
const forkedContextId = getString(result.context_id);
|
|
378
|
+
if (!forkedContextId) {
|
|
379
|
+
throw new APIError("Failed to fork context: context_id missing from response", {
|
|
380
|
+
statusCode: response.status,
|
|
381
|
+
response: response.data
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
const contextInfo = new ContextInfo({
|
|
385
|
+
id: forkedContextId,
|
|
386
|
+
status: normalizeContextStatus(result.locked),
|
|
387
|
+
metadata: result.metadata ?? {},
|
|
388
|
+
createdAt: parseTimestamp(result.created_at),
|
|
389
|
+
updatedAt: parseTimestamp(result.updated_at)
|
|
390
|
+
});
|
|
391
|
+
getLogger().info(`Successfully forked context '${contextId}' -> '${contextInfo.id}'`);
|
|
392
|
+
return contextInfo;
|
|
393
|
+
}
|
|
363
394
|
/**
|
|
364
395
|
* Delete a persistent context.
|
|
365
396
|
*/
|
|
@@ -406,7 +437,7 @@ var ContextsResource = class {
|
|
|
406
437
|
const suffix = contextId ? `: ${contextId}` : "";
|
|
407
438
|
throw new ContextNotFoundError(`Context not found${suffix}`);
|
|
408
439
|
}
|
|
409
|
-
if (response.status === 409 && errorCode === "context_locked") {
|
|
440
|
+
if (response.status === 409 && (errorCode === "context_locked" || errorCode === "CONTEXT_FORK_SOURCE_LOCKED")) {
|
|
410
441
|
throw new ContextLockedError(errorMessage, {
|
|
411
442
|
activeSessionId: getString(metadata.activeSessionId),
|
|
412
443
|
retryAfter: typeof metadata.retryAfter === "number" ? metadata.retryAfter : void 0
|
|
@@ -844,22 +875,40 @@ var SessionsResource = class {
|
|
|
844
875
|
});
|
|
845
876
|
}
|
|
846
877
|
const containerId = getString3(result.container_id) ?? null;
|
|
847
|
-
const wsUrl = await this._getWebSocketDebuggerUrl(sessionId);
|
|
848
878
|
const projectId = options.projectId ?? this.client.projectId;
|
|
879
|
+
const createdSession = await this._getCreatedSession(sessionId, projectId);
|
|
880
|
+
const wsUrl = await this._getWebSocketDebuggerUrl(sessionId);
|
|
849
881
|
getLogger().info(`Session created successfully: id=${sessionId}, container_id=${containerId}`);
|
|
850
882
|
return new SessionInfo({
|
|
851
883
|
id: sessionId,
|
|
852
|
-
status: "active",
|
|
884
|
+
status: createdSession?.status ?? "active",
|
|
853
885
|
apiKey: this.client.apiKey,
|
|
854
886
|
projectId,
|
|
855
|
-
browserType: options.browserMode ?? "normal",
|
|
856
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
857
|
-
inspectUrl: `${this.client.baseUrl}/inspect?session_id=${sessionId}`,
|
|
858
|
-
containerId,
|
|
859
|
-
ws: wsUrl,
|
|
887
|
+
browserType: createdSession?.browserType ?? (options.browserMode ?? "normal"),
|
|
888
|
+
createdAt: createdSession?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
889
|
+
inspectUrl: createdSession?.inspectUrl ?? `${this.client.baseUrl}/inspect?session_id=${sessionId}`,
|
|
890
|
+
containerId: createdSession?.containerId ?? containerId,
|
|
891
|
+
ws: wsUrl ?? createdSession?.ws ?? null,
|
|
860
892
|
client: this.client
|
|
861
893
|
});
|
|
862
894
|
}
|
|
895
|
+
/**
|
|
896
|
+
* Fetch one session by id.
|
|
897
|
+
*
|
|
898
|
+
* The upstream API uses POST /instance/session for this lookup, so this SDK method
|
|
899
|
+
* intentionally uses POST even though the operation is read-only.
|
|
900
|
+
*/
|
|
901
|
+
async get(sessionId, projectId) {
|
|
902
|
+
const response = await this.client._post(`${this.client.baseUrl}/instance/session`, {
|
|
903
|
+
api_key: this.client.apiKey,
|
|
904
|
+
project_id: projectId ?? this.client.projectId,
|
|
905
|
+
session_id: sessionId
|
|
906
|
+
});
|
|
907
|
+
if (response.status >= 400) {
|
|
908
|
+
this.handleGetError(response, sessionId);
|
|
909
|
+
}
|
|
910
|
+
return this.mapSessionInfo(asRecord3(response.data), projectId);
|
|
911
|
+
}
|
|
863
912
|
/**
|
|
864
913
|
* List sessions for the current project.
|
|
865
914
|
*/
|
|
@@ -879,22 +928,7 @@ var SessionsResource = class {
|
|
|
879
928
|
const result = asRecord3(response.data);
|
|
880
929
|
const sessionsData = Array.isArray(result.sessions) ? result.sessions : [];
|
|
881
930
|
const paginationData = asRecord3(result.pagination);
|
|
882
|
-
const sessions = sessionsData.map((item) =>
|
|
883
|
-
const session = asRecord3(item);
|
|
884
|
-
const sessionId = getString3(session.id) ?? "";
|
|
885
|
-
return new SessionInfo({
|
|
886
|
-
id: sessionId,
|
|
887
|
-
status: getString3(session.status) ?? "active",
|
|
888
|
-
apiKey: getString3(session.api_key) ?? this.client.apiKey,
|
|
889
|
-
projectId: getString3(session.project_id) ?? (options.projectId ?? this.client.projectId),
|
|
890
|
-
browserType: getString3(session.browser_type) ?? "normal",
|
|
891
|
-
createdAt: getString3(session.created_at) ?? "",
|
|
892
|
-
inspectUrl: getString3(session.inspect_url) ?? `${this.client.baseUrl}/inspect?session_id=${sessionId}`,
|
|
893
|
-
containerId: getString3(session.container_id) ?? null,
|
|
894
|
-
ws: getString3(session.ws) ?? null,
|
|
895
|
-
client: this.client
|
|
896
|
-
});
|
|
897
|
-
});
|
|
931
|
+
const sessions = sessionsData.map((item) => this.mapSessionInfo(asRecord3(item), options.projectId));
|
|
898
932
|
const pagination = new PaginationInfo({
|
|
899
933
|
currentPage: getNumber(paginationData.currentPage) ?? 1,
|
|
900
934
|
pageSize: getNumber(paginationData.pageSize) ?? sessions.length,
|
|
@@ -988,6 +1022,24 @@ var SessionsResource = class {
|
|
|
988
1022
|
response: response.data
|
|
989
1023
|
});
|
|
990
1024
|
}
|
|
1025
|
+
handleGetError(response, sessionId) {
|
|
1026
|
+
const errorData = asRecord3(response.data);
|
|
1027
|
+
const errorMessage = getString3(errorData.error) ?? getString3(errorData.message) ?? "Unknown error";
|
|
1028
|
+
if (response.status === 401) {
|
|
1029
|
+
throw new AuthenticationError(
|
|
1030
|
+
`Authentication failed: ${errorMessage}. Please check your API key and project ID.`
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
if (response.status === 404) {
|
|
1034
|
+
throw new SessionNotFoundError(
|
|
1035
|
+
`Session not found: ${errorMessage}. Session ID '${sessionId}' may not exist in this project.`
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
1038
|
+
throw new APIError(`Failed to get session: ${errorMessage}`, {
|
|
1039
|
+
statusCode: response.status,
|
|
1040
|
+
response: response.data
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
991
1043
|
handleListError(response) {
|
|
992
1044
|
const errorData = asRecord3(response.data);
|
|
993
1045
|
const errorMessage = getString3(errorData.error) ?? getString3(errorData.message) ?? "Unknown error";
|
|
@@ -1019,6 +1071,32 @@ var SessionsResource = class {
|
|
|
1019
1071
|
response: response.data
|
|
1020
1072
|
});
|
|
1021
1073
|
}
|
|
1074
|
+
mapSessionInfo(raw, projectId) {
|
|
1075
|
+
const sessionId = getString3(raw.session_id) ?? getString3(raw.id) ?? "";
|
|
1076
|
+
return new SessionInfo({
|
|
1077
|
+
id: sessionId,
|
|
1078
|
+
status: getString3(raw.status) ?? "active",
|
|
1079
|
+
apiKey: getString3(raw.api_key) ?? this.client.apiKey,
|
|
1080
|
+
projectId: getString3(raw.project_id) ?? (projectId ?? this.client.projectId),
|
|
1081
|
+
browserType: getString3(raw.browser_type) ?? getString3(raw.browser_mode) ?? "normal",
|
|
1082
|
+
createdAt: getString3(raw.created_at) ?? "",
|
|
1083
|
+
inspectUrl: getString3(raw.inspect_url) ?? `${this.client.baseUrl}/inspect?session_id=${sessionId}`,
|
|
1084
|
+
containerId: getString3(raw.container_id) ?? null,
|
|
1085
|
+
ws: getString3(raw.ws) ?? null,
|
|
1086
|
+
client: this.client
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
async _getCreatedSession(sessionId, projectId) {
|
|
1090
|
+
try {
|
|
1091
|
+
return await this.get(sessionId, projectId);
|
|
1092
|
+
} catch (error) {
|
|
1093
|
+
getLogger().warn(
|
|
1094
|
+
`Failed to fetch created session ${sessionId}; falling back to locally derived session fields:`,
|
|
1095
|
+
error
|
|
1096
|
+
);
|
|
1097
|
+
return null;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1022
1100
|
};
|
|
1023
1101
|
|
|
1024
1102
|
// src/client.ts
|
|
@@ -1147,7 +1225,7 @@ var Lexmount = class {
|
|
|
1147
1225
|
};
|
|
1148
1226
|
|
|
1149
1227
|
// src/index.ts
|
|
1150
|
-
var VERSION = "0.2.
|
|
1228
|
+
var VERSION = "0.2.5";
|
|
1151
1229
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1152
1230
|
0 && (module.exports = {
|
|
1153
1231
|
APIError,
|