ugly-app 0.1.413 → 0.1.415

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.
@@ -8,4 +8,6 @@ export declare function writeLocalToken(projectId: string, token: string): void;
8
8
  * and strips them from .uglyapp.
9
9
  */
10
10
  export declare function migrateTokensFromUglyApp(cwd: string): void;
11
+ /** Test hook: re-arm migration on the next call. */
12
+ export declare function __resetMigrationForTest(): void;
11
13
  //# sourceMappingURL=authStore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"authStore.d.ts","sourceRoot":"","sources":["../../src/cli/authStore.ts"],"names":[],"mappings":"AA0CA,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEnE;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIrE;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEpE;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAItE;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAkC1D"}
1
+ {"version":3,"file":"authStore.d.ts","sourceRoot":"","sources":["../../src/cli/authStore.ts"],"names":[],"mappings":"AA+DA,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEnE;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAErE;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEpE;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEtE;AAmDD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAkC1D;AAED,oDAAoD;AACpD,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C"}
@@ -1,6 +1,17 @@
1
1
  /**
2
- * Manages per-project auth tokens stored in ~/.ugly-bot/auth.json and ~/.ugly-bot/local.json.
3
- * These files are user-local and never committed to git.
2
+ * Per-project auth tokens, one file per project.
3
+ *
4
+ * ~/.ugly-bot/auth.json — global user identity only
5
+ * ({ token, userId, serverUrl }, see uglyBotAuth.ts)
6
+ * ~/.ugly-bot/<projectId>.json — cloud project token ({ token })
7
+ * ~/.ugly-bot/<projectId>.local.json — local project token ({ token })
8
+ *
9
+ * One file per project keeps `auth.json` strictly global so the studio
10
+ * sandbox can seed it into the per-project sandbox HOME without leaking
11
+ * other projects' tokens.
12
+ *
13
+ * Legacy layout (single `auth.json` / `local.json` with a `projects`
14
+ * map) is migrated on first read.
4
15
  */
5
16
  import fs from 'node:fs';
6
17
  import path from 'node:path';
@@ -8,47 +19,95 @@ import os from 'node:os';
8
19
  const AUTH_DIR = path.join(os.homedir(), '.ugly-bot');
9
20
  const AUTH_FILE = path.join(AUTH_DIR, 'auth.json');
10
21
  const LOCAL_FILE = path.join(AUTH_DIR, 'local.json');
11
- function readStore(filePath) {
22
+ function projectFile(projectId, mode) {
23
+ const suffix = mode === 'local' ? '.local.json' : '.json';
24
+ return path.join(AUTH_DIR, `${projectId}${suffix}`);
25
+ }
26
+ function readJson(filePath) {
12
27
  try {
13
28
  const raw = fs.readFileSync(filePath, 'utf-8');
14
29
  const parsed = JSON.parse(raw);
15
- if (parsed && typeof parsed === 'object' && parsed.projects) {
30
+ if (parsed && typeof parsed === 'object')
16
31
  return parsed;
17
- }
18
32
  }
19
33
  catch {
20
34
  // File doesn't exist or is malformed
21
35
  }
22
- return { projects: {} };
36
+ return null;
37
+ }
38
+ function readProjectToken(projectId, mode) {
39
+ ensureMigrated();
40
+ const data = readJson(projectFile(projectId, mode));
41
+ if (data && typeof data.token === 'string')
42
+ return data.token;
43
+ return undefined;
23
44
  }
24
- function writeStore(filePath, store) {
45
+ function writeProjectToken(projectId, mode, token) {
46
+ ensureMigrated();
25
47
  fs.mkdirSync(AUTH_DIR, { recursive: true });
26
- // Merge with existing file to preserve global auth fields (token, userId, serverUrl)
27
- let existing = {};
28
- try {
29
- existing = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
30
- }
31
- catch {
32
- // File doesn't exist or is malformed — start fresh
33
- }
34
- existing.projects = store.projects;
35
- fs.writeFileSync(filePath, JSON.stringify(existing, null, 2) + '\n', 'utf-8');
48
+ fs.writeFileSync(projectFile(projectId, mode), JSON.stringify({ token }, null, 2) + '\n', 'utf-8');
36
49
  }
37
50
  export function readAuthToken(projectId) {
38
- return readStore(AUTH_FILE).projects[projectId]?.token;
51
+ return readProjectToken(projectId, 'cloud');
39
52
  }
40
53
  export function writeAuthToken(projectId, token) {
41
- const store = readStore(AUTH_FILE);
42
- store.projects[projectId] = { token };
43
- writeStore(AUTH_FILE, store);
54
+ writeProjectToken(projectId, 'cloud', token);
44
55
  }
45
56
  export function readLocalToken(projectId) {
46
- return readStore(LOCAL_FILE).projects[projectId]?.token;
57
+ return readProjectToken(projectId, 'local');
47
58
  }
48
59
  export function writeLocalToken(projectId, token) {
49
- const store = readStore(LOCAL_FILE);
50
- store.projects[projectId] = { token };
51
- writeStore(LOCAL_FILE, store);
60
+ writeProjectToken(projectId, 'local', token);
61
+ }
62
+ let migrationChecked = false;
63
+ /**
64
+ * Idempotent. Splits legacy `auth.json.projects` / `local.json.projects`
65
+ * into per-project files and strips the `projects` key from `auth.json`
66
+ * (deletes `local.json` outright since it had no global data). Runs at
67
+ * most once per process, gated by a module-local flag — repeated reads
68
+ * don't re-stat every file.
69
+ */
70
+ function ensureMigrated() {
71
+ if (migrationChecked)
72
+ return;
73
+ migrationChecked = true;
74
+ migrateLegacyFile(AUTH_FILE, 'cloud');
75
+ migrateLegacyFile(LOCAL_FILE, 'local');
76
+ }
77
+ function migrateLegacyFile(filePath, mode) {
78
+ const data = readJson(filePath);
79
+ if (!data)
80
+ return;
81
+ const projects = data.projects;
82
+ if (!projects || typeof projects !== 'object')
83
+ return;
84
+ fs.mkdirSync(AUTH_DIR, { recursive: true });
85
+ for (const [projectId, entry] of Object.entries(projects)) {
86
+ if (!entry || typeof entry !== 'object')
87
+ continue;
88
+ const token = entry.token;
89
+ if (typeof token !== 'string')
90
+ continue;
91
+ const target = projectFile(projectId, mode);
92
+ // Don't clobber a per-project file the user may already have written.
93
+ if (fs.existsSync(target))
94
+ continue;
95
+ fs.writeFileSync(target, JSON.stringify({ token }, null, 2) + '\n', 'utf-8');
96
+ }
97
+ if (mode === 'cloud') {
98
+ // auth.json keeps its global fields (token, userId, serverUrl) — just drop `projects`.
99
+ delete data.projects;
100
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
101
+ }
102
+ else {
103
+ // local.json had no global data — remove it entirely.
104
+ try {
105
+ fs.unlinkSync(filePath);
106
+ }
107
+ catch {
108
+ /* already gone */
109
+ }
110
+ }
52
111
  }
53
112
  /**
54
113
  * Migrate tokens from .uglyapp to ~/.ugly-bot/ auth store.
@@ -88,4 +147,8 @@ export function migrateTokensFromUglyApp(cwd) {
88
147
  // .uglyapp doesn't exist or is malformed — skip migration
89
148
  }
90
149
  }
150
+ /** Test hook: re-arm migration on the next call. */
151
+ export function __resetMigrationForTest() {
152
+ migrationChecked = false;
153
+ }
91
154
  //# sourceMappingURL=authStore.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"authStore.js","sourceRoot":"","sources":["../../src/cli/authStore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"authStore.js","sourceRoot":"","sources":["../../src/cli/authStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AACtD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;AACnD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;AAErD,SAAS,WAAW,CAAC,SAAiB,EAAE,IAAuB;IAC7D,MAAM,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC;IAC1D,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,SAAS,GAAG,MAAM,EAAE,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,QAAQ,CAAC,QAAgB;IAChC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QAC1D,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,qCAAqC;IACvC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,gBAAgB,CACvB,SAAiB,EACjB,IAAuB;IAEvB,cAAc,EAAE,CAAC;IACjB,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;IACpD,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC;IAC9D,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,iBAAiB,CACxB,SAAiB,EACjB,IAAuB,EACvB,KAAa;IAEb,cAAc,EAAE,CAAC;IACjB,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,EAAE,CAAC,aAAa,CACd,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,EAC5B,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EACzC,OAAO,CACR,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,OAAO,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,KAAa;IAC7D,iBAAiB,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,OAAO,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,SAAiB,EAAE,KAAa;IAC9D,iBAAiB,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAC/C,CAAC;AAED,IAAI,gBAAgB,GAAG,KAAK,CAAC;AAE7B;;;;;;GAMG;AACH,SAAS,cAAc;IACrB,IAAI,gBAAgB;QAAE,OAAO;IAC7B,gBAAgB,GAAG,IAAI,CAAC;IACxB,iBAAiB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACtC,iBAAiB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAgB,EAAE,IAAuB;IAClE,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChC,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC/B,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO;IAEtD,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAC7C,QAAmC,CACpC,EAAE,CAAC;QACF,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,SAAS;QAClD,MAAM,KAAK,GAAI,KAA6B,CAAC,KAAK,CAAC;QACnD,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,SAAS;QACxC,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAC5C,sEAAsE;QACtE,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,SAAS;QACpC,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IAC/E,CAAC;IAED,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,uFAAuF;QACvF,OAAO,IAAI,CAAC,QAAQ,CAAC;QACrB,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IAC5E,CAAC;SAAM,CAAC;QACN,sDAAsD;QACtD,IAAI,CAAC;YACH,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,kBAAkB;QACpB,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAW;IAClD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC9C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QAC1D,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ;YAAE,OAAO;QAEtE,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,IAAI,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;YAC5C,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACjD,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,cAAc,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;YACxD,CAAC;YACD,OAAO,MAAM,CAAC,YAAY,CAAC;YAC3B,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;QAED,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;YAC1C,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAClD,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,eAAe,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;YACvD,CAAC;YACD,OAAO,MAAM,CAAC,UAAU,CAAC;YACzB,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACZ,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;YAC9E,OAAO,CAAC,GAAG,CAAC,yDAAyD,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,0DAA0D;IAC5D,CAAC;AACH,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,uBAAuB;IACrC,gBAAgB,GAAG,KAAK,CAAC;AAC3B,CAAC"}
@@ -41,7 +41,7 @@ export async function scaffoldProject(projectName) {
41
41
  await fs.writeFile(path.join(migrationsDir, '001_bootstrap.ts'), migrationCode, 'utf-8');
42
42
  console.log('[ugly-app] Generated server/migrations/001_bootstrap.ts');
43
43
  console.log('[ugly-app] Installing dependencies...');
44
- execSync('npm install', { cwd: destDir, stdio: 'inherit' });
44
+ execSync('pnpm install', { cwd: destDir, stdio: 'inherit' });
45
45
  const localPort = DEFAULT_LOCAL_PORT;
46
46
  // Only alphanumeric — safe for R2 buckets, Postgres usernames, Docker tags
47
47
  const generateId = customAlphabet('abcdefghijklmnopqrstuvwxyz0123456789', 10);
@@ -61,8 +61,8 @@ export async function scaffoldProject(projectName) {
61
61
  console.log(`
62
62
  [ugly-app] Done! Next steps:
63
63
  cd ${projectName}
64
- npm run db:migrate # create tables and indexes
65
- npm run dev # start everything
64
+ pnpm run db:migrate # create tables and indexes
65
+ pnpm run dev # start everything
66
66
  `);
67
67
  }
68
68
  //# sourceMappingURL=scaffold.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/cli/scaffold.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/cli/scaffold.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  /**
2
- * Credentials stored in ~/.ugly-bot/auth.json.
3
- * Same format as the Python CLI and VSCode extension.
2
+ * Global user identity stored in ~/.ugly-bot/auth.json. Per-project
3
+ * tokens live in `<projectId>.json` / `<projectId>.local.json` siblings
4
+ * — see authStore.ts. Same format as the Python CLI and VSCode extension.
4
5
  */
5
6
  export interface UglyBotAuth {
6
7
  token: string;
@@ -1 +1 @@
1
- {"version":3,"file":"uglyBotAuth.d.ts","sourceRoot":"","sources":["../../src/cli/uglyBotAuth.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD,wBAAgB,eAAe,IAAI,WAAW,GAAG,IAAI,CAQpD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAavD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAavD"}
1
+ {"version":3,"file":"uglyBotAuth.d.ts","sourceRoot":"","sources":["../../src/cli/uglyBotAuth.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD,wBAAgB,eAAe,IAAI,WAAW,GAAG,IAAI,CAoBpD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAWvD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAavD"}
@@ -8,7 +8,17 @@ export function readUglyBotAuth() {
8
8
  if (!fs.existsSync(authFile))
9
9
  return null;
10
10
  const raw = fs.readFileSync(authFile, 'utf-8');
11
- return JSON.parse(raw);
11
+ const parsed = JSON.parse(raw);
12
+ if (typeof parsed.token !== 'string' ||
13
+ typeof parsed.userId !== 'string' ||
14
+ typeof parsed.serverUrl !== 'string') {
15
+ return null;
16
+ }
17
+ return {
18
+ token: parsed.token,
19
+ userId: parsed.userId,
20
+ serverUrl: parsed.serverUrl,
21
+ };
12
22
  }
13
23
  catch {
14
24
  return null;
@@ -16,18 +26,7 @@ export function readUglyBotAuth() {
16
26
  }
17
27
  export function saveUglyBotAuth(auth) {
18
28
  fs.mkdirSync(authDir, { recursive: true });
19
- // Merge with existing file to preserve project tokens and other keys
20
- let existing = {};
21
- try {
22
- existing = JSON.parse(fs.readFileSync(authFile, 'utf-8'));
23
- }
24
- catch {
25
- // File doesn't exist or is malformed — start fresh
26
- }
27
- existing.token = auth.token;
28
- existing.userId = auth.userId;
29
- existing.serverUrl = auth.serverUrl;
30
- fs.writeFileSync(authFile, JSON.stringify(existing, null, 2) + '\n', 'utf-8');
29
+ fs.writeFileSync(authFile, JSON.stringify({ token: auth.token, userId: auth.userId, serverUrl: auth.serverUrl }, null, 2) + '\n', 'utf-8');
31
30
  }
32
31
  /**
33
32
  * Returns true if the token exists and is not expired.
@@ -1 +1 @@
1
- {"version":3,"file":"uglyBotAuth.js","sourceRoot":"","sources":["../../src/cli/uglyBotAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAYxB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;AAEjD,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1C,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAiB;IAC/C,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,qEAAqE;IACrE,IAAI,QAAQ,GAA4B,EAAE,CAAC;IAC3C,IAAI,CAAC;QACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAA4B,CAAC;IACvF,CAAC;IAAC,MAAM,CAAC;QACP,mDAAmD;IACrD,CAAC;IACD,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IAC5B,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;IACpC,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AAChF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAiB;IAC5C,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CACxB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CACjC,CAAC;QACtB,IAAI,CAAC,OAAO,CAAC,GAAG;YAAE,OAAO,KAAK,CAAC;QAC/B,wCAAwC;QACxC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,GAAG,GAAG,EAAE,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"uglyBotAuth.js","sourceRoot":"","sources":["../../src/cli/uglyBotAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAaxB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;AAEjD,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1C,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyB,CAAC;QACvD,IACE,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;YAChC,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ;YACjC,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,EACpC,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,SAAS,EAAE,MAAM,CAAC,SAAS;SAC5B,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAiB;IAC/C,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,EAAE,CAAC,aAAa,CACd,QAAQ,EACR,IAAI,CAAC,SAAS,CACZ,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,EACrE,IAAI,EACJ,CAAC,CACF,GAAG,IAAI,EACR,OAAO,CACR,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAiB;IAC5C,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CACxB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CACjC,CAAC;QACtB,IAAI,CAAC,OAAO,CAAC,GAAG;YAAE,OAAO,KAAK,CAAC;QAC/B,wCAAwC;QACxC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,GAAG,GAAG,EAAE,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -1,2 +1,2 @@
1
- export declare const CLI_VERSION = "0.1.413";
1
+ export declare const CLI_VERSION = "0.1.415";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by prebuild — do not edit manually
2
- export const CLI_VERSION = "0.1.413";
2
+ export const CLI_VERSION = "0.1.415";
3
3
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugly-app",
3
- "version": "0.1.413",
3
+ "version": "0.1.415",
4
4
  "type": "module",
5
5
  "main": "./dist/server/index.js",
6
6
  "exports": {
@@ -1,6 +1,17 @@
1
1
  /**
2
- * Manages per-project auth tokens stored in ~/.ugly-bot/auth.json and ~/.ugly-bot/local.json.
3
- * These files are user-local and never committed to git.
2
+ * Per-project auth tokens, one file per project.
3
+ *
4
+ * ~/.ugly-bot/auth.json — global user identity only
5
+ * ({ token, userId, serverUrl }, see uglyBotAuth.ts)
6
+ * ~/.ugly-bot/<projectId>.json — cloud project token ({ token })
7
+ * ~/.ugly-bot/<projectId>.local.json — local project token ({ token })
8
+ *
9
+ * One file per project keeps `auth.json` strictly global so the studio
10
+ * sandbox can seed it into the per-project sandbox HOME without leaking
11
+ * other projects' tokens.
12
+ *
13
+ * Legacy layout (single `auth.json` / `local.json` with a `projects`
14
+ * map) is migrated on first read.
4
15
  */
5
16
  import fs from 'node:fs';
6
17
  import path from 'node:path';
@@ -10,54 +21,109 @@ const AUTH_DIR = path.join(os.homedir(), '.ugly-bot');
10
21
  const AUTH_FILE = path.join(AUTH_DIR, 'auth.json');
11
22
  const LOCAL_FILE = path.join(AUTH_DIR, 'local.json');
12
23
 
13
- interface AuthStore {
14
- projects: Record<string, { token: string }>;
24
+ function projectFile(projectId: string, mode: 'cloud' | 'local'): string {
25
+ const suffix = mode === 'local' ? '.local.json' : '.json';
26
+ return path.join(AUTH_DIR, `${projectId}${suffix}`);
15
27
  }
16
28
 
17
- function readStore(filePath: string): AuthStore {
29
+ function readJson(filePath: string): Record<string, unknown> | null {
18
30
  try {
19
31
  const raw = fs.readFileSync(filePath, 'utf-8');
20
- const parsed = JSON.parse(raw) as AuthStore;
21
- if (parsed && typeof parsed === 'object' && parsed.projects) {
22
- return parsed;
23
- }
32
+ const parsed = JSON.parse(raw) as Record<string, unknown>;
33
+ if (parsed && typeof parsed === 'object') return parsed;
24
34
  } catch {
25
35
  // File doesn't exist or is malformed
26
36
  }
27
- return { projects: {} };
37
+ return null;
38
+ }
39
+
40
+ function readProjectToken(
41
+ projectId: string,
42
+ mode: 'cloud' | 'local',
43
+ ): string | undefined {
44
+ ensureMigrated();
45
+ const data = readJson(projectFile(projectId, mode));
46
+ if (data && typeof data.token === 'string') return data.token;
47
+ return undefined;
28
48
  }
29
49
 
30
- function writeStore(filePath: string, store: AuthStore): void {
50
+ function writeProjectToken(
51
+ projectId: string,
52
+ mode: 'cloud' | 'local',
53
+ token: string,
54
+ ): void {
55
+ ensureMigrated();
31
56
  fs.mkdirSync(AUTH_DIR, { recursive: true });
32
- // Merge with existing file to preserve global auth fields (token, userId, serverUrl)
33
- let existing: Record<string, unknown> = {};
34
- try {
35
- existing = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as Record<string, unknown>;
36
- } catch {
37
- // File doesn't exist or is malformed — start fresh
38
- }
39
- existing.projects = store.projects;
40
- fs.writeFileSync(filePath, JSON.stringify(existing, null, 2) + '\n', 'utf-8');
57
+ fs.writeFileSync(
58
+ projectFile(projectId, mode),
59
+ JSON.stringify({ token }, null, 2) + '\n',
60
+ 'utf-8',
61
+ );
41
62
  }
42
63
 
43
64
  export function readAuthToken(projectId: string): string | undefined {
44
- return readStore(AUTH_FILE).projects[projectId]?.token;
65
+ return readProjectToken(projectId, 'cloud');
45
66
  }
46
67
 
47
68
  export function writeAuthToken(projectId: string, token: string): void {
48
- const store = readStore(AUTH_FILE);
49
- store.projects[projectId] = { token };
50
- writeStore(AUTH_FILE, store);
69
+ writeProjectToken(projectId, 'cloud', token);
51
70
  }
52
71
 
53
72
  export function readLocalToken(projectId: string): string | undefined {
54
- return readStore(LOCAL_FILE).projects[projectId]?.token;
73
+ return readProjectToken(projectId, 'local');
55
74
  }
56
75
 
57
76
  export function writeLocalToken(projectId: string, token: string): void {
58
- const store = readStore(LOCAL_FILE);
59
- store.projects[projectId] = { token };
60
- writeStore(LOCAL_FILE, store);
77
+ writeProjectToken(projectId, 'local', token);
78
+ }
79
+
80
+ let migrationChecked = false;
81
+
82
+ /**
83
+ * Idempotent. Splits legacy `auth.json.projects` / `local.json.projects`
84
+ * into per-project files and strips the `projects` key from `auth.json`
85
+ * (deletes `local.json` outright since it had no global data). Runs at
86
+ * most once per process, gated by a module-local flag — repeated reads
87
+ * don't re-stat every file.
88
+ */
89
+ function ensureMigrated(): void {
90
+ if (migrationChecked) return;
91
+ migrationChecked = true;
92
+ migrateLegacyFile(AUTH_FILE, 'cloud');
93
+ migrateLegacyFile(LOCAL_FILE, 'local');
94
+ }
95
+
96
+ function migrateLegacyFile(filePath: string, mode: 'cloud' | 'local'): void {
97
+ const data = readJson(filePath);
98
+ if (!data) return;
99
+ const projects = data.projects;
100
+ if (!projects || typeof projects !== 'object') return;
101
+
102
+ fs.mkdirSync(AUTH_DIR, { recursive: true });
103
+ for (const [projectId, entry] of Object.entries(
104
+ projects as Record<string, unknown>,
105
+ )) {
106
+ if (!entry || typeof entry !== 'object') continue;
107
+ const token = (entry as { token?: unknown }).token;
108
+ if (typeof token !== 'string') continue;
109
+ const target = projectFile(projectId, mode);
110
+ // Don't clobber a per-project file the user may already have written.
111
+ if (fs.existsSync(target)) continue;
112
+ fs.writeFileSync(target, JSON.stringify({ token }, null, 2) + '\n', 'utf-8');
113
+ }
114
+
115
+ if (mode === 'cloud') {
116
+ // auth.json keeps its global fields (token, userId, serverUrl) — just drop `projects`.
117
+ delete data.projects;
118
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
119
+ } else {
120
+ // local.json had no global data — remove it entirely.
121
+ try {
122
+ fs.unlinkSync(filePath);
123
+ } catch {
124
+ /* already gone */
125
+ }
126
+ }
61
127
  }
62
128
 
63
129
  /**
@@ -100,3 +166,8 @@ export function migrateTokensFromUglyApp(cwd: string): void {
100
166
  // .uglyapp doesn't exist or is malformed — skip migration
101
167
  }
102
168
  }
169
+
170
+ /** Test hook: re-arm migration on the next call. */
171
+ export function __resetMigrationForTest(): void {
172
+ migrationChecked = false;
173
+ }
@@ -54,7 +54,7 @@ export async function scaffoldProject(projectName: string): Promise<void> {
54
54
  console.log('[ugly-app] Generated server/migrations/001_bootstrap.ts');
55
55
 
56
56
  console.log('[ugly-app] Installing dependencies...');
57
- execSync('npm install', { cwd: destDir, stdio: 'inherit' });
57
+ execSync('pnpm install', { cwd: destDir, stdio: 'inherit' });
58
58
 
59
59
  const localPort = DEFAULT_LOCAL_PORT;
60
60
  // Only alphanumeric — safe for R2 buckets, Postgres usernames, Docker tags
@@ -80,7 +80,7 @@ export async function scaffoldProject(projectName: string): Promise<void> {
80
80
  console.log(`
81
81
  [ugly-app] Done! Next steps:
82
82
  cd ${projectName}
83
- npm run db:migrate # create tables and indexes
84
- npm run dev # start everything
83
+ pnpm run db:migrate # create tables and indexes
84
+ pnpm run dev # start everything
85
85
  `);
86
86
  }
@@ -3,8 +3,9 @@ import os from 'os';
3
3
  import path from 'path';
4
4
 
5
5
  /**
6
- * Credentials stored in ~/.ugly-bot/auth.json.
7
- * Same format as the Python CLI and VSCode extension.
6
+ * Global user identity stored in ~/.ugly-bot/auth.json. Per-project
7
+ * tokens live in `<projectId>.json` / `<projectId>.local.json` siblings
8
+ * — see authStore.ts. Same format as the Python CLI and VSCode extension.
8
9
  */
9
10
  export interface UglyBotAuth {
10
11
  token: string;
@@ -19,7 +20,19 @@ export function readUglyBotAuth(): UglyBotAuth | null {
19
20
  try {
20
21
  if (!fs.existsSync(authFile)) return null;
21
22
  const raw = fs.readFileSync(authFile, 'utf-8');
22
- return JSON.parse(raw) as UglyBotAuth;
23
+ const parsed = JSON.parse(raw) as Partial<UglyBotAuth>;
24
+ if (
25
+ typeof parsed.token !== 'string' ||
26
+ typeof parsed.userId !== 'string' ||
27
+ typeof parsed.serverUrl !== 'string'
28
+ ) {
29
+ return null;
30
+ }
31
+ return {
32
+ token: parsed.token,
33
+ userId: parsed.userId,
34
+ serverUrl: parsed.serverUrl,
35
+ };
23
36
  } catch {
24
37
  return null;
25
38
  }
@@ -27,17 +40,15 @@ export function readUglyBotAuth(): UglyBotAuth | null {
27
40
 
28
41
  export function saveUglyBotAuth(auth: UglyBotAuth): void {
29
42
  fs.mkdirSync(authDir, { recursive: true });
30
- // Merge with existing file to preserve project tokens and other keys
31
- let existing: Record<string, unknown> = {};
32
- try {
33
- existing = JSON.parse(fs.readFileSync(authFile, 'utf-8')) as Record<string, unknown>;
34
- } catch {
35
- // File doesn't exist or is malformed — start fresh
36
- }
37
- existing.token = auth.token;
38
- existing.userId = auth.userId;
39
- existing.serverUrl = auth.serverUrl;
40
- fs.writeFileSync(authFile, JSON.stringify(existing, null, 2) + '\n', 'utf-8');
43
+ fs.writeFileSync(
44
+ authFile,
45
+ JSON.stringify(
46
+ { token: auth.token, userId: auth.userId, serverUrl: auth.serverUrl },
47
+ null,
48
+ 2,
49
+ ) + '\n',
50
+ 'utf-8',
51
+ );
41
52
  }
42
53
 
43
54
  /**
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by prebuild — do not edit manually
2
- export const CLI_VERSION = "0.1.413";
2
+ export const CLI_VERSION = "0.1.415";