amalgm 0.1.257 → 0.1.258
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/lib/cli.js +72 -3
- package/lib/layout.js +30 -9
- package/lib/shared-realtime-tunnel.js +12 -14
- package/package.json +2 -2
- package/runtime/scripts/amalgm-mcp/lib/layout.js +32 -9
- package/runtime/scripts/amalgm-mcp/registration/entity-cloud.js +186 -32
- package/runtime/scripts/amalgm-mcp/registration/entity-content.js +33 -6
- package/runtime/scripts/amalgm-mcp/registration/index.js +21 -0
- package/runtime/scripts/amalgm-mcp/registration/service.js +102 -11
- package/runtime/scripts/amalgm-mcp/registry/index.js +1 -1
- package/runtime/scripts/amalgm-mcp/server/routes/entities.js +6 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-cloud.test.js +67 -8
- package/runtime/scripts/amalgm-mcp/tests/entity-materialization.test.js +3 -3
- package/runtime/scripts/amalgm-mcp/tests/registration.service.test.js +45 -3
- package/runtime/scripts/amalgm-mcp/tests/system-catalog.test.js +3 -6
- package/runtime/scripts/amalgm-mcp/tests/toolbox-artifacts.test.js +88 -0
- package/runtime/scripts/amalgm-mcp/tests/toolbox-cloud-registration.test.js +91 -0
- package/runtime/scripts/amalgm-mcp/toolbox/artifacts.js +225 -0
- package/runtime/scripts/amalgm-mcp/toolbox/store.js +59 -10
- package/runtime/scripts/amalgm-mcp/toolbox/system-catalog.js +14 -5
package/lib/cli.js
CHANGED
|
@@ -120,7 +120,7 @@ function usage() {
|
|
|
120
120
|
' amalgm stop',
|
|
121
121
|
' amalgm register [path ...] Register local ground (default: current directory)',
|
|
122
122
|
' amalgm add <entity-uuid> [directory] Add a cloud-backed tree to this machine',
|
|
123
|
-
' amalgm status',
|
|
123
|
+
' amalgm status [path-or-uuid] Show cloud-backed entity and watcher state',
|
|
124
124
|
' amalgm doctor',
|
|
125
125
|
' amalgm service [install|start|stop|status|uninstall] [--mode auto|systemd|launchd|windows|portable]',
|
|
126
126
|
' amalgm update [--tag latest|canary]',
|
|
@@ -1112,7 +1112,7 @@ async function waitForServices(timeoutMs) {
|
|
|
1112
1112
|
return latest;
|
|
1113
1113
|
}
|
|
1114
1114
|
|
|
1115
|
-
async function
|
|
1115
|
+
async function runtimeStatus() {
|
|
1116
1116
|
const pid = readPid();
|
|
1117
1117
|
const record = loadComputerRecord({ migrate: true });
|
|
1118
1118
|
const runtimeState = readJson(RUNTIME_STATE_FILE, null);
|
|
@@ -1177,6 +1177,75 @@ async function status() {
|
|
|
1177
1177
|
}
|
|
1178
1178
|
}
|
|
1179
1179
|
|
|
1180
|
+
/** `amalgm status [path-or-uuid]` is a read-only view of entity truth. The
|
|
1181
|
+
* runtime owns paths, bindings, and cloud freshness; the CLI only narrows and
|
|
1182
|
+
* renders the returned facts. Daemon status remains available as
|
|
1183
|
+
* `amalgm service status`. */
|
|
1184
|
+
async function entityStatus(positionals) {
|
|
1185
|
+
if (positionals.length > 1) throw new Error('Usage: amalgm status [path-or-uuid]');
|
|
1186
|
+
const port = servicePorts().find(([name]) => name === 'amalgm-mcp')?.[1];
|
|
1187
|
+
let answer = null;
|
|
1188
|
+
if (port) {
|
|
1189
|
+
try {
|
|
1190
|
+
answer = await runtimeGet(port, '/entities/status');
|
|
1191
|
+
} catch (error) {
|
|
1192
|
+
if (error.code !== 'ECONNREFUSED') throw error;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
if (!answer) {
|
|
1196
|
+
console.error('Amalgm runtime is not running. Start it with `amalgm start`.');
|
|
1197
|
+
process.exitCode = 1;
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
if (answer.status !== 200 || !answer.payload) {
|
|
1201
|
+
console.error(`Error: ${answer.payload?.error || `runtime responded ${answer.status}`}`);
|
|
1202
|
+
process.exitCode = 1;
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
const status = answer.payload;
|
|
1207
|
+
const selector = positionals[0] || null;
|
|
1208
|
+
const uuid = selector && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(selector)
|
|
1209
|
+
? selector.toLowerCase()
|
|
1210
|
+
: null;
|
|
1211
|
+
const localPath = selector && !uuid ? path.resolve(selector) : null;
|
|
1212
|
+
const entities = (status.entities || []).filter((entity) => !selector
|
|
1213
|
+
|| entity.uuid === uuid || entity.localPath === localPath);
|
|
1214
|
+
if (selector && entities.length === 0) {
|
|
1215
|
+
console.error(`No cloud-backed entity matches ${selector}.`);
|
|
1216
|
+
process.exitCode = 1;
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
const cloud = status.cloud || {};
|
|
1221
|
+
const replica = cloud.replica;
|
|
1222
|
+
if (!replica) {
|
|
1223
|
+
console.log(`Cloud: ${cloud.identity?.enabled ? 'initializing' : 'not signed in'}`);
|
|
1224
|
+
} else {
|
|
1225
|
+
console.log(`Cloud: ${cloud.catalog?.current ? 'current' : 'catching up'} (head ${replica.headVersion})`);
|
|
1226
|
+
}
|
|
1227
|
+
const outbox = cloud.outbox || {};
|
|
1228
|
+
console.log(`Delivery: ${outbox.pending || 0} pending, ${outbox.syncing || 0} sending, ${outbox.rejected || 0} rejected`);
|
|
1229
|
+
const jobs = cloud.materializations || {};
|
|
1230
|
+
if ((jobs.waitingForContent || 0) + (jobs.materializing || 0) + (jobs.failed || 0) > 0) {
|
|
1231
|
+
console.log(`Materializations: ${jobs.waitingForContent || 0} waiting, ${jobs.materializing || 0} applying, ${jobs.failed || 0} failed`);
|
|
1232
|
+
}
|
|
1233
|
+
if (entities.length === 0) {
|
|
1234
|
+
console.log('Entities: none on this cloud replica.');
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
console.log('Entities:');
|
|
1238
|
+
for (const entity of entities) {
|
|
1239
|
+
console.log(` ${entity.uuid} ${entity.type} ${entity.status}`);
|
|
1240
|
+
if (entity.localPath) console.log(` Local: ${entity.localPath}`);
|
|
1241
|
+
else console.log(' Local: not materialized on this machine');
|
|
1242
|
+
if (entity.root) {
|
|
1243
|
+
const watcher = entity.root.watching && !entity.root.degraded ? 'on' : 'unavailable';
|
|
1244
|
+
console.log(` Watching: ${watcher}`);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1180
1249
|
async function doctor() {
|
|
1181
1250
|
ensureBaseDirs();
|
|
1182
1251
|
|
|
@@ -1649,7 +1718,7 @@ async function main(argv) {
|
|
|
1649
1718
|
if (command === 'stop') return stop();
|
|
1650
1719
|
if (command === 'register') return register(positionals);
|
|
1651
1720
|
if (command === 'add') return add(positionals);
|
|
1652
|
-
if (command === 'status') return
|
|
1721
|
+
if (command === 'status') return entityStatus(positionals);
|
|
1653
1722
|
if (command === 'doctor') return doctor();
|
|
1654
1723
|
if (command === 'service') return serviceCommand(options, positionals);
|
|
1655
1724
|
if (command === 'update') return update(options);
|
package/lib/layout.js
CHANGED
|
@@ -33,7 +33,9 @@ const ROOT_MANIFEST_NAME = '.amalgm-root.json';
|
|
|
33
33
|
const USER_MANIFEST_NAME = '.amalgm-user.json';
|
|
34
34
|
const LAYOUT_VERSION = 1;
|
|
35
35
|
const USER_HOME_MANIFEST_NAME = 'home.json';
|
|
36
|
-
|
|
36
|
+
// Changing the declared exact graph changes the identity contract. A client
|
|
37
|
+
// must never accept a prior graph and silently invent a new managed child.
|
|
38
|
+
const USER_HOME_SCHEMA_VERSION = 2;
|
|
37
39
|
|
|
38
40
|
// Stable per-user product namespaces. Their authority is NOT inferred from
|
|
39
41
|
// their path: user content and exact platform-managed targets are declared
|
|
@@ -59,6 +61,19 @@ const USER_HOME_ROOTS = Object.freeze([
|
|
|
59
61
|
// Platform management is an exact-node fact. It deliberately does not
|
|
60
62
|
// cascade from `system/`: human permissions may still inherit normally, while
|
|
61
63
|
// only these particular entities are product-managed.
|
|
64
|
+
//
|
|
65
|
+
// A Toolbox tool is one complete entity: its definition and actions travel in
|
|
66
|
+
// the same file. These names are deliberately stable and human-readable.
|
|
67
|
+
const SYSTEM_TOOL_ARTIFACTS = Object.freeze([
|
|
68
|
+
{ key: 'amalgm-automations', toolId: 'automations', relativePath: 'toolbox/amalgm-automations.json', rootKey: 'toolbox' },
|
|
69
|
+
{ key: 'amalgm-notifications', toolId: 'notifications', relativePath: 'toolbox/amalgm-notifications.json', rootKey: 'toolbox' },
|
|
70
|
+
{ key: 'amalgm-memories', toolId: 'memories', relativePath: 'toolbox/amalgm-memories.json', rootKey: 'toolbox' },
|
|
71
|
+
{ key: 'amalgm-agents', toolId: 'agents', relativePath: 'toolbox/amalgm-agents.json', rootKey: 'toolbox' },
|
|
72
|
+
{ key: 'amalgm-apps', toolId: 'apps', relativePath: 'toolbox/amalgm-apps.json', rootKey: 'toolbox' },
|
|
73
|
+
{ key: 'amalgm-cua', toolId: 'computer-use', relativePath: 'toolbox/amalgm-cua.json', rootKey: 'toolbox' },
|
|
74
|
+
{ key: 'amalgm-browser', toolId: 'browser', relativePath: 'toolbox/amalgm-browser.json', rootKey: 'toolbox' },
|
|
75
|
+
]);
|
|
76
|
+
|
|
62
77
|
const USER_HOME_EXACT_MANAGED = Object.freeze([
|
|
63
78
|
...USER_HOME_ROOTS.map(({ key, relativePath }) => ({
|
|
64
79
|
key: `${key}-root`, relativePath, rootKey: key,
|
|
@@ -66,6 +81,7 @@ const USER_HOME_EXACT_MANAGED = Object.freeze([
|
|
|
66
81
|
{ key: 'home-manifest', relativePath: 'system/home.json', rootKey: 'system' },
|
|
67
82
|
{ key: 'defaults', relativePath: 'system/defaults', rootKey: 'system' },
|
|
68
83
|
{ key: 'policy', relativePath: 'system/policy', rootKey: 'system' },
|
|
84
|
+
...SYSTEM_TOOL_ARTIFACTS,
|
|
69
85
|
]);
|
|
70
86
|
|
|
71
87
|
function readJson(file, fallback = null) {
|
|
@@ -106,6 +122,7 @@ function declaredUserHome(userDir) {
|
|
|
106
122
|
*/
|
|
107
123
|
function ensureUserHome(userDir) {
|
|
108
124
|
const declaration = declaredUserHome(userDir);
|
|
125
|
+
const materializedExactPaths = [];
|
|
109
126
|
for (const root of declaration.roots) {
|
|
110
127
|
fs.mkdirSync(root.path, { recursive: true, mode: 0o700 });
|
|
111
128
|
}
|
|
@@ -115,15 +132,18 @@ function ensureUserHome(userDir) {
|
|
|
115
132
|
}
|
|
116
133
|
|
|
117
134
|
const manifest = declaration.exactManaged.find((entry) => entry.key === 'home-manifest');
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
135
|
+
const manifestContents = {
|
|
136
|
+
schemaVersion: USER_HOME_SCHEMA_VERSION,
|
|
137
|
+
kind: 'amalgm-user-home',
|
|
138
|
+
roots: declaration.roots.map(({ key, relativePath }) => ({ key, path: relativePath })),
|
|
139
|
+
exactManaged: declaration.exactManaged.map(({ key, relativePath, scope }) => ({ key, path: relativePath, scope })),
|
|
140
|
+
};
|
|
141
|
+
if (manifest && JSON.stringify(readJson(manifest.path, null)) !== JSON.stringify(manifestContents)) {
|
|
142
|
+
writeJson(manifest.path, manifestContents);
|
|
143
|
+
materializedExactPaths.push(manifest.relativePath);
|
|
125
144
|
}
|
|
126
|
-
|
|
145
|
+
|
|
146
|
+
return { ...declaration, materializedExactPaths };
|
|
127
147
|
}
|
|
128
148
|
|
|
129
149
|
function lstatOrNull(target) {
|
|
@@ -401,6 +421,7 @@ module.exports = {
|
|
|
401
421
|
LAYOUT_VERSION,
|
|
402
422
|
PRIMITIVE_DIRS,
|
|
403
423
|
ROOT_MANIFEST_NAME,
|
|
424
|
+
SYSTEM_TOOL_ARTIFACTS,
|
|
404
425
|
USER_MANIFEST_NAME,
|
|
405
426
|
USER_HOME_EXACT_MANAGED,
|
|
406
427
|
USER_HOME_MANIFEST_NAME,
|
|
@@ -19,8 +19,8 @@ const RETRY_MS = 1_000;
|
|
|
19
19
|
const REAUTHORIZE_MS = 30_000;
|
|
20
20
|
const REPO_OBJECT_REQUEST_TIMEOUT_MS = 15_000;
|
|
21
21
|
const COOKIE_PRIVATE_CONTRACT = 'browser-cookie-jar@1';
|
|
22
|
-
const USER_HOME_PRIVATE_CONTRACT = 'amalgm-user-home@
|
|
23
|
-
const ENTITY_REGISTRY_PRIVATE_CONTRACT = 'amalgm-entity-registry@
|
|
22
|
+
const USER_HOME_PRIVATE_CONTRACT = 'amalgm-user-home@2';
|
|
23
|
+
const ENTITY_REGISTRY_PRIVATE_CONTRACT = 'amalgm-entity-registry@3';
|
|
24
24
|
|
|
25
25
|
function createSharedRealtimeTunnel({
|
|
26
26
|
send,
|
|
@@ -938,13 +938,10 @@ function createSharedRealtimeTunnel({
|
|
|
938
938
|
committedAt: frame.committed_at,
|
|
939
939
|
}).then((ack) => {
|
|
940
940
|
entityMutationInflight.delete(frame.request_id);
|
|
941
|
-
//
|
|
942
|
-
//
|
|
943
|
-
//
|
|
944
|
-
//
|
|
945
|
-
// legacy empty entity resource is seeded in ordered records first;
|
|
946
|
-
// intermediate heads are intentionally not imported as a usable
|
|
947
|
-
// snapshot, because they are not the complete declared home yet.
|
|
941
|
+
// The runtime reports whether it converged its catalog locally from
|
|
942
|
+
// this acknowledgement. Only when it could not (a version gap, a
|
|
943
|
+
// reconciliation handshake, a stale local view) is the compacted
|
|
944
|
+
// snapshot re-downloaded; the ordinary in-order ack costs nothing.
|
|
948
945
|
if (ack?.requiresSnapshot !== false) void ensurePrivateEntityRegistry();
|
|
949
946
|
scheduleEntityMutationDrain();
|
|
950
947
|
}).catch(() => {
|
|
@@ -1109,11 +1106,12 @@ function createSharedRealtimeTunnel({
|
|
|
1109
1106
|
version: frame.version,
|
|
1110
1107
|
committedAt: frame.committed_at,
|
|
1111
1108
|
actorUserId: frame.actor_user_id,
|
|
1112
|
-
})).then(() => {
|
|
1113
|
-
if (entityRegistryMutation) {
|
|
1114
|
-
// The
|
|
1115
|
-
//
|
|
1116
|
-
// compact and return its active snapshot
|
|
1109
|
+
})).then((result) => {
|
|
1110
|
+
if (entityRegistryMutation && result?.requiresSnapshot !== false) {
|
|
1111
|
+
// The runtime could not converge its catalog from this frame alone
|
|
1112
|
+
// (a gap, a snapshot replacement, or a stale local view). Ask the
|
|
1113
|
+
// authority to compact and return its active snapshot; an in-order
|
|
1114
|
+
// frame needs no re-download.
|
|
1117
1115
|
void ensurePrivateEntityRegistry();
|
|
1118
1116
|
}
|
|
1119
1117
|
}).catch((error) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amalgm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.258",
|
|
4
4
|
"description": "Amalgm local computer runtime: login, MCP, chat, events, previews, and tunnels.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"sync-runtime": "node ../../scripts/sync-npm-package-runtime.mjs",
|
|
18
18
|
"prepack": "node ../../scripts/sync-npm-package-runtime.mjs",
|
|
19
19
|
"pack:dry": "npm pack --dry-run",
|
|
20
|
-
"check": "node --check bin/amalgm.js && node --check lib/app-chat.js && node --check lib/react.js && node --check lib/auth-store.js && node --check lib/cli.js && node --check lib/identity-adopt.js && node --check lib/layout.js && node --check lib/migrate-layout.js && node --check lib/paths.js && node --check lib/process-cleanup.js && node --check lib/runtime-identity.js && node --check lib/runtime-manifest.js && node --check lib/service.js && node --check lib/state-migration.js && node --check lib/supervisor.js && node --check lib/tunnel-chat.js && node --check lib/tunnel-events.js && node --check lib/updater.js && node --check runtime/lib/runtime-manifest.js && node --check runtime/scripts/runtime-auth.js && node --check runtime/scripts/proxy-token-store.js && node --check runtime/scripts/local-gateway.js && node --check runtime/scripts/port-monitor.js && node --check runtime/scripts/chat-server.js && node --check runtime/scripts/chat-server/index.js && node --check runtime/scripts/chat-server/config.js && node --check runtime/scripts/chat-core/tooling/native-binaries.js && node --check runtime/scripts/chat-core/tooling/package-import.js && node --check runtime/scripts/chat-core/tooling/runtime-home.js && node --check runtime/scripts/amalgm-mcp/index.js && node --check runtime/scripts/amalgm-mcp/config.js && node --check runtime/scripts/amalgm-mcp/events/pr-check-runner.js && node --check runtime/scripts/amalgm-mcp/registration/entity-cloud.js && node --check runtime/scripts/amalgm-mcp/registration/entity-content.js && node --check runtime/scripts/amalgm-mcp/registration/service.js && node --check runtime/scripts/amalgm-mcp/server/routes/entities.js && node --check runtime/scripts/lib/project-paths.js && node --check runtime/scripts/lib/runtime-paths.js"
|
|
20
|
+
"check": "node --check bin/amalgm.js && node --check lib/app-chat.js && node --check lib/react.js && node --check lib/auth-store.js && node --check lib/cli.js && node --check lib/identity-adopt.js && node --check lib/layout.js && node --check lib/migrate-layout.js && node --check lib/paths.js && node --check lib/process-cleanup.js && node --check lib/runtime-identity.js && node --check lib/runtime-manifest.js && node --check lib/service.js && node --check lib/state-migration.js && node --check lib/supervisor.js && node --check lib/tunnel-chat.js && node --check lib/tunnel-events.js && node --check lib/updater.js && node --check runtime/lib/runtime-manifest.js && node --check runtime/scripts/runtime-auth.js && node --check runtime/scripts/proxy-token-store.js && node --check runtime/scripts/local-gateway.js && node --check runtime/scripts/port-monitor.js && node --check runtime/scripts/chat-server.js && node --check runtime/scripts/chat-server/index.js && node --check runtime/scripts/chat-server/config.js && node --check runtime/scripts/chat-core/tooling/native-binaries.js && node --check runtime/scripts/chat-core/tooling/package-import.js && node --check runtime/scripts/chat-core/tooling/runtime-home.js && node --check runtime/scripts/amalgm-mcp/index.js && node --check runtime/scripts/amalgm-mcp/config.js && node --check runtime/scripts/amalgm-mcp/events/pr-check-runner.js && node --check runtime/scripts/amalgm-mcp/registration/entity-cloud.js && node --check runtime/scripts/amalgm-mcp/registration/entity-content.js && node --check runtime/scripts/amalgm-mcp/registration/service.js && node --check runtime/scripts/amalgm-mcp/toolbox/artifacts.js && node --check runtime/scripts/amalgm-mcp/server/routes/entities.js && node --check runtime/scripts/lib/project-paths.js && node --check runtime/scripts/lib/runtime-paths.js"
|
|
21
21
|
},
|
|
22
22
|
"engines": {
|
|
23
23
|
"node": ">=20"
|
|
@@ -33,7 +33,9 @@ const ROOT_MANIFEST_NAME = '.amalgm-root.json';
|
|
|
33
33
|
const USER_MANIFEST_NAME = '.amalgm-user.json';
|
|
34
34
|
const LAYOUT_VERSION = 1;
|
|
35
35
|
const USER_HOME_MANIFEST_NAME = 'home.json';
|
|
36
|
-
|
|
36
|
+
// Changing the declared exact graph changes the identity contract. A client
|
|
37
|
+
// must never accept a prior graph and silently invent a new managed child.
|
|
38
|
+
const USER_HOME_SCHEMA_VERSION = 2;
|
|
37
39
|
|
|
38
40
|
// Stable per-user product namespaces. Their authority is NOT inferred from
|
|
39
41
|
// their path: user content and exact platform-managed targets are declared
|
|
@@ -59,6 +61,21 @@ const USER_HOME_ROOTS = Object.freeze([
|
|
|
59
61
|
// Platform management is an exact-node fact. It deliberately does not
|
|
60
62
|
// cascade from `system/`: human permissions may still inherit normally, while
|
|
61
63
|
// only these particular entities are product-managed.
|
|
64
|
+
//
|
|
65
|
+
// A Toolbox tool is one complete entity: its definition and actions travel in
|
|
66
|
+
// the same file. These names are deliberately stable and human-readable. They
|
|
67
|
+
// are not an `amalgm/` subtree because that would falsely imply ownership
|
|
68
|
+
// cascades to every future child of that directory.
|
|
69
|
+
const SYSTEM_TOOL_ARTIFACTS = Object.freeze([
|
|
70
|
+
{ key: 'amalgm-automations', toolId: 'automations', relativePath: 'toolbox/amalgm-automations.json', rootKey: 'toolbox' },
|
|
71
|
+
{ key: 'amalgm-notifications', toolId: 'notifications', relativePath: 'toolbox/amalgm-notifications.json', rootKey: 'toolbox' },
|
|
72
|
+
{ key: 'amalgm-memories', toolId: 'memories', relativePath: 'toolbox/amalgm-memories.json', rootKey: 'toolbox' },
|
|
73
|
+
{ key: 'amalgm-agents', toolId: 'agents', relativePath: 'toolbox/amalgm-agents.json', rootKey: 'toolbox' },
|
|
74
|
+
{ key: 'amalgm-apps', toolId: 'apps', relativePath: 'toolbox/amalgm-apps.json', rootKey: 'toolbox' },
|
|
75
|
+
{ key: 'amalgm-cua', toolId: 'computer-use', relativePath: 'toolbox/amalgm-cua.json', rootKey: 'toolbox' },
|
|
76
|
+
{ key: 'amalgm-browser', toolId: 'browser', relativePath: 'toolbox/amalgm-browser.json', rootKey: 'toolbox' },
|
|
77
|
+
]);
|
|
78
|
+
|
|
62
79
|
const USER_HOME_EXACT_MANAGED = Object.freeze([
|
|
63
80
|
...USER_HOME_ROOTS.map(({ key, relativePath }) => ({
|
|
64
81
|
key: `${key}-root`, relativePath, rootKey: key,
|
|
@@ -66,6 +83,7 @@ const USER_HOME_EXACT_MANAGED = Object.freeze([
|
|
|
66
83
|
{ key: 'home-manifest', relativePath: 'system/home.json', rootKey: 'system' },
|
|
67
84
|
{ key: 'defaults', relativePath: 'system/defaults', rootKey: 'system' },
|
|
68
85
|
{ key: 'policy', relativePath: 'system/policy', rootKey: 'system' },
|
|
86
|
+
...SYSTEM_TOOL_ARTIFACTS,
|
|
69
87
|
]);
|
|
70
88
|
|
|
71
89
|
function readJson(file, fallback = null) {
|
|
@@ -106,6 +124,7 @@ function declaredUserHome(userDir) {
|
|
|
106
124
|
*/
|
|
107
125
|
function ensureUserHome(userDir) {
|
|
108
126
|
const declaration = declaredUserHome(userDir);
|
|
127
|
+
const materializedExactPaths = [];
|
|
109
128
|
for (const root of declaration.roots) {
|
|
110
129
|
fs.mkdirSync(root.path, { recursive: true, mode: 0o700 });
|
|
111
130
|
}
|
|
@@ -115,15 +134,18 @@ function ensureUserHome(userDir) {
|
|
|
115
134
|
}
|
|
116
135
|
|
|
117
136
|
const manifest = declaration.exactManaged.find((entry) => entry.key === 'home-manifest');
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
137
|
+
const manifestContents = {
|
|
138
|
+
schemaVersion: USER_HOME_SCHEMA_VERSION,
|
|
139
|
+
kind: 'amalgm-user-home',
|
|
140
|
+
roots: declaration.roots.map(({ key, relativePath }) => ({ key, path: relativePath })),
|
|
141
|
+
exactManaged: declaration.exactManaged.map(({ key, relativePath, scope }) => ({ key, path: relativePath, scope })),
|
|
142
|
+
};
|
|
143
|
+
if (manifest && JSON.stringify(readJson(manifest.path, null)) !== JSON.stringify(manifestContents)) {
|
|
144
|
+
writeJson(manifest.path, manifestContents);
|
|
145
|
+
materializedExactPaths.push(manifest.relativePath);
|
|
125
146
|
}
|
|
126
|
-
|
|
147
|
+
|
|
148
|
+
return { ...declaration, materializedExactPaths };
|
|
127
149
|
}
|
|
128
150
|
|
|
129
151
|
function lstatOrNull(target) {
|
|
@@ -401,6 +423,7 @@ module.exports = {
|
|
|
401
423
|
LAYOUT_VERSION,
|
|
402
424
|
PRIMITIVE_DIRS,
|
|
403
425
|
ROOT_MANIFEST_NAME,
|
|
426
|
+
SYSTEM_TOOL_ARTIFACTS,
|
|
404
427
|
USER_MANIFEST_NAME,
|
|
405
428
|
USER_HOME_EXACT_MANAGED,
|
|
406
429
|
USER_HOME_MANIFEST_NAME,
|