amalgm 0.1.57 → 0.1.58
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 +8 -0
- package/lib/paths.js +16 -11
- package/lib/runtime-identity.js +18 -4
- package/lib/service.js +7 -0
- package/lib/state-migration.js +110 -0
- package/lib/supervisor.js +7 -0
- package/package.json +2 -2
- package/runtime/scripts/amalgm-mcp/apps/store.js +2 -0
- package/runtime/scripts/amalgm-mcp/automations/store.js +6 -5
- package/runtime/scripts/amalgm-mcp/config.js +33 -0
- package/runtime/scripts/amalgm-mcp/lib/prefs.js +2 -1
- package/runtime/scripts/amalgm-mcp/workspace/store.js +15 -8
- package/runtime/scripts/chat-core/contract.js +13 -4
- package/runtime/scripts/lib/project-paths.js +72 -0
- package/runtime/scripts/lib/runtime-paths.js +47 -0
- package/runtime/scripts/local-gateway.js +7 -3
package/lib/cli.js
CHANGED
|
@@ -13,6 +13,7 @@ const {
|
|
|
13
13
|
AMALGM_DIR,
|
|
14
14
|
AMALGM_BRANCH,
|
|
15
15
|
AMALGM_HOME,
|
|
16
|
+
AMALGM_RUNTIME_STATE_DIR,
|
|
16
17
|
DEFAULT_APP_URL,
|
|
17
18
|
DEVICE_FILE,
|
|
18
19
|
LOG_DIR,
|
|
@@ -44,6 +45,7 @@ const {
|
|
|
44
45
|
runtimeServiceProcesses,
|
|
45
46
|
supervisorProcesses,
|
|
46
47
|
} = require('./process-cleanup');
|
|
48
|
+
const { migrateLegacyUserState } = require('./state-migration');
|
|
47
49
|
const {
|
|
48
50
|
runtimePortsForStatus,
|
|
49
51
|
runtimeServiceNames,
|
|
@@ -138,6 +140,7 @@ function ensureDir(dir, mode = 0o700) {
|
|
|
138
140
|
function ensureBaseDirs() {
|
|
139
141
|
ensureDir(AMALGM_DIR, 0o700);
|
|
140
142
|
ensureDir(LOG_DIR, 0o700);
|
|
143
|
+
migrateLegacyUserState();
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
function readJson(file, fallback = null) {
|
|
@@ -203,6 +206,7 @@ const SAFE_DAEMON_ENV_KEYS = [
|
|
|
203
206
|
'AMALGM_NATIVE_NODE_MODULES',
|
|
204
207
|
'AMALGM_RUNTIME_INSTANCE_ID',
|
|
205
208
|
'AMALGM_RUNTIME_LABEL',
|
|
209
|
+
'AMALGM_RUNTIME_STATE_DIR',
|
|
206
210
|
'AMALGM_RUNTIME_USER_ID',
|
|
207
211
|
'AMALGM_SKIP_NATIVE_INSTALL',
|
|
208
212
|
'CHAT_SERVER_PORT',
|
|
@@ -232,6 +236,8 @@ function daemonEnv(localOnly) {
|
|
|
232
236
|
if (process.env[key]) env[key] = process.env[key];
|
|
233
237
|
}
|
|
234
238
|
env.AMALGM_DIR = AMALGM_DIR;
|
|
239
|
+
env.AMALGM_HOME = AMALGM_HOME;
|
|
240
|
+
env.AMALGM_RUNTIME_STATE_DIR = AMALGM_RUNTIME_STATE_DIR;
|
|
235
241
|
env.AMALGM_RUNTIME_INSTANCE_ID = AMALGM_RUNTIME_INSTANCE_ID;
|
|
236
242
|
env.AMALGM_RUNTIME_LABEL = AMALGM_RUNTIME_LABEL;
|
|
237
243
|
env.AMALGM_BRANCH = AMALGM_BRANCH;
|
|
@@ -1073,6 +1079,7 @@ async function status() {
|
|
|
1073
1079
|
console.log(`Label: ${AMALGM_RUNTIME_LABEL} (${AMALGM_BRANCH})`);
|
|
1074
1080
|
console.log(`User scope: ${AMALGM_RUNTIME_USER_SCOPE}`);
|
|
1075
1081
|
console.log(`State: ${AMALGM_DIR}`);
|
|
1082
|
+
console.log(`Runtime state: ${AMALGM_RUNTIME_STATE_DIR}`);
|
|
1076
1083
|
if (runtimeState?.gateway_url) console.log(`Gateway: ${runtimeState.gateway_url}`);
|
|
1077
1084
|
if (lastShutdown?.signal || lastShutdown?.reason) {
|
|
1078
1085
|
console.log(
|
|
@@ -1235,6 +1242,7 @@ async function doctor() {
|
|
|
1235
1242
|
console.log(`Label: ${AMALGM_RUNTIME_LABEL} (${AMALGM_BRANCH})`);
|
|
1236
1243
|
console.log(`User scope: ${AMALGM_RUNTIME_USER_SCOPE}`);
|
|
1237
1244
|
console.log(`State: ${AMALGM_DIR}`);
|
|
1245
|
+
console.log(`Runtime state: ${AMALGM_RUNTIME_STATE_DIR}`);
|
|
1238
1246
|
for (const check of checks) {
|
|
1239
1247
|
console.log(`${check.level.padEnd(4)} ${check.name.padEnd(18)} ${check.detail}`);
|
|
1240
1248
|
}
|
package/lib/paths.js
CHANGED
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
runtimeBranchForLabel,
|
|
11
11
|
runtimeInstanceIdForDir,
|
|
12
12
|
scopedAmalgmDir,
|
|
13
|
+
scopedRuntimeDir,
|
|
13
14
|
} = require('./runtime-identity');
|
|
14
15
|
|
|
15
16
|
const PACKAGE_VERSION = require('../package.json').version;
|
|
@@ -26,28 +27,32 @@ const AMALGM_RUNTIME_USER_SCOPE = resolveRuntimeUserScope(
|
|
|
26
27
|
const AMALGM_DIR = process.env.AMALGM_DIR
|
|
27
28
|
? path.resolve(process.env.AMALGM_DIR)
|
|
28
29
|
: scopedAmalgmDir(AMALGM_HOME, AMALGM_RUNTIME_USER_SCOPE, AMALGM_RUNTIME_LABEL);
|
|
30
|
+
const AMALGM_RUNTIME_STATE_DIR = process.env.AMALGM_RUNTIME_STATE_DIR
|
|
31
|
+
? path.resolve(process.env.AMALGM_RUNTIME_STATE_DIR)
|
|
32
|
+
: scopedRuntimeDir(AMALGM_HOME, AMALGM_RUNTIME_USER_SCOPE, AMALGM_RUNTIME_LABEL);
|
|
29
33
|
const AMALGM_RUNTIME_INSTANCE_ID = process.env.AMALGM_RUNTIME_INSTANCE_ID
|
|
30
|
-
|| runtimeInstanceIdForDir(
|
|
34
|
+
|| runtimeInstanceIdForDir(AMALGM_RUNTIME_STATE_DIR);
|
|
31
35
|
// Physical machine identity is intentionally shared across runtime labels.
|
|
32
|
-
//
|
|
36
|
+
// User data below is scoped by user; live runtime files are scoped by label.
|
|
33
37
|
const DEVICE_FILE = path.join(AMALGM_HOME, 'device.json');
|
|
34
38
|
const COMPUTER_FILE = path.join(AMALGM_DIR, 'computer.json');
|
|
35
39
|
const AUTH_FILE = path.join(AMALGM_DIR, 'auth.json');
|
|
36
|
-
const RUNTIME_TOKEN_FILE = path.join(
|
|
37
|
-
const PID_FILE = path.join(
|
|
38
|
-
const RUNTIME_STATE_FILE = path.join(
|
|
39
|
-
const LOG_DIR = path.join(
|
|
40
|
-
const SERVICE_DIR = path.join(
|
|
41
|
-
const SERVICE_PID_FILE = path.join(
|
|
42
|
-
const SERVICE_STATE_FILE = path.join(
|
|
43
|
-
const SERVICE_STOP_FILE = path.join(
|
|
44
|
-
const SHUTDOWN_REASON_FILE = path.join(
|
|
40
|
+
const RUNTIME_TOKEN_FILE = path.join(AMALGM_RUNTIME_STATE_DIR, 'runtime-token.json');
|
|
41
|
+
const PID_FILE = path.join(AMALGM_RUNTIME_STATE_DIR, 'amalgm.pid');
|
|
42
|
+
const RUNTIME_STATE_FILE = path.join(AMALGM_RUNTIME_STATE_DIR, 'runtime-state.json');
|
|
43
|
+
const LOG_DIR = path.join(AMALGM_RUNTIME_STATE_DIR, 'logs');
|
|
44
|
+
const SERVICE_DIR = path.join(AMALGM_RUNTIME_STATE_DIR, 'service');
|
|
45
|
+
const SERVICE_PID_FILE = path.join(AMALGM_RUNTIME_STATE_DIR, 'amalgm-service.pid');
|
|
46
|
+
const SERVICE_STATE_FILE = path.join(AMALGM_RUNTIME_STATE_DIR, 'service-state.json');
|
|
47
|
+
const SERVICE_STOP_FILE = path.join(AMALGM_RUNTIME_STATE_DIR, 'amalgm-service.stop');
|
|
48
|
+
const SHUTDOWN_REASON_FILE = path.join(AMALGM_RUNTIME_STATE_DIR, 'shutdown-reason.json');
|
|
45
49
|
const DEFAULT_APP_URL = process.env.AMALGM_APP_URL || 'https://amalgm.ai';
|
|
46
50
|
|
|
47
51
|
module.exports = {
|
|
48
52
|
AMALGM_BRANCH,
|
|
49
53
|
AMALGM_DIR,
|
|
50
54
|
AMALGM_HOME,
|
|
55
|
+
AMALGM_RUNTIME_STATE_DIR,
|
|
51
56
|
AMALGM_RUNTIME_INSTANCE_ID,
|
|
52
57
|
AMALGM_RUNTIME_LABEL,
|
|
53
58
|
AMALGM_RUNTIME_USER_SCOPE,
|
package/lib/runtime-identity.js
CHANGED
|
@@ -88,11 +88,19 @@ function discoverLegacyUserScope(homeDir, label = '') {
|
|
|
88
88
|
const usersDir = path.join(homeDir, 'users');
|
|
89
89
|
for (const entry of fs.readdirSync(usersDir, { withFileTypes: true })) {
|
|
90
90
|
if (!entry.isDirectory()) continue;
|
|
91
|
-
const
|
|
92
|
-
const
|
|
91
|
+
const userDir = path.join(usersDir, entry.name);
|
|
92
|
+
const userRecord = readJson(path.join(userDir, 'computer.json'), null);
|
|
93
|
+
const fromUserRecord = userIdFromRecord(userRecord);
|
|
94
|
+
if (fromUserRecord) return fromUserRecord;
|
|
95
|
+
const userAuth = readJson(path.join(userDir, 'auth.json'), null);
|
|
96
|
+
const fromUserAuth = userIdFromRecord(userAuth);
|
|
97
|
+
if (fromUserAuth) return fromUserAuth;
|
|
98
|
+
|
|
99
|
+
const oldScopedDir = path.join(userDir, runtimeLabel);
|
|
100
|
+
const scopedRecord = readJson(path.join(oldScopedDir, 'computer.json'), null);
|
|
93
101
|
const fromScopedRecord = userIdFromRecord(scopedRecord);
|
|
94
102
|
if (fromScopedRecord) return fromScopedRecord;
|
|
95
|
-
const scopedAuth = readJson(path.join(
|
|
103
|
+
const scopedAuth = readJson(path.join(oldScopedDir, 'auth.json'), null);
|
|
96
104
|
const fromScopedAuth = userIdFromRecord(scopedAuth);
|
|
97
105
|
if (fromScopedAuth) return fromScopedAuth;
|
|
98
106
|
}
|
|
@@ -117,7 +125,12 @@ function defaultAmalgmHome(env = process.env) {
|
|
|
117
125
|
}
|
|
118
126
|
|
|
119
127
|
function scopedAmalgmDir(homeDir, userScope, label) {
|
|
120
|
-
|
|
128
|
+
assertRuntimeLabel(label);
|
|
129
|
+
return path.join(homeDir, 'users', sanitizeScopeSegment(userScope));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function scopedRuntimeDir(homeDir, userScope, label) {
|
|
133
|
+
return path.join(scopedAmalgmDir(homeDir, userScope, label), 'runtimes', assertRuntimeLabel(label));
|
|
121
134
|
}
|
|
122
135
|
|
|
123
136
|
function runtimeInstanceIdForDir(dir) {
|
|
@@ -142,4 +155,5 @@ module.exports = {
|
|
|
142
155
|
runtimeLabelFromVersion,
|
|
143
156
|
sanitizeScopeSegment,
|
|
144
157
|
scopedAmalgmDir,
|
|
158
|
+
scopedRuntimeDir,
|
|
145
159
|
};
|
package/lib/service.js
CHANGED
|
@@ -7,12 +7,14 @@ const { spawn, spawnSync } = require('child_process');
|
|
|
7
7
|
|
|
8
8
|
const {
|
|
9
9
|
AMALGM_DIR,
|
|
10
|
+
AMALGM_HOME,
|
|
10
11
|
AMALGM_BRANCH,
|
|
11
12
|
LOG_DIR,
|
|
12
13
|
PACKAGE_ROOT,
|
|
13
14
|
PID_FILE,
|
|
14
15
|
AMALGM_RUNTIME_INSTANCE_ID,
|
|
15
16
|
AMALGM_RUNTIME_LABEL,
|
|
17
|
+
AMALGM_RUNTIME_STATE_DIR,
|
|
16
18
|
AMALGM_RUNTIME_USER_SCOPE,
|
|
17
19
|
SERVICE_DIR,
|
|
18
20
|
SERVICE_PID_FILE,
|
|
@@ -43,6 +45,7 @@ const SERVICE_ENV_KEYS = [
|
|
|
43
45
|
'AMALGM_HOME',
|
|
44
46
|
'AMALGM_RUNTIME_INSTANCE_ID',
|
|
45
47
|
'AMALGM_RUNTIME_LABEL',
|
|
48
|
+
'AMALGM_RUNTIME_STATE_DIR',
|
|
46
49
|
'AMALGM_RUNTIME_USER_ID',
|
|
47
50
|
'AMALGM_NATIVE_NODE_MODULES',
|
|
48
51
|
'AMALGM_PROJECTS_DIR',
|
|
@@ -235,6 +238,8 @@ function detectServiceBackend(mode = 'auto') {
|
|
|
235
238
|
function buildServiceEnv(localOnly) {
|
|
236
239
|
const env = {
|
|
237
240
|
AMALGM_DIR,
|
|
241
|
+
AMALGM_HOME,
|
|
242
|
+
AMALGM_RUNTIME_STATE_DIR,
|
|
238
243
|
AMALGM_RUNTIME_INSTANCE_ID,
|
|
239
244
|
AMALGM_RUNTIME_LABEL,
|
|
240
245
|
AMALGM_BRANCH,
|
|
@@ -261,6 +266,8 @@ function writePortableScript(localOnly) {
|
|
|
261
266
|
'#!/bin/sh',
|
|
262
267
|
'set -u',
|
|
263
268
|
`AMALGM_DIR=${shellQuote(AMALGM_DIR)}`,
|
|
269
|
+
`AMALGM_HOME=${shellQuote(AMALGM_HOME)}`,
|
|
270
|
+
`AMALGM_RUNTIME_STATE_DIR=${shellQuote(AMALGM_RUNTIME_STATE_DIR)}`,
|
|
264
271
|
`LOG_DIR=${shellQuote(LOG_DIR)}`,
|
|
265
272
|
`PID_FILE=${shellQuote(SERVICE_PID_FILE)}`,
|
|
266
273
|
`WORKER_PID_FILE=${shellQuote(PID_FILE)}`,
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
AMALGM_DIR,
|
|
9
|
+
AMALGM_HOME,
|
|
10
|
+
} = require('./paths');
|
|
11
|
+
|
|
12
|
+
const MARKER_FILE = path.join(AMALGM_DIR, '.user-state-migrated.json');
|
|
13
|
+
|
|
14
|
+
const DATA_FILES = [
|
|
15
|
+
'amalgm.db',
|
|
16
|
+
'apps.json',
|
|
17
|
+
'artifacts.json',
|
|
18
|
+
'agents.json',
|
|
19
|
+
'tasks.json',
|
|
20
|
+
'event-triggers.json',
|
|
21
|
+
'workflows.json',
|
|
22
|
+
'mcp-connections.json',
|
|
23
|
+
'computer.json',
|
|
24
|
+
'auth.json',
|
|
25
|
+
'.amalgm-credentials',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const DATA_DIRS = [
|
|
29
|
+
'apps',
|
|
30
|
+
'browser',
|
|
31
|
+
'browser-sessions',
|
|
32
|
+
'contexts',
|
|
33
|
+
'harness-configs',
|
|
34
|
+
'memories',
|
|
35
|
+
'.memories',
|
|
36
|
+
'task-runs',
|
|
37
|
+
'toolbox',
|
|
38
|
+
'uploads',
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
function readJson(file, fallback = null) {
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
44
|
+
} catch {
|
|
45
|
+
return fallback;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function writeJson(file, data) {
|
|
50
|
+
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
51
|
+
fs.writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`, { mode: 0o600 });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function copyFileIfUseful(source, target) {
|
|
55
|
+
if (!fs.existsSync(source)) return false;
|
|
56
|
+
|
|
57
|
+
const targetExists = fs.existsSync(target);
|
|
58
|
+
if (targetExists && path.basename(source) === 'amalgm.db') {
|
|
59
|
+
const sourceSize = fs.statSync(source).size;
|
|
60
|
+
const targetSize = fs.statSync(target).size;
|
|
61
|
+
if (targetSize > 64 * 1024 || sourceSize <= targetSize) return false;
|
|
62
|
+
} else if (targetExists && fs.statSync(target).size > 64) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
67
|
+
fs.copyFileSync(source, target);
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function copyDirIfMissing(source, target) {
|
|
72
|
+
if (!fs.existsSync(source) || fs.existsSync(target)) return false;
|
|
73
|
+
fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
74
|
+
fs.cpSync(source, target, { recursive: true, force: false, errorOnExist: true });
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function migrateLegacyUserState() {
|
|
79
|
+
if (path.resolve(AMALGM_DIR) === path.resolve(AMALGM_HOME)) return { migrated: false, reason: 'legacy-root' };
|
|
80
|
+
if (path.resolve(AMALGM_DIR).startsWith(path.resolve(os.tmpdir()))) return { migrated: false, reason: 'temp-dir' };
|
|
81
|
+
if (readJson(MARKER_FILE, null)) return { migrated: false, reason: 'already-migrated' };
|
|
82
|
+
|
|
83
|
+
fs.mkdirSync(AMALGM_DIR, { recursive: true, mode: 0o700 });
|
|
84
|
+
|
|
85
|
+
const copied = [];
|
|
86
|
+
for (const fileName of DATA_FILES) {
|
|
87
|
+
const source = path.join(AMALGM_HOME, fileName);
|
|
88
|
+
const target = path.join(AMALGM_DIR, fileName);
|
|
89
|
+
if (copyFileIfUseful(source, target)) copied.push(fileName);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const dirName of DATA_DIRS) {
|
|
93
|
+
const source = path.join(AMALGM_HOME, dirName);
|
|
94
|
+
const target = path.join(AMALGM_DIR, dirName);
|
|
95
|
+
if (copyDirIfMissing(source, target)) copied.push(`${dirName}/`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
writeJson(MARKER_FILE, {
|
|
99
|
+
migrated_at: new Date().toISOString(),
|
|
100
|
+
source: AMALGM_HOME,
|
|
101
|
+
target: AMALGM_DIR,
|
|
102
|
+
copied,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return { migrated: copied.length > 0, copied };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = {
|
|
109
|
+
migrateLegacyUserState,
|
|
110
|
+
};
|
package/lib/supervisor.js
CHANGED
|
@@ -11,10 +11,12 @@ const net = require('net');
|
|
|
11
11
|
const {
|
|
12
12
|
AMALGM_DIR,
|
|
13
13
|
AMALGM_BRANCH,
|
|
14
|
+
AMALGM_HOME,
|
|
14
15
|
LOG_DIR,
|
|
15
16
|
PID_FILE,
|
|
16
17
|
AMALGM_RUNTIME_INSTANCE_ID,
|
|
17
18
|
AMALGM_RUNTIME_LABEL,
|
|
19
|
+
AMALGM_RUNTIME_STATE_DIR,
|
|
18
20
|
AMALGM_RUNTIME_USER_SCOPE,
|
|
19
21
|
RUNTIME_DIR,
|
|
20
22
|
RUNTIME_STATE_FILE,
|
|
@@ -221,6 +223,11 @@ function baseRuntimeEnv(record, ports, options = {}) {
|
|
|
221
223
|
AMALGM_AUTH_BACKUP_ENABLED: process.env.AMALGM_AUTH_BACKUP_ENABLED || 'false',
|
|
222
224
|
AMALGM_CREATE_AUTH_WATCH_DIRS: process.env.AMALGM_CREATE_AUTH_WATCH_DIRS || 'false',
|
|
223
225
|
AMALGM_DIR,
|
|
226
|
+
AMALGM_HOME,
|
|
227
|
+
AMALGM_RUNTIME_STATE_DIR,
|
|
228
|
+
AMALGM_RUNTIME_STATE_FILE: RUNTIME_STATE_FILE,
|
|
229
|
+
AMALGM_RUNTIME_TOKEN_FILE: RUNTIME_TOKEN_FILE,
|
|
230
|
+
AMALGM_LOG_DIR: LOG_DIR,
|
|
224
231
|
AMALGM_RUNTIME_DIR: RUNTIME_DIR,
|
|
225
232
|
AMALGM_WORKSPACES_DIR: workspaceRoot,
|
|
226
233
|
AMALGM_PROJECTS_DIR: workspaceRoot,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amalgm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.58",
|
|
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/auth-store.js && node --check lib/cli.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/supervisor.js && node --check lib/tunnel-chat.js && node --check lib/tunnel-events.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/amalgm-mcp/index.js && node --check runtime/scripts/amalgm-mcp/config.js"
|
|
20
|
+
"check": "node --check bin/amalgm.js && node --check lib/auth-store.js && node --check lib/cli.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 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/amalgm-mcp/index.js && node --check runtime/scripts/amalgm-mcp/config.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"
|
|
@@ -17,6 +17,7 @@ const {
|
|
|
17
17
|
} = require('../config');
|
|
18
18
|
const { ensureDir, readJson, writeJsonAtomic } = require('../lib/storage');
|
|
19
19
|
const { appendStateEvent } = require('../state/events');
|
|
20
|
+
const { canonicalProjectPath } = require('../../lib/project-paths');
|
|
20
21
|
|
|
21
22
|
function ensureAppsDirs() {
|
|
22
23
|
ensureDir(APPS_DIR);
|
|
@@ -55,6 +56,7 @@ function normalizeApp(app) {
|
|
|
55
56
|
return {
|
|
56
57
|
...app,
|
|
57
58
|
kind: 'app',
|
|
59
|
+
...(app.cwd ? { cwd: canonicalProjectPath(app.cwd) } : {}),
|
|
58
60
|
appRef,
|
|
59
61
|
app_ref: appRef,
|
|
60
62
|
appUrl: publicUrl,
|
|
@@ -8,6 +8,7 @@ const { appendStateEvent, insertStateEvent, publishStateEvent } = require('../st
|
|
|
8
8
|
const { normalizeTaskSchedule } = require('../tasks/schedule-normalization');
|
|
9
9
|
const { compileWorkflowText, splitEventRef } = require('../workflows/compiler');
|
|
10
10
|
const { getWebhookUrl } = require('../events/webhook-url');
|
|
11
|
+
const { canonicalProjectPath } = require('../../lib/project-paths');
|
|
11
12
|
const { validateCellReferences } = require('./cell-references');
|
|
12
13
|
const { validateWorkflowToolActions } = require('./tool-actions');
|
|
13
14
|
|
|
@@ -127,7 +128,7 @@ function normalizeWorkflowRecord(input = {}, existing = null) {
|
|
|
127
128
|
name: cleanString(input.name) || existing?.name || 'Untitled workflow',
|
|
128
129
|
description: input.description ?? existing?.description ?? '',
|
|
129
130
|
enabled: input.enabled ?? existing?.enabled ?? true,
|
|
130
|
-
projectPath: input.projectPath ?? existing?.projectPath ?? null,
|
|
131
|
+
projectPath: canonicalProjectPath(input.projectPath ?? existing?.projectPath ?? null) || null,
|
|
131
132
|
workflowText,
|
|
132
133
|
workflowIr,
|
|
133
134
|
compilerErrors,
|
|
@@ -166,7 +167,7 @@ function rowToWorkflow(row) {
|
|
|
166
167
|
name: row.name,
|
|
167
168
|
description: row.description || '',
|
|
168
169
|
enabled: row.enabled === 1,
|
|
169
|
-
projectPath: row.project_path || null,
|
|
170
|
+
projectPath: canonicalProjectPath(row.project_path) || null,
|
|
170
171
|
workflowText: row.workflow_text || parsed.workflowText,
|
|
171
172
|
workflowIr: parseJson(row.workflow_ir_json, parsed.workflowIr || null),
|
|
172
173
|
compilerErrors: parseJson(row.compiler_errors_json, parsed.compilerErrors || null),
|
|
@@ -276,7 +277,7 @@ function normalizeTriggerRecord(input = {}, automation, existing = null) {
|
|
|
276
277
|
name: cleanString(input.name) || existing?.name || automation.name || 'Automation trigger',
|
|
277
278
|
description: input.description ?? existing?.description ?? '',
|
|
278
279
|
enabled: input.enabled ?? existing?.enabled ?? automation.enabled !== false,
|
|
279
|
-
projectPath: input.projectPath ?? existing?.projectPath ?? automation.projectPath ?? null,
|
|
280
|
+
projectPath: canonicalProjectPath(input.projectPath ?? existing?.projectPath ?? automation.projectPath ?? null) || null,
|
|
280
281
|
createdAt: isoOrNull(input.createdAt) || existing?.createdAt || timestamp,
|
|
281
282
|
updatedAt: existing ? timestamp : (isoOrNull(input.updatedAt) || null),
|
|
282
283
|
lastFiredAt: isoOrNull(input.lastFiredAt) || existing?.lastFiredAt || null,
|
|
@@ -429,7 +430,7 @@ function rowToAutomationBase(row) {
|
|
|
429
430
|
name: row.name,
|
|
430
431
|
description: row.description || '',
|
|
431
432
|
enabled: row.enabled === 1,
|
|
432
|
-
projectPath: row.project_path || null,
|
|
433
|
+
projectPath: canonicalProjectPath(row.project_path) || null,
|
|
433
434
|
workflowId: row.workflow_id,
|
|
434
435
|
createdAt: row.created_at,
|
|
435
436
|
updatedAt: row.updated_at,
|
|
@@ -856,7 +857,7 @@ function rowToAutomationRun(row) {
|
|
|
856
857
|
status: row.status,
|
|
857
858
|
startedAt: row.started_at || parsed.startedAt || null,
|
|
858
859
|
finishedAt: row.finished_at || parsed.finishedAt || null,
|
|
859
|
-
projectPath: row.project_path || parsed.projectPath || null,
|
|
860
|
+
projectPath: canonicalProjectPath(row.project_path || parsed.projectPath) || null,
|
|
860
861
|
error: row.error || parsed.error || null,
|
|
861
862
|
createdAt: row.created_at,
|
|
862
863
|
updatedAt: row.updated_at,
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Same pattern as scripts/credential-adapter.js.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
const fs = require('fs');
|
|
9
10
|
const path = require('path');
|
|
10
11
|
const os = require('os');
|
|
11
12
|
|
|
@@ -13,6 +14,7 @@ const PORT = parseInt(process.env.AMALGM_MCP_PORT || '8083', 10);
|
|
|
13
14
|
const MCP_PROTOCOL_VERSION = '2024-11-05';
|
|
14
15
|
|
|
15
16
|
const AMALGM_DIR = process.env.AMALGM_DIR || path.join(os.homedir(), '.amalgm');
|
|
17
|
+
const AMALGM_HOME = process.env.AMALGM_HOME || path.join(os.homedir(), '.amalgm');
|
|
16
18
|
|
|
17
19
|
// Storage file/dir layout under AMALGM_DIR.
|
|
18
20
|
const STORAGE_DIR = AMALGM_DIR; // tasks.json etc. live directly under ~/.amalgm
|
|
@@ -90,10 +92,41 @@ const APP_ROUTE_SYNC_INTERVAL_MS = parseInt(
|
|
|
90
92
|
10,
|
|
91
93
|
);
|
|
92
94
|
|
|
95
|
+
function copyIfUseful(source, target) {
|
|
96
|
+
if (!fs.existsSync(source)) return;
|
|
97
|
+
if (fs.existsSync(target) && fs.statSync(target).size > 64 * 1024) return;
|
|
98
|
+
fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
99
|
+
fs.copyFileSync(source, target);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function migrateLegacyUserState() {
|
|
103
|
+
if (path.resolve(AMALGM_DIR) === path.resolve(AMALGM_HOME)) return;
|
|
104
|
+
if (path.resolve(AMALGM_DIR).startsWith(path.resolve(os.tmpdir()))) return;
|
|
105
|
+
const marker = path.join(AMALGM_DIR, '.runtime-legacy-state-migrated.json');
|
|
106
|
+
if (fs.existsSync(marker)) return;
|
|
107
|
+
fs.mkdirSync(AMALGM_DIR, { recursive: true, mode: 0o700 });
|
|
108
|
+
for (const fileName of [
|
|
109
|
+
'amalgm.db',
|
|
110
|
+
'apps.json',
|
|
111
|
+
'artifacts.json',
|
|
112
|
+
'agents.json',
|
|
113
|
+
'tasks.json',
|
|
114
|
+
'event-triggers.json',
|
|
115
|
+
'workflows.json',
|
|
116
|
+
'mcp-connections.json',
|
|
117
|
+
]) {
|
|
118
|
+
copyIfUseful(path.join(AMALGM_HOME, fileName), path.join(AMALGM_DIR, fileName));
|
|
119
|
+
}
|
|
120
|
+
fs.writeFileSync(marker, `${JSON.stringify({ migrated_at: new Date().toISOString(), source: AMALGM_HOME }, null, 2)}\n`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
migrateLegacyUserState();
|
|
124
|
+
|
|
93
125
|
module.exports = {
|
|
94
126
|
PORT,
|
|
95
127
|
MCP_PROTOCOL_VERSION,
|
|
96
128
|
AMALGM_DIR,
|
|
129
|
+
AMALGM_HOME,
|
|
97
130
|
STORAGE_DIR,
|
|
98
131
|
LOCAL_DB_FILE,
|
|
99
132
|
TASKS_FILE,
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const { openLocalDb } = require('../state/db');
|
|
9
9
|
const { insertStateEvent, publishStateEvent } = require('../state/events');
|
|
10
|
+
const { canonicalProjectPath } = require('../../lib/project-paths');
|
|
10
11
|
|
|
11
12
|
const DEFAULT_SELECTED_MODELS = {
|
|
12
13
|
claude_code: 'anthropic/claude-opus-4.7',
|
|
@@ -382,7 +383,7 @@ function normalizePreferences(input) {
|
|
|
382
383
|
last_used: {
|
|
383
384
|
harness: lastUsedHarness,
|
|
384
385
|
model: lastUsedModel,
|
|
385
|
-
cwd: cleanString(rawLastUsed.cwd ?? raw.last_cwd ?? raw.selected_cwd),
|
|
386
|
+
cwd: canonicalProjectPath(cleanString(rawLastUsed.cwd ?? raw.last_cwd ?? raw.selected_cwd)),
|
|
386
387
|
},
|
|
387
388
|
usage,
|
|
388
389
|
recent_prefs: recentPrefs,
|
|
@@ -6,6 +6,10 @@ const path = require('path');
|
|
|
6
6
|
const { AMALGM_COMPUTER_ID } = require('../config');
|
|
7
7
|
const { openLocalDb } = require('../state/db');
|
|
8
8
|
const { insertStateEvent, publishStateEvent } = require('../state/events');
|
|
9
|
+
const {
|
|
10
|
+
canonicalProjectPath,
|
|
11
|
+
isLegacyManagedWorkspacePath,
|
|
12
|
+
} = require('../../lib/project-paths');
|
|
9
13
|
|
|
10
14
|
function cleanString(value) {
|
|
11
15
|
return typeof value === 'string' && value.trim() ? value.trim() : '';
|
|
@@ -65,17 +69,18 @@ function normalizeWorkspaceRecord(input, options = {}) {
|
|
|
65
69
|
throw new Error('Workspace path must be an absolute local path');
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
const
|
|
72
|
+
const legacyManagedPath = isLegacyManagedWorkspacePath(localPath);
|
|
73
|
+
const resolvedPath = canonicalProjectPath(localPath);
|
|
69
74
|
const computerId = stableComputerId(input.computerId || input.computer_id || options.computerId);
|
|
70
75
|
const uri = cleanString(input.uri) || workspaceUriForPath(resolvedPath, computerId);
|
|
71
76
|
const id = cleanString(input.id) || workspaceIdForUri(uri);
|
|
72
77
|
const createdAt = cleanString(input.createdAt || input.created_at) || nowIso();
|
|
73
78
|
const updatedAt = nowIso();
|
|
74
79
|
const managed = input.managed !== undefined
|
|
75
|
-
? Boolean(input.managed)
|
|
80
|
+
? Boolean(input.managed) && !legacyManagedPath
|
|
76
81
|
: isManagedWorkspacePath(resolvedPath, options.workspaceRoot);
|
|
77
82
|
const status = cleanString(input.status) || (fs.existsSync(resolvedPath) ? 'ready' : 'missing');
|
|
78
|
-
const source = cleanString(input.source) || (managed ? 'managed' : 'computer');
|
|
83
|
+
const source = legacyManagedPath ? 'computer' : (cleanString(input.source) || (managed ? 'managed' : 'computer'));
|
|
79
84
|
const name = cleanString(input.name) || basename(resolvedPath);
|
|
80
85
|
|
|
81
86
|
return {
|
|
@@ -102,18 +107,20 @@ function normalizeWorkspaceRecord(input, options = {}) {
|
|
|
102
107
|
function rowToWorkspace(row) {
|
|
103
108
|
if (!row) return null;
|
|
104
109
|
const parsed = parseJson(row.workspace_json, {});
|
|
110
|
+
const resolvedPath = row.path ? canonicalProjectPath(row.path) : row.path;
|
|
111
|
+
const legacyManagedPath = row.path ? isLegacyManagedWorkspacePath(row.path) : false;
|
|
105
112
|
return {
|
|
106
113
|
...parsed,
|
|
107
114
|
id: row.id,
|
|
108
115
|
name: row.name,
|
|
109
116
|
uri: row.uri,
|
|
110
|
-
path:
|
|
111
|
-
localPath:
|
|
117
|
+
path: resolvedPath,
|
|
118
|
+
localPath: resolvedPath,
|
|
112
119
|
locationType: row.location_type,
|
|
113
120
|
computerId: row.computer_id || parsed.computerId || null,
|
|
114
|
-
source: row.source,
|
|
115
|
-
managed: row.managed === 1,
|
|
116
|
-
status: row.status,
|
|
121
|
+
source: legacyManagedPath ? 'computer' : row.source,
|
|
122
|
+
managed: legacyManagedPath ? false : row.managed === 1,
|
|
123
|
+
status: resolvedPath && fs.existsSync(resolvedPath) ? 'ready' : row.status,
|
|
117
124
|
createdAt: row.created_at,
|
|
118
125
|
updatedAt: row.updated_at,
|
|
119
126
|
};
|
|
@@ -4,6 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const { AMALGM_DIR, DEFAULT_CWD, PROXY_BASE_URL, PROXY_TOKEN } = require('../chat-server/config');
|
|
7
|
+
const { canonicalProjectPath } = require('../lib/project-paths');
|
|
7
8
|
const { authEnvelope, coerceAuth, fingerprint } = require('./auth');
|
|
8
9
|
const { runtimePort } = require('../../lib/runtime-manifest');
|
|
9
10
|
|
|
@@ -241,15 +242,23 @@ function existingDirectory(candidate) {
|
|
|
241
242
|
}
|
|
242
243
|
}
|
|
243
244
|
|
|
245
|
+
function safeProcessCwd() {
|
|
246
|
+
try {
|
|
247
|
+
return process.cwd();
|
|
248
|
+
} catch {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
244
253
|
function resolveCwd(rawCwd, options = {}) {
|
|
245
|
-
const fallback = existingDirectory(expandHome(options.cwd))
|
|
246
|
-
|| existingDirectory(expandHome(DEFAULT_CWD))
|
|
247
|
-
|| existingDirectory(
|
|
254
|
+
const fallback = existingDirectory(canonicalProjectPath(expandHome(options.cwd)))
|
|
255
|
+
|| existingDirectory(canonicalProjectPath(expandHome(DEFAULT_CWD)))
|
|
256
|
+
|| existingDirectory(safeProcessCwd())
|
|
248
257
|
|| os.homedir();
|
|
249
258
|
const expanded = expandHome(rawCwd);
|
|
250
259
|
if (!expanded) return fallback;
|
|
251
260
|
const absolute = path.isAbsolute(expanded) ? expanded : path.resolve(fallback, expanded);
|
|
252
|
-
return existingDirectory(absolute) || fallback;
|
|
261
|
+
return existingDirectory(canonicalProjectPath(absolute)) || fallback;
|
|
253
262
|
}
|
|
254
263
|
|
|
255
264
|
function nullableString(value) {
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
function cleanString(value) {
|
|
8
|
+
return typeof value === 'string' && value.trim() ? value.trim() : '';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function expandHome(value) {
|
|
12
|
+
const input = cleanString(value);
|
|
13
|
+
if (!input) return '';
|
|
14
|
+
if (input === '~') return os.homedir();
|
|
15
|
+
if (input.startsWith('~/')) return path.join(os.homedir(), input.slice(2));
|
|
16
|
+
return input;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isWithin(root, target) {
|
|
20
|
+
const relative = path.relative(root, target);
|
|
21
|
+
return relative === '' || (!!relative && !relative.startsWith('..') && !path.isAbsolute(relative));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function legacyWorkspaceRoots() {
|
|
25
|
+
const home = process.env.AMALGM_HOME || path.join(os.homedir(), '.amalgm');
|
|
26
|
+
return [
|
|
27
|
+
process.env.AMALGM_LEGACY_WORKSPACES_DIR,
|
|
28
|
+
path.join(home, 'workspaces'),
|
|
29
|
+
path.join(home, 'projects'),
|
|
30
|
+
]
|
|
31
|
+
.filter(Boolean)
|
|
32
|
+
.map((entry) => path.resolve(expandHome(entry)));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function legacyManagedRelativePath(localPath) {
|
|
36
|
+
const resolved = path.resolve(expandHome(localPath));
|
|
37
|
+
const currentRoot = cleanString(process.env.AMALGM_WORKSPACES_DIR || process.env.AMALGM_PROJECTS_DIR);
|
|
38
|
+
|
|
39
|
+
for (const root of legacyWorkspaceRoots()) {
|
|
40
|
+
if (currentRoot && path.resolve(root) === path.resolve(expandHome(currentRoot))) continue;
|
|
41
|
+
if (!isWithin(root, resolved)) continue;
|
|
42
|
+
const relative = path.relative(root, resolved);
|
|
43
|
+
return relative && !relative.startsWith('..') ? relative : '';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function canonicalProjectPath(value) {
|
|
50
|
+
const expanded = expandHome(value);
|
|
51
|
+
if (!expanded) return '';
|
|
52
|
+
const absolute = path.isAbsolute(expanded) ? path.resolve(expanded) : path.resolve(expanded);
|
|
53
|
+
const legacyRelative = legacyManagedRelativePath(absolute);
|
|
54
|
+
|
|
55
|
+
if (legacyRelative) {
|
|
56
|
+
const homeCandidate = path.join(os.homedir(), legacyRelative);
|
|
57
|
+
if (fs.existsSync(homeCandidate)) return path.resolve(homeCandidate);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return absolute;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isLegacyManagedWorkspacePath(value) {
|
|
64
|
+
return Boolean(cleanString(value) && legacyManagedRelativePath(value));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = {
|
|
68
|
+
canonicalProjectPath,
|
|
69
|
+
cleanString,
|
|
70
|
+
expandHome,
|
|
71
|
+
isLegacyManagedWorkspacePath,
|
|
72
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
function normalizeRuntimeLabel(value) {
|
|
7
|
+
const raw = String(value || '').trim().toLowerCase();
|
|
8
|
+
if (['preview', 'canary', 'native-chat', 'staging'].includes(raw)) return 'preview';
|
|
9
|
+
if (['local', 'dev', 'development'].includes(raw)) return 'local';
|
|
10
|
+
return 'main';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function amalgmDir() {
|
|
14
|
+
return process.env.AMALGM_DIR || path.join(os.homedir(), '.amalgm');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function runtimeLabel() {
|
|
18
|
+
return normalizeRuntimeLabel(process.env.AMALGM_RUNTIME_LABEL || process.env.AMALGM_BRANCH);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function runtimeStateDir() {
|
|
22
|
+
return (
|
|
23
|
+
process.env.AMALGM_RUNTIME_STATE_DIR
|
|
24
|
+
|| path.join(amalgmDir(), 'runtimes', runtimeLabel())
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function runtimeStateFile() {
|
|
29
|
+
return process.env.AMALGM_RUNTIME_STATE_FILE || path.join(runtimeStateDir(), 'runtime-state.json');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function runtimeTokenFile() {
|
|
33
|
+
return process.env.AMALGM_RUNTIME_TOKEN_FILE || path.join(runtimeStateDir(), 'runtime-token.json');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function runtimeLogDir() {
|
|
37
|
+
return process.env.AMALGM_LOG_DIR || path.join(runtimeStateDir(), 'logs');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
amalgmDir,
|
|
42
|
+
runtimeLabel,
|
|
43
|
+
runtimeLogDir,
|
|
44
|
+
runtimeStateDir,
|
|
45
|
+
runtimeStateFile,
|
|
46
|
+
runtimeTokenFile,
|
|
47
|
+
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* One discoverable loopback entrypoint for the local Amalgm runtime. The
|
|
7
7
|
* individual services still run as separate processes, but clients only need
|
|
8
|
-
* to find this gateway port from
|
|
8
|
+
* to find this gateway port from the label-scoped runtime-state.json.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
const crypto = require('crypto');
|
|
@@ -23,6 +23,10 @@ const {
|
|
|
23
23
|
runtimeAuthHeaders,
|
|
24
24
|
} = require('./runtime-auth');
|
|
25
25
|
const { runtimePort } = require('../lib/runtime-manifest');
|
|
26
|
+
const {
|
|
27
|
+
runtimeLogDir,
|
|
28
|
+
runtimeStateFile,
|
|
29
|
+
} = require('./lib/runtime-paths');
|
|
26
30
|
|
|
27
31
|
function loadPty() {
|
|
28
32
|
const candidates = [
|
|
@@ -45,10 +49,10 @@ function loadPty() {
|
|
|
45
49
|
const pty = loadPty();
|
|
46
50
|
|
|
47
51
|
const AMALGM_DIR = process.env.AMALGM_DIR || path.join(os.homedir(), '.amalgm');
|
|
48
|
-
const STATE_FILE =
|
|
52
|
+
const STATE_FILE = runtimeStateFile();
|
|
49
53
|
const APPS_FILE = path.join(AMALGM_DIR, 'apps.json');
|
|
50
54
|
const LEGACY_ARTIFACTS_FILE = path.join(AMALGM_DIR, 'artifacts.json');
|
|
51
|
-
const LOG_DIR =
|
|
55
|
+
const LOG_DIR = runtimeLogDir();
|
|
52
56
|
const BIND_HOST = process.env.AMALGM_BIND_HOST || '127.0.0.1';
|
|
53
57
|
const OWNER = process.env.AMALGM_RUNTIME_SOURCE || 'local';
|
|
54
58
|
const RUNTIME_LABEL = process.env.AMALGM_RUNTIME_LABEL || 'main';
|