@ricsam/isolate-client 0.0.1 → 0.1.1

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 CHANGED
@@ -1,45 +1,114 @@
1
1
  # @ricsam/isolate-client
2
2
 
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
4
-
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
6
-
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
8
-
9
- ## Purpose
10
-
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@ricsam/isolate-client`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
15
-
16
- ## What is OIDC Trusted Publishing?
17
-
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
19
-
20
- ## Setup Instructions
21
-
22
- To properly configure OIDC trusted publishing for this package:
23
-
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
28
-
29
- ## DO NOT USE THIS PACKAGE
30
-
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
36
-
37
- ## More Information
38
-
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
42
-
43
- ---
44
-
45
- **Maintained for OIDC setup purposes only**
3
+ Client library for connecting to the isolate daemon. Works with **any JavaScript runtime** (Node.js, Bun, Deno) since it only requires standard socket APIs.
4
+
5
+ ```bash
6
+ npm add @ricsam/isolate-client
7
+ ```
8
+
9
+ **Features:**
10
+ - Connect via Unix socket or TCP
11
+ - Create and manage remote runtimes
12
+ - Execute code in isolated V8 contexts
13
+ - Dispatch HTTP requests to isolate handlers
14
+ - Bidirectional callbacks (console, fetch, fs)
15
+ - Test environment and Playwright support
16
+
17
+ **Basic Usage:**
18
+
19
+ ```typescript
20
+ import { connect } from "@ricsam/isolate-client";
21
+
22
+ // Connect to daemon
23
+ const client = await connect({
24
+ socket: "/tmp/isolate-daemon.sock",
25
+ // Or TCP: host: "127.0.0.1", port: 47891
26
+ });
27
+
28
+ // Create a runtime with callbacks
29
+ const runtime = await client.createRuntime({
30
+ memoryLimit: 128,
31
+ console: {
32
+ log: (...args) => console.log("[isolate]", ...args),
33
+ error: (...args) => console.error("[isolate]", ...args),
34
+ },
35
+ fetch: async (request) => fetch(request),
36
+ fs: {
37
+ readFile: async (path) => Bun.file(path).arrayBuffer(),
38
+ writeFile: async (path, data) => Bun.write(path, data),
39
+ stat: async (path) => {
40
+ const stat = await Bun.file(path).stat();
41
+ return { isFile: true, isDirectory: false, size: stat.size };
42
+ },
43
+ },
44
+ });
45
+
46
+ // Execute code
47
+ await runtime.eval(`console.log("Hello from isolate!")`);
48
+
49
+ // Set up HTTP handler and dispatch requests
50
+ await runtime.eval(`
51
+ serve({
52
+ fetch(request) {
53
+ return Response.json({ message: "Hello!" });
54
+ }
55
+ });
56
+ `);
57
+
58
+ const response = await runtime.dispatchRequest(
59
+ new Request("http://localhost/api")
60
+ );
61
+ console.log(await response.json()); // { message: "Hello!" }
62
+
63
+ // Cleanup
64
+ await runtime.dispose();
65
+ await client.close();
66
+ ```
67
+
68
+ **Test Environment:**
69
+
70
+ ```typescript
71
+ // Setup test framework in isolate
72
+ await runtime.setupTestEnvironment();
73
+
74
+ // Define tests
75
+ await runtime.eval(`
76
+ describe("math", () => {
77
+ it("adds numbers", () => {
78
+ expect(1 + 1).toBe(2);
79
+ });
80
+ });
81
+ `);
82
+
83
+ // Run tests
84
+ const results = await runtime.runTests();
85
+ console.log(`${results.passed}/${results.total} passed`);
86
+ ```
87
+
88
+ **Playwright Integration:**
89
+
90
+ ```typescript
91
+ // Setup Playwright (daemon launches browser)
92
+ await runtime.setupPlaywright({
93
+ browserType: "chromium",
94
+ headless: true,
95
+ onConsoleLog: (log) => console.log("[browser]", log),
96
+ });
97
+
98
+ // Define browser tests
99
+ await runtime.eval(`
100
+ test("homepage loads", async () => {
101
+ await page.goto("https://example.com");
102
+ await expect(page.getByText("Example Domain")).toBeVisible();
103
+ });
104
+ `);
105
+
106
+ // Run tests
107
+ const results = await runtime.runPlaywrightTests();
108
+ console.log(`${results.passed}/${results.total} passed`);
109
+
110
+ // Get collected data
111
+ const data = await runtime.getCollectedData();
112
+ console.log("Console logs:", data.consoleLogs);
113
+ console.log("Network requests:", data.networkRequests);
114
+ ```
@@ -0,0 +1,504 @@
1
+ // @bun @bun-cjs
2
+ (function(exports, require, module, __filename, __dirname) {var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
7
+ var __toCommonJS = (from) => {
8
+ var entry = __moduleCache.get(from), desc;
9
+ if (entry)
10
+ return entry;
11
+ entry = __defProp({}, "__esModule", { value: true });
12
+ if (from && typeof from === "object" || typeof from === "function")
13
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
14
+ get: () => from[key],
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ }));
17
+ __moduleCache.set(from, entry);
18
+ return entry;
19
+ };
20
+ var __export = (target, all) => {
21
+ for (var name in all)
22
+ __defProp(target, name, {
23
+ get: all[name],
24
+ enumerable: true,
25
+ configurable: true,
26
+ set: (newValue) => all[name] = () => newValue
27
+ });
28
+ };
29
+
30
+ // packages/isolate-client/src/connection.ts
31
+ var exports_connection = {};
32
+ __export(exports_connection, {
33
+ connect: () => connect
34
+ });
35
+ module.exports = __toCommonJS(exports_connection);
36
+ var import_node_net = require("net");
37
+ var import_isolate_protocol = require("@ricsam/isolate-protocol");
38
+ var DEFAULT_TIMEOUT = 30000;
39
+ async function connect(options = {}) {
40
+ const socket = await createSocket(options);
41
+ const state = {
42
+ socket,
43
+ pendingRequests: new Map,
44
+ callbacks: new Map,
45
+ nextRequestId: 1,
46
+ nextCallbackId: 1,
47
+ connected: true,
48
+ playwrightEventHandlers: new Map
49
+ };
50
+ const parser = import_isolate_protocol.createFrameParser();
51
+ socket.on("data", (data) => {
52
+ try {
53
+ for (const frame of parser.feed(new Uint8Array(data))) {
54
+ handleMessage(frame.message, state);
55
+ }
56
+ } catch (err) {
57
+ console.error("Error parsing frame:", err);
58
+ }
59
+ });
60
+ socket.on("close", () => {
61
+ state.connected = false;
62
+ for (const [, pending] of state.pendingRequests) {
63
+ pending.reject(new Error("Connection closed"));
64
+ }
65
+ state.pendingRequests.clear();
66
+ });
67
+ socket.on("error", (err) => {
68
+ console.error("Socket error:", err);
69
+ });
70
+ return {
71
+ createRuntime: (runtimeOptions) => createRuntime(state, runtimeOptions),
72
+ close: async () => {
73
+ state.connected = false;
74
+ socket.destroy();
75
+ },
76
+ isConnected: () => state.connected
77
+ };
78
+ }
79
+ function createSocket(options) {
80
+ return new Promise((resolve, reject) => {
81
+ const timeout = options.timeout ?? DEFAULT_TIMEOUT;
82
+ let socket;
83
+ const onError = (err) => {
84
+ reject(err);
85
+ };
86
+ const onConnect = () => {
87
+ socket.removeListener("error", onError);
88
+ resolve(socket);
89
+ };
90
+ if (options.socket) {
91
+ socket = import_node_net.connect(options.socket, onConnect);
92
+ } else {
93
+ socket = import_node_net.connect(options.port ?? 47891, options.host ?? "127.0.0.1", onConnect);
94
+ }
95
+ socket.on("error", onError);
96
+ const timeoutId = setTimeout(() => {
97
+ socket.destroy();
98
+ reject(new Error("Connection timeout"));
99
+ }, timeout);
100
+ socket.once("connect", () => {
101
+ clearTimeout(timeoutId);
102
+ });
103
+ });
104
+ }
105
+ function handleMessage(message, state) {
106
+ switch (message.type) {
107
+ case import_isolate_protocol.MessageType.RESPONSE_OK: {
108
+ const response = message;
109
+ const pending = state.pendingRequests.get(response.requestId);
110
+ if (pending) {
111
+ state.pendingRequests.delete(response.requestId);
112
+ if (pending.timeoutId)
113
+ clearTimeout(pending.timeoutId);
114
+ pending.resolve(response.data);
115
+ }
116
+ break;
117
+ }
118
+ case import_isolate_protocol.MessageType.RESPONSE_ERROR: {
119
+ const response = message;
120
+ const pending = state.pendingRequests.get(response.requestId);
121
+ if (pending) {
122
+ state.pendingRequests.delete(response.requestId);
123
+ if (pending.timeoutId)
124
+ clearTimeout(pending.timeoutId);
125
+ const error = new Error(response.message);
126
+ if (response.details) {
127
+ error.name = response.details.name;
128
+ if (response.details.stack) {
129
+ error.stack = response.details.stack;
130
+ }
131
+ }
132
+ pending.reject(error);
133
+ }
134
+ break;
135
+ }
136
+ case import_isolate_protocol.MessageType.CALLBACK_INVOKE: {
137
+ const invoke = message;
138
+ handleCallbackInvoke(invoke, state);
139
+ break;
140
+ }
141
+ case import_isolate_protocol.MessageType.PONG:
142
+ break;
143
+ case import_isolate_protocol.MessageType.PLAYWRIGHT_EVENT: {
144
+ const event = message;
145
+ const handler = state.playwrightEventHandlers.get(event.isolateId);
146
+ if (handler) {
147
+ switch (event.eventType) {
148
+ case "consoleLog":
149
+ handler.onConsoleLog?.(event.payload);
150
+ break;
151
+ case "networkRequest":
152
+ handler.onNetworkRequest?.(event.payload);
153
+ break;
154
+ case "networkResponse":
155
+ handler.onNetworkResponse?.(event.payload);
156
+ break;
157
+ }
158
+ }
159
+ break;
160
+ }
161
+ default:
162
+ console.warn(`Unexpected message type: ${message.type}`);
163
+ }
164
+ }
165
+ async function handleCallbackInvoke(invoke, state) {
166
+ const callback = state.callbacks.get(invoke.callbackId);
167
+ const response = {
168
+ type: import_isolate_protocol.MessageType.CALLBACK_RESPONSE,
169
+ requestId: invoke.requestId
170
+ };
171
+ if (!callback) {
172
+ response.error = {
173
+ name: "Error",
174
+ message: `Unknown callback: ${invoke.callbackId}`
175
+ };
176
+ } else {
177
+ try {
178
+ const result = await callback(...invoke.args);
179
+ response.result = result;
180
+ } catch (err) {
181
+ const error = err;
182
+ response.error = {
183
+ name: error.name,
184
+ message: error.message,
185
+ stack: error.stack
186
+ };
187
+ }
188
+ }
189
+ sendMessage(state.socket, response);
190
+ }
191
+ function sendMessage(socket, message) {
192
+ const frame = import_isolate_protocol.buildFrame(message);
193
+ socket.write(frame);
194
+ }
195
+ function sendRequest(state, message, timeout = DEFAULT_TIMEOUT) {
196
+ return new Promise((resolve, reject) => {
197
+ if (!state.connected) {
198
+ reject(new Error("Not connected"));
199
+ return;
200
+ }
201
+ const requestId = message.requestId;
202
+ const timeoutId = setTimeout(() => {
203
+ state.pendingRequests.delete(requestId);
204
+ reject(new Error("Request timeout"));
205
+ }, timeout);
206
+ state.pendingRequests.set(requestId, {
207
+ resolve,
208
+ reject,
209
+ timeoutId
210
+ });
211
+ sendMessage(state.socket, message);
212
+ });
213
+ }
214
+ async function createRuntime(state, options = {}) {
215
+ const callbacks = {};
216
+ if (options.console) {
217
+ callbacks.console = registerConsoleCallbacks(state, options.console);
218
+ }
219
+ if (options.fetch) {
220
+ callbacks.fetch = registerFetchCallback(state, options.fetch);
221
+ }
222
+ if (options.fs) {
223
+ callbacks.fs = registerFsCallbacks(state, options.fs);
224
+ }
225
+ const requestId = state.nextRequestId++;
226
+ const request = {
227
+ type: import_isolate_protocol.MessageType.CREATE_RUNTIME,
228
+ requestId,
229
+ options: {
230
+ memoryLimit: options.memoryLimit,
231
+ callbacks
232
+ }
233
+ };
234
+ const result = await sendRequest(state, request);
235
+ const isolateId = result.isolateId;
236
+ return {
237
+ isolateId,
238
+ eval: async (code, filename) => {
239
+ const reqId = state.nextRequestId++;
240
+ const req = {
241
+ type: import_isolate_protocol.MessageType.EVAL,
242
+ requestId: reqId,
243
+ isolateId,
244
+ code,
245
+ filename
246
+ };
247
+ const res = await sendRequest(state, req);
248
+ return res.value;
249
+ },
250
+ dispatchRequest: async (request2, dispatchOptions) => {
251
+ const reqId = state.nextRequestId++;
252
+ const serialized = await serializeRequest(request2);
253
+ const req = {
254
+ type: import_isolate_protocol.MessageType.DISPATCH_REQUEST,
255
+ requestId: reqId,
256
+ isolateId,
257
+ request: serialized,
258
+ options: dispatchOptions
259
+ };
260
+ const res = await sendRequest(state, req, dispatchOptions?.timeout ?? DEFAULT_TIMEOUT);
261
+ return deserializeResponse(res.response);
262
+ },
263
+ tick: async (ms) => {
264
+ const reqId = state.nextRequestId++;
265
+ const req = {
266
+ type: import_isolate_protocol.MessageType.TICK,
267
+ requestId: reqId,
268
+ isolateId,
269
+ ms
270
+ };
271
+ await sendRequest(state, req);
272
+ },
273
+ setupTestEnvironment: async () => {
274
+ const reqId = state.nextRequestId++;
275
+ const req = {
276
+ type: import_isolate_protocol.MessageType.SETUP_TEST_ENV,
277
+ requestId: reqId,
278
+ isolateId
279
+ };
280
+ await sendRequest(state, req);
281
+ },
282
+ runTests: async (timeout) => {
283
+ const reqId = state.nextRequestId++;
284
+ const req = {
285
+ type: import_isolate_protocol.MessageType.RUN_TESTS,
286
+ requestId: reqId,
287
+ isolateId,
288
+ timeout
289
+ };
290
+ return sendRequest(state, req, timeout ?? DEFAULT_TIMEOUT);
291
+ },
292
+ setupPlaywright: async (playwrightOptions) => {
293
+ const reqId = state.nextRequestId++;
294
+ const req = {
295
+ type: import_isolate_protocol.MessageType.SETUP_PLAYWRIGHT,
296
+ requestId: reqId,
297
+ isolateId,
298
+ options: {
299
+ browserType: playwrightOptions?.browserType,
300
+ headless: playwrightOptions?.headless,
301
+ baseURL: playwrightOptions?.baseURL
302
+ }
303
+ };
304
+ if (playwrightOptions?.onConsoleLog || playwrightOptions?.onNetworkRequest || playwrightOptions?.onNetworkResponse) {
305
+ state.playwrightEventHandlers.set(isolateId, {
306
+ onConsoleLog: playwrightOptions.onConsoleLog,
307
+ onNetworkRequest: playwrightOptions.onNetworkRequest,
308
+ onNetworkResponse: playwrightOptions.onNetworkResponse
309
+ });
310
+ }
311
+ await sendRequest(state, req);
312
+ },
313
+ runPlaywrightTests: async (timeout) => {
314
+ const reqId = state.nextRequestId++;
315
+ const req = {
316
+ type: import_isolate_protocol.MessageType.RUN_PLAYWRIGHT_TESTS,
317
+ requestId: reqId,
318
+ isolateId,
319
+ timeout
320
+ };
321
+ return sendRequest(state, req, timeout ?? DEFAULT_TIMEOUT);
322
+ },
323
+ resetPlaywrightTests: async () => {
324
+ const reqId = state.nextRequestId++;
325
+ const req = {
326
+ type: import_isolate_protocol.MessageType.RESET_PLAYWRIGHT_TESTS,
327
+ requestId: reqId,
328
+ isolateId
329
+ };
330
+ await sendRequest(state, req);
331
+ },
332
+ getCollectedData: async () => {
333
+ const reqId = state.nextRequestId++;
334
+ const req = {
335
+ type: import_isolate_protocol.MessageType.GET_COLLECTED_DATA,
336
+ requestId: reqId,
337
+ isolateId
338
+ };
339
+ return sendRequest(state, req);
340
+ },
341
+ dispose: async () => {
342
+ state.playwrightEventHandlers.delete(isolateId);
343
+ const reqId = state.nextRequestId++;
344
+ const req = {
345
+ type: import_isolate_protocol.MessageType.DISPOSE_RUNTIME,
346
+ requestId: reqId,
347
+ isolateId
348
+ };
349
+ await sendRequest(state, req);
350
+ }
351
+ };
352
+ }
353
+ function registerConsoleCallbacks(state, callbacks) {
354
+ const registrations = {};
355
+ const register = (name, fn) => {
356
+ const callbackId = state.nextCallbackId++;
357
+ state.callbacks.set(callbackId, (_level, ...args) => {
358
+ fn(...args);
359
+ });
360
+ registrations[name] = { callbackId, name, async: false };
361
+ };
362
+ if (callbacks.log)
363
+ register("log", callbacks.log);
364
+ if (callbacks.warn)
365
+ register("warn", callbacks.warn);
366
+ if (callbacks.error)
367
+ register("error", callbacks.error);
368
+ if (callbacks.info)
369
+ register("info", callbacks.info);
370
+ if (callbacks.debug)
371
+ register("debug", callbacks.debug);
372
+ if (callbacks.dir)
373
+ register("dir", callbacks.dir);
374
+ return registrations;
375
+ }
376
+ function registerFetchCallback(state, callback) {
377
+ const callbackId = state.nextCallbackId++;
378
+ state.callbacks.set(callbackId, async (serialized) => {
379
+ const request = deserializeRequest(serialized);
380
+ const response = await callback(request);
381
+ return serializeResponse(response);
382
+ });
383
+ return { callbackId, name: "fetch", async: true };
384
+ }
385
+ function registerFsCallbacks(state, callbacks) {
386
+ const registrations = {};
387
+ if (callbacks.readFile) {
388
+ const callbackId = state.nextCallbackId++;
389
+ state.callbacks.set(callbackId, async (path) => {
390
+ const result = await callbacks.readFile(path);
391
+ return new Uint8Array(result);
392
+ });
393
+ registrations.readFile = { callbackId, name: "readFile", async: true };
394
+ }
395
+ if (callbacks.writeFile) {
396
+ const callbackId = state.nextCallbackId++;
397
+ state.callbacks.set(callbackId, async (path, data) => {
398
+ let buffer;
399
+ if (data instanceof Uint8Array) {
400
+ buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
401
+ } else if (Array.isArray(data)) {
402
+ buffer = new Uint8Array(data).buffer;
403
+ } else if (data instanceof ArrayBuffer) {
404
+ buffer = data;
405
+ } else {
406
+ buffer = new ArrayBuffer(0);
407
+ }
408
+ await callbacks.writeFile(path, buffer);
409
+ });
410
+ registrations.writeFile = { callbackId, name: "writeFile", async: true };
411
+ }
412
+ if (callbacks.unlink) {
413
+ const callbackId = state.nextCallbackId++;
414
+ state.callbacks.set(callbackId, async (path) => {
415
+ await callbacks.unlink(path);
416
+ });
417
+ registrations.unlink = { callbackId, name: "unlink", async: true };
418
+ }
419
+ if (callbacks.readdir) {
420
+ const callbackId = state.nextCallbackId++;
421
+ state.callbacks.set(callbackId, async (path) => {
422
+ return callbacks.readdir(path);
423
+ });
424
+ registrations.readdir = { callbackId, name: "readdir", async: true };
425
+ }
426
+ if (callbacks.mkdir) {
427
+ const callbackId = state.nextCallbackId++;
428
+ state.callbacks.set(callbackId, async (path, options) => {
429
+ await callbacks.mkdir(path, options);
430
+ });
431
+ registrations.mkdir = { callbackId, name: "mkdir", async: true };
432
+ }
433
+ if (callbacks.rmdir) {
434
+ const callbackId = state.nextCallbackId++;
435
+ state.callbacks.set(callbackId, async (path) => {
436
+ await callbacks.rmdir(path);
437
+ });
438
+ registrations.rmdir = { callbackId, name: "rmdir", async: true };
439
+ }
440
+ if (callbacks.stat) {
441
+ const callbackId = state.nextCallbackId++;
442
+ state.callbacks.set(callbackId, async (path) => {
443
+ return callbacks.stat(path);
444
+ });
445
+ registrations.stat = { callbackId, name: "stat", async: true };
446
+ }
447
+ if (callbacks.rename) {
448
+ const callbackId = state.nextCallbackId++;
449
+ state.callbacks.set(callbackId, async (from, to) => {
450
+ await callbacks.rename(from, to);
451
+ });
452
+ registrations.rename = { callbackId, name: "rename", async: true };
453
+ }
454
+ return registrations;
455
+ }
456
+ async function serializeRequest(request) {
457
+ const headers = [];
458
+ request.headers.forEach((value, key) => {
459
+ headers.push([key, value]);
460
+ });
461
+ let body = null;
462
+ if (request.body) {
463
+ body = new Uint8Array(await request.arrayBuffer());
464
+ }
465
+ return {
466
+ method: request.method,
467
+ url: request.url,
468
+ headers,
469
+ body
470
+ };
471
+ }
472
+ async function serializeResponse(response) {
473
+ const headers = [];
474
+ response.headers.forEach((value, key) => {
475
+ headers.push([key, value]);
476
+ });
477
+ let body = null;
478
+ if (response.body) {
479
+ body = new Uint8Array(await response.arrayBuffer());
480
+ }
481
+ return {
482
+ status: response.status,
483
+ statusText: response.statusText,
484
+ headers,
485
+ body
486
+ };
487
+ }
488
+ function deserializeRequest(data) {
489
+ return new Request(data.url, {
490
+ method: data.method,
491
+ headers: data.headers,
492
+ body: data.body
493
+ });
494
+ }
495
+ function deserializeResponse(data) {
496
+ return new Response(data.body, {
497
+ status: data.status,
498
+ statusText: data.statusText,
499
+ headers: data.headers
500
+ });
501
+ }
502
+ })
503
+
504
+ //# debugId=AC520FAF0080FE0764756E2164756E21