@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.
@@ -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
- await send(chatId, 'āœ… Tunnel started:');
936
- await sendHtml(chatId, `<a href="${result.url}">${result.url}</a>`);
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 tmpFile = join(homedir(), '.forge', '.note-tmp.txt');
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 = join(homedir(), '.forge');
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(homedir(), '.forge', 'settings.yaml');
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aion0/forge",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
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,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 = join(homedir(), '.forge');
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 join(CONFIG_DIR, 'data');
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, TEMPLATES_DIR, getDataDir()]) {
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
- const defaults = getDefaultConfig();
36
- writeFileSync(CONFIG_FILE, YAML.stringify(defaults), 'utf-8');
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');
@@ -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
- try { db.exec('ALTER TABLE tasks ADD COLUMN scheduled_at TEXT'); } catch {}
25
- try { db.exec('ALTER TABLE tasks ADD COLUMN mode TEXT NOT NULL DEFAULT \'prompt\''); } catch {}
26
- try { db.exec('ALTER TABLE tasks ADD COLUMN watch_config TEXT'); } catch {}
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
- skill_content TEXT,
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