@aion0/forge 0.10.70 → 0.10.72

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/RELEASE_NOTES.md CHANGED
@@ -1,23 +1,8 @@
1
- # Forge v0.10.70
1
+ # Forge v0.10.72
2
2
 
3
3
  Released: 2026-06-11
4
4
 
5
- ## Changes since v0.10.69
5
+ ## Changes since v0.10.71
6
6
 
7
- ### Other
8
- - revert: remove CLI quick-setup wizard + AUTH_SECRET persist (keep BRIDGE/CHAT port fixes)
9
- - feat(setup): ask for department in enterprise wizard
10
- - fix(auth): persist AUTH_SECRET so sessions survive refresh/restart
11
- - fix(server): per-instance CHAT_PORT (webPort+5) — fixes 2nd-instance settings/sync
12
- - feat(server): minimal first-run quick setup (public/enterprise)
13
- - revert: remove first-run wizard + AUTH_SECRET persist + onboarding tweaks
14
- - fix(setup): skip first-run wizard when instance already configured
15
- - fix(setup): normalize half-typed email against tenant domain
16
- - fix(auth): persist AUTH_SECRET so sessions survive refresh/restart
17
- - fix(onboarding): show banner only, don't auto-open the modal each load
18
- - fix(server): per-instance BRIDGE_PORT (webPort+4) + wizard sets onboardingCompleted
19
- - feat(server): first-run public/enterprise quick-setup wizard
20
- - fix(pipeline): run finalizePipeline on git-preflight abort
21
7
 
22
-
23
- **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.10.69...v0.10.70
8
+ **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.10.71...v0.10.72
@@ -262,6 +262,25 @@ process.env.BRIDGE_PORT = String(bridgePort);
262
262
  process.env.CHAT_PORT = String(chatPort);
263
263
  process.env.FORGE_DATA_DIR = DATA_DIR;
264
264
 
265
+ // Persist AUTH_SECRET so every next-server worker uses the same key.
266
+ // lib/auth.ts generates a random one per process when unset, and Next prod
267
+ // mode spawns multiple workers — without a stable secret, a cookie signed
268
+ // by worker A is rejected by worker B, so the user randomly gets bounced
269
+ // to /login every few requests. Forge-server.mjs is the only common parent
270
+ // of all workers; lib/auth.ts is intentionally untouched.
271
+ if (!process.env.AUTH_SECRET) {
272
+ const SECRET_FILE = join(DATA_DIR, '.auth-secret');
273
+ let secret = '';
274
+ try { if (existsSync(SECRET_FILE)) secret = readFileSync(SECRET_FILE, 'utf-8').trim(); } catch {}
275
+ if (!secret) {
276
+ const { randomBytes } = await import('node:crypto');
277
+ secret = randomBytes(32).toString('hex');
278
+ if (!existsSync(DATA_DIR)) mkdirSync(DATA_DIR, { recursive: true });
279
+ writeFileSync(SECRET_FILE, secret, { mode: 0o600 });
280
+ }
281
+ process.env.AUTH_SECRET = secret;
282
+ }
283
+
265
284
  // ── Password setup (first run or --reset-password) ──
266
285
  // Skipped in --add-enterprise-key persist-and-exit mode: registering a
267
286
  // key from install.sh must not block on an interactive prompt.
package/lib/projects.ts CHANGED
@@ -386,15 +386,25 @@ export function resolveOrCloneProject(name: string | undefined): ResolveResult {
386
386
  // 2. `name` is empty / unknown — pull the gitlab connector's
387
387
  // `default_project_path` (e.g. "fortinet/fortinac-dev") and use
388
388
  // it as the fallback repo.
389
- // Either way we clone into <dataDir>/cloned-projects/ and reuse on
390
- // subsequent runs. The previous behaviour silently dropped to scratch
391
- // and code-fix pipelines exploded with "fatal: not a git repository".
389
+ // Before cloning, scan local projectRoots for a checkout whose git
390
+ // origin already matches saves a redundant clone and also covers the
391
+ // "server just booted, registry/settings cache not warm yet" window
392
+ // where the user's real local FortiNAC was missed by the name-based
393
+ // lookups above.
392
394
  const gl = readGitlabConnector();
393
395
  if (gl) {
394
396
  const targetPath = trimmed && trimmed.includes('/')
395
397
  ? trimmed
396
398
  : (gl.default_project_path || '').trim();
397
399
  if (targetPath) {
400
+ const projects = scanProjects();
401
+ const want = normalizeRepoPath(targetPath) || targetPath.toLowerCase().replace(/^\/+|\/+$/g, '');
402
+ const wantBase = want.split('/').pop()!;
403
+ const byRepo = projects.filter((p) => p.repo && p.repo === want);
404
+ if (byRepo.length === 1) return { project: byRepo[0], source: 'existing' };
405
+ const byRepoBase = projects.filter((p) => p.repo && p.repo.split('/').pop() === wantBase);
406
+ if (byRepoBase.length === 1) return { project: byRepoBase[0], source: 'existing' };
407
+
398
408
  const cloned = tryGitlabClone(targetPath);
399
409
  if (cloned) return { project: cloned, source: 'gitlab-cloned', clone_url: `${gl.base_url}/${targetPath.replace(/^\/+|\/+$/g, '')}.git` };
400
410
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aion0/forge",
3
- "version": "0.10.70",
3
+ "version": "0.10.72",
4
4
  "description": "Unified AI workflow platform — multi-model task orchestration, persistent sessions, web terminal, remote access",
5
5
  "type": "module",
6
6
  "scripts": {