notioncode 0.1.0 → 0.1.2
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 +22 -9
- package/agent-runtime-server/package-lock.json +4377 -0
- package/agent-runtime-server/package.json +36 -0
- package/agent-runtime-server/scripts/fix-node-pty.js +67 -0
- package/agent-runtime-server/server/agent-session-service.js +816 -0
- package/agent-runtime-server/server/claude-sdk.js +836 -0
- package/agent-runtime-server/server/cli.js +330 -0
- package/agent-runtime-server/server/constants/config.js +5 -0
- package/agent-runtime-server/server/cursor-cli.js +335 -0
- package/agent-runtime-server/server/database/db.js +653 -0
- package/agent-runtime-server/server/database/init.sql +99 -0
- package/agent-runtime-server/server/gemini-cli.js +460 -0
- package/agent-runtime-server/server/gemini-response-handler.js +79 -0
- package/agent-runtime-server/server/index.js +2569 -0
- package/agent-runtime-server/server/load-env.js +32 -0
- package/agent-runtime-server/server/middleware/auth.js +132 -0
- package/agent-runtime-server/server/openai-codex.js +512 -0
- package/agent-runtime-server/server/projects.js +2594 -0
- package/agent-runtime-server/server/providers/claude/adapter.js +278 -0
- package/agent-runtime-server/server/providers/codex/adapter.js +248 -0
- package/agent-runtime-server/server/providers/cursor/adapter.js +353 -0
- package/agent-runtime-server/server/providers/gemini/adapter.js +186 -0
- package/agent-runtime-server/server/providers/registry.js +44 -0
- package/agent-runtime-server/server/providers/types.js +119 -0
- package/agent-runtime-server/server/providers/utils.js +29 -0
- package/agent-runtime-server/server/routes/agent-sessions.js +238 -0
- package/agent-runtime-server/server/routes/agent.js +1244 -0
- package/agent-runtime-server/server/routes/auth.js +144 -0
- package/agent-runtime-server/server/routes/cli-auth.js +478 -0
- package/agent-runtime-server/server/routes/codex.js +329 -0
- package/agent-runtime-server/server/routes/commands.js +596 -0
- package/agent-runtime-server/server/routes/cursor.js +798 -0
- package/agent-runtime-server/server/routes/gemini.js +24 -0
- package/agent-runtime-server/server/routes/git.js +1508 -0
- package/agent-runtime-server/server/routes/mcp-utils.js +48 -0
- package/agent-runtime-server/server/routes/mcp.js +552 -0
- package/agent-runtime-server/server/routes/messages.js +61 -0
- package/agent-runtime-server/server/routes/plugins.js +307 -0
- package/agent-runtime-server/server/routes/projects.js +548 -0
- package/agent-runtime-server/server/routes/settings.js +276 -0
- package/agent-runtime-server/server/routes/taskmaster.js +1963 -0
- package/agent-runtime-server/server/routes/user.js +123 -0
- package/agent-runtime-server/server/services/notification-orchestrator.js +227 -0
- package/agent-runtime-server/server/services/vapid-keys.js +35 -0
- package/agent-runtime-server/server/sessionManager.js +226 -0
- package/agent-runtime-server/server/utils/commandParser.js +303 -0
- package/agent-runtime-server/server/utils/frontmatter.js +18 -0
- package/agent-runtime-server/server/utils/gitConfig.js +34 -0
- package/agent-runtime-server/server/utils/mcp-detector.js +198 -0
- package/agent-runtime-server/server/utils/plugin-loader.js +457 -0
- package/agent-runtime-server/server/utils/plugin-process-manager.js +184 -0
- package/agent-runtime-server/server/utils/taskmaster-websocket.js +129 -0
- package/agent-runtime-server/shared/modelConstants.js +12 -0
- package/agent-runtime-server/shared/modelConstants.test.js +34 -0
- package/agent-runtime-server/shared/networkHosts.js +22 -0
- package/agent-runtime-server/test_sdk.mjs +16 -0
- package/bin/bridges/darwin-x64/nocode-bridge +0 -0
- package/bin/{nocode-local.js → notioncode.js} +2 -8
- package/dist/assets/icon-CQtd7WEB.png +0 -0
- package/dist/assets/index-D_1ZrHDe.js +1 -0
- package/dist/assets/index-DhCWie1Z.css +1 -0
- package/dist/assets/index-DkGqIiwF.js +689 -0
- package/dist/index.html +46 -0
- package/dist/onboarding/step1_create.png +0 -0
- package/dist/onboarding/step2_capabilities.png +0 -0
- package/dist/onboarding/step2b_content_access.png +0 -0
- package/dist/onboarding/step2c_page_access.png +0 -0
- package/dist/onboarding/step3_token.png +0 -0
- package/dist/onboarding/step4_webhook.png +0 -0
- package/dist/onboarding/step6a_verify.png +0 -0
- package/dist/onboarding/step6b_copy_verify_token.png +0 -0
- package/dist/tinyfish-fish-only.png +0 -0
- package/lib/certs.js +332 -0
- package/lib/install.js +48 -4
- package/lib/start.js +346 -29
- package/package.json +10 -4
- package/src/shared/modelRegistry.d.ts +24 -0
- package/src/shared/modelRegistry.js +163 -0
package/lib/start.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
+
import { createReadStream, existsSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import http from 'node:http';
|
|
3
|
+
import https from 'node:https';
|
|
1
4
|
import path from 'node:path';
|
|
2
5
|
import { spawn, spawnSync } from 'node:child_process';
|
|
3
|
-
import { existsSync } from 'node:fs';
|
|
4
6
|
import { fileURLToPath } from 'node:url';
|
|
5
7
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
6
8
|
|
|
9
|
+
import defaultBrowserId from 'default-browser-id';
|
|
10
|
+
|
|
7
11
|
import { commandForPlatform } from './platform.js';
|
|
8
12
|
import { ensureBridgeBinary } from './install.js';
|
|
13
|
+
import { diagnoseTrustedLocalhostCert, ensureTrustedLocalhostCert } from './certs.js';
|
|
9
14
|
|
|
10
15
|
const BRIDGE_HEALTH_URL = 'http://127.0.0.1:3456/healthz';
|
|
11
16
|
const LOCAL_UI_URL = 'http://127.0.0.1:1420/';
|
|
17
|
+
const LOCAL_UI_HTTPS_URL = 'https://localhost:1420/';
|
|
12
18
|
const CLOUD_URL = process.env.NOCODE_CLOUD_URL || 'https://www.notioncode.live';
|
|
13
19
|
|
|
14
20
|
function packageRootDir() {
|
|
@@ -20,10 +26,18 @@ function detectRuntimeRoot() {
|
|
|
20
26
|
return process.env.NOCODE_RUNTIME_ROOT;
|
|
21
27
|
}
|
|
22
28
|
|
|
29
|
+
const packageRoot = packageRootDir();
|
|
30
|
+
if (
|
|
31
|
+
existsSync(path.join(packageRoot, 'agent-runtime-server', 'package.json')) ||
|
|
32
|
+
existsSync(path.join(packageRoot, 'dist', 'index.html'))
|
|
33
|
+
) {
|
|
34
|
+
return packageRoot;
|
|
35
|
+
}
|
|
36
|
+
|
|
23
37
|
const candidates = [
|
|
24
|
-
path.resolve(
|
|
38
|
+
path.resolve(packageRoot, '..', '..'),
|
|
25
39
|
process.cwd(),
|
|
26
|
-
path.resolve(
|
|
40
|
+
path.resolve(packageRoot, '..'),
|
|
27
41
|
];
|
|
28
42
|
|
|
29
43
|
for (const candidate of candidates) {
|
|
@@ -44,10 +58,34 @@ function spawnLogged(cmd, args, options = {}) {
|
|
|
44
58
|
});
|
|
45
59
|
}
|
|
46
60
|
|
|
61
|
+
function requestReady(url, options = {}) {
|
|
62
|
+
const target = new URL(url);
|
|
63
|
+
const client = target.protocol === 'https:' ? https : http;
|
|
64
|
+
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
const request = client.request(
|
|
67
|
+
target,
|
|
68
|
+
{
|
|
69
|
+
method: 'GET',
|
|
70
|
+
...(target.protocol === 'https:' ? { rejectUnauthorized: options.rejectUnauthorized !== false } : {}),
|
|
71
|
+
},
|
|
72
|
+
(response) => {
|
|
73
|
+
response.resume();
|
|
74
|
+
resolve(response.statusCode >= 200 && response.statusCode < 400);
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
request.on('error', reject);
|
|
79
|
+
request.setTimeout(2_000, () => {
|
|
80
|
+
request.destroy(new Error(`Timed out waiting for ${url}`));
|
|
81
|
+
});
|
|
82
|
+
request.end();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
47
86
|
async function isReady(url) {
|
|
48
87
|
try {
|
|
49
|
-
|
|
50
|
-
return response.ok;
|
|
88
|
+
return await requestReady(url);
|
|
51
89
|
} catch {
|
|
52
90
|
return false;
|
|
53
91
|
}
|
|
@@ -58,9 +96,9 @@ async function waitFor(url, timeoutMs, label) {
|
|
|
58
96
|
let lastError = null;
|
|
59
97
|
while (Date.now() < deadline) {
|
|
60
98
|
try {
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
63
|
-
lastError = new Error(`${label} returned
|
|
99
|
+
const ready = await requestReady(url);
|
|
100
|
+
if (ready) return;
|
|
101
|
+
lastError = new Error(`${label} returned a non-ready response`);
|
|
64
102
|
} catch (error) {
|
|
65
103
|
lastError = error;
|
|
66
104
|
}
|
|
@@ -69,6 +107,26 @@ async function waitFor(url, timeoutMs, label) {
|
|
|
69
107
|
throw lastError || new Error(`${label} did not become ready.`);
|
|
70
108
|
}
|
|
71
109
|
|
|
110
|
+
async function verifyTrustedBridge(url) {
|
|
111
|
+
const deadline = Date.now() + 10_000;
|
|
112
|
+
let lastError = null;
|
|
113
|
+
|
|
114
|
+
while (Date.now() < deadline) {
|
|
115
|
+
try {
|
|
116
|
+
const ready = await requestReady(url, { rejectUnauthorized: true });
|
|
117
|
+
if (ready) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
lastError = new Error(`Trusted bridge check returned a non-ready response for ${url}`);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
lastError = error;
|
|
123
|
+
}
|
|
124
|
+
await sleep(300);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
throw lastError || new Error(`Timed out verifying trusted bridge at ${url}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
72
130
|
function openBrowser(url) {
|
|
73
131
|
if (process.env.NOCODE_NO_OPEN === '1') return;
|
|
74
132
|
if (process.platform === 'darwin') {
|
|
@@ -82,11 +140,169 @@ function openBrowser(url) {
|
|
|
82
140
|
spawn(commandForPlatform('xdg-open'), [url], { stdio: 'ignore', detached: true }).unref();
|
|
83
141
|
}
|
|
84
142
|
|
|
143
|
+
function trimOutput(value) {
|
|
144
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function detectDefaultBrowser() {
|
|
148
|
+
if (process.env.NOCODE_DEFAULT_BROWSER?.trim()) {
|
|
149
|
+
return process.env.NOCODE_DEFAULT_BROWSER.trim();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
if (process.platform === 'darwin') {
|
|
154
|
+
const bundleId = defaultBrowserId();
|
|
155
|
+
if (bundleId) {
|
|
156
|
+
return trimOutput(bundleId);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (process.platform === 'win32') {
|
|
161
|
+
const result = spawnSync(
|
|
162
|
+
'reg',
|
|
163
|
+
[
|
|
164
|
+
'query',
|
|
165
|
+
'HKCU\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\https\\UserChoice',
|
|
166
|
+
'/v',
|
|
167
|
+
'ProgId',
|
|
168
|
+
],
|
|
169
|
+
{ encoding: 'utf8' }
|
|
170
|
+
);
|
|
171
|
+
if (result.status === 0) {
|
|
172
|
+
return trimOutput(result.stdout);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const result = spawnSync('xdg-settings', ['get', 'default-web-browser'], {
|
|
177
|
+
encoding: 'utf8',
|
|
178
|
+
});
|
|
179
|
+
if (result.status === 0) {
|
|
180
|
+
return trimOutput(result.stdout);
|
|
181
|
+
}
|
|
182
|
+
} catch {
|
|
183
|
+
// Ignore detection failures and fall back to HTTP.
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return '';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function isSafariDefaultBrowser(defaultBrowser) {
|
|
190
|
+
const normalized = defaultBrowser.toLowerCase();
|
|
191
|
+
return (
|
|
192
|
+
normalized.includes('com.apple.safari') ||
|
|
193
|
+
normalized.includes('safari.app') ||
|
|
194
|
+
normalized.includes('safari technology preview')
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function shouldUseHttpsLocalUi() {
|
|
199
|
+
const explicit = process.env.NOCODE_LOCAL_UI_HTTPS?.trim().toLowerCase();
|
|
200
|
+
if (explicit === '1' || explicit === 'true' || explicit === 'https') {
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
if (explicit === '0' || explicit === 'false' || explicit === 'http') {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return isSafariDefaultBrowser(detectDefaultBrowser());
|
|
208
|
+
}
|
|
209
|
+
|
|
85
210
|
function hasLocalUiRepo() {
|
|
86
211
|
const runtimeRoot = detectRuntimeRoot();
|
|
87
212
|
return existsSync(path.join(runtimeRoot, 'package.json')) && existsSync(path.join(runtimeRoot, 'node_modules', 'vite', 'bin', 'vite.js'));
|
|
88
213
|
}
|
|
89
214
|
|
|
215
|
+
function hasBundledLocalUi(runtimeRoot) {
|
|
216
|
+
return existsSync(path.join(runtimeRoot, 'dist', 'index.html'));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function mimeTypeForPath(filePath) {
|
|
220
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
221
|
+
switch (ext) {
|
|
222
|
+
case '.html':
|
|
223
|
+
return 'text/html; charset=utf-8';
|
|
224
|
+
case '.js':
|
|
225
|
+
return 'text/javascript; charset=utf-8';
|
|
226
|
+
case '.css':
|
|
227
|
+
return 'text/css; charset=utf-8';
|
|
228
|
+
case '.json':
|
|
229
|
+
return 'application/json; charset=utf-8';
|
|
230
|
+
case '.svg':
|
|
231
|
+
return 'image/svg+xml';
|
|
232
|
+
case '.png':
|
|
233
|
+
return 'image/png';
|
|
234
|
+
case '.jpg':
|
|
235
|
+
case '.jpeg':
|
|
236
|
+
return 'image/jpeg';
|
|
237
|
+
case '.webp':
|
|
238
|
+
return 'image/webp';
|
|
239
|
+
case '.woff2':
|
|
240
|
+
return 'font/woff2';
|
|
241
|
+
case '.txt':
|
|
242
|
+
return 'text/plain; charset=utf-8';
|
|
243
|
+
default:
|
|
244
|
+
return 'application/octet-stream';
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function resolveStaticAssetPath(rootDir, requestUrl) {
|
|
249
|
+
const url = new URL(requestUrl, 'http://localhost');
|
|
250
|
+
const normalizedPath = decodeURIComponent(url.pathname);
|
|
251
|
+
const candidate = normalizedPath === '/'
|
|
252
|
+
? path.join(rootDir, 'index.html')
|
|
253
|
+
: path.join(rootDir, normalizedPath.replace(/^\/+/, ''));
|
|
254
|
+
|
|
255
|
+
const normalizedRoot = `${path.resolve(rootDir)}${path.sep}`;
|
|
256
|
+
const resolvedCandidate = path.resolve(candidate);
|
|
257
|
+
if (resolvedCandidate !== path.resolve(rootDir, 'index.html') && !resolvedCandidate.startsWith(normalizedRoot)) {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (existsSync(resolvedCandidate)) {
|
|
262
|
+
const stats = statSync(resolvedCandidate);
|
|
263
|
+
if (stats.isFile()) {
|
|
264
|
+
return resolvedCandidate;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (!path.extname(normalizedPath)) {
|
|
269
|
+
return path.join(rootDir, 'index.html');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function startBundledLocalUiServer({ runtimeRoot, useHttpsLocalUi, localHttps, localUiHost }) {
|
|
276
|
+
const distRoot = path.join(runtimeRoot, 'dist');
|
|
277
|
+
const listener = (req, res) => {
|
|
278
|
+
const targetFile = resolveStaticAssetPath(distRoot, req.url || '/');
|
|
279
|
+
if (!targetFile || !existsSync(targetFile)) {
|
|
280
|
+
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
281
|
+
res.end('Not found');
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
res.writeHead(200, {
|
|
286
|
+
'Content-Type': mimeTypeForPath(targetFile),
|
|
287
|
+
'Cache-Control': targetFile.endsWith('index.html') ? 'no-cache' : 'public, max-age=31536000, immutable',
|
|
288
|
+
});
|
|
289
|
+
createReadStream(targetFile).pipe(res);
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const server = useHttpsLocalUi
|
|
293
|
+
? https.createServer(
|
|
294
|
+
{
|
|
295
|
+
cert: readFileSync(localHttps.certFile),
|
|
296
|
+
key: readFileSync(localHttps.keyFile),
|
|
297
|
+
},
|
|
298
|
+
listener
|
|
299
|
+
)
|
|
300
|
+
: http.createServer(listener);
|
|
301
|
+
|
|
302
|
+
server.listen(1420, localUiHost);
|
|
303
|
+
return server;
|
|
304
|
+
}
|
|
305
|
+
|
|
90
306
|
function hasCargoBridgeSource(runtimeRoot) {
|
|
91
307
|
return existsSync(path.join(runtimeRoot, 'src-tauri', 'Cargo.toml'));
|
|
92
308
|
}
|
|
@@ -98,15 +314,19 @@ function canUseCargo() {
|
|
|
98
314
|
return result.status === 0;
|
|
99
315
|
}
|
|
100
316
|
|
|
101
|
-
function
|
|
102
|
-
const
|
|
103
|
-
if (!existsSync(path.join(
|
|
317
|
+
function ensureSidecarInstallIfPresent(runtimeRoot) {
|
|
318
|
+
const sidecarRoot = path.join(runtimeRoot, 'agent-runtime-server');
|
|
319
|
+
if (!existsSync(path.join(sidecarRoot, 'package.json'))) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (existsSync(path.join(sidecarRoot, 'node_modules', '.package-lock.json'))) {
|
|
104
324
|
return;
|
|
105
325
|
}
|
|
106
326
|
|
|
107
327
|
const npmCmd = commandForPlatform('npm');
|
|
108
|
-
const result = spawnSync(npmCmd, ['
|
|
109
|
-
cwd:
|
|
328
|
+
const result = spawnSync(npmCmd, ['install'], {
|
|
329
|
+
cwd: sidecarRoot,
|
|
110
330
|
stdio: 'inherit',
|
|
111
331
|
});
|
|
112
332
|
if (result.status !== 0) {
|
|
@@ -114,32 +334,77 @@ function ensureSidecarInstallIfRepoPresent() {
|
|
|
114
334
|
}
|
|
115
335
|
}
|
|
116
336
|
|
|
337
|
+
function hasBundledSidecar(runtimeRoot) {
|
|
338
|
+
return existsSync(path.join(runtimeRoot, 'agent-runtime-server', 'server', 'index.js'));
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function ensureSidecarInstallIfRepoPresent() {
|
|
342
|
+
const runtimeRoot = detectRuntimeRoot();
|
|
343
|
+
if (!existsSync(path.join(runtimeRoot, 'agent-runtime-server', 'package.json'))) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
ensureSidecarInstallIfPresent(runtimeRoot);
|
|
347
|
+
}
|
|
348
|
+
|
|
117
349
|
export async function startLocalCompanion(options = {}) {
|
|
118
350
|
const runtimeRoot = detectRuntimeRoot();
|
|
119
351
|
const requestedLocalUi = options.withLocalUi !== false;
|
|
120
|
-
const
|
|
352
|
+
const shouldStartRepoLocalUi = requestedLocalUi && hasLocalUiRepo();
|
|
353
|
+
const shouldStartBundledLocalUi = requestedLocalUi && !shouldStartRepoLocalUi && hasBundledLocalUi(runtimeRoot);
|
|
354
|
+
const shouldStartLocalUi = shouldStartRepoLocalUi || shouldStartBundledLocalUi;
|
|
121
355
|
const useCargoBridge = hasCargoBridgeSource(runtimeRoot) && canUseCargo();
|
|
122
|
-
const
|
|
356
|
+
const useHttpsLocalUi =
|
|
357
|
+
typeof options.localUiHttps === 'boolean' ? options.localUiHttps : shouldUseHttpsLocalUi();
|
|
358
|
+
const localUiUrl = useHttpsLocalUi ? LOCAL_UI_HTTPS_URL : LOCAL_UI_URL;
|
|
359
|
+
const localUiHost = useHttpsLocalUi ? 'localhost' : '127.0.0.1';
|
|
360
|
+
let localHttps = null;
|
|
361
|
+
let bundledLocalUiServer = null;
|
|
123
362
|
|
|
124
|
-
|
|
363
|
+
if (shouldStartLocalUi && useHttpsLocalUi) {
|
|
364
|
+
localHttps = await ensureTrustedLocalhostCert();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (hasBundledSidecar(runtimeRoot)) {
|
|
368
|
+
ensureSidecarInstallIfPresent(runtimeRoot);
|
|
369
|
+
} else {
|
|
370
|
+
ensureSidecarInstallIfRepoPresent();
|
|
371
|
+
}
|
|
125
372
|
|
|
126
373
|
let vite = null;
|
|
127
374
|
if (shouldStartLocalUi) {
|
|
128
|
-
if (await isReady(
|
|
129
|
-
console.log(
|
|
375
|
+
if (await isReady(localUiUrl)) {
|
|
376
|
+
console.log(`[notioncode] Reusing existing local UI on ${localUiUrl} .`);
|
|
130
377
|
} else {
|
|
131
|
-
console.log(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
378
|
+
console.log(`[notioncode] Starting local UI on ${localUiUrl} ...`);
|
|
379
|
+
if (shouldStartRepoLocalUi) {
|
|
380
|
+
vite = spawnLogged(commandForPlatform('node'), ['node_modules/vite/bin/vite.js', '--host', localUiHost], {
|
|
381
|
+
cwd: runtimeRoot,
|
|
382
|
+
env: {
|
|
383
|
+
VITE_SINGLE_USER_MODE: 'true',
|
|
384
|
+
NOCODE_LOCAL_UI_USE_HTTPS: useHttpsLocalUi ? '1' : '0',
|
|
385
|
+
NOCODE_LOCAL_UI_HOST: localUiHost,
|
|
386
|
+
...(localHttps
|
|
387
|
+
? {
|
|
388
|
+
NOCODE_LOCAL_UI_CERT_FILE: localHttps.certFile,
|
|
389
|
+
NOCODE_LOCAL_UI_KEY_FILE: localHttps.keyFile,
|
|
390
|
+
}
|
|
391
|
+
: {}),
|
|
392
|
+
},
|
|
393
|
+
});
|
|
394
|
+
} else if (shouldStartBundledLocalUi) {
|
|
395
|
+
bundledLocalUiServer = startBundledLocalUiServer({
|
|
396
|
+
runtimeRoot,
|
|
397
|
+
useHttpsLocalUi,
|
|
398
|
+
localHttps,
|
|
399
|
+
localUiHost,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
138
402
|
}
|
|
139
403
|
}
|
|
140
404
|
|
|
141
405
|
let bridge = null;
|
|
142
|
-
|
|
406
|
+
const bridgeAlreadyReady = await isReady(BRIDGE_HEALTH_URL);
|
|
407
|
+
if (bridgeAlreadyReady) {
|
|
143
408
|
console.log('[notioncode] Reusing existing bridge on http://127.0.0.1:3456 .');
|
|
144
409
|
} else {
|
|
145
410
|
console.log('[notioncode] Starting local bridge on http://127.0.0.1:3456 ...');
|
|
@@ -150,6 +415,7 @@ export async function startLocalCompanion(options = {}) {
|
|
|
150
415
|
{ cwd: runtimeRoot }
|
|
151
416
|
);
|
|
152
417
|
} else {
|
|
418
|
+
const bridgeExecutable = await ensureBridgeBinary(options.version);
|
|
153
419
|
bridge = spawnLogged(bridgeExecutable, [], {
|
|
154
420
|
cwd: runtimeRoot,
|
|
155
421
|
env: {
|
|
@@ -162,6 +428,7 @@ export async function startLocalCompanion(options = {}) {
|
|
|
162
428
|
const shutdown = (code = 0) => {
|
|
163
429
|
vite?.kill('SIGTERM');
|
|
164
430
|
bridge?.kill('SIGTERM');
|
|
431
|
+
bundledLocalUiServer?.close();
|
|
165
432
|
setTimeout(() => process.exit(code), 250);
|
|
166
433
|
};
|
|
167
434
|
|
|
@@ -180,15 +447,65 @@ export async function startLocalCompanion(options = {}) {
|
|
|
180
447
|
const bridgeTimeoutMs = useCargoBridge ? 180_000 : 15_000;
|
|
181
448
|
await waitFor(BRIDGE_HEALTH_URL, bridgeTimeoutMs, 'Bridge');
|
|
182
449
|
if (shouldStartLocalUi) {
|
|
183
|
-
await waitFor(
|
|
450
|
+
await waitFor(localUiUrl, 15_000, 'Local UI');
|
|
451
|
+
if (useHttpsLocalUi) {
|
|
452
|
+
try {
|
|
453
|
+
await verifyTrustedBridge(localUiUrl);
|
|
454
|
+
} catch (error) {
|
|
455
|
+
throw new Error(
|
|
456
|
+
`Safari-compatible local bridge could not be initialized.\n\nReason:\n ${error instanceof Error ? error.message : String(error)}\n\nFix:\n Install mkcert and trust the local certificate authority, then rerun:\n mkcert -install\n npx notioncode start`
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
184
460
|
}
|
|
185
461
|
|
|
186
462
|
console.log('[notioncode] Ready.');
|
|
187
463
|
console.log(`[notioncode] Cloud UI: ${CLOUD_URL}`);
|
|
188
464
|
if (shouldStartLocalUi) {
|
|
189
|
-
console.log(`[notioncode] Local UI: ${
|
|
465
|
+
console.log(`[notioncode] Local UI: ${localUiUrl}`);
|
|
190
466
|
}
|
|
191
|
-
const entryUrl =
|
|
467
|
+
const entryUrl = CLOUD_URL;
|
|
192
468
|
console.log(`[notioncode] Opening: ${entryUrl}`);
|
|
193
469
|
openBrowser(entryUrl);
|
|
194
470
|
}
|
|
471
|
+
|
|
472
|
+
export async function runDoctor() {
|
|
473
|
+
const certs = diagnoseTrustedLocalhostCert();
|
|
474
|
+
const defaultBrowser = detectDefaultBrowser();
|
|
475
|
+
const useHttpsLocalUi = shouldUseHttpsLocalUi();
|
|
476
|
+
const checks = [
|
|
477
|
+
['node', process.version],
|
|
478
|
+
['platform', `${process.platform}/${process.arch}`],
|
|
479
|
+
['default browser', defaultBrowser || 'unknown'],
|
|
480
|
+
['local UI scheme', useHttpsLocalUi ? 'https' : 'http'],
|
|
481
|
+
['mkcert', certs.mkcertInstalled ? 'installed' : 'missing'],
|
|
482
|
+
['localhost cert', certs.certExists ? certs.certFile : 'missing'],
|
|
483
|
+
['localhost key', certs.keyExists ? certs.keyFile : 'missing'],
|
|
484
|
+
];
|
|
485
|
+
|
|
486
|
+
for (const [label, detail] of checks) {
|
|
487
|
+
console.log(`[OK] ${label}: ${detail}`);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const bridgeReady = await isReady(BRIDGE_HEALTH_URL);
|
|
491
|
+
console.log(`[${bridgeReady ? 'OK' : 'WARN'}] bridge healthz: ${bridgeReady ? BRIDGE_HEALTH_URL : 'not reachable'}`);
|
|
492
|
+
|
|
493
|
+
const selectedLocalUiUrl = useHttpsLocalUi ? LOCAL_UI_HTTPS_URL : LOCAL_UI_URL;
|
|
494
|
+
const localUiReachable = await isReady(selectedLocalUiUrl);
|
|
495
|
+
console.log(
|
|
496
|
+
`[${localUiReachable ? 'OK' : 'WARN'}] selected local UI: ${
|
|
497
|
+
localUiReachable ? selectedLocalUiUrl : 'not reachable'
|
|
498
|
+
}`
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
if (useHttpsLocalUi && localUiReachable) {
|
|
502
|
+
try {
|
|
503
|
+
await verifyTrustedBridge(LOCAL_UI_HTTPS_URL);
|
|
504
|
+
console.log(`[OK] trusted TLS: ${LOCAL_UI_HTTPS_URL}`);
|
|
505
|
+
} catch (error) {
|
|
506
|
+
console.log(
|
|
507
|
+
`[WARN] trusted TLS: ${error instanceof Error ? error.message : String(error)}`
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
package/package.json
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "notioncode",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Local companion runtime used by the `npx notioncode start` setup flow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"default-browser-id": "^5.0.1"
|
|
9
|
+
},
|
|
7
10
|
"bin": {
|
|
8
|
-
"notioncode": "bin/
|
|
11
|
+
"notioncode": "bin/notioncode.js"
|
|
9
12
|
},
|
|
10
13
|
"files": [
|
|
14
|
+
"agent-runtime-server",
|
|
11
15
|
"bin",
|
|
16
|
+
"dist",
|
|
12
17
|
"lib",
|
|
13
|
-
"README.md"
|
|
18
|
+
"README.md",
|
|
19
|
+
"src"
|
|
14
20
|
],
|
|
15
21
|
"publishConfig": {
|
|
16
22
|
"access": "public"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type AgentProvider = 'claude' | 'cursor' | 'codex' | 'gemini';
|
|
2
|
+
|
|
3
|
+
export interface ModelOption {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ModelConfig {
|
|
9
|
+
OPTIONS: ModelOption[];
|
|
10
|
+
DEFAULT: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const CLAUDE_MODELS: ModelConfig;
|
|
14
|
+
export const CURSOR_MODELS: ModelConfig;
|
|
15
|
+
export const CODEX_MODELS: ModelConfig;
|
|
16
|
+
export const GEMINI_MODELS: ModelConfig;
|
|
17
|
+
|
|
18
|
+
export const MODEL_CONFIGS: Record<AgentProvider, ModelConfig>;
|
|
19
|
+
|
|
20
|
+
export function getModelOptions(provider: AgentProvider): ModelOption[];
|
|
21
|
+
export function getDefaultModel(provider: AgentProvider): string;
|
|
22
|
+
export function isSupportedModel(provider: AgentProvider, value: string | null | undefined): boolean;
|
|
23
|
+
export function migrateLegacyModel(provider: AgentProvider, value: string | null | undefined): string | null;
|
|
24
|
+
export function normalizeModel(provider: AgentProvider, value: string | null | undefined): string;
|