@modelprofile.com/browser-runtime 5.0.0 → 5.1.0
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/changelog.md +9 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.artifactstore.d.ts +4 -3
- package/dist_ts/classes.artifactstore.js +19 -5
- package/dist_ts/classes.runtime.js +12 -6
- package/package.json +1 -1
- package/readme.md +2 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.artifactstore.ts +19 -1
- package/ts/classes.runtime.ts +11 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modelprofile.com/browser-runtime",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Parent-owned, resource-centric Chromium runtime with revisioned attachment fencing, authenticated human and agent control, fail-closed egress, bounded artifacts, and Flex/MCP adapters.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
package/readme.md
CHANGED
|
@@ -126,7 +126,7 @@ renewal leaves the original expiry unchanged. Only one authorization callback pe
|
|
|
126
126
|
may remain unsettled, including after timeout; runtime-wide renewal admission is bounded
|
|
127
127
|
by `maxCapabilities`. Hosts must stop scheduling renewal when their viewer disconnects.
|
|
128
128
|
|
|
129
|
-
Agent actions are exactly `navigate`, `snapshot`, `screenshot`, `click`, `fill`, and `press`. Human leases additionally expose tab lifecycle, viewport, raw input, frame subscription/acknowledgement and refresh, and exact-resource artifact reads/deletes. JavaScript evaluation is not public.
|
|
129
|
+
Agent actions are exactly `navigate`, `snapshot`, `screenshot`, `click`, `fill`, and `press`. Agents can read or delete screenshots created by their exact lease using `readArtifact()` and `deleteArtifact()`. Human leases additionally expose tab lifecycle, viewport, raw input, frame subscription/acknowledgement and refresh, and exact-resource artifact reads/deletes. JavaScript evaluation is not public.
|
|
130
130
|
|
|
131
131
|
All participants enter one strict resource FIFO. `maxQueuedOperationsPerLease` defaults to 128 and accepts 1 through 1,024; each participant has that bound, and the resource queue is bounded by its product with `maxCapabilitiesPerResource`. Overflow fails with `QUOTA_EXCEEDED`. Releasing a participant cancels only its queued and active work. Resource termination and shutdown cancel everyone. Started uncertain work must quiesce or the exact incarnation is terminated before operation settlement.
|
|
132
132
|
|
|
@@ -164,7 +164,7 @@ The MCP tool list is exactly `browser_navigate`, `browser_snapshot`, `browser_sc
|
|
|
164
164
|
|
|
165
165
|
Each running resource owns one authenticated loopback `BrowserEgressProxy` carrying immutable `projectId` and `browserResourceId`. HTTP, WebSocket Upgrade, and CONNECT share strict public-unicast DNS/IP validation, numeric dialing, bounded lifetimes, and fail-closed policy.
|
|
166
166
|
|
|
167
|
-
Artifact identity and APIs use `(projectId, browserResourceId, artifactId)`. Keyed project/resource directories prevent caller IDs from entering paths. Admission is serialized across per-resource, per-project, and global count/byte quotas. Reads use no-follow handles and verify size and SHA-256.
|
|
167
|
+
Artifact identity and APIs use `(projectId, browserResourceId, artifactId)`. Keyed project/resource directories prevent caller IDs from entering paths. Admission is serialized across per-resource, per-project, and global count/byte quotas. Reads use no-follow handles and verify size and SHA-256. Screenshot actions return metadata. Direct agent leases can read/delete only artifacts created by that exact lease; other agent and human artifacts remain unavailable. Human artifact access remains scoped to the resource. Read completion revalidates the lease and clears bytes if authority ended during IO. The built-in framed/Flex/MCP action adapters continue to return metadata; hosts can return image content after a scoped lease read. The artifact store accepts an optional opaque owner ID on `store()` and an expected owner ID on `read()`/`delete()`; omitting an expected owner retains its trusted parent API.
|
|
168
168
|
|
|
169
169
|
## Verification
|
|
170
170
|
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@modelprofile.com/browser-runtime',
|
|
6
|
-
version: '5.
|
|
6
|
+
version: '5.1.0',
|
|
7
7
|
description: 'Parent-owned, resource-centric Chromium runtime with revisioned attachment fencing, authenticated human and agent control, fail-closed egress, bounded artifacts, and Flex/MCP adapters.'
|
|
8
8
|
}
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
interface IStoredArtifact {
|
|
16
16
|
metadata: IBrowserArtifactMetadata;
|
|
17
17
|
digest: string;
|
|
18
|
+
ownerId?: string;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
interface IResourceArtifacts {
|
|
@@ -164,11 +165,14 @@ export class BrowserArtifactStore {
|
|
|
164
165
|
browserResourceIdArg: string,
|
|
165
166
|
mimeTypeArg: string,
|
|
166
167
|
data: Uint8Array,
|
|
168
|
+
ownerIdArg?: string,
|
|
167
169
|
): Promise<IBrowserArtifactMetadata> {
|
|
168
170
|
this.requireStarted();
|
|
169
171
|
const projectId = this.validateProjectId(projectIdArg);
|
|
170
172
|
const browserResourceId = this.validateResourceId(browserResourceIdArg);
|
|
171
173
|
const mimeType = validateBoundedString(mimeTypeArg, 'mimeType', 1, 128);
|
|
174
|
+
const ownerId = ownerIdArg === undefined ? undefined
|
|
175
|
+
: validateBoundedString(ownerIdArg, 'ownerId', 1, 128);
|
|
172
176
|
if (!(data instanceof Uint8Array)) {
|
|
173
177
|
throw new BrowserRuntimeError('INVALID_INPUT', 'data must be Uint8Array');
|
|
174
178
|
}
|
|
@@ -212,6 +216,7 @@ export class BrowserArtifactStore {
|
|
|
212
216
|
const stored: IStoredArtifact = {
|
|
213
217
|
metadata,
|
|
214
218
|
digest: plugins.crypto.createHash('sha256').update(bytes).digest('hex'),
|
|
219
|
+
...(ownerId === undefined ? {} : { ownerId }),
|
|
215
220
|
};
|
|
216
221
|
const resourceDirectory = this.resourcePath(project, resource);
|
|
217
222
|
const finalPath = plugins.path.join(resourceDirectory, artifactId);
|
|
@@ -252,6 +257,7 @@ export class BrowserArtifactStore {
|
|
|
252
257
|
projectIdArg: string,
|
|
253
258
|
browserResourceIdArg: string,
|
|
254
259
|
artifactIdArg: string,
|
|
260
|
+
expectedOwnerIdArg?: string,
|
|
255
261
|
): Promise<Uint8Array> {
|
|
256
262
|
this.requireStarted();
|
|
257
263
|
const projectId = this.validateProjectId(projectIdArg);
|
|
@@ -261,6 +267,7 @@ export class BrowserArtifactStore {
|
|
|
261
267
|
let handle: plugins.fsPromises.FileHandle | undefined;
|
|
262
268
|
try {
|
|
263
269
|
const resolved = this.resolveArtifact(projectId, browserResourceId, artifactId);
|
|
270
|
+
this.assertArtifactOwner(resolved.stored, expectedOwnerIdArg);
|
|
264
271
|
if (resolved.stored.metadata.expiresAt <= this.now()) {
|
|
265
272
|
await this.deleteArtifact(projectId, browserResourceId, resolved);
|
|
266
273
|
throw new BrowserRuntimeError('INVALID_INPUT', 'artifact is unavailable');
|
|
@@ -300,6 +307,7 @@ export class BrowserArtifactStore {
|
|
|
300
307
|
projectIdArg: string,
|
|
301
308
|
browserResourceIdArg: string,
|
|
302
309
|
artifactIdArg: string,
|
|
310
|
+
expectedOwnerIdArg?: string,
|
|
303
311
|
): Promise<void> {
|
|
304
312
|
this.requireStarted();
|
|
305
313
|
const projectId = this.validateProjectId(projectIdArg);
|
|
@@ -307,16 +315,26 @@ export class BrowserArtifactStore {
|
|
|
307
315
|
const artifactId = this.validateArtifactId(artifactIdArg);
|
|
308
316
|
const release = await this.mutex.acquire();
|
|
309
317
|
try {
|
|
318
|
+
const resolved = this.resolveArtifact(projectId, browserResourceId, artifactId);
|
|
319
|
+
this.assertArtifactOwner(resolved.stored, expectedOwnerIdArg);
|
|
310
320
|
await this.deleteArtifact(
|
|
311
321
|
projectId,
|
|
312
322
|
browserResourceId,
|
|
313
|
-
|
|
323
|
+
resolved,
|
|
314
324
|
);
|
|
315
325
|
} finally {
|
|
316
326
|
release();
|
|
317
327
|
}
|
|
318
328
|
}
|
|
319
329
|
|
|
330
|
+
private assertArtifactOwner(storedArg: IStoredArtifact, expectedOwnerIdArg?: string): void {
|
|
331
|
+
if (expectedOwnerIdArg === undefined) return;
|
|
332
|
+
const expectedOwnerId = validateBoundedString(expectedOwnerIdArg, 'ownerId', 1, 128);
|
|
333
|
+
if (storedArg.ownerId !== expectedOwnerId) {
|
|
334
|
+
throw new BrowserRuntimeError('INVALID_INPUT', 'artifact is unavailable');
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
320
338
|
public async purgeExpired(resource?: IBrowserResourceKey): Promise<number> {
|
|
321
339
|
this.requireStarted();
|
|
322
340
|
const validated = resource === undefined ? undefined : {
|
package/ts/classes.runtime.ts
CHANGED
|
@@ -1649,23 +1649,30 @@ export class BrowserRuntime {
|
|
|
1649
1649
|
lease: ILeaseRecord,
|
|
1650
1650
|
artifactId: string,
|
|
1651
1651
|
): Promise<Uint8Array> {
|
|
1652
|
-
this.requireHuman(lease);
|
|
1653
1652
|
this.requireValidLease(lease);
|
|
1654
|
-
|
|
1653
|
+
const bytes = await this.requireArtifactStore().read(
|
|
1655
1654
|
lease.capability.projectId,
|
|
1656
1655
|
lease.capability.browserResourceId,
|
|
1657
1656
|
artifactId,
|
|
1657
|
+
lease.role === 'agent' ? lease.leaseId : undefined,
|
|
1658
1658
|
);
|
|
1659
|
+
try {
|
|
1660
|
+
this.requireValidLease(lease);
|
|
1661
|
+
return bytes;
|
|
1662
|
+
} catch (errorArg) {
|
|
1663
|
+
bytes.fill(0);
|
|
1664
|
+
throw errorArg;
|
|
1665
|
+
}
|
|
1659
1666
|
}
|
|
1660
1667
|
|
|
1661
1668
|
/** @internal */
|
|
1662
1669
|
public async deleteLeaseArtifact(lease: ILeaseRecord, artifactId: string): Promise<void> {
|
|
1663
|
-
this.requireHuman(lease);
|
|
1664
1670
|
this.requireValidLease(lease);
|
|
1665
1671
|
await this.requireArtifactStore().delete(
|
|
1666
1672
|
lease.capability.projectId,
|
|
1667
1673
|
lease.capability.browserResourceId,
|
|
1668
1674
|
artifactId,
|
|
1675
|
+
lease.role === 'agent' ? lease.leaseId : undefined,
|
|
1669
1676
|
);
|
|
1670
1677
|
}
|
|
1671
1678
|
|
|
@@ -2564,6 +2571,7 @@ export class BrowserRuntime {
|
|
|
2564
2571
|
lease.capability.browserResourceId,
|
|
2565
2572
|
snapshot.mimeType,
|
|
2566
2573
|
snapshot.data,
|
|
2574
|
+
lease.leaseId,
|
|
2567
2575
|
);
|
|
2568
2576
|
return { action: 'screenshot', artifact } satisfies IBrowserScreenshotResult;
|
|
2569
2577
|
} finally {
|