@amalgm/shell 0.1.119 → 0.1.121
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/PURPOSE.md +47 -0
- package/dist/boot.d.ts +1 -7
- package/dist/boot.js +53 -21
- package/dist/boot.js.map +1 -1
- package/dist/builtin-agents.d.ts +3 -2
- package/dist/builtin-agents.js +41 -8
- package/dist/builtin-agents.js.map +1 -1
- package/dist/chat-composition.d.ts +7 -0
- package/dist/chat-composition.js +38 -6
- package/dist/chat-composition.js.map +1 -1
- package/dist/cli.js +17 -0
- package/dist/cli.js.map +1 -1
- package/dist/gateway.d.ts +2 -0
- package/dist/gateway.js +65 -1
- package/dist/gateway.js.map +1 -1
- package/dist/machine-host.d.ts +3 -0
- package/dist/machine-host.js +102 -3
- package/dist/machine-host.js.map +1 -1
- package/dist/official-mcp-host.d.ts +48 -0
- package/dist/official-mcp-host.js +44 -0
- package/dist/official-mcp-host.js.map +1 -0
- package/dist/platform-product-host.d.ts +30 -0
- package/dist/platform-product-host.js +114 -0
- package/dist/platform-product-host.js.map +1 -0
- package/dist/platform-tools.d.ts +20 -0
- package/dist/platform-tools.js +108 -0
- package/dist/platform-tools.js.map +1 -0
- package/dist/user-ground-host.d.ts +7 -2
- package/dist/user-ground-host.js +65 -62
- package/dist/user-ground-host.js.map +1 -1
- package/npm-shrinkwrap.json +137 -18
- package/package.json +7 -5
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { AutomationRunExecutor, createAutomationClient, createMachineRunsClient, } from '@amalgm/automations';
|
|
2
|
+
import { createChannelsClient } from '@amalgm/channels';
|
|
3
|
+
export function createPlatformProductHost(options) {
|
|
4
|
+
const automations = createAutomationClient({
|
|
5
|
+
baseUrl: options.automationsBaseUrl,
|
|
6
|
+
headers: headersFor(options.authorization, 'amalgm-automations', automationScopes),
|
|
7
|
+
...(options.fetch ? { fetch: options.fetch } : {}),
|
|
8
|
+
});
|
|
9
|
+
const channels = createChannelsClient({
|
|
10
|
+
baseUrl: options.channelsBaseUrl,
|
|
11
|
+
headers: headersFor(options.authorization, 'amalgm-channels', channelScopes),
|
|
12
|
+
...(options.fetch ? { fetch: options.fetch } : {}),
|
|
13
|
+
});
|
|
14
|
+
const runs = createMachineRunsClient({
|
|
15
|
+
baseUrl: options.automationsBaseUrl,
|
|
16
|
+
headers: headersFor(options.authorization, 'amalgm-automations', () => ['runs:execute']),
|
|
17
|
+
...(options.fetch ? { fetch: options.fetch } : {}),
|
|
18
|
+
});
|
|
19
|
+
const executor = new AutomationRunExecutor(runs, actionRegistry({
|
|
20
|
+
'channels.notify_user': channelNotification(channels),
|
|
21
|
+
}));
|
|
22
|
+
const runner = createRunner({
|
|
23
|
+
claim: () => runs.claim({ limit: 8, leaseSeconds: 60 }),
|
|
24
|
+
execute: (run) => executor.execute(run),
|
|
25
|
+
intervalMs: options.pollIntervalMs ?? 1_000,
|
|
26
|
+
log: options.log,
|
|
27
|
+
});
|
|
28
|
+
return Object.freeze({ automations, channels, runner });
|
|
29
|
+
}
|
|
30
|
+
function headersFor(authorization, audience, scopes) {
|
|
31
|
+
return async (method, url) => {
|
|
32
|
+
const grant = await authorization.authorize({ audience, scopes: scopes(method, url), method, url });
|
|
33
|
+
return { authorization: grant.authorization, dpop: grant.dpop };
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function automationScopes(method, url) {
|
|
37
|
+
if (method.toUpperCase() !== 'GET')
|
|
38
|
+
return ['automations:write'];
|
|
39
|
+
return new URL(url).pathname.includes('/runs') ? ['runs:read'] : ['automations:read'];
|
|
40
|
+
}
|
|
41
|
+
function channelScopes(method) {
|
|
42
|
+
return [method.toUpperCase() === 'GET' ? 'channels:read' : 'channels:send'];
|
|
43
|
+
}
|
|
44
|
+
function actionRegistry(actions) {
|
|
45
|
+
return Object.freeze({
|
|
46
|
+
async call(input) {
|
|
47
|
+
const { actionId, payload, idempotencyKey } = input;
|
|
48
|
+
const action = actions[actionId];
|
|
49
|
+
if (!action)
|
|
50
|
+
throw new Error(`Automation action is not installed: ${actionId}`);
|
|
51
|
+
return action(payload, idempotencyKey);
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function channelNotification(channels) {
|
|
56
|
+
return async (payload, idempotencyKey) => {
|
|
57
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
58
|
+
throw new Error('channels.notify_user input must be an object');
|
|
59
|
+
}
|
|
60
|
+
const delivery = await channels.notify({
|
|
61
|
+
...payload,
|
|
62
|
+
idempotencyKey,
|
|
63
|
+
});
|
|
64
|
+
return delivery;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function createRunner(options) {
|
|
68
|
+
let active = null;
|
|
69
|
+
let timer = null;
|
|
70
|
+
const drain = () => {
|
|
71
|
+
if (active)
|
|
72
|
+
return active;
|
|
73
|
+
active = options.claim()
|
|
74
|
+
.then(async (runs) => {
|
|
75
|
+
if (runs.length)
|
|
76
|
+
options.log?.('automations.claimed', { count: runs.length });
|
|
77
|
+
const settled = await Promise.allSettled(runs.map(options.execute));
|
|
78
|
+
settled.forEach((result) => {
|
|
79
|
+
if (result.status === 'rejected')
|
|
80
|
+
options.log?.('automations.run.failed', { error: safe(result.reason) });
|
|
81
|
+
});
|
|
82
|
+
})
|
|
83
|
+
.catch((error) => options.log?.('automations.poll.failed', { error: safe(error) }))
|
|
84
|
+
.finally(() => { active = null; });
|
|
85
|
+
return active;
|
|
86
|
+
};
|
|
87
|
+
return Object.freeze({
|
|
88
|
+
start() {
|
|
89
|
+
if (timer)
|
|
90
|
+
return;
|
|
91
|
+
timer = setInterval(drain, options.intervalMs);
|
|
92
|
+
timer.unref();
|
|
93
|
+
void drain();
|
|
94
|
+
},
|
|
95
|
+
async close() {
|
|
96
|
+
if (timer)
|
|
97
|
+
clearInterval(timer);
|
|
98
|
+
timer = null;
|
|
99
|
+
await active;
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function safe(error) {
|
|
104
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
105
|
+
const details = error && typeof error === 'object'
|
|
106
|
+
? [field(error, 'code'), field(error, 'status')].filter(Boolean).join(' ')
|
|
107
|
+
: '';
|
|
108
|
+
return `${message}${details ? ` (${details})` : ''}`.replace(/[\r\n]+/g, ' ').slice(0, 500);
|
|
109
|
+
}
|
|
110
|
+
function field(value, key) {
|
|
111
|
+
const item = Reflect.get(value, key);
|
|
112
|
+
return typeof item === 'string' || typeof item === 'number' ? `${key}=${item}` : '';
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=platform-product-host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-product-host.js","sourceRoot":"","sources":["../src/platform-product-host.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ToolDefinition } from '@amalgm/tools';
|
|
2
|
+
interface ToolServerLike {
|
|
3
|
+
readonly tools: readonly {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly description?: string;
|
|
6
|
+
readonly inputSchema?: Readonly<Record<string, unknown>>;
|
|
7
|
+
}[];
|
|
8
|
+
}
|
|
9
|
+
export interface PlatformToolDefinitionOptions {
|
|
10
|
+
readonly agents: ToolServerLike;
|
|
11
|
+
readonly apps: ToolServerLike;
|
|
12
|
+
readonly browser: ToolServerLike;
|
|
13
|
+
readonly products?: Readonly<{
|
|
14
|
+
automations: boolean;
|
|
15
|
+
channels: boolean;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
/** One system Toolbox record per public Amalgm product capability. */
|
|
19
|
+
export declare function platformToolDefinitions(options: PlatformToolDefinitionOptions): Promise<readonly ToolDefinition[]>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { createAutomationMcpServer } from '@amalgm/automations/mcp';
|
|
2
|
+
import { createChannelsMcpServer } from '@amalgm/channels/mcp';
|
|
3
|
+
import { chatMcpToolDescriptors } from '@amalgm/chat/mcp';
|
|
4
|
+
import { defineHostMcpTool, managementToolDescriptors, } from '@amalgm/tools';
|
|
5
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
6
|
+
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
|
|
7
|
+
function descriptors(server) {
|
|
8
|
+
return server.tools.map((tool) => ({
|
|
9
|
+
name: tool.name,
|
|
10
|
+
...(tool.description ? { description: tool.description } : {}),
|
|
11
|
+
inputSchema: (tool.inputSchema || {
|
|
12
|
+
type: 'object', properties: {}, additionalProperties: false,
|
|
13
|
+
}),
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
16
|
+
function platformTool(input) {
|
|
17
|
+
return defineHostMcpTool({
|
|
18
|
+
id: input.id,
|
|
19
|
+
name: input.name,
|
|
20
|
+
description: input.description,
|
|
21
|
+
serverName: input.id,
|
|
22
|
+
route: input.route,
|
|
23
|
+
tools: input.tools,
|
|
24
|
+
metadata: { repository: input.repository },
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/** Project an owning SDK's official MCP descriptors into one Toolbox record. */
|
|
28
|
+
async function officialDescriptors(server) {
|
|
29
|
+
const client = new Client({ name: 'amalgm-shell-tool-projection', version: '1.0.0' });
|
|
30
|
+
const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair();
|
|
31
|
+
try {
|
|
32
|
+
await server.connect(serverTransport);
|
|
33
|
+
await client.connect(clientTransport);
|
|
34
|
+
const listed = await client.listTools();
|
|
35
|
+
return listed.tools.map((tool) => ({
|
|
36
|
+
name: tool.name,
|
|
37
|
+
...(tool.description ? { description: tool.description } : {}),
|
|
38
|
+
inputSchema: tool.inputSchema,
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
await client.close().catch(() => undefined);
|
|
43
|
+
await server.close().catch(() => undefined);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** One system Toolbox record per public Amalgm product capability. */
|
|
47
|
+
export async function platformToolDefinitions(options) {
|
|
48
|
+
const products = options.products ?? { automations: true, channels: true };
|
|
49
|
+
return Object.freeze([
|
|
50
|
+
platformTool({
|
|
51
|
+
id: 'browser',
|
|
52
|
+
name: 'Browser',
|
|
53
|
+
description: 'Browse and operate the web in visible Electron or isolated headless Chromium sessions.',
|
|
54
|
+
repository: 'amalgm-browser',
|
|
55
|
+
route: '/mcp/browser',
|
|
56
|
+
tools: descriptors(options.browser),
|
|
57
|
+
}),
|
|
58
|
+
...(products.automations ? [platformTool({
|
|
59
|
+
id: 'automations',
|
|
60
|
+
name: 'Automations',
|
|
61
|
+
description: 'Create, inspect, change, and review durable Amalgm automations.',
|
|
62
|
+
repository: 'amalgm-automations',
|
|
63
|
+
route: '/mcp/automations',
|
|
64
|
+
tools: await officialDescriptors(createAutomationMcpServer({})),
|
|
65
|
+
})] : []),
|
|
66
|
+
platformTool({
|
|
67
|
+
id: 'agents',
|
|
68
|
+
name: 'Agents',
|
|
69
|
+
description: 'Manage Amalgm agent identities, installations, and immutable revisions.',
|
|
70
|
+
repository: 'amalgm-agents',
|
|
71
|
+
route: '/mcp/agents',
|
|
72
|
+
tools: descriptors(options.agents),
|
|
73
|
+
}),
|
|
74
|
+
...(products.channels ? [platformTool({
|
|
75
|
+
id: 'channels',
|
|
76
|
+
name: 'Channels',
|
|
77
|
+
description: 'Send and inspect durable notifications for the current Amalgm user.',
|
|
78
|
+
repository: 'amalgm-channels',
|
|
79
|
+
route: '/mcp/channels',
|
|
80
|
+
tools: await officialDescriptors(createChannelsMcpServer({})),
|
|
81
|
+
})] : []),
|
|
82
|
+
platformTool({
|
|
83
|
+
id: 'chat',
|
|
84
|
+
name: 'Chat',
|
|
85
|
+
description: 'Inspect durable conversations and send messages to existing agent conversations.',
|
|
86
|
+
repository: 'amalgm-chat',
|
|
87
|
+
route: '/mcp/chat',
|
|
88
|
+
tools: chatMcpToolDescriptors(),
|
|
89
|
+
}),
|
|
90
|
+
platformTool({
|
|
91
|
+
id: 'apps',
|
|
92
|
+
name: 'Apps',
|
|
93
|
+
description: 'Validate, deploy, inspect, start, and stop versioned Amalgm applications.',
|
|
94
|
+
repository: 'amalgm-apps',
|
|
95
|
+
route: '/mcp/apps',
|
|
96
|
+
tools: descriptors(options.apps),
|
|
97
|
+
}),
|
|
98
|
+
platformTool({
|
|
99
|
+
id: 'tools',
|
|
100
|
+
name: 'Tools',
|
|
101
|
+
description: 'Inspect and manage the canonical Toolbox catalog and immutable tool deployments.',
|
|
102
|
+
repository: 'amalgm-tools',
|
|
103
|
+
route: '/mcp/platform-tools',
|
|
104
|
+
tools: managementToolDescriptors(),
|
|
105
|
+
}),
|
|
106
|
+
]);
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=platform-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-tools.js","sourceRoot":"","sources":["../src/platform-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -197,6 +197,11 @@ export declare class UserGroundHost {
|
|
|
197
197
|
* the inline bound; anything else returns null and the block upload path
|
|
198
198
|
* runs unchanged. */
|
|
199
199
|
private readInlineCargo;
|
|
200
|
+
/** One self-contained cargo segment on the wire: declared artifacts in
|
|
201
|
+
* header order, their bytes concatenated behind them, one verified round
|
|
202
|
+
* trip. Returns the authority's sealed hashes for the stream law to
|
|
203
|
+
* prove coverage. */
|
|
204
|
+
private putCargo;
|
|
200
205
|
private uploadSnapshotContent;
|
|
201
206
|
/** Download a set of immutable artifacts straight from content storage.
|
|
202
207
|
* The set is the primitive: every storage object the set still needs —
|
|
@@ -229,8 +234,8 @@ export declare class UserGroundHost {
|
|
|
229
234
|
private publishPendingSnapshots;
|
|
230
235
|
/** Seal this publication's graph pages. Pages are immutable, content
|
|
231
236
|
* addressed, and already in memory, so the authority's seal inventory alone
|
|
232
|
-
* decides what still has to move and whole pages ride cargo
|
|
233
|
-
* may not commit until every page it newly names is sealed. */
|
|
237
|
+
* decides what still has to move and whole pages ride the cargo stream. The
|
|
238
|
+
* head may not commit until every page it newly names is sealed. */
|
|
234
239
|
private uploadGraphPages;
|
|
235
240
|
private publishLoadedSnapshot;
|
|
236
241
|
}
|
package/dist/user-ground-host.js
CHANGED
|
@@ -3,7 +3,7 @@ import { existsSync, lstatSync, realpathSync, readFileSync, readdirSync, readlin
|
|
|
3
3
|
import { open, readFile } from "node:fs/promises";
|
|
4
4
|
import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { buildUserHomeManifest, buildUserManifest, liveMachineStateDir, scopedAmalgmDir, shippedUserHomeDeclaration, } from "@amalgm/core/identity";
|
|
6
|
-
import { CHUNK_BYTES, CONTENT_CONTRACT, INLINE_CARGO_MAX_BYTES, ENTITY_CLOUD_CONTRACT, ENTITY_CLOUD_SCHEMA_VERSION, artifactForRecord, canonicalVersion, checkContentManifest, classifyDirectory, classifyFile, classifyRegisteredRoot, createLocalEntityRecord, createEntityRecordAuthorityPort, convergeUserGround, decodeAcceptedEntityRecord, deriveUploadManifest, createUserGroundEnrollmentPolicy, isRepositoryMetadataEntry, indexRepositoryTerritory, downloadAll as downloadAllArtifacts, encodeEntityRecord, EntityApplyRail, EntityRecordRail, membershipHash, pathIsSuspect,
|
|
6
|
+
import { CHUNK_BYTES, CONTENT_CONTRACT, INLINE_CARGO_MAX_BYTES, ENTITY_CLOUD_CONTRACT, ENTITY_CLOUD_SCHEMA_VERSION, artifactForRecord, canonicalVersion, checkContentManifest, classifyDirectory, classifyFile, classifyRegisteredRoot, createLocalEntityRecord, createEntityRecordAuthorityPort, convergeUserGround, decodeAcceptedEntityRecord, deriveUploadManifest, createUserGroundEnrollmentPolicy, isRepositoryMetadataEntry, indexRepositoryTerritory, downloadAll as downloadAllArtifacts, encodeEntityRecord, EntityApplyRail, EntityRecordRail, membershipHash, pathIsSuspect, PRESIGN_BATCH_OBJECTS, privateEntityResourceId, repositoryIdentityHash, sameRecords, snapshotFromRecords, stableJson, streamCargo, travelingRecords, upload as uploadArtifact, userGroundRecords, GRAPH_HEAD_OPERATION_KIND, } from "@amalgm/live";
|
|
7
7
|
import Database from "better-sqlite3";
|
|
8
8
|
import { atomicCopy, atomicWrite, ensurePrivateDir, ensureUserDir } from "./filesystem.js";
|
|
9
9
|
import { emitFilesStage, } from "./files-observability.js";
|
|
@@ -27,9 +27,10 @@ import { NodeWatchHost, } from "./watching/index.js";
|
|
|
27
27
|
import { GroundCoordinator } from "./ground-coordination.js";
|
|
28
28
|
import { adoptSnapshotDeliveryBaseline } from "./snapshot-delivery-baseline.js";
|
|
29
29
|
import { WireClient, WireRequestError } from "./wire-client.js";
|
|
30
|
-
/**
|
|
31
|
-
*
|
|
32
|
-
|
|
30
|
+
/** One cargo segment loads its ground bytes across this many concurrent
|
|
31
|
+
* reads. Reading and hashing overlap the wire without letting a
|
|
32
|
+
* 256-artifact segment claim hundreds of file descriptors at once. */
|
|
33
|
+
const CARGO_SEGMENT_LOAD_CONCURRENCY = 8;
|
|
33
34
|
/** Storage-object lanes for one download set. Tiny objects use the wide
|
|
34
35
|
* request window, while the separate byte budget narrows large chunks so
|
|
35
36
|
* request concurrency can never silently become an unbounded memory claim. */
|
|
@@ -1309,6 +1310,25 @@ export class UserGroundHost {
|
|
|
1309
1310
|
}
|
|
1310
1311
|
return inline;
|
|
1311
1312
|
}
|
|
1313
|
+
/** One self-contained cargo segment on the wire: declared artifacts in
|
|
1314
|
+
* header order, their bytes concatenated behind them, one verified round
|
|
1315
|
+
* trip. Returns the authority's sealed hashes for the stream law to
|
|
1316
|
+
* prove coverage. */
|
|
1317
|
+
async putCargo(resourceId, artifacts, payload) {
|
|
1318
|
+
const encoded = await encodeContentWireBytes(payload);
|
|
1319
|
+
const frame = await retryContent(() => this.wire.requestBinary({
|
|
1320
|
+
type: "private.entity-content.put-cargo",
|
|
1321
|
+
resource_id: resourceId,
|
|
1322
|
+
artifacts,
|
|
1323
|
+
bytes: payload.byteLength,
|
|
1324
|
+
wire_bytes: encoded.bytes.byteLength,
|
|
1325
|
+
...(encoded.encoding ? { content_encoding: encoded.encoding } : {}),
|
|
1326
|
+
}, encoded.bytes, ["private.entity-content.stored-cargo"]));
|
|
1327
|
+
return {
|
|
1328
|
+
sealed: Array.isArray(frame.sealed) ? frame.sealed.map(String) : [],
|
|
1329
|
+
wireBytes: encoded.bytes.byteLength,
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1312
1332
|
async uploadSnapshotContent(identity, resourceId, records, trace, manifestHints) {
|
|
1313
1333
|
const cache = this.cacheDir(identity);
|
|
1314
1334
|
const byHash = new Map();
|
|
@@ -1401,19 +1421,22 @@ export class UserGroundHost {
|
|
|
1401
1421
|
}
|
|
1402
1422
|
throw new StaleCaptureError(`${source.artifact.contentHash} no longer matches its source`, { cause: error });
|
|
1403
1423
|
};
|
|
1404
|
-
// Complete small artifacts ride cargo
|
|
1405
|
-
// verified and sealed by the authority in
|
|
1406
|
-
// frame count scales with bytes moved, never
|
|
1407
|
-
//
|
|
1424
|
+
// Complete small artifacts ride the cargo stream: whole artifacts per
|
|
1425
|
+
// self-contained segment, each verified and sealed by the authority in
|
|
1426
|
+
// its own round trip, so the frame count scales with bytes moved, never
|
|
1427
|
+
// with file count — and Live's bounded window keeps later segments on
|
|
1428
|
+
// the wire while earlier durability acknowledgements are still
|
|
1429
|
+
// outstanding. Large artifacts keep per-artifact transfer where chunk
|
|
1430
|
+
// reuse pays.
|
|
1408
1431
|
const small = sources.filter((source) => source.bytes <= CHUNK_BYTES);
|
|
1409
1432
|
const large = sources.filter((source) => source.bytes > CHUNK_BYTES);
|
|
1410
|
-
let
|
|
1411
|
-
const
|
|
1412
|
-
const loaded =
|
|
1433
|
+
let cargoSegments = 0;
|
|
1434
|
+
const sendCargoSegment = async (segment) => {
|
|
1435
|
+
const loaded = new Array(segment.items.length);
|
|
1436
|
+
await boundedForEach(segment.items.map((item, index) => ({ source: item.source, index })), CARGO_SEGMENT_LOAD_CONCURRENCY, async ({ source, index }) => {
|
|
1413
1437
|
try {
|
|
1414
1438
|
const bytes = source.linkTarget ?? await readFile(source.file);
|
|
1415
|
-
|
|
1416
|
-
source,
|
|
1439
|
+
loaded[index] = {
|
|
1417
1440
|
bytes,
|
|
1418
1441
|
manifest: deriveUploadManifest(source.artifact, bytes, sha256Hex),
|
|
1419
1442
|
};
|
|
@@ -1423,30 +1446,17 @@ export class UserGroundHost {
|
|
|
1423
1446
|
stale(source, error);
|
|
1424
1447
|
throw error;
|
|
1425
1448
|
}
|
|
1426
|
-
})
|
|
1427
|
-
const
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
content_hash: item.source.artifact.contentHash,
|
|
1434
|
-
bytes: item.manifest.bytes,
|
|
1435
|
-
chunks: item.manifest.chunks.map((chunk) => ({ sha256: chunk.sha256, bytes: chunk.bytes })),
|
|
1436
|
-
})),
|
|
1437
|
-
bytes: payload.byteLength,
|
|
1438
|
-
wire_bytes: encoded.bytes.byteLength,
|
|
1439
|
-
...(encoded.encoding ? { content_encoding: encoded.encoding } : {}),
|
|
1440
|
-
}, encoded.bytes, ["private.entity-content.stored-cargo"]));
|
|
1441
|
-
const sealed = new Set(Array.isArray(frame.sealed) ? frame.sealed.map(String) : []);
|
|
1442
|
-
for (const item of loaded) {
|
|
1443
|
-
if (!sealed.has(item.source.artifact.contentHash)) {
|
|
1444
|
-
throw new Error(`content authority did not seal ${item.source.artifact.contentHash}`);
|
|
1445
|
-
}
|
|
1449
|
+
});
|
|
1450
|
+
const { sealed, wireBytes } = await this.putCargo(resourceId, loaded.map((item, index) => ({
|
|
1451
|
+
content_hash: segment.items[index].contentHash,
|
|
1452
|
+
bytes: item.manifest.bytes,
|
|
1453
|
+
chunks: item.manifest.chunks.map((chunk) => ({ sha256: chunk.sha256, bytes: chunk.bytes })),
|
|
1454
|
+
})), Buffer.concat(loaded.map((item) => item.bytes)));
|
|
1455
|
+
for (const item of loaded)
|
|
1446
1456
|
uploadedChunks += item.manifest.chunks.length;
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1457
|
+
uploadedBytes += wireBytes;
|
|
1458
|
+
cargoSegments += 1;
|
|
1459
|
+
return sealed;
|
|
1450
1460
|
};
|
|
1451
1461
|
const transfer = async (source) => {
|
|
1452
1462
|
const { artifact, file, manifest: knownManifest, bytes: fileBytes } = source;
|
|
@@ -1546,7 +1556,11 @@ export class UserGroundHost {
|
|
|
1546
1556
|
}
|
|
1547
1557
|
};
|
|
1548
1558
|
try {
|
|
1549
|
-
await
|
|
1559
|
+
await streamCargo(small.map((source) => ({
|
|
1560
|
+
contentHash: source.artifact.contentHash,
|
|
1561
|
+
bytes: source.bytes,
|
|
1562
|
+
source,
|
|
1563
|
+
})), { send: sendCargoSegment });
|
|
1550
1564
|
for (const source of large) {
|
|
1551
1565
|
await transfer(source);
|
|
1552
1566
|
}
|
|
@@ -1565,7 +1579,7 @@ export class UserGroundHost {
|
|
|
1565
1579
|
uploadedChunks,
|
|
1566
1580
|
reusedChunks,
|
|
1567
1581
|
inventoryRequests,
|
|
1568
|
-
|
|
1582
|
+
cargoSegments,
|
|
1569
1583
|
},
|
|
1570
1584
|
});
|
|
1571
1585
|
}
|
|
@@ -2291,8 +2305,8 @@ export class UserGroundHost {
|
|
|
2291
2305
|
}
|
|
2292
2306
|
/** Seal this publication's graph pages. Pages are immutable, content
|
|
2293
2307
|
* addressed, and already in memory, so the authority's seal inventory alone
|
|
2294
|
-
* decides what still has to move and whole pages ride cargo
|
|
2295
|
-
* may not commit until every page it newly names is sealed. */
|
|
2308
|
+
* decides what still has to move and whole pages ride the cargo stream. The
|
|
2309
|
+
* head may not commit until every page it newly names is sealed. */
|
|
2296
2310
|
async uploadGraphPages(resourceId, pages, trace) {
|
|
2297
2311
|
if (pages.length === 0)
|
|
2298
2312
|
return;
|
|
@@ -2300,32 +2314,21 @@ export class UserGroundHost {
|
|
|
2300
2314
|
const sealed = await this.sealedContentHashes(resourceId, pages.map((page) => page.sha256));
|
|
2301
2315
|
const unsealed = pages
|
|
2302
2316
|
.filter((page) => !sealed.has(page.sha256))
|
|
2303
|
-
.map((page) => ({ page, bytes: page.bytes.byteLength }));
|
|
2317
|
+
.map((page) => ({ contentHash: page.sha256, bytes: page.bytes.byteLength, page }));
|
|
2304
2318
|
let uploadedBytes = 0;
|
|
2305
|
-
await
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
const frame = await retryContent(() => this.wire.requestBinary({
|
|
2309
|
-
type: "private.entity-content.put-cargo",
|
|
2310
|
-
resource_id: resourceId,
|
|
2319
|
+
await streamCargo(unsealed, {
|
|
2320
|
+
send: async (segment) => {
|
|
2321
|
+
const { sealed: stored, wireBytes } = await this.putCargo(resourceId,
|
|
2311
2322
|
// A page is a single-chunk artifact: its identity, its only chunk,
|
|
2312
2323
|
// and its bytes are one and the same sha.
|
|
2313
|
-
|
|
2314
|
-
content_hash: item.
|
|
2324
|
+
segment.items.map((item) => ({
|
|
2325
|
+
content_hash: item.contentHash,
|
|
2315
2326
|
bytes: item.bytes,
|
|
2316
|
-
chunks: [{ sha256: item.
|
|
2317
|
-
})),
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
}, encoded.bytes, ["private.entity-content.stored-cargo"]));
|
|
2322
|
-
const stored = new Set(Array.isArray(frame.sealed) ? frame.sealed.map(String) : []);
|
|
2323
|
-
for (const item of wave.items) {
|
|
2324
|
-
if (!stored.has(item.page.sha256)) {
|
|
2325
|
-
throw new Error(`content authority did not seal graph page ${item.page.sha256}`);
|
|
2326
|
-
}
|
|
2327
|
-
}
|
|
2328
|
-
uploadedBytes += encoded.bytes.byteLength;
|
|
2327
|
+
chunks: [{ sha256: item.contentHash, bytes: item.bytes }],
|
|
2328
|
+
})), Buffer.concat(segment.items.map((item) => item.page.bytes)));
|
|
2329
|
+
uploadedBytes += wireBytes;
|
|
2330
|
+
return stored;
|
|
2331
|
+
},
|
|
2329
2332
|
});
|
|
2330
2333
|
this.stage({
|
|
2331
2334
|
primitive: "upload",
|