@aion0/forge 0.9.14 → 0.9.16

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,15 +1,14 @@
1
- # Forge v0.9.14
1
+ # Forge v0.9.16
2
2
 
3
- Released: 2026-05-27
3
+ Released: 2026-05-28
4
4
 
5
- ## Changes since v0.9.13
5
+ ## Changes since v0.9.15
6
+
7
+ ### Bug Fixes
8
+ - fix: convert all @/ aliases in lib/ and src/ to relative paths
6
9
 
7
10
  ### Other
8
- - fix(pipeline): apply workflow.input defaults for missing fields
9
- - feat(chat): trigger_pipeline schema validation + self-evolving rules
10
- - feat(chat): pipeline input schemas + Forge context tools
11
- - refactor(chat): LLM layer on Vercel AI SDK (provider-agnostic streaming)
12
- - feat(chat): trigger_pipeline + dispatch_task builtin tools
11
+ - fix(chat): replace @/src/* aliases with relative paths in lib/
13
12
 
14
13
 
15
- **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.9.13...v0.9.14
14
+ **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.9.15...v0.9.16
@@ -51,6 +51,25 @@ function expandEntry(entry: ConnectorEntry, settings: Record<string, any> | unde
51
51
  return out;
52
52
  }
53
53
 
54
+ /** Pure UI label derived from tool protocols. The actual dispatch goes by
55
+ * each tool's `protocol` field (see lib/chat/tool-dispatcher.ts switch) —
56
+ * this is just so the ConnectorsTab pill stops calling a connector
57
+ * browser-side when every one of its tools is server-side (e.g. gitlab,
58
+ * whose tools all use protocol: http). The legacy connector-level `mode`
59
+ * field is ignored. */
60
+ function deriveModeLabel(entries: ConnectorEntry[]): 'server-side' | 'browser-side' | 'mixed' {
61
+ const ps = new Set<string>();
62
+ for (const e of entries) {
63
+ for (const t of Object.values(e.tools || {})) {
64
+ ps.add((t as any).protocol || 'browser');
65
+ }
66
+ }
67
+ if (ps.size === 0) return 'browser-side';
68
+ const allServer = [...ps].every((p) => p === 'http' || p === 'shell');
69
+ const allBrowser = [...ps].every((p) => p === 'browser');
70
+ return allServer ? 'server-side' : allBrowser ? 'browser-side' : 'mixed';
71
+ }
72
+
54
73
  function toConnectorPayload(def: ConnectorDefinition, config: Record<string, any> | undefined, installed: boolean) {
55
74
  const entries = getConnectorEntries(def).map(e => expandEntry(e, config));
56
75
 
@@ -69,7 +88,7 @@ function toConnectorPayload(def: ConnectorDefinition, config: Record<string, any
69
88
  version: def.version,
70
89
  author: def.author || 'forge',
71
90
  description: def.description || '',
72
- mode: 'browser-side', // every connector is browser-facing
91
+ mode: deriveModeLabel(entries),
73
92
  installed,
74
93
  ...(hostMatch ? { host_match: hostMatch } : {}),
75
94
  ...(loginRedirect ? { login_redirect: loginRedirect } : {}),
package/install.sh CHANGED
@@ -76,11 +76,25 @@ if [ ${#missing_opts[@]} -gt 0 ]; then
76
76
  fi
77
77
 
78
78
  if [ "$1" = "local" ] || [ "$1" = "--local" ]; then
79
- echo "[forge] Installing from local source..."
80
- npm uninstall -g @aion0/forge 2>/dev/null || true
81
- npm link
82
- echo "[forge] Building..."
79
+ # Build a real npm tarball from the current working tree and install it
80
+ # globally exactly the way `npm install -g @aion0/forge` would after a
81
+ # publish. This runs the `prepack` hook (bundles cli/mw.mjs), respects
82
+ # .npmignore, and uses npm (not pnpm) for the global install — so the
83
+ # behaviour matches what a real user gets from `forge upgrade`.
84
+ echo "[forge] Building from local source (npm pack flow)..."
85
+ SRC_DIR="$(pwd)"
86
+ echo "[forge] Running pnpm build..."
83
87
  pnpm build || echo "[forge] Build completed with warnings (non-critical)"
88
+ echo "[forge] Packing tarball..."
89
+ PACK_DIR="$(mktemp -d -t forge-pack-XXXXXX)"
90
+ TARBALL="$(cd "$PACK_DIR" && npm pack "$SRC_DIR" --silent)"
91
+ TARBALL_PATH="$PACK_DIR/$TARBALL"
92
+ echo "[forge] Built $TARBALL_PATH"
93
+ echo "[forge] Uninstalling previous global install..."
94
+ npm uninstall -g @aion0/forge 2>/dev/null || true
95
+ echo "[forge] Installing tarball globally..."
96
+ (cd /tmp && npm install -g "$TARBALL_PATH")
97
+ rm -rf "$PACK_DIR"
84
98
  else
85
99
  echo "[forge] Installing from npm..."
86
100
  rm -rf "$(npm root -g)/@aion0/forge" 2>/dev/null || true
@@ -1,6 +1,6 @@
1
1
  // Server-side SDK — used by craft authors in their `server.ts` file.
2
2
 
3
- import type { CraftServerDef, CraftRouteHandler } from '@/lib/crafts/types';
3
+ import type { CraftServerDef, CraftRouteHandler } from '../crafts/types';
4
4
 
5
5
  export function defineCraftServer(def: CraftServerDef): CraftServerDef {
6
6
  // Identity wrapper for type checking + future validation hooks.
@@ -11,4 +11,4 @@ export function defineCraftServer(def: CraftServerDef): CraftServerDef {
11
11
  }
12
12
 
13
13
  // Re-export types so authors can `import type { ... } from '@forge/craft/server'`.
14
- export type { CraftServerDef, CraftRouteHandler, ForgeServerApi, CraftRouteHandlerCtx } from '@/lib/crafts/types';
14
+ export type { CraftServerDef, CraftRouteHandler, ForgeServerApi, CraftRouteHandlerCtx } from '../crafts/types';
@@ -6,7 +6,7 @@ import { execSync } from 'node:child_process';
6
6
  import { tmpdir } from 'node:os';
7
7
  import * as esbuild from 'esbuild';
8
8
  import { pathToFileURL } from 'node:url';
9
- import { createTask } from '@/lib/task-manager';
9
+ import { createTask } from '../task-manager';
10
10
  import type { CraftDescriptor, CraftServerDef, CraftRouteHandler, ForgeServerApi } from './types';
11
11
 
12
12
  // Module cache: dir → { mtimeMs, mod }
package/lib/flows.ts CHANGED
@@ -9,7 +9,7 @@ import { join } from 'node:path';
9
9
  import YAML from 'yaml';
10
10
  import { createTask } from './task-manager';
11
11
  import { getProjectInfo } from './projects';
12
- import type { Task } from '@/src/types';
12
+ import type { Task } from '../src/types';
13
13
  import { getDataDir } from './dirs';
14
14
 
15
15
  const FLOWS_DIR = join(getDataDir(), 'flows');
@@ -14,8 +14,8 @@
14
14
  */
15
15
 
16
16
  import { execSync } from 'node:child_process';
17
- import { getDb } from '@/src/core/db/database';
18
- import { getDbPath } from '@/src/config';
17
+ import { getDb } from '../src/core/db/database';
18
+ import { getDbPath } from '../src/config';
19
19
  import { startPipeline } from './pipeline';
20
20
 
21
21
  function db() { return getDb(getDbPath()); }
@@ -10,8 +10,8 @@
10
10
  */
11
11
 
12
12
  import { execSync } from 'node:child_process';
13
- import { getDb } from '@/src/core/db/database';
14
- import { getDbPath } from '@/src/config';
13
+ import { getDb } from '../src/core/db/database';
14
+ import { getDbPath } from '../src/config';
15
15
  import { startPipeline } from './pipeline';
16
16
  import { loadSettings } from './settings';
17
17
  import { homedir } from 'node:os';
@@ -14,13 +14,13 @@ import {
14
14
  import { CronExpressionParser } from 'cron-parser';
15
15
  import { ensureInstalledInProject } from '../skills';
16
16
  import type { Job, JobRunStatus, PipelineDispatchParams, ChatDispatchParams } from './types';
17
- import { dispatchTool } from '@/lib/chat/tool-dispatcher';
17
+ import { dispatchTool } from '../chat/tool-dispatcher';
18
18
  import { dispatchToPipeline, dispatchToChat, dispatchToChatSummary } from './dispatcher';
19
- import { getDb } from '@/src/core/db/database';
20
- import { getDbPath } from '@/src/config';
19
+ import { getDb } from '../../src/core/db/database';
20
+ import { getDbPath } from '../../src/config';
21
21
  import { existsSync, readFileSync } from 'node:fs';
22
22
  import { join as joinPath } from 'node:path';
23
- import { getDataDir } from '@/lib/dirs';
23
+ import { getDataDir } from '../dirs';
24
24
 
25
25
  /** Reconcile stale pipeline_runs rows against the canonical JSON
26
26
  * state on disk. Called before any count, so counts return real
package/lib/jobs/store.ts CHANGED
@@ -3,10 +3,10 @@
3
3
  * created on first use (mirrors lib/issue-scanner.ts style).
4
4
  */
5
5
 
6
- import { getDb } from '@/src/core/db/database';
7
- import { getDbPath } from '@/src/config';
6
+ import { getDb } from '../../src/core/db/database';
7
+ import { getDbPath } from '../../src/config';
8
8
  import { randomUUID } from 'node:crypto';
9
- import { toIsoUTC } from '@/lib/iso-time';
9
+ import { toIsoUTC } from '../iso-time';
10
10
  import { CronExpressionParser } from 'cron-parser';
11
11
  import type {
12
12
  Job, JobRun, JobDispatch, CreateJobInput,
@@ -3,8 +3,8 @@
3
3
  * Stores notifications in SQLite, auto-cleans based on retention setting.
4
4
  */
5
5
 
6
- import { getDb } from '@/src/core/db/database';
7
- import { getDbPath } from '@/src/config';
6
+ import { getDb } from '../src/core/db/database';
7
+ import { getDbPath } from '../src/config';
8
8
  import { loadSettings } from './settings';
9
9
 
10
10
  export interface Notification {
package/lib/notify.ts CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { loadSettings } from './settings';
6
6
  import { addNotification } from './notifications';
7
- import type { Task } from '@/src/types';
7
+ import type { Task } from '../src/types';
8
8
 
9
9
  /** Look up the shared pipelineTaskIds Set via globalThis Symbol.
10
10
  * pipeline.ts populates it on module init; using the Symbol avoids
@@ -9,8 +9,8 @@
9
9
  * - config.scanType: 'github-issues' enables automatic issue scanning + dedup
10
10
  */
11
11
 
12
- import { getDb } from '@/src/core/db/database';
13
- import { getDbPath } from '@/src/config';
12
+ import { getDb } from '../src/core/db/database';
13
+ import { getDbPath } from '../src/config';
14
14
  import { startPipeline, getPipeline } from './pipeline';
15
15
  import { randomUUID } from 'node:crypto';
16
16
  import { execSync } from 'node:child_process';
package/lib/pipeline.ts CHANGED
@@ -14,7 +14,7 @@ import { createTask, getTask, onTaskEvent, taskModelOverrides, taskAppendSystemP
14
14
  import { getProjectInfo } from './projects';
15
15
  import { loadSettings } from './settings';
16
16
  import { getAgent, listAgents } from './agents';
17
- import type { Task } from '@/src/types';
17
+ import type { Task } from '../src/types';
18
18
  import { getDataDir } from './dirs';
19
19
 
20
20
  const PIPELINES_DIR = join(getDataDir(), 'pipelines');
@@ -8,7 +8,7 @@
8
8
  import { existsSync, readFileSync, readdirSync, writeFileSync, unlinkSync, mkdirSync } from 'node:fs';
9
9
  import { join } from 'node:path';
10
10
  import YAML from 'yaml';
11
- import { getDataDir } from '@/lib/dirs';
11
+ import { getDataDir } from '../dirs';
12
12
  import type { Prompt, CreatePromptInput, UpdatePromptInput } from './types';
13
13
 
14
14
  function dir(): string {
@@ -15,11 +15,11 @@
15
15
  * null and set to 'done'/'failed'/'skipped' atomically).
16
16
  */
17
17
 
18
- import { getDb } from '@/src/core/db/database';
19
- import { getDbPath } from '@/src/config';
18
+ import { getDb } from '../../src/core/db/database';
19
+ import { getDbPath } from '../../src/config';
20
20
  import { getScheduleRun, getSchedule, setScheduleRunAction } from './store';
21
- import { appendMessage, getSession } from '@/lib/chat/session-store';
22
- import { loadSettings } from '@/lib/settings';
21
+ import { appendMessage, getSession } from '../chat/session-store';
22
+ import { loadSettings } from '../settings';
23
23
  import type { Schedule, ScheduleRun, ScheduleActionStatus } from './types';
24
24
 
25
25
  function db() { return getDb(getDbPath()); }
@@ -13,18 +13,18 @@
13
13
  */
14
14
 
15
15
  import { CronExpressionParser } from 'cron-parser';
16
- import { startPipeline, getWorkflow } from '@/lib/pipeline';
16
+ import { startPipeline, getWorkflow } from '../pipeline';
17
17
  import {
18
18
  createTask,
19
19
  getTask,
20
20
  onTaskEvent,
21
21
  taskAppendSystemPromptOverrides,
22
- } from '@/lib/task-manager';
23
- import { getProjectInfo, SCRATCH_PROJECT_NAME } from '@/lib/projects';
24
- import { ensureInstalledInProject } from '@/lib/skills';
25
- import { getPrompt } from '@/lib/prompts/store';
26
- import { getDb } from '@/src/core/db/database';
27
- import { getDbPath } from '@/src/config';
22
+ } from '../task-manager';
23
+ import { getProjectInfo, SCRATCH_PROJECT_NAME } from '../projects';
24
+ import { ensureInstalledInProject } from '../skills';
25
+ import { getPrompt } from '../prompts/store';
26
+ import { getDb } from '../../src/core/db/database';
27
+ import { getDbPath } from '../../src/config';
28
28
  import {
29
29
  ensureSchema,
30
30
  listDueSchedules,
@@ -16,10 +16,10 @@
16
16
  import { existsSync, readFileSync } from 'node:fs';
17
17
  import { join as joinPath } from 'node:path';
18
18
  import { randomUUID } from 'node:crypto';
19
- import { getDb } from '@/src/core/db/database';
20
- import { getDbPath } from '@/src/config';
21
- import { getDataDir } from '@/lib/dirs';
22
- import { toIsoUTC } from '@/lib/iso-time';
19
+ import { getDb } from '../../src/core/db/database';
20
+ import { getDbPath } from '../../src/config';
21
+ import { getDataDir } from '../dirs';
22
+ import { toIsoUTC } from '../iso-time';
23
23
  import type {
24
24
  Schedule,
25
25
  ScheduleRun,
@@ -1,9 +1,9 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { getDb } from '@/src/core/db/database';
3
- import { getDbPath, loadTemplate } from '@/src/config';
4
- import { chatStream, type ChatResult } from '@/src/core/providers/chat';
5
- import { getMemoryMessages } from '@/src/core/memory/strategy';
6
- import type { Session, SessionStatus, Message, ProviderName, MemoryConfig } from '@/src/types';
2
+ import { getDb } from '../src/core/db/database';
3
+ import { getDbPath, loadTemplate } from '../src/config';
4
+ import { chatStream, type ChatResult } from '../src/core/providers/chat';
5
+ import { getMemoryMessages } from '../src/core/memory/strategy';
6
+ import type { Session, SessionStatus, Message, ProviderName, MemoryConfig } from '../src/types';
7
7
 
8
8
  // Re-export the SessionManager but as a singleton for server-side use
9
9
  class SessionManager {
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { randomUUID } from 'node:crypto';
9
- import { getDb } from '@/src/core/db/database';
9
+ import { getDb } from '../src/core/db/database';
10
10
  import { join } from 'node:path';
11
11
  import { getDataDir } from './dirs';
12
12
  import {
package/lib/skills.ts CHANGED
@@ -5,8 +5,8 @@
5
5
  import { existsSync, readdirSync, statSync, readFileSync, writeFileSync, mkdirSync, rmSync, cpSync } from 'node:fs';
6
6
  import { join, dirname, basename, relative, sep } from 'node:path';
7
7
  import { homedir } from 'node:os';
8
- import { getDb } from '@/src/core/db/database';
9
- import { getDbPath } from '@/src/config';
8
+ import { getDb } from '../src/core/db/database';
9
+ import { getDbPath } from '../src/config';
10
10
  import { loadSettings } from './settings';
11
11
 
12
12
  type ItemType = 'skill' | 'command';
@@ -7,14 +7,14 @@ import { randomUUID } from 'node:crypto';
7
7
  import { spawn, execSync } from 'node:child_process';
8
8
  import { realpathSync } from 'node:fs';
9
9
  import * as pty from 'node-pty';
10
- import { getDb } from '@/src/core/db/database';
11
- import { getDbPath } from '@/src/config';
10
+ import { getDb } from '../src/core/db/database';
11
+ import { getDbPath } from '../src/config';
12
12
  import { loadSettings } from './settings';
13
13
  import { notifyTaskComplete, notifyTaskFailed } from './notify';
14
14
  import { getInstalledConnector } from './connectors/registry';
15
15
  import { getAgent } from './agents';
16
16
  import { recordUsage } from './usage-scanner';
17
- import type { Task, TaskLogEntry, TaskStatus, TaskMode, WatchConfig } from '@/src/types';
17
+ import type { Task, TaskLogEntry, TaskStatus, TaskMode, WatchConfig } from '../src/types';
18
18
 
19
19
  import { toIsoUTC } from './iso-time';
20
20
 
@@ -14,7 +14,7 @@ import { listClaudeSessions, getSessionFilePath, readSessionEntries } from './cl
14
14
  import { listWatchers, createWatcher, deleteWatcher, toggleWatcher } from './session-watcher';
15
15
  import { startTunnel, stopTunnel, getTunnelStatus } from './cloudflared';
16
16
  // Password verification is done via require() in handler functions
17
- import type { Task, TaskLogEntry } from '@/src/types';
17
+ import type { Task, TaskLogEntry } from '../src/types';
18
18
 
19
19
  // Persist state across hot-reloads
20
20
  const globalKey = Symbol.for('mw-telegram-state');
@@ -5,8 +5,8 @@
5
5
 
6
6
  import { readdirSync, readFileSync, statSync } from 'node:fs';
7
7
  import { join, basename } from 'node:path';
8
- import { getDb } from '@/src/core/db/database';
9
- import { getDbPath } from '@/src/config';
8
+ import { getDb } from '../src/core/db/database';
9
+ import { getDbPath } from '../src/config';
10
10
  import { getClaudeDir } from './dirs';
11
11
 
12
12
  function db() { return getDb(getDbPath()); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aion0/forge",
3
- "version": "0.9.14",
3
+ "version": "0.9.16",
4
4
  "description": "Unified AI workflow platform — multi-model task orchestration, persistent sessions, web terminal, remote access",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -1,8 +1,8 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import YAML from 'yaml';
4
- import type { AppConfig, ProviderName, SessionTemplate } from '@/src/types';
5
- import { getConfigDir as _getConfigDir, getDataDir as _getDataDir } from '@/lib/dirs';
4
+ import type { AppConfig, ProviderName, SessionTemplate } from '../types';
5
+ import { getConfigDir as _getConfigDir, getDataDir as _getDataDir } from '../../lib/dirs';
6
6
 
7
7
  const CONFIG_DIR = _getConfigDir();
8
8
  const CONFIG_FILE = join(CONFIG_DIR, 'config.yaml');
@@ -1,4 +1,4 @@
1
- import type { Message, MemoryConfig } from '@/src/types';
1
+ import type { Message, MemoryConfig } from '../../types';
2
2
 
3
3
  /**
4
4
  * Apply memory strategy to filter/transform messages before sending to AI.
@@ -1,6 +1,6 @@
1
1
  import { streamText, generateText, type ModelMessage } from 'ai';
2
- import { getModel } from '@/src/core/providers/registry';
3
- import type { ProviderName } from '@/src/types';
2
+ import { getModel } from './registry';
3
+ import type { ProviderName } from '../../types';
4
4
 
5
5
  export interface ChatOptions {
6
6
  provider: ProviderName;
@@ -2,8 +2,8 @@ import { createAnthropic } from '@ai-sdk/anthropic';
2
2
  import { createGoogleGenerativeAI } from '@ai-sdk/google';
3
3
  import { createOpenAI } from '@ai-sdk/openai';
4
4
  import type { LanguageModel } from 'ai';
5
- import { loadConfig, getProviderApiKey } from '@/src/config';
6
- import type { ProviderName } from '@/src/types';
5
+ import { loadConfig, getProviderApiKey } from '../../config';
6
+ import type { ProviderName } from '../../types';
7
7
 
8
8
  const providerInstances = new Map<string, LanguageModel>();
9
9
 
@@ -1,10 +1,10 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import type { ModelMessage } from 'ai';
3
- import { getDb } from '@/src/core/db/database';
4
- import { getDbPath, loadTemplate } from '@/src/config';
5
- import { chatStream, type ChatResult } from '@/src/core/providers/chat';
6
- import { getMemoryMessages } from '@/src/core/memory/strategy';
7
- import type { Session, SessionStatus, Message, ProviderName, MemoryConfig } from '@/src/types';
3
+ import { getDb } from '../db/database';
4
+ import { getDbPath, loadTemplate } from '../../config';
5
+ import { chatStream, type ChatResult } from '../providers/chat';
6
+ import { getMemoryMessages } from '../memory/strategy';
7
+ import type { Session, SessionStatus, Message, ProviderName, MemoryConfig } from '../../types';
8
8
 
9
9
  export class SessionManager {
10
10
  private db;