@aion0/forge 0.3.0 ā 0.3.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/CLAUDE.md +9 -2
- package/README.md +9 -9
- package/app/api/claude-templates/route.ts +145 -0
- package/app/api/docs/sessions/route.ts +3 -3
- package/app/api/monitor/route.ts +2 -2
- package/app/api/pipelines/route.ts +2 -2
- package/app/api/preview/[...path]/route.ts +2 -2
- package/app/api/preview/route.ts +3 -4
- package/app/api/projects/route.ts +19 -0
- package/app/api/skills/local/route.ts +228 -0
- package/app/api/skills/route.ts +36 -11
- package/app/api/terminal-state/route.ts +2 -2
- package/app/login/page.tsx +1 -1
- package/bin/forge-server.mjs +49 -33
- package/cli/mw.ts +27 -9
- package/components/Dashboard.tsx +14 -0
- package/components/ProjectManager.tsx +581 -42
- package/components/SessionView.tsx +1 -1
- package/components/SettingsModal.tsx +18 -1
- package/components/SkillsPanel.tsx +515 -29
- package/components/WebTerminal.tsx +50 -5
- package/instrumentation.ts +2 -2
- package/lib/claude-sessions.ts +2 -2
- package/lib/claude-templates.ts +227 -0
- package/lib/cloudflared.ts +4 -3
- package/lib/crypto.ts +3 -4
- package/lib/dirs.ts +34 -0
- package/lib/flows.ts +2 -2
- package/lib/init.ts +2 -2
- package/lib/password.ts +2 -2
- package/lib/pipeline.ts +3 -3
- package/lib/session-watcher.ts +2 -2
- package/lib/settings.ts +4 -2
- package/lib/skills.ts +444 -79
- package/lib/telegram-bot.ts +12 -5
- package/lib/terminal-standalone.ts +3 -2
- package/package.json +1 -1
- package/src/config/index.ts +6 -7
- package/src/core/db/database.ts +17 -5
package/lib/telegram-bot.ts
CHANGED
|
@@ -260,7 +260,7 @@ async function handleMessage(msg: any) {
|
|
|
260
260
|
await handleTunnelStatus(chatId);
|
|
261
261
|
break;
|
|
262
262
|
case '/tunnel_start':
|
|
263
|
-
await handleTunnelStart(chatId, args[0]);
|
|
263
|
+
await handleTunnelStart(chatId, args[0], msg.message_id);
|
|
264
264
|
break;
|
|
265
265
|
case '/tunnel_stop':
|
|
266
266
|
await handleTunnelStop(chatId);
|
|
@@ -892,10 +892,13 @@ async function handleTunnelStatus(chatId: number) {
|
|
|
892
892
|
}
|
|
893
893
|
}
|
|
894
894
|
|
|
895
|
-
async function handleTunnelStart(chatId: number, password?: string) {
|
|
895
|
+
async function handleTunnelStart(chatId: number, password?: string, userMsgId?: number) {
|
|
896
896
|
const settings = loadSettings();
|
|
897
897
|
if (String(chatId) !== settings.telegramChatId) { await send(chatId, 'ā Unauthorized'); return; }
|
|
898
898
|
|
|
899
|
+
// Delete user's message containing password
|
|
900
|
+
if (userMsgId && password) deleteMessageLater(chatId, userMsgId, 0);
|
|
901
|
+
|
|
899
902
|
// Require admin password
|
|
900
903
|
if (!password) {
|
|
901
904
|
await send(chatId, 'š Usage: /tunnel_start <password>');
|
|
@@ -932,8 +935,11 @@ async function handleTunnelStart(chatId: number, password?: string) {
|
|
|
932
935
|
await send(chatId, 'š Starting tunnel...');
|
|
933
936
|
const result = await startTunnel();
|
|
934
937
|
if (result.url) {
|
|
935
|
-
|
|
936
|
-
|
|
938
|
+
const { getSessionCode } = require('./password');
|
|
939
|
+
const code = getSessionCode();
|
|
940
|
+
// Send URL + code, auto-delete after 60 seconds
|
|
941
|
+
const msgUrl = await sendHtml(chatId, `ā
Tunnel started:\n<a href="${result.url}">${result.url}</a>\n\nš Session code: <code>${code || 'N/A'}</code>\n\n<i>This message will be deleted in 60 seconds</i>`);
|
|
942
|
+
if (msgUrl) deleteMessageLater(chatId, msgUrl, 60);
|
|
937
943
|
} else {
|
|
938
944
|
await send(chatId, `ā Failed: ${result.error}`);
|
|
939
945
|
}
|
|
@@ -1240,7 +1246,8 @@ async function sendNoteToDocsClaude(chatId: number, content: string) {
|
|
|
1240
1246
|
}
|
|
1241
1247
|
|
|
1242
1248
|
// Write content to a temp file, then use tmux to send a prompt referencing it
|
|
1243
|
-
const
|
|
1249
|
+
const { getDataDir: _getDataDir } = require('./dirs');
|
|
1250
|
+
const tmpFile = join(_getDataDir(), '.note-tmp.txt');
|
|
1244
1251
|
try {
|
|
1245
1252
|
writeFileSync(tmpFile, content, 'utf-8');
|
|
1246
1253
|
|
|
@@ -28,6 +28,7 @@ import { WebSocketServer, WebSocket } from 'ws';
|
|
|
28
28
|
import * as pty from 'node-pty';
|
|
29
29
|
import { execSync } from 'node:child_process';
|
|
30
30
|
import { homedir } from 'node:os';
|
|
31
|
+
import { getDataDir } from './dirs';
|
|
31
32
|
import { readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
|
|
32
33
|
import { join } from 'node:path';
|
|
33
34
|
|
|
@@ -39,7 +40,7 @@ delete process.env.CLAUDECODE;
|
|
|
39
40
|
|
|
40
41
|
// āāā Shared state persistence āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
41
42
|
|
|
42
|
-
const STATE_DIR =
|
|
43
|
+
const STATE_DIR = getDataDir();
|
|
43
44
|
const STATE_FILE = join(STATE_DIR, 'terminal-state.json');
|
|
44
45
|
|
|
45
46
|
function loadTerminalState(): unknown {
|
|
@@ -118,7 +119,7 @@ const MAX_SESSIONS = 10;
|
|
|
118
119
|
|
|
119
120
|
function getDefaultCwd(): string {
|
|
120
121
|
try {
|
|
121
|
-
const settingsPath = join(
|
|
122
|
+
const settingsPath = join(getDataDir(), 'settings.yaml');
|
|
122
123
|
const raw = readFileSync(settingsPath, 'utf-8');
|
|
123
124
|
const match = raw.match(/projectRoots:\s*\n((?:\s+-\s+.+\n?)*)/);
|
|
124
125
|
if (match) {
|
package/package.json
CHANGED
package/src/config/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';
|
|
2
|
-
import { homedir } from 'node:os';
|
|
3
2
|
import { join } from 'node:path';
|
|
4
3
|
import YAML from 'yaml';
|
|
5
4
|
import type { AppConfig, ProviderName, SessionTemplate } from '@/src/types';
|
|
5
|
+
import { getConfigDir as _getConfigDir, getDataDir as _getDataDir } from '@/lib/dirs';
|
|
6
6
|
|
|
7
|
-
const CONFIG_DIR =
|
|
7
|
+
const CONFIG_DIR = _getConfigDir();
|
|
8
8
|
const CONFIG_FILE = join(CONFIG_DIR, 'config.yaml');
|
|
9
9
|
const TEMPLATES_DIR = join(CONFIG_DIR, 'templates');
|
|
10
10
|
|
|
@@ -13,7 +13,7 @@ export function getConfigDir(): string {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function getDataDir(): string {
|
|
16
|
-
return
|
|
16
|
+
return _getDataDir();
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export function getDbPath(): string {
|
|
@@ -21,7 +21,7 @@ export function getDbPath(): string {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function ensureDirs() {
|
|
24
|
-
for (const dir of [CONFIG_DIR,
|
|
24
|
+
for (const dir of [CONFIG_DIR, getDataDir()]) {
|
|
25
25
|
if (!existsSync(dir)) {
|
|
26
26
|
mkdirSync(dir, { recursive: true });
|
|
27
27
|
}
|
|
@@ -32,9 +32,8 @@ export function loadConfig(): AppConfig {
|
|
|
32
32
|
ensureDirs();
|
|
33
33
|
|
|
34
34
|
if (!existsSync(CONFIG_FILE)) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return defaults;
|
|
35
|
+
// Don't auto-create config.yaml ā return defaults in memory only
|
|
36
|
+
return getDefaultConfig();
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
const raw = readFileSync(CONFIG_FILE, 'utf-8');
|
package/src/core/db/database.ts
CHANGED
|
@@ -20,10 +20,19 @@ export function getDb(dbPath: string): Database.Database {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function initSchema(db: Database.Database) {
|
|
23
|
-
// Migrations for existing tables
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
// Migrations for existing tables (catch duplicate column errors silently)
|
|
24
|
+
const migrate = (sql: string) => {
|
|
25
|
+
try { db.exec(sql); } catch (e: any) {
|
|
26
|
+
if (!String(e.message).includes('duplicate column')) console.error('[db] Migration failed:', sql, e.message);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
migrate('ALTER TABLE tasks ADD COLUMN scheduled_at TEXT');
|
|
30
|
+
migrate("ALTER TABLE tasks ADD COLUMN mode TEXT NOT NULL DEFAULT 'prompt'");
|
|
31
|
+
migrate('ALTER TABLE tasks ADD COLUMN watch_config TEXT');
|
|
32
|
+
migrate("ALTER TABLE skills ADD COLUMN type TEXT NOT NULL DEFAULT 'skill'");
|
|
33
|
+
migrate('ALTER TABLE skills ADD COLUMN archive TEXT');
|
|
34
|
+
migrate("ALTER TABLE skills ADD COLUMN installed_version TEXT NOT NULL DEFAULT ''");
|
|
35
|
+
migrate('ALTER TABLE skills ADD COLUMN rating REAL DEFAULT 0');
|
|
27
36
|
|
|
28
37
|
db.exec(`
|
|
29
38
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
@@ -123,16 +132,19 @@ function initSchema(db: Database.Database) {
|
|
|
123
132
|
-- Skills registry cache
|
|
124
133
|
CREATE TABLE IF NOT EXISTS skills (
|
|
125
134
|
name TEXT PRIMARY KEY,
|
|
135
|
+
type TEXT NOT NULL DEFAULT 'skill',
|
|
126
136
|
display_name TEXT NOT NULL,
|
|
127
137
|
description TEXT,
|
|
128
138
|
author TEXT,
|
|
129
139
|
version TEXT,
|
|
130
140
|
tags TEXT,
|
|
131
141
|
score INTEGER DEFAULT 0,
|
|
142
|
+
rating REAL DEFAULT 0,
|
|
132
143
|
source_url TEXT,
|
|
133
|
-
|
|
144
|
+
archive TEXT,
|
|
134
145
|
installed_global INTEGER NOT NULL DEFAULT 0,
|
|
135
146
|
installed_projects TEXT NOT NULL DEFAULT '[]',
|
|
147
|
+
installed_version TEXT NOT NULL DEFAULT '',
|
|
136
148
|
synced_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
137
149
|
);
|
|
138
150
|
|