@mindexec/cli 0.2.477 → 0.2.478
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/README.md +323 -323
- package/codex-model-catalog.js +202 -202
- package/codex-runtime.js +1608 -1608
- package/desktop-workspace-state.cjs +120 -120
- package/electron/main.cjs +517 -517
- package/electron/preload.cjs +17 -17
- package/electron/recurring-update-owner-smoke.mjs +55 -55
- package/electron/recurring-update-owner.cjs +97 -97
- package/electron/source-smoke.mjs +108 -108
- package/electron/update-feed-resolver.cjs +79 -79
- package/electron/update-feed.json +10 -10
- package/electron/update-manager-smoke.mjs +146 -146
- package/electron/update-manager.cjs +297 -297
- package/electron/windows-package-smoke.mjs +156 -156
- package/launch-bridge.cjs +236 -236
- package/lzo1x.js +110 -110
- package/mode4-atlas-worker.js +440 -440
- package/mode4-atlas.js +130 -130
- package/package.json +1 -1
- package/port-guard.cjs +285 -285
- package/remote-fast/osx-arm64/MacScreenCaptureKitH264.swift +339 -339
- package/remote-fast/osx-arm64/MacSystemAudioCapture.swift +214 -214
- package/remote-fast/osx-arm64/UnixRawCaptureHelper.mjs +80 -80
- package/remote-fast/osx-arm64/livedesk-raw-capture-resampler.mjs +73 -73
- package/remote-fast/osx-arm64/mindexec-remote-fast.dll +0 -0
- package/remote-fast/osx-x64/MacScreenCaptureKitH264.swift +339 -339
- package/remote-fast/osx-x64/MacSystemAudioCapture.swift +214 -214
- package/remote-fast/osx-x64/UnixRawCaptureHelper.mjs +80 -80
- package/remote-fast/osx-x64/livedesk-raw-capture-resampler.mjs +73 -73
- package/remote-fast/osx-x64/mindexec-remote-fast.dll +0 -0
- package/remote-fast/win-x64/mindexec-remote-fast.dll +0 -0
- package/remote-hub.js +3698 -3698
- package/scripts/auth-session-smoke.mjs +262 -262
- package/scripts/board-render-ownership-smoke.mjs +85 -85
- package/scripts/codex-model-catalog-smoke.mjs +150 -150
- package/scripts/codex-runtime-home-smoke.mjs +58 -58
- package/scripts/codex-sdk-image-live-smoke.mjs +43 -43
- package/scripts/codex-sdk-image-runtime-smoke.mjs +122 -122
- package/scripts/codex-sdk-runtime-smoke.mjs +89 -89
- package/scripts/desktop-update-publisher-smoke.mjs +89 -89
- package/scripts/desktop-update-worker-smoke.mjs +72 -72
- package/scripts/desktop-workspace-state-smoke.mjs +81 -81
- package/scripts/drive-installed-update-smoke.mjs +67 -67
- package/scripts/npm-fresh-install-smoke.mjs +216 -216
- package/scripts/publish-mindexec-desktop-updates.mjs +224 -224
- package/scripts/remote-agent-managed-smoke.mjs +318 -318
- package/scripts/remote-agent-package-smoke.mjs +64 -64
- package/scripts/remote-agent-ws-smoke.mjs +269 -269
- package/scripts/remote-fast-live-rate-smoke.mjs +343 -343
- package/scripts/remote-fast-mdm-browser-smoke.mjs +453 -453
- package/scripts/remote-fleet-render-smoke.mjs +1350 -1350
- package/scripts/remote-frame-ws-smoke.mjs +234 -234
- package/scripts/remote-http-smoke.mjs +592 -592
- package/scripts/remote-hub-identity-smoke.mjs +146 -146
- package/scripts/remote-hub-scale-smoke.mjs +124 -124
- package/scripts/remote-hub-smoke.mjs +699 -699
- package/scripts/remote-input-ws-smoke.mjs +263 -263
- package/scripts/remote-registry-follower-smoke.mjs +815 -815
- package/scripts/setup-tree-sitter-grammars.mjs +59 -59
- package/server.js +7690 -7690
- package/start-bridge.bat +32 -32
- package/start-bridge.sh +81 -81
- package/wwwroot/_content/MindExecution.Plugins.Admin/css/admin-dashboard.css +546 -546
- package/wwwroot/_content/MindExecution.Plugins.YouTube/css/youtube-search.css +251 -251
- package/wwwroot/_content/MindExecution.Shared/css/app.css +1 -1
- package/wwwroot/_content/MindExecution.Shared/css/mind-map-overrides.css +3789 -3789
- package/wwwroot/_content/MindExecution.Shared/js/agent-visualization.js +27 -27
- package/wwwroot/_content/MindExecution.Shared/js/background-themes.js +1449 -1449
- package/wwwroot/_content/MindExecution.Shared/js/code-master.js +8316 -8316
- package/wwwroot/_content/MindExecution.Shared/js/file-system-helper.js +338 -338
- package/wwwroot/_content/MindExecution.Shared/js/marked.min.js +69 -69
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-animated-image-preview.js +270 -270
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-automation-edge-motion.js +170 -170
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-board-render-ownership.js +81 -81
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +10506 -10506
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-hotpath.js +902 -902
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +24637 -24637
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dev-guards.js +325 -325
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js +1491 -1491
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-glow-shader.js +70 -70
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-image-policy.js +218 -218
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +7065 -7065
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-plan-worker.js +160 -160
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-renderer.js +9956 -9956
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-video-placeholder.js +107 -107
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-logic-workers.js +1004 -1004
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-menu-manager.js +1351 -1351
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-multi-select.js +865 -865
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-node-search-worker.js +554 -554
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +4744 -4744
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-normal-boundary-optimizer.js +624 -624
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js +241 -241
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-pipeline.js +2290 -2290
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-remote-codecs.js +202 -202
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-render-plan.js +1971 -1971
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-text-lod-system.js +93 -93
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js +2391 -2391
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-video-playback-controls.js +95 -95
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-visibility-worker.js +895 -895
- package/wwwroot/_content/MindExecution.Shared/js/mindmap-toolbar.js +636 -636
- package/wwwroot/_content/MindExecution.Shared/js/native-drop-handler.js +45 -45
- package/wwwroot/_content/MindExecution.Shared/js/plan-master.js +117 -117
- package/wwwroot/_content/MindExecution.Shared/js/renderers/CSS3DRenderer.js +114 -114
- package/wwwroot/_content/MindExecution.Shared/js/texture-worker-manager.js +10 -10
- package/wwwroot/_content/MindExecution.Shared/js/three.min.js +6 -6
- package/wwwroot/_content/MindExecution.Shared/js/token-manager.js +7 -7
- package/wwwroot/_content/MindExecution.Shared/js/troika-bundle.js +6 -6
- package/wwwroot/_content/MindExecution.Shared/js/troika-bundle.js.map +2 -2
- package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/css/all.min.css +8 -8
- package/wwwroot/_content/MindExecution.Shared/models/vocab.txt +30522 -30522
- package/wwwroot/_framework/{Google.Protobuf.9h59ukbel7.dll → Google.Protobuf.33g2gb5is0.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Core.ty1xewqp36.dll → MindExecution.Core.z100xo4mg0.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Kernel.h7rqjoo69o.dll → MindExecution.Kernel.i9cldrx8yc.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Admin.wrfrcvhjde.dll → MindExecution.Plugins.Admin.48lzqddb0a.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Business.csbcyjurdh.dll → MindExecution.Plugins.Business.7m8u1q142p.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.buomiysqjn.dll → MindExecution.Plugins.Concept.cl5wmr53od.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Directory.7k00i2v769.dll → MindExecution.Plugins.Directory.slipi39041.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.85i5ocraro.dll → MindExecution.Plugins.PlanMaster.qfge05l671.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.nbolrfakte.dll → MindExecution.Plugins.YouTube.tn7v29ut77.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Shared.nqh4porv7x.dll → MindExecution.Shared.rjqlf0umjn.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Web.211q3bf4mr.dll → MindExecution.Web.rwawrxxk38.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +23 -23
- package/wwwroot/_headers +40 -40
- package/wwwroot/_redirects +1 -1
- package/wwwroot/index.html +772 -772
- package/wwwroot/js/marketing-tool.js +180 -180
- package/wwwroot/manifest.webmanifest +23 -23
- package/wwwroot/service-worker-assets.js +78 -106
- package/wwwroot/service-worker.js +33 -33
- package/tree-sitter-grammars/README.md +0 -18
- package/tree-sitter-grammars/tree-sitter-c_sharp.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-go.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-java.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-javascript.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-python.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-rust.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-tsx.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-typescript.wasm +0 -0
- package/wwwroot/_content/MindExecution.Plugins.Directory/background.png +0 -0
- package/wwwroot/_content/MindExecution.Shared/fonts/NotoSansKR-Bold.ttf +0 -0
- package/wwwroot/_content/MindExecution.Shared/fonts/NotoSansKR-Regular.ttf +0 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js.backup +0 -1059
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js.bak +0 -434
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js.backup +0 -372
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js.backup +0 -1258
|
@@ -1,262 +1,262 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import assert from 'node:assert/strict';
|
|
4
|
-
import { spawn } from 'node:child_process';
|
|
5
|
-
import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
|
|
6
|
-
import net from 'node:net';
|
|
7
|
-
import os from 'node:os';
|
|
8
|
-
import path from 'node:path';
|
|
9
|
-
import { fileURLToPath } from 'node:url';
|
|
10
|
-
|
|
11
|
-
const BRIDGE_TOKEN = 'auth-session-smoke-token';
|
|
12
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
-
const bridgeRoot = path.resolve(__dirname, '..');
|
|
14
|
-
|
|
15
|
-
function wait(ms) {
|
|
16
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async function findFreePort() {
|
|
20
|
-
return await new Promise((resolve, reject) => {
|
|
21
|
-
const server = net.createServer();
|
|
22
|
-
server.unref();
|
|
23
|
-
server.once('error', reject);
|
|
24
|
-
server.listen(0, '127.0.0.1', () => {
|
|
25
|
-
const address = server.address();
|
|
26
|
-
const port = typeof address === 'object' && address ? address.port : 0;
|
|
27
|
-
server.close(() => resolve(port));
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function fetchJson(url, options = {}) {
|
|
33
|
-
const response = await fetch(url, {
|
|
34
|
-
...options,
|
|
35
|
-
headers: {
|
|
36
|
-
...(options.body ? { 'Content-Type': 'application/json' } : {}),
|
|
37
|
-
...(options.token ? { 'X-Bridge-Token': options.token } : {}),
|
|
38
|
-
...(options.headers || {})
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
let payload = null;
|
|
43
|
-
try {
|
|
44
|
-
payload = await response.json();
|
|
45
|
-
} catch {
|
|
46
|
-
// Ignore non-JSON responses.
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
status: response.status,
|
|
51
|
-
ok: response.ok,
|
|
52
|
-
payload
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function spawnBridge({ bridgePort, workspacePath, authDataRoot }) {
|
|
57
|
-
const child = spawn(process.execPath, ['server.js'], {
|
|
58
|
-
cwd: bridgeRoot,
|
|
59
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
60
|
-
windowsHide: true,
|
|
61
|
-
env: {
|
|
62
|
-
...process.env,
|
|
63
|
-
BRIDGE_PORT: String(bridgePort),
|
|
64
|
-
BRIDGE_TOKEN,
|
|
65
|
-
BRIDGE_REQUIRE_TOKEN: '1',
|
|
66
|
-
WORKSPACE_PATH: workspacePath,
|
|
67
|
-
MINDEXEC_AUTH_DATA_ROOT: authDataRoot,
|
|
68
|
-
MINDEXEC_REMOTE_HUB: '0',
|
|
69
|
-
MINDEXEC_REMOTE_REGISTRY_FOLLOWER: '0',
|
|
70
|
-
NO_COLOR: '1'
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
let stdout = '';
|
|
75
|
-
let stderr = '';
|
|
76
|
-
child.stdout.on('data', chunk => {
|
|
77
|
-
stdout += chunk.toString();
|
|
78
|
-
});
|
|
79
|
-
child.stderr.on('data', chunk => {
|
|
80
|
-
stderr += chunk.toString();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
const details = () => `stdout=${stdout}\nstderr=${stderr}`;
|
|
84
|
-
const stop = async () => {
|
|
85
|
-
if (!child.killed) {
|
|
86
|
-
child.kill();
|
|
87
|
-
}
|
|
88
|
-
await Promise.race([
|
|
89
|
-
new Promise(resolve => child.once('exit', resolve)),
|
|
90
|
-
wait(3000)
|
|
91
|
-
]);
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
child,
|
|
96
|
-
details,
|
|
97
|
-
stop
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
async function waitForBridge(baseUrl, details) {
|
|
102
|
-
const startedAt = Date.now();
|
|
103
|
-
while (Date.now() - startedAt < 30000) {
|
|
104
|
-
try {
|
|
105
|
-
const response = await fetchJson(`${baseUrl}/api/status`);
|
|
106
|
-
if (response.ok && response.payload?.status) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
} catch {
|
|
110
|
-
// Bridge is still starting.
|
|
111
|
-
}
|
|
112
|
-
await wait(100);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
throw new Error(`Timed out waiting for LocalBridge.\n${details()}`);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
async function withBridge({ workspacePath, authDataRoot }, action) {
|
|
119
|
-
const bridgePort = await findFreePort();
|
|
120
|
-
const bridge = spawnBridge({ bridgePort, workspacePath, authDataRoot });
|
|
121
|
-
const baseUrl = `http://127.0.0.1:${bridgePort}`;
|
|
122
|
-
try {
|
|
123
|
-
await waitForBridge(baseUrl, bridge.details);
|
|
124
|
-
await action(baseUrl);
|
|
125
|
-
} finally {
|
|
126
|
-
await bridge.stop();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'mindexec-auth-session-smoke-'));
|
|
131
|
-
try {
|
|
132
|
-
const authDataRoot = path.join(tempRoot, 'stable-auth');
|
|
133
|
-
const workspaceA = path.join(tempRoot, 'workspace-a');
|
|
134
|
-
const workspaceB = path.join(tempRoot, 'workspace-b');
|
|
135
|
-
await mkdir(workspaceA, { recursive: true });
|
|
136
|
-
await mkdir(workspaceB, { recursive: true });
|
|
137
|
-
|
|
138
|
-
const sessionPayload = JSON.stringify({
|
|
139
|
-
access_token: 'access-token-a',
|
|
140
|
-
refresh_token: 'refresh-token-a',
|
|
141
|
-
expires_at: Math.floor(Date.now() / 1000) + 600,
|
|
142
|
-
user: {
|
|
143
|
-
id: 'user-a',
|
|
144
|
-
email: 'auth-smoke@example.com'
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
let expectedStablePayload = sessionPayload;
|
|
148
|
-
|
|
149
|
-
function makeTimedSessionPayload(accessToken, refreshToken, expiresInSeconds) {
|
|
150
|
-
return JSON.stringify({
|
|
151
|
-
access_token: accessToken,
|
|
152
|
-
refresh_token: refreshToken,
|
|
153
|
-
expires_at: Math.floor(Date.now() / 1000) + expiresInSeconds,
|
|
154
|
-
user: {
|
|
155
|
-
id: 'user-a',
|
|
156
|
-
email: 'auth-smoke@example.com'
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
await withBridge({ workspacePath: workspaceA, authDataRoot }, async (baseUrl) => {
|
|
162
|
-
const unauthorized = await fetchJson(`${baseUrl}/api/auth/session`);
|
|
163
|
-
assert.equal(unauthorized.status, 401);
|
|
164
|
-
|
|
165
|
-
const missing = await fetchJson(`${baseUrl}/api/auth/session`, { token: BRIDGE_TOKEN });
|
|
166
|
-
assert.equal(missing.status, 404);
|
|
167
|
-
|
|
168
|
-
const invalid = await fetchJson(`${baseUrl}/api/auth/session`, {
|
|
169
|
-
method: 'POST',
|
|
170
|
-
token: BRIDGE_TOKEN,
|
|
171
|
-
body: JSON.stringify({ content: 'not-json' })
|
|
172
|
-
});
|
|
173
|
-
assert.equal(invalid.status, 400);
|
|
174
|
-
|
|
175
|
-
const saved = await fetchJson(`${baseUrl}/api/auth/session`, {
|
|
176
|
-
method: 'POST',
|
|
177
|
-
token: BRIDGE_TOKEN,
|
|
178
|
-
body: JSON.stringify({ content: sessionPayload })
|
|
179
|
-
});
|
|
180
|
-
assert.equal(saved.status, 200);
|
|
181
|
-
assert.equal(saved.payload?.success, true);
|
|
182
|
-
|
|
183
|
-
const loaded = await fetchJson(`${baseUrl}/api/auth/session`, { token: BRIDGE_TOKEN });
|
|
184
|
-
assert.equal(loaded.status, 200);
|
|
185
|
-
assert.equal(loaded.payload?.content, sessionPayload);
|
|
186
|
-
assert.equal(loaded.payload?.source, 'stable');
|
|
187
|
-
|
|
188
|
-
const newerPayload = makeTimedSessionPayload('access-token-newer', 'refresh-token-newer', 3600);
|
|
189
|
-
const newerSaved = await fetchJson(`${baseUrl}/api/auth/session`, {
|
|
190
|
-
method: 'POST',
|
|
191
|
-
token: BRIDGE_TOKEN,
|
|
192
|
-
body: JSON.stringify({ content: newerPayload })
|
|
193
|
-
});
|
|
194
|
-
assert.equal(newerSaved.status, 200);
|
|
195
|
-
|
|
196
|
-
const staleBrowserPayload = makeTimedSessionPayload('access-token-stale-browser', 'refresh-token-stale-browser', 120);
|
|
197
|
-
const staleSaved = await fetchJson(`${baseUrl}/api/auth/session`, {
|
|
198
|
-
method: 'POST',
|
|
199
|
-
token: BRIDGE_TOKEN,
|
|
200
|
-
body: JSON.stringify({ content: staleBrowserPayload })
|
|
201
|
-
});
|
|
202
|
-
assert.equal(staleSaved.status, 200);
|
|
203
|
-
|
|
204
|
-
const afterStale = await fetchJson(`${baseUrl}/api/auth/session`, { token: BRIDGE_TOKEN });
|
|
205
|
-
assert.equal(afterStale.status, 200);
|
|
206
|
-
assert.equal(JSON.parse(afterStale.payload?.content || '{}').access_token, 'access-token-newer');
|
|
207
|
-
|
|
208
|
-
const newestPayload = makeTimedSessionPayload('access-token-newest', 'refresh-token-newest', 7200);
|
|
209
|
-
const newestSaved = await fetchJson(`${baseUrl}/api/auth/session`, {
|
|
210
|
-
method: 'POST',
|
|
211
|
-
token: BRIDGE_TOKEN,
|
|
212
|
-
body: JSON.stringify({ content: newestPayload })
|
|
213
|
-
});
|
|
214
|
-
assert.equal(newestSaved.status, 200);
|
|
215
|
-
expectedStablePayload = newestPayload;
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
await withBridge({ workspacePath: workspaceB, authDataRoot }, async (baseUrl) => {
|
|
219
|
-
const loaded = await fetchJson(`${baseUrl}/api/auth/session`, { token: BRIDGE_TOKEN });
|
|
220
|
-
assert.equal(loaded.status, 200);
|
|
221
|
-
assert.equal(loaded.payload?.content, expectedStablePayload);
|
|
222
|
-
assert.equal(loaded.payload?.source, 'stable');
|
|
223
|
-
|
|
224
|
-
const deleted = await fetchJson(`${baseUrl}/api/auth/session`, {
|
|
225
|
-
method: 'DELETE',
|
|
226
|
-
token: BRIDGE_TOKEN
|
|
227
|
-
});
|
|
228
|
-
assert.equal(deleted.status, 200);
|
|
229
|
-
assert.equal(deleted.payload?.success, true);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
const legacyPayload = JSON.stringify({
|
|
233
|
-
access_token: 'legacy-access-token',
|
|
234
|
-
refresh_token: 'legacy-refresh-token',
|
|
235
|
-
user: {
|
|
236
|
-
id: 'legacy-user',
|
|
237
|
-
email: 'legacy-auth-smoke@example.com'
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
const legacyPath = path.join(workspaceB, '.mindexec', 'auth', 'supabase-session.json');
|
|
241
|
-
await mkdir(path.dirname(legacyPath), { recursive: true });
|
|
242
|
-
await writeFile(legacyPath, legacyPayload, 'utf8');
|
|
243
|
-
|
|
244
|
-
await withBridge({ workspacePath: workspaceB, authDataRoot }, async (baseUrl) => {
|
|
245
|
-
const migrated = await fetchJson(`${baseUrl}/api/auth/session`, { token: BRIDGE_TOKEN });
|
|
246
|
-
assert.equal(migrated.status, 200);
|
|
247
|
-
assert.equal(migrated.payload?.content, legacyPayload);
|
|
248
|
-
assert.equal(migrated.payload?.source, 'legacy-workspace');
|
|
249
|
-
assert.equal(migrated.payload?.migrated, true);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
await withBridge({ workspacePath: workspaceA, authDataRoot }, async (baseUrl) => {
|
|
253
|
-
const loaded = await fetchJson(`${baseUrl}/api/auth/session`, { token: BRIDGE_TOKEN });
|
|
254
|
-
assert.equal(loaded.status, 200);
|
|
255
|
-
assert.equal(loaded.payload?.content, legacyPayload);
|
|
256
|
-
assert.equal(loaded.payload?.source, 'stable');
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
console.log('Auth session persistence smoke OK');
|
|
260
|
-
} finally {
|
|
261
|
-
await rm(tempRoot, { recursive: true, force: true });
|
|
262
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import assert from 'node:assert/strict';
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
|
+
import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
|
|
6
|
+
import net from 'node:net';
|
|
7
|
+
import os from 'node:os';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
|
|
11
|
+
const BRIDGE_TOKEN = 'auth-session-smoke-token';
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const bridgeRoot = path.resolve(__dirname, '..');
|
|
14
|
+
|
|
15
|
+
function wait(ms) {
|
|
16
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function findFreePort() {
|
|
20
|
+
return await new Promise((resolve, reject) => {
|
|
21
|
+
const server = net.createServer();
|
|
22
|
+
server.unref();
|
|
23
|
+
server.once('error', reject);
|
|
24
|
+
server.listen(0, '127.0.0.1', () => {
|
|
25
|
+
const address = server.address();
|
|
26
|
+
const port = typeof address === 'object' && address ? address.port : 0;
|
|
27
|
+
server.close(() => resolve(port));
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function fetchJson(url, options = {}) {
|
|
33
|
+
const response = await fetch(url, {
|
|
34
|
+
...options,
|
|
35
|
+
headers: {
|
|
36
|
+
...(options.body ? { 'Content-Type': 'application/json' } : {}),
|
|
37
|
+
...(options.token ? { 'X-Bridge-Token': options.token } : {}),
|
|
38
|
+
...(options.headers || {})
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
let payload = null;
|
|
43
|
+
try {
|
|
44
|
+
payload = await response.json();
|
|
45
|
+
} catch {
|
|
46
|
+
// Ignore non-JSON responses.
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
status: response.status,
|
|
51
|
+
ok: response.ok,
|
|
52
|
+
payload
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function spawnBridge({ bridgePort, workspacePath, authDataRoot }) {
|
|
57
|
+
const child = spawn(process.execPath, ['server.js'], {
|
|
58
|
+
cwd: bridgeRoot,
|
|
59
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
60
|
+
windowsHide: true,
|
|
61
|
+
env: {
|
|
62
|
+
...process.env,
|
|
63
|
+
BRIDGE_PORT: String(bridgePort),
|
|
64
|
+
BRIDGE_TOKEN,
|
|
65
|
+
BRIDGE_REQUIRE_TOKEN: '1',
|
|
66
|
+
WORKSPACE_PATH: workspacePath,
|
|
67
|
+
MINDEXEC_AUTH_DATA_ROOT: authDataRoot,
|
|
68
|
+
MINDEXEC_REMOTE_HUB: '0',
|
|
69
|
+
MINDEXEC_REMOTE_REGISTRY_FOLLOWER: '0',
|
|
70
|
+
NO_COLOR: '1'
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
let stdout = '';
|
|
75
|
+
let stderr = '';
|
|
76
|
+
child.stdout.on('data', chunk => {
|
|
77
|
+
stdout += chunk.toString();
|
|
78
|
+
});
|
|
79
|
+
child.stderr.on('data', chunk => {
|
|
80
|
+
stderr += chunk.toString();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const details = () => `stdout=${stdout}\nstderr=${stderr}`;
|
|
84
|
+
const stop = async () => {
|
|
85
|
+
if (!child.killed) {
|
|
86
|
+
child.kill();
|
|
87
|
+
}
|
|
88
|
+
await Promise.race([
|
|
89
|
+
new Promise(resolve => child.once('exit', resolve)),
|
|
90
|
+
wait(3000)
|
|
91
|
+
]);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
child,
|
|
96
|
+
details,
|
|
97
|
+
stop
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function waitForBridge(baseUrl, details) {
|
|
102
|
+
const startedAt = Date.now();
|
|
103
|
+
while (Date.now() - startedAt < 30000) {
|
|
104
|
+
try {
|
|
105
|
+
const response = await fetchJson(`${baseUrl}/api/status`);
|
|
106
|
+
if (response.ok && response.payload?.status) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
} catch {
|
|
110
|
+
// Bridge is still starting.
|
|
111
|
+
}
|
|
112
|
+
await wait(100);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
throw new Error(`Timed out waiting for LocalBridge.\n${details()}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function withBridge({ workspacePath, authDataRoot }, action) {
|
|
119
|
+
const bridgePort = await findFreePort();
|
|
120
|
+
const bridge = spawnBridge({ bridgePort, workspacePath, authDataRoot });
|
|
121
|
+
const baseUrl = `http://127.0.0.1:${bridgePort}`;
|
|
122
|
+
try {
|
|
123
|
+
await waitForBridge(baseUrl, bridge.details);
|
|
124
|
+
await action(baseUrl);
|
|
125
|
+
} finally {
|
|
126
|
+
await bridge.stop();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'mindexec-auth-session-smoke-'));
|
|
131
|
+
try {
|
|
132
|
+
const authDataRoot = path.join(tempRoot, 'stable-auth');
|
|
133
|
+
const workspaceA = path.join(tempRoot, 'workspace-a');
|
|
134
|
+
const workspaceB = path.join(tempRoot, 'workspace-b');
|
|
135
|
+
await mkdir(workspaceA, { recursive: true });
|
|
136
|
+
await mkdir(workspaceB, { recursive: true });
|
|
137
|
+
|
|
138
|
+
const sessionPayload = JSON.stringify({
|
|
139
|
+
access_token: 'access-token-a',
|
|
140
|
+
refresh_token: 'refresh-token-a',
|
|
141
|
+
expires_at: Math.floor(Date.now() / 1000) + 600,
|
|
142
|
+
user: {
|
|
143
|
+
id: 'user-a',
|
|
144
|
+
email: 'auth-smoke@example.com'
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
let expectedStablePayload = sessionPayload;
|
|
148
|
+
|
|
149
|
+
function makeTimedSessionPayload(accessToken, refreshToken, expiresInSeconds) {
|
|
150
|
+
return JSON.stringify({
|
|
151
|
+
access_token: accessToken,
|
|
152
|
+
refresh_token: refreshToken,
|
|
153
|
+
expires_at: Math.floor(Date.now() / 1000) + expiresInSeconds,
|
|
154
|
+
user: {
|
|
155
|
+
id: 'user-a',
|
|
156
|
+
email: 'auth-smoke@example.com'
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
await withBridge({ workspacePath: workspaceA, authDataRoot }, async (baseUrl) => {
|
|
162
|
+
const unauthorized = await fetchJson(`${baseUrl}/api/auth/session`);
|
|
163
|
+
assert.equal(unauthorized.status, 401);
|
|
164
|
+
|
|
165
|
+
const missing = await fetchJson(`${baseUrl}/api/auth/session`, { token: BRIDGE_TOKEN });
|
|
166
|
+
assert.equal(missing.status, 404);
|
|
167
|
+
|
|
168
|
+
const invalid = await fetchJson(`${baseUrl}/api/auth/session`, {
|
|
169
|
+
method: 'POST',
|
|
170
|
+
token: BRIDGE_TOKEN,
|
|
171
|
+
body: JSON.stringify({ content: 'not-json' })
|
|
172
|
+
});
|
|
173
|
+
assert.equal(invalid.status, 400);
|
|
174
|
+
|
|
175
|
+
const saved = await fetchJson(`${baseUrl}/api/auth/session`, {
|
|
176
|
+
method: 'POST',
|
|
177
|
+
token: BRIDGE_TOKEN,
|
|
178
|
+
body: JSON.stringify({ content: sessionPayload })
|
|
179
|
+
});
|
|
180
|
+
assert.equal(saved.status, 200);
|
|
181
|
+
assert.equal(saved.payload?.success, true);
|
|
182
|
+
|
|
183
|
+
const loaded = await fetchJson(`${baseUrl}/api/auth/session`, { token: BRIDGE_TOKEN });
|
|
184
|
+
assert.equal(loaded.status, 200);
|
|
185
|
+
assert.equal(loaded.payload?.content, sessionPayload);
|
|
186
|
+
assert.equal(loaded.payload?.source, 'stable');
|
|
187
|
+
|
|
188
|
+
const newerPayload = makeTimedSessionPayload('access-token-newer', 'refresh-token-newer', 3600);
|
|
189
|
+
const newerSaved = await fetchJson(`${baseUrl}/api/auth/session`, {
|
|
190
|
+
method: 'POST',
|
|
191
|
+
token: BRIDGE_TOKEN,
|
|
192
|
+
body: JSON.stringify({ content: newerPayload })
|
|
193
|
+
});
|
|
194
|
+
assert.equal(newerSaved.status, 200);
|
|
195
|
+
|
|
196
|
+
const staleBrowserPayload = makeTimedSessionPayload('access-token-stale-browser', 'refresh-token-stale-browser', 120);
|
|
197
|
+
const staleSaved = await fetchJson(`${baseUrl}/api/auth/session`, {
|
|
198
|
+
method: 'POST',
|
|
199
|
+
token: BRIDGE_TOKEN,
|
|
200
|
+
body: JSON.stringify({ content: staleBrowserPayload })
|
|
201
|
+
});
|
|
202
|
+
assert.equal(staleSaved.status, 200);
|
|
203
|
+
|
|
204
|
+
const afterStale = await fetchJson(`${baseUrl}/api/auth/session`, { token: BRIDGE_TOKEN });
|
|
205
|
+
assert.equal(afterStale.status, 200);
|
|
206
|
+
assert.equal(JSON.parse(afterStale.payload?.content || '{}').access_token, 'access-token-newer');
|
|
207
|
+
|
|
208
|
+
const newestPayload = makeTimedSessionPayload('access-token-newest', 'refresh-token-newest', 7200);
|
|
209
|
+
const newestSaved = await fetchJson(`${baseUrl}/api/auth/session`, {
|
|
210
|
+
method: 'POST',
|
|
211
|
+
token: BRIDGE_TOKEN,
|
|
212
|
+
body: JSON.stringify({ content: newestPayload })
|
|
213
|
+
});
|
|
214
|
+
assert.equal(newestSaved.status, 200);
|
|
215
|
+
expectedStablePayload = newestPayload;
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
await withBridge({ workspacePath: workspaceB, authDataRoot }, async (baseUrl) => {
|
|
219
|
+
const loaded = await fetchJson(`${baseUrl}/api/auth/session`, { token: BRIDGE_TOKEN });
|
|
220
|
+
assert.equal(loaded.status, 200);
|
|
221
|
+
assert.equal(loaded.payload?.content, expectedStablePayload);
|
|
222
|
+
assert.equal(loaded.payload?.source, 'stable');
|
|
223
|
+
|
|
224
|
+
const deleted = await fetchJson(`${baseUrl}/api/auth/session`, {
|
|
225
|
+
method: 'DELETE',
|
|
226
|
+
token: BRIDGE_TOKEN
|
|
227
|
+
});
|
|
228
|
+
assert.equal(deleted.status, 200);
|
|
229
|
+
assert.equal(deleted.payload?.success, true);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const legacyPayload = JSON.stringify({
|
|
233
|
+
access_token: 'legacy-access-token',
|
|
234
|
+
refresh_token: 'legacy-refresh-token',
|
|
235
|
+
user: {
|
|
236
|
+
id: 'legacy-user',
|
|
237
|
+
email: 'legacy-auth-smoke@example.com'
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
const legacyPath = path.join(workspaceB, '.mindexec', 'auth', 'supabase-session.json');
|
|
241
|
+
await mkdir(path.dirname(legacyPath), { recursive: true });
|
|
242
|
+
await writeFile(legacyPath, legacyPayload, 'utf8');
|
|
243
|
+
|
|
244
|
+
await withBridge({ workspacePath: workspaceB, authDataRoot }, async (baseUrl) => {
|
|
245
|
+
const migrated = await fetchJson(`${baseUrl}/api/auth/session`, { token: BRIDGE_TOKEN });
|
|
246
|
+
assert.equal(migrated.status, 200);
|
|
247
|
+
assert.equal(migrated.payload?.content, legacyPayload);
|
|
248
|
+
assert.equal(migrated.payload?.source, 'legacy-workspace');
|
|
249
|
+
assert.equal(migrated.payload?.migrated, true);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
await withBridge({ workspacePath: workspaceA, authDataRoot }, async (baseUrl) => {
|
|
253
|
+
const loaded = await fetchJson(`${baseUrl}/api/auth/session`, { token: BRIDGE_TOKEN });
|
|
254
|
+
assert.equal(loaded.status, 200);
|
|
255
|
+
assert.equal(loaded.payload?.content, legacyPayload);
|
|
256
|
+
assert.equal(loaded.payload?.source, 'stable');
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
console.log('Auth session persistence smoke OK');
|
|
260
|
+
} finally {
|
|
261
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
262
|
+
}
|