@firefunc-agent/runner 0.7.0 → 0.7.1
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/dist/job.js +43 -5
- package/dist/viewer.js +4 -0
- package/package.json +3 -3
package/dist/job.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdtempSync, rmSync, existsSync, mkdirSync, writeFileSync, copyFileSync } from 'node:fs';
|
|
1
|
+
import { mkdtempSync, rmSync, existsSync, mkdirSync, writeFileSync, copyFileSync, appendFileSync, chmodSync, readdirSync, } from 'node:fs';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
3
|
import { join, basename, dirname } from 'node:path';
|
|
4
4
|
import { exec, execOut, spawnBackground, killProcessTree } from './exec.js';
|
|
@@ -13,9 +13,20 @@ const gitMutex = createKeyedMutex();
|
|
|
13
13
|
function saveSessionLog(runId, stdout, stderr) {
|
|
14
14
|
try {
|
|
15
15
|
const dir = join(dirname(configPath()), 'logs');
|
|
16
|
-
mkdirSync(dir, { recursive: true });
|
|
16
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
17
|
+
try {
|
|
18
|
+
chmodSync(dir, 0o700);
|
|
19
|
+
for (const f of readdirSync(dir)) {
|
|
20
|
+
if (f.endsWith('.log'))
|
|
21
|
+
chmodSync(join(dir, f), 0o600);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
}
|
|
17
26
|
const p = join(dir, `${runId}.log`);
|
|
18
|
-
writeFileSync(p, `=== agent stdout ===\n${stdout}\n\n=== agent stderr ===\n${stderr}\n`)
|
|
27
|
+
writeFileSync(p, redact(`=== agent stdout ===\n${stdout}\n\n=== agent stderr ===\n${stderr}\n`), {
|
|
28
|
+
mode: 0o600,
|
|
29
|
+
});
|
|
19
30
|
return p;
|
|
20
31
|
}
|
|
21
32
|
catch {
|
|
@@ -221,9 +232,20 @@ export async function runJob(job, cfg, api, viewer, opts) {
|
|
|
221
232
|
log(`session dev-server PORT=${opts.devServerPort} (parallel-safe)`);
|
|
222
233
|
}
|
|
223
234
|
if (cfg.envFile) {
|
|
235
|
+
try {
|
|
236
|
+
const gitDir = (await execOut('git', ['-C', worktree, 'rev-parse', '--git-dir'])).trim();
|
|
237
|
+
if (gitDir) {
|
|
238
|
+
const abs = gitDir.startsWith('/') ? gitDir : join(worktree, gitDir);
|
|
239
|
+
mkdirSync(join(abs, 'info'), { recursive: true });
|
|
240
|
+
appendFileSync(join(abs, 'info', 'exclude'), '\n# FireFunc: never commit the injected env file\n.env\n');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
catch (e) {
|
|
244
|
+
log(`could not write git exclude for .env: ${e.message}`);
|
|
245
|
+
}
|
|
224
246
|
try {
|
|
225
247
|
copyFileSync(cfg.envFile, join(worktree, '.env'));
|
|
226
|
-
log(`copied ${cfg.envFile} → worktree/.env`);
|
|
248
|
+
log(`copied ${cfg.envFile} → worktree/.env (excluded from commits)`);
|
|
227
249
|
}
|
|
228
250
|
catch (e) {
|
|
229
251
|
log(`envFile copy failed (${cfg.envFile}): ${e.message}`);
|
|
@@ -337,7 +359,7 @@ export async function runJob(job, cfg, api, viewer, opts) {
|
|
|
337
359
|
: `${engineName} exited abnormally (code ${engineRun.code}). ${engineRun.stderr.slice(0, 300)}`);
|
|
338
360
|
}
|
|
339
361
|
const title = job.title ? `fix: ${job.title}` : `fix: ${job.externalId ?? 'FireFunc auto-fix'}`;
|
|
340
|
-
await exec('git', ['-C', worktree, 'add', '-A']);
|
|
362
|
+
await exec('git', ['-C', worktree, 'add', '-A', '--', '.', ':(exclude).env']);
|
|
341
363
|
const vsBase = await exec('git', [
|
|
342
364
|
'-C',
|
|
343
365
|
worktree,
|
|
@@ -369,6 +391,22 @@ export async function runJob(job, cfg, api, viewer, opts) {
|
|
|
369
391
|
const gitCred = job.repo ? await api.gitCredential(job.runId) : null;
|
|
370
392
|
if (gitCred)
|
|
371
393
|
log(`using a FireFunc-issued git credential for ${gitCred.repo}`);
|
|
394
|
+
const staged = await execOut('git', [
|
|
395
|
+
'-C',
|
|
396
|
+
worktree,
|
|
397
|
+
'show',
|
|
398
|
+
'--name-only',
|
|
399
|
+
'--format=',
|
|
400
|
+
'HEAD',
|
|
401
|
+
]);
|
|
402
|
+
const leaked = staged
|
|
403
|
+
.split('\n')
|
|
404
|
+
.map((l) => l.trim())
|
|
405
|
+
.filter((l) => l === '.env' || l.endsWith('/.env'));
|
|
406
|
+
if (leaked.length > 0) {
|
|
407
|
+
return fail(`refusing to push: the commit contains ${leaked.join(', ')}, which can hold live credentials. ` +
|
|
408
|
+
`This is a FireFunc bug — please report it. Nothing was pushed.`);
|
|
409
|
+
}
|
|
372
410
|
const push = gitCred
|
|
373
411
|
? await pushBranch(worktree, branch, gitCred.repo, gitCred)
|
|
374
412
|
: await exec('git', ['-C', worktree, 'push', '--force-with-lease', 'origin', `${branch}:${branch}`], { timeoutMs: 120_000 });
|
package/dist/viewer.js
CHANGED
|
@@ -11,6 +11,10 @@ export function createSessionViewer(opts) {
|
|
|
11
11
|
const port = opts.port ?? 8787;
|
|
12
12
|
let url = null;
|
|
13
13
|
const server = createServer((req, res) => {
|
|
14
|
+
const host = req.headers.host;
|
|
15
|
+
if (host && host !== `127.0.0.1:${port}` && host !== `localhost:${port}`) {
|
|
16
|
+
return void res.writeHead(403, { 'content-type': 'text/plain' }).end('forbidden');
|
|
17
|
+
}
|
|
14
18
|
const path = new URL(req.url ?? '/', 'http://127.0.0.1').pathname;
|
|
15
19
|
if (path === '/healthz')
|
|
16
20
|
return void res.writeHead(200).end('ok');
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firefunc-agent/runner",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
|
-
"description": "FireFunc self-hosted runner
|
|
6
|
+
"description": "FireFunc self-hosted runner \u2014 an always-on daemon that runs YOUR local coding agent (Claude Code, Codex, Gemini CLI, Cursor CLI) on work FireFunc routes to it, opens pull requests, and reports back. Your machine, your subscriptions.",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"firefunc",
|
|
9
9
|
"runner",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"dev": "tsx src/cli.ts",
|
|
36
36
|
"start": "tsx src/cli.ts start",
|
|
37
37
|
"build": "npm run clean && tsc -p .",
|
|
38
|
-
"prepack": "npm run build && node -e \"const{existsSync}=require('fs');if(!existsSync('dist/cli.js')){console.error('dist/cli.js is missing
|
|
38
|
+
"prepack": "npm run build && node -e \"const{existsSync}=require('fs');if(!existsSync('dist/cli.js')){console.error('dist/cli.js is missing \u2014 refusing to pack a package with no code in it');process.exit(1)}\"",
|
|
39
39
|
"lint": "echo '[runner] lint TBD'",
|
|
40
40
|
"typecheck": "tsc -p . --noEmit",
|
|
41
41
|
"test": "vitest run --passWithNoTests",
|