amalgm 0.1.138 → 0.1.139

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.
Files changed (39) hide show
  1. package/bin/amalgm.js +73 -0
  2. package/lib/auth-store.js +16 -2
  3. package/lib/cli.js +48 -7
  4. package/lib/identity-adopt.js +43 -0
  5. package/lib/layout.js +17 -10
  6. package/lib/migrate-layout.js +1 -1
  7. package/lib/state-migration.js +23 -1
  8. package/package.json +2 -2
  9. package/runtime/scripts/amalgm-mcp/agents/store.js +8 -2
  10. package/runtime/scripts/amalgm-mcp/apps/supervisor.js +116 -11
  11. package/runtime/scripts/amalgm-mcp/browser/backend.js +5 -2
  12. package/runtime/scripts/amalgm-mcp/browser/cli.js +2 -1
  13. package/runtime/scripts/amalgm-mcp/config.js +26 -5
  14. package/runtime/scripts/amalgm-mcp/events/desktop-release-runner.js +119 -53
  15. package/runtime/scripts/amalgm-mcp/events/internal-workflows.js +2 -2
  16. package/runtime/scripts/amalgm-mcp/events/npm-release-runner.js +4 -2
  17. package/runtime/scripts/amalgm-mcp/events/pr-check-runner.js +4 -2
  18. package/runtime/scripts/amalgm-mcp/fs/rest.js +20 -1
  19. package/runtime/scripts/amalgm-mcp/lib/layout.js +17 -10
  20. package/runtime/scripts/amalgm-mcp/lib/mcp-resolver.js +2 -2
  21. package/runtime/scripts/amalgm-mcp/project-context/store.js +27 -5
  22. package/runtime/scripts/amalgm-mcp/tests/agents-source-of-truth.test.js +13 -0
  23. package/runtime/scripts/amalgm-mcp/tests/apps-supervisor.test.js +190 -0
  24. package/runtime/scripts/amalgm-mcp/tests/browser-routing.test.js +2 -3
  25. package/runtime/scripts/amalgm-mcp/tests/config-migration.test.js +37 -1
  26. package/runtime/scripts/amalgm-mcp/tests/desktop-release-runner.test.js +51 -0
  27. package/runtime/scripts/amalgm-mcp/tests/files-resource.test.js +25 -0
  28. package/runtime/scripts/amalgm-mcp/tests/project-context.test.js +59 -0
  29. package/runtime/scripts/amalgm-mcp/toolbox/runner.js +6 -1
  30. package/runtime/scripts/chat-core/contract.js +2 -2
  31. package/runtime/scripts/chat-core/credentials/store.js +2 -1
  32. package/runtime/scripts/chat-core/recorder.js +2 -1
  33. package/runtime/scripts/chat-core/tooling/native-binaries.js +2 -1
  34. package/runtime/scripts/chat-server/config.js +2 -2
  35. package/runtime/scripts/credential-adapter.js +2 -2
  36. package/runtime/scripts/lib/runtime-paths.js +61 -1
  37. package/runtime/scripts/local-gateway.js +2 -1
  38. package/runtime/scripts/proxy-token-store.js +7 -4
  39. package/runtime/scripts/proxy-token-store.test.js +112 -0
@@ -1,16 +1,15 @@
1
1
  'use strict';
2
2
 
3
3
  const fs = require('fs');
4
- const os = require('os');
5
4
  const path = require('path');
5
+ const { amalgmDir, runtimeStateDir } = require('./lib/runtime-paths');
6
6
 
7
7
  const DEFAULT_PROXY_BASE_URL = 'https://amalgm-api-proxy-v2.fly.dev';
8
8
  const REFRESH_BUFFER_MS = 10 * 60 * 1000;
9
9
 
10
- const AMALGM_DIR = process.env.AMALGM_DIR || path.join(os.homedir(), '.amalgm');
10
+ const AMALGM_DIR = amalgmDir();
11
11
  const AMALGM_RUNTIME_LABEL = normalizeRuntimeLabel(process.env.AMALGM_RUNTIME_LABEL || process.env.AMALGM_BRANCH);
12
- const AMALGM_RUNTIME_STATE_DIR = process.env.AMALGM_RUNTIME_STATE_DIR
13
- || path.join(AMALGM_DIR, 'runtimes', AMALGM_RUNTIME_LABEL);
12
+ const AMALGM_RUNTIME_STATE_DIR = runtimeStateDir();
14
13
  const COMPUTER_FILE = path.join(AMALGM_DIR, 'computer.json');
15
14
  const AUTH_FILE = path.join(AMALGM_DIR, 'auth.json');
16
15
  const RUNTIME_COMPUTER_FILE = path.join(AMALGM_RUNTIME_STATE_DIR, 'computer.json');
@@ -146,6 +145,10 @@ async function refreshProxyToken() {
146
145
  schema_version: 1,
147
146
  computer_id: computerId,
148
147
  device_id: deviceId,
148
+ user_id: cleanString(record?.user_id) || cleanString(auth?.user_id),
149
+ user_email: cleanString(refreshed.user_email).toLowerCase()
150
+ || cleanString(record?.user_email).toLowerCase()
151
+ || cleanString(auth?.user_email).toLowerCase(),
149
152
  app_url: appUrl,
150
153
  proxy: {
151
154
  ...(auth.proxy || {}),
@@ -0,0 +1,112 @@
1
+ 'use strict';
2
+
3
+ const test = require('node:test');
4
+ const assert = require('node:assert/strict');
5
+ const fs = require('fs');
6
+ const os = require('os');
7
+ const path = require('path');
8
+
9
+ const ORIGINAL_ENV = { ...process.env };
10
+ const SCRIPTS_DIR = __dirname;
11
+
12
+ function resetEnv() {
13
+ for (const key of Object.keys(process.env)) {
14
+ if (key.startsWith('AMALGM_') || key === 'HOME') delete process.env[key];
15
+ }
16
+ for (const [key, value] of Object.entries(ORIGINAL_ENV)) {
17
+ if (key.startsWith('AMALGM_') || key === 'HOME') continue;
18
+ process.env[key] = value;
19
+ }
20
+ }
21
+
22
+ function clearRuntimeModules() {
23
+ for (const key of Object.keys(require.cache)) {
24
+ if (key.startsWith(SCRIPTS_DIR)) delete require.cache[key];
25
+ }
26
+ }
27
+
28
+ function writeJson(file, data) {
29
+ fs.mkdirSync(path.dirname(file), { recursive: true });
30
+ fs.writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`);
31
+ }
32
+
33
+ function readJson(file) {
34
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
35
+ }
36
+
37
+ test.afterEach(() => {
38
+ resetEnv();
39
+ clearRuntimeModules();
40
+ });
41
+
42
+ test('proxy token refresh persists user_email while keeping refresh request uuid-only', async () => {
43
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'amalgm-proxy-token-'));
44
+ const previousFetch = globalThis.fetch;
45
+ try {
46
+ const home = path.join(root, 'amalgm');
47
+ const emailDir = path.join(home, 'users', 'person@example.com');
48
+ const legacyDir = path.join(home, 'users', 'user-uuid');
49
+ fs.mkdirSync(path.join(emailDir, 'runtimes', 'main'), { recursive: true });
50
+ fs.symlinkSync('person@example.com', legacyDir);
51
+
52
+ const runtimeDir = path.join(legacyDir, 'runtimes', 'main');
53
+ writeJson(path.join(runtimeDir, 'computer.json'), {
54
+ computer_id: 'computer-a',
55
+ device_id: 'device-a',
56
+ user_id: 'user-uuid',
57
+ runtime_label: 'main',
58
+ app_url: 'https://app.example.test',
59
+ });
60
+ writeJson(path.join(runtimeDir, 'auth.json'), {
61
+ schema_version: 1,
62
+ computer_id: 'computer-a',
63
+ device_id: 'device-a',
64
+ user_id: 'user-uuid',
65
+ app_url: 'https://app.example.test',
66
+ machine: { token: 'machine-token' },
67
+ proxy: {
68
+ token: 'old-token',
69
+ expires_at: new Date(Date.now() - 1000).toISOString(),
70
+ },
71
+ });
72
+
73
+ process.env.HOME = root;
74
+ process.env.AMALGM_HOME = home;
75
+ process.env.AMALGM_DIR = legacyDir;
76
+ process.env.AMALGM_RUNTIME_STATE_DIR = runtimeDir;
77
+ process.env.AMALGM_RUNTIME_LABEL = 'main';
78
+
79
+ let refreshBody = null;
80
+ globalThis.fetch = async (url, init) => {
81
+ assert.equal(String(url), 'https://app.example.test/api/computers/proxy-token');
82
+ assert.equal(init?.headers?.authorization, 'Bearer machine-token');
83
+ refreshBody = JSON.parse(String(init?.body || '{}'));
84
+ return {
85
+ ok: true,
86
+ json: async () => ({
87
+ proxy_token: 'new-token',
88
+ proxy_token_expires_in: 3600,
89
+ proxy_token_expires_at: '2030-01-01T00:00:00.000Z',
90
+ user_email: 'Person@Example.com',
91
+ }),
92
+ };
93
+ };
94
+
95
+ const { ensureFreshProxyToken } = require('./proxy-token-store');
96
+ const token = await ensureFreshProxyToken({ force: true });
97
+
98
+ assert.equal(token, 'new-token');
99
+ assert.deepEqual(refreshBody, {
100
+ computer_id: 'computer-a',
101
+ device_id: 'device-a',
102
+ });
103
+
104
+ const nextAuth = readJson(path.join(runtimeDir, 'auth.json'));
105
+ assert.equal(nextAuth.user_id, 'user-uuid');
106
+ assert.equal(nextAuth.user_email, 'person@example.com');
107
+ assert.equal(nextAuth.proxy.token, 'new-token');
108
+ } finally {
109
+ globalThis.fetch = previousFetch;
110
+ fs.rmSync(root, { recursive: true, force: true });
111
+ }
112
+ });