agentic-factory-bridge 1.3.0 → 1.3.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 +2 -2
- package/bin/cli.js +1 -1
- package/bridge.js +186 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# agentic-factory-bridge
|
|
2
2
|
|
|
3
|
-
Local bridge that connects the [Atos Agentic Factory](https://
|
|
3
|
+
Local bridge that connects the [Atos Agentic Factory](https://agentic-factory.onrender.com) marketplace to [OpenCode CLI](https://opencode.ai) on your machine.
|
|
4
4
|
|
|
5
5
|
## What it does
|
|
6
6
|
|
|
@@ -40,7 +40,7 @@ The bridge starts on `http://localhost:3001`. Keep this terminal open while usin
|
|
|
40
40
|
|
|
41
41
|
### 3. Use the marketplace
|
|
42
42
|
|
|
43
|
-
Go to [Atos Agentic Factory](https://
|
|
43
|
+
Go to [Atos Agentic Factory](https://agentic-factory.onrender.com), navigate to any agent profile, and click **"Open with OpenCode"**.
|
|
44
44
|
|
|
45
45
|
## Commands
|
|
46
46
|
|
package/bin/cli.js
CHANGED
|
@@ -126,7 +126,7 @@ async function setup() {
|
|
|
126
126
|
console.log('');
|
|
127
127
|
console.log(' Next steps:');
|
|
128
128
|
console.log(' 1. Run: agentic-factory-bridge start');
|
|
129
|
-
console.log(' 2. Open https://
|
|
129
|
+
console.log(' 2. Open https://agentic-factory.onrender.com');
|
|
130
130
|
console.log(' 3. Click "Refresh detection" on the OpenCode page');
|
|
131
131
|
console.log('');
|
|
132
132
|
}
|
package/bridge.js
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* MARKETPLACE_API_URL — Marketplace backend URL (default: http://localhost:3000/api)
|
|
27
27
|
* OPENCODE_PATH — Path to opencode binary (default: "opencode" — must be in PATH)
|
|
28
28
|
* BRIDGE_SECRET — Shared secret for HMAC authentication (default: auto-generated)
|
|
29
|
-
* BRIDGE_CORS_ORIGIN — Allowed CORS origins, comma-separated (default: http://localhost:4200,https://
|
|
29
|
+
* BRIDGE_CORS_ORIGIN — Allowed CORS origins, comma-separated (default: http://localhost:4200,https://agentic-factory.onrender.com)
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
const express = require('express');
|
|
@@ -49,8 +49,7 @@ const BRIDGE_VERSION = require(path.join(__dirname, 'package.json')).version;
|
|
|
49
49
|
// Security configuration
|
|
50
50
|
const BRIDGE_SECRET = process.env.BRIDGE_SECRET || crypto.randomBytes(32).toString('hex');
|
|
51
51
|
const CORS_ORIGIN =
|
|
52
|
-
process.env.BRIDGE_CORS_ORIGIN ||
|
|
53
|
-
'http://localhost:4200,https://atos-agentic-factory.onrender.com,https://atos-agentic-factory-qzwe.onrender.com';
|
|
52
|
+
process.env.BRIDGE_CORS_ORIGIN || 'http://localhost:4200,https://agentic-factory.onrender.com';
|
|
54
53
|
|
|
55
54
|
// Rate limiting configuration
|
|
56
55
|
const RATE_LIMITS = {
|
|
@@ -58,6 +57,7 @@ const RATE_LIMITS = {
|
|
|
58
57
|
install: { windowMs: 3600000, max: 2 }, // 2 installs per hour
|
|
59
58
|
register: { windowMs: 3600000, max: 3 }, // 3 registrations per hour
|
|
60
59
|
update: { windowMs: 3600000, max: 3 }, // 3 updates per hour
|
|
60
|
+
cliUpdate: { windowMs: 3600000, max: 3 }, // 3 CLI updates per hour
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
// In-memory store limits
|
|
@@ -809,7 +809,7 @@ app.post(
|
|
|
809
809
|
const apiUrl =
|
|
810
810
|
req.body.marketplaceUrl ||
|
|
811
811
|
process.env.MARKETPLACE_API_URL ||
|
|
812
|
-
'https://atos-agentic-factory-
|
|
812
|
+
'https://atos-agentic-factory-backend.onrender.com/api';
|
|
813
813
|
const manifestUrl = `${apiUrl}/agents/${encodeURIComponent(agentId)}/opencode-manifest`;
|
|
814
814
|
|
|
815
815
|
manifest = await new Promise((resolve, reject) => {
|
|
@@ -931,7 +931,7 @@ app.put('/agents/:name/update', requireBridgeAuth, async (req, res) => {
|
|
|
931
931
|
const apiUrl =
|
|
932
932
|
req.body.marketplaceUrl ||
|
|
933
933
|
process.env.MARKETPLACE_API_URL ||
|
|
934
|
-
'https://atos-agentic-factory-
|
|
934
|
+
'https://atos-agentic-factory-backend.onrender.com/api';
|
|
935
935
|
const manifestUrl = `${apiUrl}/agents/${encodeURIComponent(agentId)}/opencode-manifest`;
|
|
936
936
|
|
|
937
937
|
manifest = await new Promise((resolve, reject) => {
|
|
@@ -1220,7 +1220,187 @@ app.post(
|
|
|
1220
1220
|
);
|
|
1221
1221
|
|
|
1222
1222
|
// ---------------------------------------------------------------------------
|
|
1223
|
-
//
|
|
1223
|
+
// OpenCode CLI Update
|
|
1224
|
+
// ---------------------------------------------------------------------------
|
|
1225
|
+
|
|
1226
|
+
/**
|
|
1227
|
+
* Check the latest version of opencode-ai on npm registry.
|
|
1228
|
+
* Compares with the locally installed version.
|
|
1229
|
+
* Returns { current, latest, updateAvailable }.
|
|
1230
|
+
*/
|
|
1231
|
+
function checkOpencodeNpmVersion() {
|
|
1232
|
+
return new Promise((resolve) => {
|
|
1233
|
+
// First get the installed version
|
|
1234
|
+
const installedProc = spawn(OPENCODE_PATH, ['--version'], {
|
|
1235
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1236
|
+
timeout: 10000,
|
|
1237
|
+
shell: true,
|
|
1238
|
+
});
|
|
1239
|
+
let installedStdout = '';
|
|
1240
|
+
installedProc.stdout.on('data', (data) => {
|
|
1241
|
+
installedStdout += data.toString();
|
|
1242
|
+
});
|
|
1243
|
+
installedProc.on('close', (installedCode) => {
|
|
1244
|
+
const currentVersion = installedCode === 0 ? installedStdout.trim() : null;
|
|
1245
|
+
|
|
1246
|
+
// Then get the latest version from npm
|
|
1247
|
+
const npmProc = spawn('npm', ['view', 'opencode-ai', 'version'], {
|
|
1248
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1249
|
+
timeout: 15000,
|
|
1250
|
+
shell: true,
|
|
1251
|
+
});
|
|
1252
|
+
let npmStdout = '';
|
|
1253
|
+
let npmStderr = '';
|
|
1254
|
+
npmProc.stdout.on('data', (data) => {
|
|
1255
|
+
npmStdout += data.toString();
|
|
1256
|
+
});
|
|
1257
|
+
npmProc.stderr.on('data', (data) => {
|
|
1258
|
+
npmStderr += data.toString();
|
|
1259
|
+
});
|
|
1260
|
+
npmProc.on('close', (code) => {
|
|
1261
|
+
if (code === 0 && npmStdout.trim()) {
|
|
1262
|
+
const latest = npmStdout.trim();
|
|
1263
|
+
const updateAvailable = currentVersion != null && latest !== currentVersion;
|
|
1264
|
+
resolve({ current: currentVersion, latest, updateAvailable });
|
|
1265
|
+
} else {
|
|
1266
|
+
resolve({
|
|
1267
|
+
current: currentVersion,
|
|
1268
|
+
latest: null,
|
|
1269
|
+
updateAvailable: false,
|
|
1270
|
+
error: npmStderr.trim() || 'Failed to query npm registry',
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
npmProc.on('error', (err) => {
|
|
1275
|
+
resolve({
|
|
1276
|
+
current: currentVersion,
|
|
1277
|
+
latest: null,
|
|
1278
|
+
updateAvailable: false,
|
|
1279
|
+
error: err.message,
|
|
1280
|
+
});
|
|
1281
|
+
});
|
|
1282
|
+
});
|
|
1283
|
+
installedProc.on('error', (err) => {
|
|
1284
|
+
resolve({
|
|
1285
|
+
current: null,
|
|
1286
|
+
latest: null,
|
|
1287
|
+
updateAvailable: false,
|
|
1288
|
+
error: `OpenCode CLI not found: ${err.message}`,
|
|
1289
|
+
});
|
|
1290
|
+
});
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
/**
|
|
1295
|
+
* GET /check-opencode-update — Check if a newer version of OpenCode CLI is available on npm.
|
|
1296
|
+
*/
|
|
1297
|
+
app.get('/check-opencode-update', async (_req, res) => {
|
|
1298
|
+
try {
|
|
1299
|
+
const result = await checkOpencodeNpmVersion();
|
|
1300
|
+
console.log(
|
|
1301
|
+
`[bridge] OpenCode CLI update check: current=${result.current}, latest=${result.latest}, updateAvailable=${result.updateAvailable}`,
|
|
1302
|
+
);
|
|
1303
|
+
res.json(result);
|
|
1304
|
+
} catch (err) {
|
|
1305
|
+
res.json({
|
|
1306
|
+
current: null,
|
|
1307
|
+
latest: null,
|
|
1308
|
+
updateAvailable: false,
|
|
1309
|
+
error: err.message,
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
/**
|
|
1315
|
+
* POST /update-opencode — Update OpenCode CLI to the latest version via npm install -g opencode-ai@latest.
|
|
1316
|
+
* SECURITY: Rate-limited, auth required, fixed command (no user input in spawn args).
|
|
1317
|
+
*/
|
|
1318
|
+
app.post(
|
|
1319
|
+
'/update-opencode',
|
|
1320
|
+
requireBridgeAuth,
|
|
1321
|
+
rateLimit('cliUpdate', RATE_LIMITS.cliUpdate),
|
|
1322
|
+
async (_req, res) => {
|
|
1323
|
+
console.log('[bridge] Updating OpenCode CLI via npm install -g opencode-ai@latest...');
|
|
1324
|
+
try {
|
|
1325
|
+
// On Windows, kill running opencode.exe processes to release file locks (EBUSY)
|
|
1326
|
+
if (process.platform === 'win32') {
|
|
1327
|
+
try {
|
|
1328
|
+
await new Promise((resolve) => {
|
|
1329
|
+
const kill = spawn('taskkill', ['/IM', 'opencode.exe', '/F'], {
|
|
1330
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1331
|
+
timeout: 10000,
|
|
1332
|
+
shell: true,
|
|
1333
|
+
});
|
|
1334
|
+
kill.on('close', (code) => {
|
|
1335
|
+
if (code === 0) {
|
|
1336
|
+
console.log('[bridge] Killed running opencode.exe processes before update');
|
|
1337
|
+
} else {
|
|
1338
|
+
console.log('[bridge] No opencode.exe processes to kill (or already stopped)');
|
|
1339
|
+
}
|
|
1340
|
+
resolve();
|
|
1341
|
+
});
|
|
1342
|
+
kill.on('error', () => resolve());
|
|
1343
|
+
});
|
|
1344
|
+
// Small delay to ensure file locks are released
|
|
1345
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
1346
|
+
} catch (_) {
|
|
1347
|
+
// Ignore — best effort
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
await new Promise((resolve, reject) => {
|
|
1352
|
+
const proc = spawn('npm', ['install', '-g', 'opencode-ai@latest'], {
|
|
1353
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1354
|
+
timeout: 120000,
|
|
1355
|
+
shell: true,
|
|
1356
|
+
});
|
|
1357
|
+
let stdout = '';
|
|
1358
|
+
let stderr = '';
|
|
1359
|
+
proc.stdout.on('data', (data) => {
|
|
1360
|
+
stdout += data.toString();
|
|
1361
|
+
console.log(`[bridge] npm update opencode stdout: ${data.toString().trim()}`);
|
|
1362
|
+
});
|
|
1363
|
+
proc.stderr.on('data', (data) => {
|
|
1364
|
+
stderr += data.toString();
|
|
1365
|
+
});
|
|
1366
|
+
proc.on('close', (code) => {
|
|
1367
|
+
if (code === 0) resolve({ success: true, stdout, stderr });
|
|
1368
|
+
else
|
|
1369
|
+
reject(
|
|
1370
|
+
new Error(
|
|
1371
|
+
`npm install -g opencode-ai@latest failed with code ${code}: ${stderr || stdout}`,
|
|
1372
|
+
),
|
|
1373
|
+
);
|
|
1374
|
+
});
|
|
1375
|
+
proc.on('error', (err) => {
|
|
1376
|
+
reject(new Error(`Failed to spawn npm: ${err.message}`));
|
|
1377
|
+
});
|
|
1378
|
+
});
|
|
1379
|
+
|
|
1380
|
+
// Check new version after update
|
|
1381
|
+
const versionCheck = await checkOpencodeNpmVersion();
|
|
1382
|
+
|
|
1383
|
+
res.json({
|
|
1384
|
+
success: true,
|
|
1385
|
+
message: 'OpenCode CLI updated successfully.',
|
|
1386
|
+
previousVersion: versionCheck.current, // This is now the new version after install
|
|
1387
|
+
newVersion: versionCheck.latest || versionCheck.current || 'unknown',
|
|
1388
|
+
version: versionCheck.current,
|
|
1389
|
+
path: OPENCODE_PATH,
|
|
1390
|
+
});
|
|
1391
|
+
} catch (err) {
|
|
1392
|
+
console.error(`[bridge] OpenCode CLI update error: ${err.message}`);
|
|
1393
|
+
res.status(500).json({
|
|
1394
|
+
success: false,
|
|
1395
|
+
message: `Update failed: ${err.message}`,
|
|
1396
|
+
version: null,
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
},
|
|
1400
|
+
);
|
|
1401
|
+
|
|
1402
|
+
// ---------------------------------------------------------------------------
|
|
1403
|
+
// Bridge Auto-Update
|
|
1224
1404
|
// ---------------------------------------------------------------------------
|
|
1225
1405
|
|
|
1226
1406
|
/**
|